Name | Type | is_array | initial_value |
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*******************************************************************
* _ ___ ___
* /_\ / __|/ __|
* / _ \\__ \\__ \
* /_/ \_|___//___/
* v5.1.1.0
* By Magtheridon96
*
* - This is an advanced player streak system.
* - Features:
* - Kill Streaks
* - Multikills
* - Assists
* - Combo-breaker
* - Streak/Multikill/Combo-breaker sounds
* - Texttags for Gold Gaining/Losing after each kill
* - Storage for total kills, deaths, assists, suicides,
* denies, streaks, multikills, streaks of any type,
* multikills of any type and combobreakers.
* - Events for everything except assists
* - Useful Event-responses
* - It is:
* - Dynamic
* - Easy to Use
* - Easy to Configure
* - Very Configurable
*
* - It also supports a lot of systems.
* AutoIndex was not supported because the only damage detection
* systems that were written based on it were far too troublesome.
*
* Requirements:
* -------------
*
* - A Damage Detection System.
* Supports:
* - DamageEvent By Nestharus (hiveworkshop.com/forums/jass-resources-412/snippet-damageevent-186829/)
* - Damage By Jesus4Lyf (thehelper.net/forums/showthread.php/131287-Damage)
* - Damage By Dirac (thehelper.net/forums/showthread.php/168144-Damage-Struct)
*
* - A Unit Indexer.
* Supports:
* - UnitIndexer By Nestharus (hiveworkshop.com/forums/jass-resources-412/system-unit-indexer-172090/)
* - AIDS By Jesus4Lyf (thehelper.net/forums/showthread.php/130752-Advanced-Indexing-Data-Storage)
*
* - A Sound System.
* Supports:
* - SoundTools By Magtheridon96 (hiveworkshop.com/forums/jass-resources-412/system-soundtools-207308/)
* - SoundUtils By Rising_Dusk (wc3c.net/showthread.php?t=107433)
*
* - An Event Handler.
* Supports:
* - Event By Nestharus (hiveworkshop.com/forums/jass-resources-412/snippet-event-186555/)
* - Event By Jesus4Lyf (thehelper.net/forums/showthread.php/126846-Event)
*
* - Table By Bribe
* - hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
*
* - TimerUtils By Vexorian
* - wc3c.net/showthread.php?t=101322
*
* Optional:
* ---------
*
* - RegisterPlayerUnitEvent By Magtheridon96
* - hiveworkshop.com/forums/jass-resources-412/snippet-registerplayerunitevent-203338/
*
* Configurable Functions:
* -----------------------
*
* - function GetBounty takes integer killerPlayerId, integer dyingPlayerId, unit killingUnit, unit dyingUnit returns integer
* - function GetLostGold takes integer killerPlayerId, integer dyingPlayerId, unit killingUnit, unit dyingUnit, integer killingBounty returns integer
* - function GetAssistGold takes integer killerPlayerId, integer dyingPlayerId, unit killingUnit, unit dyingUnit, integer killingBounty, integer numberAssisters returns integer
*
* - function KillingPlayerFilter takes integer playerId returns boolean
* - function AssistPlayerFilter takes integer playerId returns boolean
* - function DyingPlayerFilter takes integer playerId returns boolean
*
* - function GetFirstbloodString takes string killingName returns string
* - function GetKillString takes string killingName, string dyingName, string bounty returns string
* - function GetEndString takes string killingName, string dyingName, string streak, string bounty returns string
*
* - function GetSuicideString takes integer killingPlayerId, string killingName returns string
* - function GetDenialString takes integer killingPlayerId, string killingName, integer dyingPlayerId, string dyingName returns string
*
* How To Import:
* --------------
*
* 1- The first thing you need to do is import the required systems and set them up.
* This system supports DamageEvent by Nestharus, Damage by Jesus4Lyf and Damage
* by Dirac. In the Demo map, DamageEvent and all of the systems it uses including
* UnitIndexer are enabled. To import all the needed scripts, just copy and paste
* whatever is not disabled.
*
* There are some systems that require more than just simple copying and pasting
* though. UnitIndexer requires you to import the Undefend-based ability and configure
* the ability Id in the globals of the code.
*
* 2- Now that all your systems are implemeted, you should configure this system.
* After this documentation, there is a globals block. You need to configure it
* based on the systems you implemented/are using.
*
* 3- Now, you need to configure the rest of the globals and functions to your liking.
*
* 4- Read the 'Setup' section of this documentation to understand how to configure
* and create streaks and multikills. You will need to define the streaks and
* multikills you want, else you will have fatal errors.
*
* 5- Test this system. If certain errors occur during initialization, the system
* will notify you and exit Warcraft III.
*
* Setup:
* ------
*
* - You will face fatal errors if you do not setup this system.
* - It is very important that you read these.
*
* Streaks:
* --------
*
* - The first thing you need to do is set the minimum streak index:
*
* call StreakSystem.setMinimumStreak(3)
*
* - This means that 3 kills will give you the first streak.
* - Now, we need to start creating the streaks.
* - We will call the function newStreak like this:
*
* call StreakSystem.newStreak("haxxor spree", "is on a haxxor spree", "war3mapImported\\HaxxingSpree.mp3", 2489)
*
* - The first parameter is the name of this spree. This string will be
* used for the text that is displayed when a player ends a streak.
* - The second parameter is the string snippet that is displayed when
* a player achieves a certain streak. It is concatenated with the name
* of the player of course.
* - The third parameter is the filepath of the sound that is played
* when a player achieves a certain streak.
* - The fourth parameter is the duration of the sound in milliseconds.
*
* Multikills:
* -----------
*
* - With this setup, there is no catch. You would start calling newMultikill
* immediately.
*
* call StreakSystem.newMultikill("got a Triple Kill!", "war3mapImported\\tripleKIll.wav", 1991)
*
* - The first parameter is the string snippet that is displayed when
* a player achieves a certain multikill. It is concatenated with the
* name of the player of course.
* - The second parameter is the filepath of the sound that is played
* when a player achieves a certain streak.
* - The third parameter is the duration of the sound in milliseconds.
*
* Firstblood:
* -----------
*
* - The only thing that can be configured externally is the firstblood sound.
* - The firstblood string can be configured using the configurable function
* mentioned above: GetFirstbloodString
*
* call StreakSystem.setFirstBloodSound("war3mapImported\\firstblood.wav", 1230)
*
* - The first parameter is the filepath of the sound that is played
* when a player achieves firstblood.
* - The second parameter is the duration of the sound in milliseconds.
*
* Combobreaker:
* -------------
*
* - To configure the combo-breaker sound, we use the following function:
*
* call StreakSystem.setComboBreakerSound("war3mapImported\\combobreaker.wav", 2102)
*
* - The first parameter is the filepath of the sound that is played
* when a player breakes a combo.
* - The second parameter is the duration of the sound in milliseconds.
*
* Event Responses:
* ----------------
*
* - StreakSystem.getKillingUnitId()
* - StreakSystem.getKillingUnit()
* - StreakSystem.getDyingUnitId()
* - StreakSystem.getDyingUnit()
* - StreakSystem.getKillingPlayerId()
* - StreakSystem.getKillingPlayer()
* - StreakSystem.getDyingPlayerId()
* - StreakSystem.getDyingPlayer()
* - For: FIRSTBLOOD, STREAK, END_STREAK, MULTIKILL, COMBO_BREAKER, SUICIDE, DENIAL
* - These can be used inside END_MULTIKILL if DEATH_END_MULTI is set to true. They will only
* return valid data if a unit dies and loses a multikill.
* - Warning: Calling StreakSystem.getKillingPlayer() or StreakSystem.getDyingPlayer() inside
* a function registered to END_MULTIKILL will crash the game if the multikill simply faded.
* Use the Id counterparts instead.
*
* - StreakSystem.getEndedMultikill()
* - StreakSystem.getMultikillLoserId()
* - StreakSystem.getMultikillLoser()
* - For: END_MULTIKILL
*
* - StreakSystem.getAchievedStreak()
* - For: STREAK
* - StreakSystem.getEndedStreak()
* - For: END_STREAK
* - StreakSystem.getAchievedMultikill()
* - For: MULTIKILL
* - StreakSystem.getComboBroken()
* - For: COMBO_BREAKER
*
* API:
* ----
*
* - struct StreakSystem extends array
*
* - static boolean enabled
* - Tells whether the system is enabled or not.
* - static boolean firstblood
* - Tells whether firstblood was taken or not.
*
* - static Event FIRSTBLOOD
* - This event fires when a hero takes firstblood
* - static Event STREAK
* - This event fires when a hero is on a streak
* - static Event MULTIKILL
* - This event fires when a hero gets a multikill
* - static Event END_STREAK
* - This event fires when a streak is ended
* - static Event END_MULTIKILL
* - This event fires when a multikill is no longer valid
* - static Event COMBO_BREAKER
* - This event fires when a hero gets a combo-breaker
* - static Event SUICIDE
* - This event fires when a hero kills himself
* - static Event DENIAL
* - This event fires when a hero kills his own ally
*
* - static method newStreak takes string streakName, string streakDisplay, string soundPath, integer duration returns nothing
* - static method newMultikill takes string text, string soundPath, integer duration returns nothing
* - These two functions create a streak-type and a multikill-type. The duration is in milliseconds.
* - Since it is not very clear what the arguments should be, here is an example:
* - call StreakSystem.newStreak("killing spree", "is on a killing spree!", "war3mapImported\\KillingSpree.mp3", 2489)
* - call StreakSystem.newMultikill("is on a Rampage!!!", "war3mapImported\\Rampage.wav", 1991)
* - As you can see, the function newStreak takes the name of the streak, then the form of the
* streak message, then the path of the streak sound and finally, the duration of the streak
* sound in milliseconds. The newMultikill function takes the form of the multikill message,
* then the path of the multikill sound and finally, the duration of that sound in milliseconds.
*
* - static method setFirstBloodSound takes string path, integer duration returns nothing
* - static method setComboBreakerSound takes string path, integer duration returns nothing
* - These two functions are used to configure the sounds that are played when a player gets
* firstblood/a combo-breaker.
*
* - static method setMinimumStreak takes integer i returns nothing
* - This sets the minimum streak value.
*
* - static method setColor takes integer playerId, string color returns nothing
* - This function reconfigures the player color used in the system.
* Player colors are already configured by default, but you may
* need this just in case player colors change in-game.
*
* - static method getKillingUnitId takes nothing returns UnitIndex
* - static method getKillingUnit takes nothing returns unit
* - static method getDyingUnitId takes nothing returns UnitIndex
* - static method getDyingUnit takes nothing returns unit
* - static method getKillingPlayerId takes nothing returns integer
* - static method getKillingPlayer takes nothing returns player
* - static method getDyingPlayerId takes nothing returns integer
* - static method getDyingPlayer takes nothing returns player
* - These functions retrieve the killing players and units and the
* dying units and players for event responses.
*
* - static method getAchievedStreak takes nothing returns integer
* - static method getEndedStreak takes nothing returns integer
* - static method getAchievedMultikill takes nothing returns integer
* - static method getEndedMultikill takes nothing returns integer
* - static method getMultikillLoserId takes nothing returns integer
* - static method getMultikillLoser takes nothing returns player
* - static method getComboBroken takes nothing returns integer
* - These functions retrieve the streak and multikill data for
* for event responses.
*
* - static method getKills takes player whichPlayer returns integer
* - static method getDeaths takes player whichPlayer returns integer
* - static method getDenies takes player whichPlayer returns integer
* - static method getSuicides takes player whichPlayer returns integer
* - static method getAssists takes player whichPlayer returns integer
* - static method getStreaks takes player whichPlayer returns integer
* - static method getMultikills takes player whichPlayer returns integer
* - static method getCombobreakers takes player whichPlayer returns integer
* - static method getMultikillsOfType takes player whichPlayer returns integer
* - static method getStreaksOfType takes player whichPlayer returns integer
* - These functions are used to get the total stats of a player.
*
* - static method getKillsById takes integer playerId returns integer
* - static method getDeathsById takes integer playerId returns integer
* - static method getDeniesById takes integer playerId returns integer
* - static method getSuicidesById takes integer playerId returns integer
* - static method getAssistsById takes integer playerId returns integer
* - static method getStreaksById takes integer playerId returns integer
* - static method getMultikillsById takes integer playerId returns integer
* - static method getCombobreakersById takes integer playerId returns integer
* - static method getMultikillsOfTypeById takes integer playerId returns integer
* - static method getStreaksOfTypeById takes integer playerId returns integer
* - These functions are used to get the total stats of a player given his Id.
*
* - static method getCurrentStreak takes player p returns integer
* - static method getCurrentMultikill takes player p returns integer
* - These functions are used to get the current streak of a player
* or his current multikills.
*
* - static method getCurrentStreakById takes integer playerId returns integer
* - static method getCurrentMultikillById takes integer playerId returns integer
* - These functions are used to get the current streak of a player
* or his current multikills given his Id.
*
* - static method resetStreak takes integer playerId returns nothing
* - static method resetStreaks takes nothing returns nothing
* - These functions are used to either reset the streak of one or all players.
*
* - static method resetAssist takes integer unitId returns nothing
* - static method resetAssists takes nothing returns nothing
* - These functions are used to either reset the streak associated with one or all units.
*
* - static method operator goldGain takes nothing returns boolean
* - static method operator goldGain= takes boolean b returns nothing
* - static method operator goldLoss takes nothing returns boolean
* - static method operator goldLoss= takes boolean b returns nothing
* - Management of KillingPlayer-Goldgain and DyingPlayer-Goldloss.
*
* - static method operator bountyBase takes nothing returns integer
* - static method operator bountyBase= takes integer i returns nothing
* - static method operator bountyIncrement takes nothing returns integer
* - static method operator bountyIncrement= takes integer i returns nothing
* - Management of Bounty Formula.
*
*******************************************************************/
library ASS requires optional DamageEvent, optional Damage, optional UnitIndexer, optional AIDS, optional SoundTools, optional SoundUtils, Table, optional RegisterPlayerUnitEvent, TimerUtils, Event
/*
* Configuration
*/
globals
/*
* Whether you have Event by Nestharus or Jesus4Lyf is unknown to me.
* Hence, I put up this boolean for configuration. If you are using
* Damage and thus AIDS and Event by Jesus4Lyf, then set this to false.
*/
private constant boolean EVENT_BY_NESTHARUS = true
/*
* Whether you have Damage by Jesus4Lyf or Dirac is unknown to me.
* Hence, I put up this boolean for configuration.
*/
private constant boolean DAMAGE_BY_JESUS4LYF = false
endglobals
/*
* System Configuration
*/
globals
// Max interval between Multikills (seconds)
private constant real MULTI_TIME = 10
// Multikill Update Interval (seconds)
private constant real MULTI_UPDATE = 1
// Are sounds 3D?
private constant boolean S3D = false
// Firstblood bonus
private constant integer FIRST_GOLD = 200
// Duration of the text messages
private constant real TEXT_DURATION = 8.00
// Interval between streak and multikill sounds
private constant real INTERVAL = 0.50
// Assist Decay time (seconds)
private constant real ASSIST_DECAY = 45
// Assist Update Interval (seconds)
private constant real ASSIST_UPDATE = 1
// Can the heroes of leavers get streaks?
private constant boolean LEAVER_HEROES = true
// Leavers can assist?
private constant boolean LEAVER_CAN_ASSIST = true
// Initial Gold base
private integer baseGold = 210
// Gold increment according to formulas
private integer goldIncrement = 10
// Do you lose gold when you die?
private boolean loseGold = true
// Do you gain gold when you kill?
private boolean gainGold = true
// Do you want floating texts?
private constant boolean TEXTTAGS = true
// Texttag configuration: Age
private constant real TEXTTAG_AGE = 2.
// Texttag configuration: Fade-point
private constant real TEXTTAG_FADE = 1.5
// Texttag configuration: Size
private constant real TEXTTAG_SIZE = 0.024
// Texttag configuration: Velocity X
private constant real TEXTTAG_X = 0.
// Texttag configuration: Velocity Y
private constant real TEXTTAG_Y = 0.0355
// Give gold to assisters?
private constant boolean ASSIST_GOLD = true
// Does Suicide end a streak?
private constant boolean SUICIDE_ENDS = true
// When you deny a hero, do you get a kill?
private constant boolean DENIES_COUNT_KILLS = false
// When you get denied, do you get a death?
private constant boolean DENIES_COUNT_DEATHS = false
// Does suicide count as a death?
private constant boolean SUICIDE_DEATH = true
// Does a Death end a multikill?
private constant boolean DEATH_END_MULTI = false
// Do you want to have the Combo-breaker message and sound feature?
private constant boolean COMBO_BREAKER = true
// Minimum Streak to end to get a Combo-breaker
private constant integer COMBO_BREAKER_MIN = 3
// Should the suicide event fire before the end streak event? Or after?
// This is only important if SUICIDE_ENDS is set to true.
private constant boolean SUICIDE_FIRE_FIRST = true
endglobals
private keyword Streak
private keyword Multikill
private function GetBounty takes integer killingId, integer dyingId, unit killingUnit, unit dyingUnit returns integer
return baseGold + goldIncrement * Streak[dyingId] + goldIncrement * Streak[killingId]
endfunction
private function GetLostGold takes integer killingId, integer dyingId, unit killingUnit, unit dyingUnit, integer gold returns integer
return gold / 3
endfunction
private function GetAssistGold takes integer killingId, integer dyingId, unit killingUnit, unit dyingUnit, integer gold, integer numberAssisters returns integer
return gold / numberAssisters
endfunction
private function KillingPlayerFilter takes integer playerId returns boolean
return playerId < 12
endfunction
private function AssistPlayerFilter takes integer playerId returns boolean
return playerId < 12
endfunction
private function DyingPlayerFilter takes integer playerId returns boolean
return playerId < 12
endfunction
private function StreakPlayerFilter takes integer playerId returns boolean
return playerId < 12
endfunction
private function MultikillPlayerFilter takes integer playerId returns boolean
return playerId < 12
endfunction
private function CombobreakerPlayerFilter takes integer playerId returns boolean
return playerId < 12
endfunction
private function GetFirstbloodString takes string killingName returns string
return killingName + " just drew |cffff0000firstblood|r!"
endfunction
private function GetKillString takes string killingName, string dyingName, string bounty returns string
return killingName + " pwned " + dyingName + "'s head for |cffffcc00" + bounty + "|r gold."
endfunction
private function GetEndString takes string killingName, string dyingName, string streak, string bounty returns string
return dyingName + streak + " streak has been ended by " + killingName + " for |cffffcc00" + bounty + "|r gold."
endfunction
private function GetSuicideString takes string dyingName returns string
return dyingName + "|r has killed himself!"
endfunction
private function GetDenialString takes string killingName, string dyingName returns string
return killingName + "|r has denied his ally " + dyingName + "|r!"
endfunction
private module Init
private static method onInit takes nothing returns nothing
/*
* Player color strings configuration.
*/
set thistype.colors[0] = "|cffff0303"
set thistype.colors[1] = "|cff0042ff"
set thistype.colors[2] = "|cff1ce6b9"
set thistype.colors[3] = "|cff540081"
set thistype.colors[4] = "|cfffffc01"
set thistype.colors[5] = "|cfffeba0e"
set thistype.colors[6] = "|cff20c000"
set thistype.colors[7] = "|cffe55bb0"
set thistype.colors[8] = "|cff959697"
set thistype.colors[9] = "|cff7ebff1"
set thistype.colors[10] = "|cff106246"
set thistype.colors[11] = "|cff4e2a04"
endmethod
endmodule
/*
* Configuration Ends Here
*/
private struct PlayerActive extends array
private static boolean array playerOff
static method operator [] takes integer i returns boolean
return not playerOff[i]
endmethod
private static method run takes nothing returns boolean
set playerOff[GetPlayerId(GetTriggerPlayer())] = true
return false
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
local integer i = 15
loop
call TriggerRegisterPlayerEvent(t, Player(i), EVENT_PLAYER_LEAVE)
exitwhen i == 0
set i = i - 1
endloop
call TriggerAddCondition(t, Condition(function thistype.run))
set t = null
endmethod
endstruct
private module InitEventHandler
private static method onInit takes nothing returns nothing
set FIRSTBLOOD = Event.create()
set STREAK = Event.create()
set MULTIKILL = Event.create()
set END_STREAK = Event.create()
set END_MULTIKILL = Event.create()
set COMBO_BREAKER = Event.create()
set SUICIDE = Event.create()
set DENIAL = Event.create()
endmethod
endmodule
private struct EventHandler extends array
readonly static Event FIRSTBLOOD
readonly static Event STREAK
readonly static Event MULTIKILL
readonly static Event END_STREAK
readonly static Event END_MULTIKILL
readonly static Event COMBO_BREAKER
readonly static Event SUICIDE
readonly static Event DENIAL
static integer killingUnitId = 0
static integer dyingUnitId = 0
static integer killingPlayerId = 0
static integer dyingPlayerId = 0
static integer streakAchieved = 0
static integer streakEnded = 0
static integer multikillAchieved = 0
static integer multikillEnded = 0
static integer multikillLoserId = 0
static integer comboBroken = 0
static unit dyingUnit = null
static unit killingUnit = null
implement InitEventHandler
endstruct
private struct Multikill extends array
private static integer array value
private static real array counters
method operator multikill takes nothing returns integer
return value[this]
endmethod
method operator multikill= takes integer i returns nothing
set value[this] = i
endmethod
method operator counter takes nothing returns real
return counters[this]
endmethod
method operator counter= takes real r returns nothing
set counters[this] = r
endmethod
static method reset takes integer this returns nothing
set value[this] = 0
set counters[this] = 0
endmethod
static method resetAll takes nothing returns nothing
local integer i = 15
loop
call reset(i)
exitwhen i == 0
set i = i - 1
endloop
endmethod
private static method updateMulti takes nothing returns nothing
local integer index = 15
loop
set counters[index] = counters[index] - MULTI_UPDATE
if counters[index] <= 0 then
/*
* Cache Event Response Data and Fire Event.
*/
set EventHandler.multikillLoserId = index
set EventHandler.multikillEnded = value[index]
set value[index] = 0
set EventHandler.killingUnitId = 0
set EventHandler.dyingUnitId = 0
set EventHandler.killingUnit = null
set EventHandler.dyingUnit = null
set EventHandler.killingPlayerId = -1
set EventHandler.dyingPlayerId = -1
call EventHandler.END_MULTIKILL.fire()
endif
exitwhen index == 0
set index = index - 1
endloop
endmethod
private static method onInit takes nothing returns nothing
call TimerStart(CreateTimer(), MULTI_UPDATE, true, function thistype.updateMulti)
endmethod
endstruct
private struct Streak extends array
private static integer array value
static method operator [] takes integer i returns integer
return value[i]
endmethod
static method operator []= takes integer i, integer v returns nothing
set value[i] = v
endmethod
static method reset takes integer i returns nothing
set value[i] = 0
endmethod
static method resetAll takes nothing returns nothing
local integer i = 15
loop
call reset(i)
exitwhen i == 0
set i = i - 1
endloop
endmethod
endstruct
private function Exit takes nothing returns nothing
loop
call ExecuteFunc(Exit.name)
endloop
endfunction
private struct Assist extends array
private static Table array counters
private static Table array assisting
private static integer array count
private static integer array next
private static integer array prev
private static boolean array inList
private static boolean array allocated
static method operator [] takes integer i returns thistype
return i
endmethod
method operator [] takes integer i returns boolean
return assisting[this].boolean[i]
endmethod
method operator []= takes integer i, boolean b returns nothing
set assisting[this].boolean[i] = b
endmethod
method operator counter takes nothing returns integer
return counters[this]
endmethod
method operator counter= takes integer i returns nothing
set counters[this] = i
endmethod
method operator number takes nothing returns integer
return count[this]
endmethod
method operator number= takes integer i returns nothing
set count[this] = i
endmethod
static method reset takes integer unitId returns nothing
/*
* Set the number of assisting players to 0,
* and flush all the data in the tables.
*/
set count[unitId] = 0
call assisting[unitId].flush()
call counters[unitId].flush()
endmethod
static method resetAll takes nothing returns nothing
local integer this = next[0]
loop
exitwhen this == 0
call reset(this)
set this = next[this]
endloop
endmethod
static if not LIBRARY_UnitIndexer and not LIBRARY_AIDS then
private static unit crashThread
private static integer crashThreadInt
endif
private static method getIndexed takes nothing returns unit
static if LIBRARY_UnitIndexer then
return GetIndexedUnit()
elseif LIBRARY_AIDS then
return AIDS_GetEnteringIndexUnit()
else
return crashThread
endif
endmethod
private static method getIndexedId takes nothing returns integer
static if LIBRARY_UnitIndexer then
return GetIndexedUnitId()
elseif LIBRARY_AIDS then
return AIDS_GetIndexOfEnteringUnitAllocated()
else
return crashThreadInt
endif
endmethod
private static method getDeindexed takes nothing returns unit
static if LIBRARY_UnitIndexer then
return GetIndexedUnit()
elseif LIBRARY_AIDS then
return GetIndexUnit(AIDS_GetDecayingIndex())
else
return crashThread
endif
endmethod
private static method getDeindexedId takes nothing returns integer
static if LIBRARY_UnitIndexer then
return GetIndexedUnitId()
elseif LIBRARY_AIDS then
return AIDS_GetDecayingIndex()
else
return crashThreadInt
endif
endmethod
private static method index takes nothing returns boolean
if IsUnitType(getIndexed(), UNIT_TYPE_HERO) then
/*
* Make sure tables are initialized
*/
if counters[getIndexedId()] == 0 then
set counters[getIndexedId()] = Table.create()
set assisting[getIndexedId()] = Table.create()
endif
set allocated[getIndexedId()] = true
endif
return false
endmethod
private static method deindex takes nothing returns boolean
if IsUnitType(getDeindexed(), UNIT_TYPE_HERO) then
/*
* Reset data
*/
set count[getDeindexedId()] = 0
call counters[getDeindexedId()].flush()
call assisting[getDeindexedId()].flush()
set allocated[getDeindexedId()] = false
endif
return false
endmethod
private static integer damageTargetId = 0
private static integer damageSourceId = 0
private static unit damageTarget = null
private static method filter takes player p, integer id returns boolean
static if LEAVER_CAN_ASSIST then
return allocated[damageTargetId] and AssistPlayerFilter(id) and damageSourceId != 0 and (not assisting[damageTargetId].boolean[id]) and IsUnitEnemy(damageTarget, p) and damageSourceId != damageTargetId
else
return allocated[damageTargetId] and AssistPlayerFilter(id) and damageSourceId != 0 and (not assisting[damageTargetId].boolean[id]) and IsUnitEnemy(damageTarget, p) and damageSourceId != damageTargetId and PlayerActive[id]
endif
endmethod
private static method onDamage takes nothing returns boolean
local integer id
local player p
/*
* Data configuration based on system implemented.
*/
static if LIBRARY_DamageEvent then
set p = GetOwningPlayer(DamageEvent.source)
set id = GetPlayerId(p)
set damageTarget = DamageEvent.target
set damageTargetId = DamageEvent.targetId
set damageSourceId = DamageEvent.sourceId
elseif LIBRARY_Damage then
static if DAMAGE_BY_JESUS4LYF then
set p = GetOwningPlayer(GetEventDamageSource())
set id = GetPlayerId(p)
set damageTarget = GetTriggerUnit()
set damageTargetId = GetUnitId(damageTarget)
set damageSourceId = GetUnitId(GetEventDamageSource())
else
set p = GetOwningPlayer(Damage.source)
set id = GetPlayerId(p)
set damageTarget = Damage.target
set damageTargetId = Damage.targetId
set damageSourceId = Damage.sourceId
endif
endif
if filter(p, id) then
/*
* If the target is not in the list, we add him.
*/
if not inList[damageTargetId] then
set inList[damageTargetId] = true
set next[damageTargetId] = 0
set prev[damageTargetId] = prev[0]
set next[prev[0]] = damageTargetId
set prev[0] = damageTargetId
endif
/*
* Mark player as assisting, increase assister count and reset assist counter
*/
set assisting[damageTargetId].boolean[id] = true
set count[damageTargetId] = count[damageTargetId] + 1
set counters[damageTargetId].real[id] = ASSIST_DECAY
endif
set p = null
return false
endmethod
private static method updateAssist takes nothing returns nothing
local integer assist = next[0]
local integer index = 15
local real n = 0
loop
exitwhen assist == 0
loop
if assisting[assist].boolean[index] then
set n = counters[assist].real[index] - ASSIST_UPDATE
set counters[assist].real[index] = n
if n <= 0 then
/*
* Mark the player as not assisting, decrease
* the assisting player count, and check if we
* can remove the target from the assists list
*/
set assisting[assist].boolean[index] = false
set count[assist] = count[assist] - 1
if count[assist] == 0 then
set prev[next[assist]] = prev[assist]
set next[prev[assist]] = next[assist]
set inList[assist] = false
endif
endif
endif
exitwhen index == 0
set index = index - 1
endloop
set index = 15
set assist = next[assist]
endloop
endmethod
private static method onInit takes nothing returns nothing
static if LIBRARY_Damage then
local trigger t = CreateTrigger()
endif
static if LIBRARY_UnitIndexer then
call RegisterUnitIndexEvent(Condition(function thistype.index), UnitIndexer.INDEX)
call RegisterUnitIndexEvent(Condition(function thistype.deindex), UnitIndexer.DEINDEX)
elseif LIBRARY_AIDS then
call AIDS_RegisterOnEnterAllocated(Condition(function thistype.index))
call AIDS_RegisterOnDeallocate(Condition(function thistype.deindex))
else
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "ERROR: NO UNIT INDEXING SYSTEM FOUND.")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "SHUTING DOWN IN: 5")
call TriggerSleepAction(1)
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "4")
call TriggerSleepAction(1)
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "3")
call TriggerSleepAction(1)
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "2")
call TriggerSleepAction(1)
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "1")
call TriggerSleepAction(1)
call Exit()
endif
static if LIBRARY_DamageEvent then
call DamageEvent.ANY.register(Condition(function thistype.onDamage), 0)
elseif LIBRARY_Damage then
static if DAMAGE_BY_JESUS4LYF then
call Damage_RegisterEvent(t)
call TriggerAddCondition(t, Condition(function thistype.onDamage))
set t = null
else
call RegisterGlobalDamage(function thistype.onDamage)
endif
else
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "ERROR: NO DAMAGE DETECTING SYSTEM FOUND.")
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "SHUTING DOWN IN: 5")
call TriggerSleepAction(1)
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "4")
call TriggerSleepAction(1)
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "3")
call TriggerSleepAction(1)
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "2")
call TriggerSleepAction(1)
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "1")
call TriggerSleepAction(1)
call Exit()
endif
call TimerStart(CreateTimer(), ASSIST_UPDATE, true, function thistype.updateAssist)
endmethod
endstruct
private struct MessageHandler extends array
private static string queue = ""
static method print takes string s returns nothing
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, TEXT_DURATION, s)
endmethod
static method enqueue takes string s returns nothing
set queue = queue + s + "\n"
endmethod
static method printQueue takes nothing returns nothing
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, TEXT_DURATION, queue)
endmethod
static method clearQueue takes nothing returns nothing
set queue = ""
endmethod
endstruct
static if TEXTTAGS then
private struct TextTagHandler extends array
private static method createText takes player p, real cameraCenter, string s returns nothing
local texttag tag = CreateTextTag()
call SetTextTagText(tag, s, TEXTTAG_SIZE)
call SetTextTagPos(tag, GetCameraEyePositionX(), GetCameraEyePositionY() + cameraCenter, 16.0)
call SetTextTagVelocity(tag, TEXTTAG_X, TEXTTAG_Y)
call SetTextTagVisibility(tag, GetLocalPlayer() == p)
call SetTextTagFadepoint(tag, TEXTTAG_FADE)
call SetTextTagLifespan(tag, TEXTTAG_AGE)
call SetTextTagPermanent(tag, false)
set tag = null
endmethod
static method plus takes player p, real cameraCenter, string s returns nothing
call createText(p, cameraCenter, "|cffffcc00+" + s)
endmethod
static method minus takes player p, real cameraCenter, string s returns nothing
call createText(p, cameraCenter, "|cffff0000-" + s)
endmethod
endstruct
endif
private struct PlayerStatHandler extends array
static integer array kills
static integer array deaths
static integer array assists
static integer array denies
static integer array suicides
static integer array streaks
static integer array multikills
static integer array combobreakers
static Table array streakTypes
static Table array multikillTypes
private static method onInit takes nothing returns nothing
local integer i = 15
loop
set streakTypes[i] = Table.create()
set multikillTypes[i] = Table.create()
exitwhen i == 0
set i = i - 1
endloop
endmethod
endstruct
struct StreakSystem extends array
private static integer maxStreak = 0
private static integer minStreak = 0
private static integer maxMultikill = 1
static if LIBRARY_SoundTools then
private static Sound firstSound
private static Sound comboBreaker
private static Sound array sounds
private static Sound array multiSound
elseif LIBRARY_SoundUtils then
private static integer firstSound
private static integer comboBreaker
private static integer array sounds
private static integer array multiSound
endif
private static string array strings
private static string array multiString
private static string array endString
private static string array colors
static boolean firstblood = false
static boolean enabled = true
private static key cacheKey
private static Table cache = cacheKey
/*
* Event Response Get Functions
*/
static method getKillingUnitId takes nothing returns integer
return EventHandler.killingUnitId
endmethod
static method getKillingUnit takes nothing returns unit
return EventHandler.killingUnit
endmethod
static method getDyingUnitId takes nothing returns integer
return EventHandler.dyingUnitId
endmethod
static method getDyingUnit takes nothing returns unit
return EventHandler.dyingUnit
endmethod
static method getKillingPlayerId takes nothing returns integer
return EventHandler.killingPlayerId
endmethod
static method getKillingPlayer takes nothing returns player
return Player(EventHandler.killingPlayerId)
endmethod
static method getDyingPlayerId takes nothing returns integer
return EventHandler.dyingPlayerId
endmethod
static method getDyingPlayer takes nothing returns player
return Player(EventHandler.dyingPlayerId)
endmethod
static method getAchievedStreak takes nothing returns integer
return EventHandler.streakAchieved
endmethod
static method getEndedStreak takes nothing returns integer
return EventHandler.streakEnded
endmethod
static method getAchievedMultikill takes nothing returns integer
return EventHandler.multikillAchieved
endmethod
static method getEndedMultikill takes nothing returns integer
return EventHandler.multikillEnded
endmethod
static method getMultikillLoserId takes nothing returns integer
return EventHandler.multikillLoserId
endmethod
static method getMultikillLoser takes nothing returns player
return Player(EventHandler.multikillLoserId)
endmethod
static method getComboBroken takes nothing returns integer
return EventHandler.comboBroken
endmethod
/*
* Event registration functions
*/
static if EVENT_BY_NESTHARUS then
static method registerFirstbloodEvent takes code c returns nothing
call EventHandler.FIRSTBLOOD.register(Filter(c))
return
endmethod
static method registerStreakEvent takes code c returns nothing
call EventHandler.STREAK.register(Filter(c))
return
endmethod
static method registerMultikillEvent takes code c returns nothing
call EventHandler.MULTIKILL.register(Filter(c))
return
endmethod
static method registerStreakEndEvent takes code c returns nothing
call EventHandler.END_STREAK.register(Filter(c))
return
endmethod
static method registerMultikillEndEvent takes code c returns nothing
call EventHandler.END_MULTIKILL.register(Filter(c))
return
endmethod
static method registerCombobreakerEvent takes code c returns nothing
call EventHandler.COMBO_BREAKER.register(Filter(c))
return
endmethod
static method registerSuicideEvent takes code c returns nothing
call EventHandler.SUICIDE.register(Filter(c))
return
endmethod
static method registerDenialEvent takes code c returns nothing
call EventHandler.DENIAL.register(Filter(c))
return
endmethod
else
static method registerFirstbloodEvent takes trigger t returns nothing
call EventHandler.FIRSTBLOOD.register(t)
endmethod
static method registerStreakEvent takes trigger t returns nothing
call EventHandler.STREAK.register(t)
endmethod
static method registerMultikillEvent takes trigger t returns nothing
call EventHandler.MULTIKILL.register(t)
endmethod
static method registerStreakEndEvent takes trigger t returns nothing
call EventHandler.END_STREAK.register(t)
endmethod
static method registerMultikillEndEvent takes trigger t returns nothing
call EventHandler.END_MULTIKILL.register(t)
endmethod
static method registerCombobreakerEvent takes trigger t returns nothing
call EventHandler.COMBO_BREAKER.register(t)
endmethod
static method registerSuicideEvent takes trigger t returns nothing
call EventHandler.SUICIDE.register(t)
endmethod
static method registerDenialEvent takes trigger t returns nothing
call EventHandler.DENIAL.register(t)
endmethod
endif
/*
* API Get Total Stat Functions
*/
static method getKills takes player p returns integer
return PlayerStatHandler.kills[GetPlayerId(p)]
endmethod
static method getDeaths takes player p returns integer
return PlayerStatHandler.deaths[GetPlayerId(p)]
endmethod
static method getDenies takes player p returns integer
return PlayerStatHandler.denies[GetPlayerId(p)]
endmethod
static method getSuicides takes player p returns integer
return PlayerStatHandler.suicides[GetPlayerId(p)]
endmethod
static method getAssists takes player p returns integer
return PlayerStatHandler.assists[GetPlayerId(p)]
endmethod
static method getStreaks takes player p returns integer
return PlayerStatHandler.streaks[GetPlayerId(p)]
endmethod
static method getMultikills takes player p returns integer
return PlayerStatHandler.multikills[GetPlayerId(p)]
endmethod
static method getCombobreakers takes player p returns integer
return PlayerStatHandler.combobreakers[GetPlayerId(p)]
endmethod
static method getStreaksOfType takes player p, integer typ returns integer
return PlayerStatHandler.streakTypes[GetPlayerId(p)][typ]
endmethod
static method getMultikillsOfType takes player p, integer typ returns integer
return PlayerStatHandler.multikillTypes[GetPlayerId(p)][typ]
endmethod
static method getCurrentStreak takes player p returns integer
return Streak[GetPlayerId(p)]
endmethod
static method getCurrentMultikill takes player p returns integer
return Multikill(GetPlayerId(p)).multikill
endmethod
/*
* API Get Total Stat ById Functions
*/
static method getKillsById takes integer id returns integer
return PlayerStatHandler.kills[id]
endmethod
static method getDeathsById takes integer id returns integer
return PlayerStatHandler.deaths[id]
endmethod
static method getDeniesById takes integer id returns integer
return PlayerStatHandler.denies[id]
endmethod
static method getSuicidesById takes integer id returns integer
return PlayerStatHandler.suicides[id]
endmethod
static method getAssistsById takes integer id returns integer
return PlayerStatHandler.assists[id]
endmethod
static method getStreaksById takes integer id returns integer
return PlayerStatHandler.streaks[id]
endmethod
static method getMultikillsById takes integer id returns integer
return PlayerStatHandler.multikills[id]
endmethod
static method getCombobreakersById takes integer id returns integer
return PlayerStatHandler.combobreakers[id]
endmethod
static method getStreaksOfTypeById takes integer id, integer typ returns integer
return PlayerStatHandler.streakTypes[id][typ]
endmethod
static method getMultikillsOfTypeById takes integer id, integer typ returns integer
return PlayerStatHandler.multikillTypes[id][typ]
endmethod
static method getCurrentStreakById takes integer id returns integer
return Streak[id]
endmethod
static method getCurrentMultikillById takes integer id returns integer
return Multikill(id).multikill
endmethod
/*
* Streak and Multikill Create Functions
*/
static method newStreak takes string streakName, string streakDisplay, string soundPath, integer duration returns nothing
set maxStreak = maxStreak + 1
set strings[maxStreak] = " " + streakDisplay
set endString[maxStreak] = "'s " + streakName
static if LIBRARY_SoundTools then
set sounds[maxStreak] = NewSound(soundPath, duration, false, S3D)
elseif LIBRARY_SoundUtils then
set sounds[maxStreak] = DefineSound(soundPath, duration, false, S3D)
endif
endmethod
static method newMultikill takes string text, string soundPath, integer duration returns nothing
set maxMultikill = maxMultikill + 1
set multiString[maxMultikill] = " " + text
static if LIBRARY_SoundTools then
set multiSound[maxMultikill] = NewSound(soundPath, duration, false, S3D)
elseif LIBRARY_SoundUtils then
set multiSound[maxMultikill] = DefineSound(soundPath, duration, false, S3D)
endif
endmethod
static method setMinimumStreak takes integer i returns nothing
set minStreak = i
set maxStreak = i - 1
endmethod
static method setFirstBloodSound takes string path, integer duration returns nothing
static if LIBRARY_SoundTools then
set firstSound = NewSound(path, duration, false, S3D)
elseif LIBRARY_SoundUtils then
set firstSound = DefineSound(path, duration, false, S3D)
endif
endmethod
static method setComboBreakerSound takes string path, integer duration returns nothing
static if LIBRARY_SoundTools then
set comboBreaker = NewSound(path, duration, false, S3D)
elseif LIBRARY_SoundUtils then
set comboBreaker = DefineSound(path, duration, false, S3D)
endif
endmethod
/*
* Color Configuration
*/
static method setColor takes integer playerId, string color returns nothing
set thistype.colors[playerId] = color
endmethod
/*
* Gold Gain/Loss Control
*/
static method operator goldGain takes nothing returns boolean
return gainGold
endmethod
static method operator goldGain= takes boolean b returns nothing
set gainGold = b
endmethod
static method operator goldLoss takes nothing returns boolean
return loseGold
endmethod
static method operator goldLoss= takes boolean b returns nothing
set loseGold = b
endmethod
/*
* Bounty Formula Control
*/
static method operator bountyBase takes nothing returns integer
return baseGold
endmethod
static method operator bountyBase= takes integer v returns nothing
set baseGold = v
endmethod
static method operator bountyIncrement takes nothing returns integer
return goldIncrement
endmethod
static method operator bountyIncrement= takes integer v returns nothing
set goldIncrement = v
endmethod
/*
* Streak Functions
*/
static method resetStreak takes integer playerId returns nothing
call Streak.reset(playerId)
endmethod
static method resetAllStreaks takes nothing returns nothing
call Streak.resetAll()
endmethod
/*
* Multikill Functions
*/
static method resetMultikill takes integer playerId returns nothing
call Multikill.reset(playerId)
endmethod
static method resetAllMultikills takes nothing returns nothing
call Multikill.resetAll()
endmethod
/*
* Assist Functions
*/
static method resetAssist takes integer unitId returns nothing
call Assist.reset(unitId)
endmethod
static method resetAllAssists takes nothing returns nothing
call Assist.resetAll()
endmethod
/*
* Simple wrapper for gold gaining.
*/
private static method addGold takes player p, integer amount returns nothing
call SetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD, GetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD) + amount)
endmethod
private static method doMulti takes nothing returns nothing
local timer tmr = GetExpiredTimer()
local integer handleId = GetHandleId(tmr)
local integer multi = GetTimerData(tmr)
/*
* Make sure the multikill is greater than 1 and still valid
*/
if cache.real[handleId] > 0 and multi > 1 then
/*
* Checking if the multikill is greater than the maximum multikill,
* then displaying the correct message and playing the correct sound
*/
if multi > maxMultikill then
call MessageHandler.print(cache.string[handleId] + multiString[maxMultikill])
call RunSound(multiSound[maxMultikill])
else
call MessageHandler.print(cache.string[handleId] + multiString[multi])
call RunSound(multiSound[multi])
endif
endif
call ReleaseTimer(tmr)
set tmr = null
endmethod
private static method onDeath takes nothing returns nothing
/*
* These variables are for storing basic data like the killing unit,
* the dying unit, the killing player, etc...
*/
local unit dyingUnit
local unit killingUnit
local player dyingPlayer
local player killingPlayer
local integer dyingId
local integer killingId
/*
* Local variables that will be needed later.
*/
local integer index = 0
local integer bounty
local integer unitIdDying
local integer unitIdKilling
local integer timerId
local string assistString = ""
local string killingName
local string dyingName
local player tempPlayer
local timer delayTimer
/*
* These locals will only be declared if TEXTTAGS is set to true.
*/
static if TEXTTAGS then
local integer assistGold
local integer lostGold
local texttag tag
local real cameraCenter
endif
/*
* Make sure the dying unit is a hero and the system is enabled
*/
if enabled and IsUnitType(GetTriggerUnit(), UNIT_TYPE_HERO) then
set dyingUnit = GetTriggerUnit()
set killingUnit = GetKillingUnit()
set dyingPlayer = GetTriggerPlayer()
set killingPlayer = GetOwningPlayer(killingUnit)
set killingId = GetPlayerId(killingPlayer)
set dyingId = GetPlayerId(dyingPlayer)
if KillingPlayerFilter(killingId) and DyingPlayerFilter(dyingId) and killingUnit != null and killingPlayer != null then
static if LEAVER_STREAK then
/*
* Check if the player has left.
*/
if not PlayerActive[killingId] then
set dyingUnit = null
set killingUnit = null
set dyingPlayer = null
set killingPlayer = null
return
endif
endif
/*
* Check if the killing player and the dying player are allies.
*/
if IsPlayerAlly(killingPlayer, dyingPlayer) then
call resetAssist(GetUnitUserData(dyingUnit))
if killingPlayer != dyingPlayer then
/*
* Total Stat Incrementation
*/
set PlayerStatHandler.denies[killingId] = PlayerStatHandler.denies[killingId] + 1
static if DENIES_COUNT_DEATHS then
set PlayerStatHandler.deaths[dyingId] = PlayerStatHandler.deaths[dyingId] + 1
endif
static if DENIES_COUNT_KILLS then
set PlayerStatHandler.kills[killingId] = PlayerStatHandler.kills[killingId] + 1
endif
/*
* We print the allied-player-denial message.
*/
call MessageHandler.print(GetDenialString(colors[killingId] + GetPlayerName(killingPlayer), colors[dyingId] + GetPlayerName(dyingPlayer)))
/*
* We store the event data responses and fire the event.
*/
set EventHandler.killingUnitId = GetUnitUserData(killingUnit)
set EventHandler.killingUnit = killingUnit
set EventHandler.killingPlayerId = killingId
set EventHandler.dyingUnitId = GetUnitUserData(dyingUnit)
set EventHandler.dyingUnit = dyingUnit
set EventHandler.dyingPlayerId = dyingId
call EventHandler.DENIAL.fire()
else
/*
* Total Stat Incrementations
*/
set PlayerStatHandler.suicides[killingId] = PlayerStatHandler.suicides[killingId] + 1
static if SUICIDE_DEATH then
set PlayerStatHandler.deaths[dyingId] = PlayerStatHandler.deaths[dyingId] + 1
endif
/*
* We print the suicide message.
*/
call MessageHandler.print(GetSuicideString(colors[killingId] + GetPlayerName(dyingPlayer)))
/*
* We store the event data responses.
*/
set EventHandler.killingUnitId = GetUnitUserData(killingUnit)
set EventHandler.killingUnit = killingUnit
set EventHandler.killingPlayerId = killingId
set EventHandler.dyingUnitId = EventHandler.killingUnitId
set EventHandler.dyingUnit = EventHandler.killingUnit
set EventHandler.dyingPlayerId = killingId
static if SUICIDE_ENDS then
/*
* Cache event data and reset streak
*/
set EventHandler.streakEnded = Streak[killingId]
set Streak[killingId] = 0
endif
/*
* Fire suicide and streak-end
*/
static if SUICIDE_FIRE_FIRST then
call EventHandler.SUICIDE.fire()
static if SUICIDE_ENDS then
call EventHandler.END_STREAK.fire()
endif
else
static if SUICIDE_ENDS then
call EventHandler.END_STREAK.fire()
endif
call EventHandler.SUICIDE.fire()
endif
endif
/*
* Null all the locals and return.
*/
set dyingUnit = null
set killingUnit = null
set dyingPlayer = null
set killingPlayer = null
return
endif
/*
* Increase kills of killing player and deaths of dying player
*/
set PlayerStatHandler.kills[killingId] = PlayerStatHandler.kills[killingId] + 1
set PlayerStatHandler.deaths[dyingId] = PlayerStatHandler.deaths[dyingId] + 1
/*
* Increase streak of killing player and reset multikill counter
*/
set Streak[killingId] = Streak[killingId] + 1
set Multikill(killingId).counter = MULTI_TIME
set unitIdDying = GetUnitUserData(dyingUnit)
set unitIdKilling = GetUnitUserData(killingUnit)
/*
* Event response data
*/
set EventHandler.killingUnitId = unitIdKilling
set EventHandler.dyingUnitId = unitIdDying
set EventHandler.killingUnit = killingUnit
set EventHandler.dyingUnit = dyingUnit
set EventHandler.killingPlayerId = killingId
set EventHandler.dyingPlayerId = dyingId
static if DEATH_END_MULTI then
/*
* Cache event data, reset multikills of dying player and fire event
*/
set EventHandler.multikillEnded = Multikill(dyingId).multikill
set EventHandler.multikillLoserId = dyingId
set Multikill(dyingId).multikill = 0
set Multikill(dyingId).counter = 0
call EventHandler.END_MULTIKILL.fire()
endif
/*
* The bounty of the killing player.
*/
set bounty = GetBounty(killingId, dyingId, killingUnit, dyingUnit)
/*
* Get the colored names of the killing player and dying player.
*/
set killingName = colors[killingId] + GetPlayerName(killingPlayer) + "|r"
set dyingName = colors[dyingId] + GetPlayerName(dyingPlayer) + "|r"
static if TEXTTAGS then
set assistGold = 0
set lostGold = 0
/*
* We are dividing the camera distance by 2 to be used while placing the texttag
* at the center of the screen for the player. This will be equal to half the length of
* your field of view. Interesting, ey?
*/
set cameraCenter = GetCameraField(CAMERA_FIELD_TARGET_DISTANCE) / 2
endif
/*
* If the number of assisters is one, it means that only the killing player fought the unit.
* If the number of assisters is greater than one, there is at least one assister, so:
*/
if Assist[unitIdDying].number > 1 then
set assistString = " Assists: "
/*
* We loop through all the players
*/
loop
set tempPlayer = Player(index)
/*
* Check if the player is an enemy to the dying player and if he assisted in the kill.
*/
if Assist[unitIdDying][index] and index != killingId and index != dyingId and PlayerActive[index] then
/*
* Add player name to assist string and increase total assists
*/
set assistString = assistString + colors[index] + GetPlayerName(tempPlayer) + "|r/"
set PlayerStatHandler.assists[index] = PlayerStatHandler.assists[index] + 1
static if ASSIST_GOLD then
static if TEXTTAGS then
/*
* Give assisting player gold and create texttag
*/
set assistGold = GetAssistGold(killingId, dyingId, killingUnit, dyingUnit, bounty, Assist[unitIdDying].number)
call addGold(tempPlayer, assistGold)
call TextTagHandler.plus(tempPlayer, cameraCenter, I2S(assistGold))
else
/*
* Give assisting player gold
*/
call addGold(tempPlayer, GetAssistGold(killingId, dyingId, killingUnit, dyingUnit, bounty, Assist[unitIdDying].number))
endif
endif
endif
exitwhen index == 15
set index = index + 1
endloop
/*
* Adjust assist string.
*/
set assistString = SubString(assistString, 0, StringLength(assistString) - 1)
endif
call Assist.reset(unitIdDying)
/*
* If bounty-gain is enabled, give gold to the
* player and create a texttag if that feature
* is selected.
*/
if gainGold then
call addGold(killingPlayer, bounty)
static if TEXTTAGS then
call TextTagHandler.plus(killingPlayer, cameraCenter, I2S(bounty))
endif
endif
/*
* If gold-loss is enabled, we remove gold from the player
* and create a texttag if the feature is selected.
*/
if loseGold then
static if TEXTTAGS then
set lostGold = GetLostGold(killingId, dyingId, killingUnit, dyingUnit, bounty)
call addGold(dyingPlayer, -lostGold)
call TextTagHandler.minus(dyingPlayer, cameraCenter, I2S(lostGold))
else
call addGold(dyingPlayer, -(GetLostGold(killingId, dyingId, killingUnit, dyingUnit, bounty)))
endif
endif
if firstblood then
/*
* Evaluate dying player streak
*/
if Streak[dyingId] >= minStreak and Streak[dyingId] <= maxStreak then
call MessageHandler.enqueue(GetEndString(killingName, dyingName, endString[Streak[dyingId]], I2S(bounty)) + assistString)
set EventHandler.streakEnded = Streak[dyingId]
call EventHandler.END_STREAK.fire()
/*
* Combobreaker evaluation
*/
if Streak[dyingId] >= COMBO_BREAKER_MIN then
set PlayerStatHandler.combobreakers[killingId] = PlayerStatHandler.combobreakers[killingId] + 1
call RunSound(comboBreaker)
set EventHandler.comboBroken = Streak[dyingId]
call EventHandler.COMBO_BREAKER.fire()
endif
elseif Streak[dyingId] > maxStreak then
/*
* Display End Streak Message and fire event
*/
call MessageHandler.enqueue(GetEndString(killingName, dyingName, endString[maxStreak], I2S(bounty)) + assistString)
set EventHandler.streakEnded = maxStreak
call EventHandler.END_STREAK.fire()
/*
* Run combo breaker sound, increase total combo breakers and fire event
*/
set PlayerStatHandler.combobreakers[killingId] = PlayerStatHandler.combobreakers[killingId] + 1
call RunSound(comboBreaker)
set EventHandler.comboBroken = Streak[dyingId]
call EventHandler.COMBO_BREAKER.fire()
else
/*
* Display Ordinary Kill Message
*/
call MessageHandler.enqueue(GetKillString(killingName, dyingName, I2S(bounty)) + assistString)
endif
/*
* Evaluate killing player streak
*/
if Streak[killingId] >= minStreak and Streak[killingId] <= maxStreak then
call MessageHandler.enqueue(killingName + strings[Streak[killingId]])
call RunSound(sounds[Streak[killingId]])
/*
* Smart Algorithm for streak/streak-type tracking
*/
if Streak[killingId] == minStreak then
set PlayerStatHandler.streaks[killingId] = PlayerStatHandler.streaks[killingId] + 1
else
set PlayerStatHandler.streakTypes[killingId][Streak[killingId] - 1] = PlayerStatHandler.streakTypes[killingId][Streak[killingId] - 1] - 1
endif
/*
* Increase the number of streak types for player and fire event
*/
set PlayerStatHandler.streakTypes[killingId][Streak[killingId]] = PlayerStatHandler.streakTypes[killingId][Streak[killingId]] + 1
set EventHandler.streakAchieved = Streak[killingId]
call EventHandler.STREAK.fire()
elseif Streak[killingId] > maxStreak then
/*
* We display the streak message of the maximum streak value because
* the streak of the killing player is too large and we play the streak sound.
*/
call MessageHandler.enqueue(killingName + strings[maxStreak])
call RunSound(sounds[maxStreak])
/*
* Store the achieved streak and fire the Streak event
*/
set EventHandler.streakAchieved = maxStreak
call EventHandler.STREAK.fire()
endif
else
/*
* Register firstblood as taken, display messages, give gold, play sound and fire event
*/
set firstblood = true
call MessageHandler.enqueue(GetKillString(killingName, dyingName, I2S(bounty)) + assistString)
call MessageHandler.enqueue(GetFirstbloodString(killingName))
call RunSound(firstSound)
call addGold(killingPlayer, FIRST_GOLD)
call EventHandler.FIRSTBLOOD.fire()
static if TEXTTAGS then
call TextTagHandler.plus(killingPlayer, cameraCenter - 35., I2S(FIRST_GOLD))
endif
endif
/*
* Increase current multikills by 1 and reset dying player streak
*/
set Multikill[killingId].multikill = Multikill[killingId].multikill + 1
set Streak[dyingId] = 0
/*
* Evaluation of killing player multikills
*/
if Multikill[killingId].multikill > 1 then
/*
* Create a timer and store multikill data in it to display
* multikill message after a delay
*/
set delayTimer = NewTimerEx(Multikill[killingId].multikill)
set timerId = GetHandleId(delayTimer)
set cache.real[timerId] = Multikill[killingId].counter
set cache.string[timerId] = killingName
call TimerStart(delayTimer, INTERVAL, false, function thistype.doMulti)
/*
* Smart Algorithm for multikill/multikill-type detection
*/
if Multikill[killingId].multikill == 2 then
set PlayerStatHandler.multikills[killingId] = PlayerStatHandler.multikills[killingId] + 1
elseif Multikill[killingId].multikill <= maxMultikill then
set PlayerStatHandler.multikillTypes[killingId][Multikill[killingId].multikill - 1] = PlayerStatHandler.multikillTypes[killingId][Multikill[killingId].multikill - 1] - 1
endif
/*
* Increase number of multikill types and fire event
*/
set PlayerStatHandler.multikillTypes[killingId][Multikill[killingId].multikill] = PlayerStatHandler.multikillTypes[killingId][Multikill[killingId].multikill] + 1
set EventHandler.multikillAchieved = Multikill[killingId].multikill
call EventHandler.MULTIKILL.fire()
endif
/*
* Print all enqueued strings and clear queue
*/
call MessageHandler.printQueue()
call MessageHandler.clearQueue()
/*
* Null data
*/
set tempPlayer = null
set delayTimer = null
endif
/*
* Null data
*/
set dyingUnit = null
set killingUnit = null
set dyingPlayer = null
set killingPlayer = null
endif
endmethod
implement Init
private static method onInit takes nothing returns nothing
static if LIBRARY_RegisterPlayerUnitEvent then
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function thistype.onDeath)
else
local trigger t = CreateTrigger()
local code c = function thistype.onDeath
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
call TriggerAddCondition(t, Condition(c))
set t = null
endif
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library DamageEvent /* v3.1.1.2
*************************************************************************************
*
* Simple light damage detection. Will not run 0 damage events as only invalid attacks and spells can cause these.
* Can instance spells and attacks with fireSpell and fireAttack methods.
*
*************************************************************************************
*
* */uses/*
*
* */ UnitIndexer /* hiveworkshop.com/forums/jass-functions-413/unit-indexer-172090/
* */ PriorityEvent /* hiveworkshop.com/forums/submissions-414/snippet-priority-event-213573/
* */ BinaryHeap /* hiveworkshop.com/forums/jass-resources-412/snippet-binary-heap-199353/
*
************************************************************************************
*
* SETTINGS
*/
globals
/*************************************************************************************
*
* How many units can refresh at a given moment (when a trigger is rebuilt).
* larger size means less triggers but harder refreshes.
*
*************************************************************************************/
private constant integer TRIGGER_SIZE = 80
endglobals
/*
*************************************************************************************
*
* struct DamageEvent extends array
*
* Fields
* ------------------
* static constant PriorityEvent ANY
*
* readonly static UnitIndex targetId
* readonly static UnitIndex sourceId
* readonly static unit target
* readonly static unit source
* readonly static real amount
* readonly static integer depth
* - if depth == 1, then current damage is the original attack, not a modifier
*
* static boolean enabled
*
* readonly static integer damageType
* - useful for custom attack types (spell, hero, chaos, etc)
* readonly static integer damageId
* - useful for things like ability ids
* readonly static integer instance
* - useful for passing in a struct
*
* Methods
* ------------------
* static method unitDamageTarget takes unit attacker, widget target, real amount, integer damageType, integer damageId, integer instance returns boolean
*
*************************************************************************************
*
* module ExtendDamageEvent extends DamageEvent, AdvDamageEvent (if exists)
*
* module DamageEvent extends DamageEvent, AdvDamageEvent (if exists)
*
* Interface
* ------------------
* private static constant method PRIORITY takes nothing returns integer (optional)
* - without this declared, the priority will be set to 0 (no priority)
* private static method onDamage takes nothing returns nothing (optional)
* private static method filter takes nothing returns boolean (optional)
*
*************************************************************************************/
private struct DamageEventProperties extends array
static method operator target takes nothing returns unit
return GetUnitById(DamageEvent.targetId)
endmethod
static method operator source takes nothing returns unit
return GetUnitById(DamageEvent.sourceId)
endmethod
endstruct
//! runtextmacro optional ADV_DAMAGE_EVENT_EXT_CODE()
globals
private boolexpr damageCondition
endglobals
private struct DamageTrigger extends array
private static integer instanceCount = 0
private thistype first
private thistype next
private thistype prev
readonly thistype parent
private integer inactiveUnits
readonly integer activeUnits
private trigger damageTrigger
private method registerUnit takes UnitIndex whichUnit returns boolean
if (activeUnits < TRIGGER_SIZE) then
call TriggerRegisterUnitEvent(damageTrigger, GetUnitById(whichUnit), EVENT_UNIT_DAMAGED)
set activeUnits = activeUnits + 1
return true
endif
return false
endmethod
private method unregisterUnit takes UnitIndex whichUnit returns nothing
set inactiveUnits = inactiveUnits + 1
set activeUnits = activeUnits - 1
endmethod
private method createTrigger takes nothing returns nothing
set damageTrigger = CreateTrigger()
call TriggerAddCondition(damageTrigger, damageCondition)
endmethod
private method remakeTrigger takes nothing returns nothing
call DestroyTrigger(damageTrigger)
call createTrigger()
endmethod
private method rebuildTrigger takes nothing returns nothing
local thistype current = first
call remakeTrigger()
/*
* Iterate over all units registered to the trigger and reregister them
*/
set current.prev.next = 0
loop
exitwhen 0 == current
call TriggerRegisterUnitEvent(damageTrigger, GetUnitById(current), EVENT_UNIT_DAMAGED)
set current = current.next
endloop
set first.prev.next = current
endmethod
private method remake takes nothing returns nothing
if (inactiveUnits == TRIGGER_SIZE) then
set inactiveUnits = 0
call rebuildTrigger()
endif
endmethod
private method addToList takes thistype whichUnit returns nothing
set whichUnit.parent = this
if (0 == first) then
set first = whichUnit
set whichUnit.next = whichUnit
set whichUnit.prev = whichUnit
else
set this = first
set whichUnit.prev = prev
set whichUnit.next = this
set prev.next = whichUnit
set prev = whichUnit
endif
endmethod
method add takes thistype whichUnit returns boolean
if (0 == this) then
return false
endif
if (registerUnit(whichUnit)) then
call addToList(whichUnit)
return true
endif
return false
endmethod
private method removeFromList takes thistype whichUnit returns nothing
set whichUnit.parent = 0
set whichUnit.prev.next = whichUnit.next
set whichUnit.next.prev = whichUnit.prev
if (first == whichUnit) then
set first = whichUnit.next
if (first == whichUnit) then
set first = 0
endif
endif
endmethod
static method remove takes thistype whichUnit returns nothing
local thistype this = whichUnit.parent
call removeFromList(whichUnit)
call unregisterUnit(whichUnit)
call remake()
endmethod
private static method allocate takes nothing returns thistype
set instanceCount = instanceCount + 1
return instanceCount
endmethod
static method create takes nothing returns thistype
local thistype this = allocate()
call createTrigger()
return this
endmethod
endstruct
private struct DamageTriggerHeapInner extends array
private static method compare takes DamageTrigger trig1, DamageTrigger trig2 returns boolean
return trig1.activeUnits <= trig2.activeUnits
endmethod
implement BinaryHeap
endstruct
private struct DamageTriggerHeap extends array
private static DamageTriggerHeapInner array parent
static method add takes UnitIndex whichUnit returns nothing
local DamageTrigger damageTrigger = DamageTriggerHeapInner.root.value
if (not damageTrigger.add(whichUnit)) then
set damageTrigger = DamageTrigger.create()
call damageTrigger.add(whichUnit)
set parent[damageTrigger] = DamageTriggerHeapInner.insert(damageTrigger)
else
call parent[damageTrigger].modify(damageTrigger)
endif
endmethod
static method remove takes UnitIndex whichUnit returns nothing
local DamageTrigger damageTrigger = DamageTrigger(whichUnit).parent
call DamageTrigger.remove(whichUnit)
call parent[damageTrigger].modify(damageTrigger)
endmethod
endstruct
private module DamageEventMod
readonly static PriorityEvent ANY
readonly static UnitIndex targetId
readonly static UnitIndex sourceId
readonly static real amount
static boolean enabled
private static integer array damageType_p
private static integer array damageId_p
private static integer array instance_p
private static integer runInstanceCount_p
private static integer runAttackCount_p
private static method allocateAttack takes integer damageType, integer damageId, integer instance returns nothing
set runInstanceCount_p = runInstanceCount_p + 1
set damageType_p[runInstanceCount_p] = damageType
set damageId_p[runInstanceCount_p] = damageId
set instance_p[runInstanceCount_p] = instance
endmethod
static method unitDamageTarget takes unit attacker, widget target, real amount, integer damageType, integer damageId, integer instance returns boolean
if (enabled) then
call allocateAttack(damageType, damageId, instance)
return UnitDamageTarget(attacker, target, amount, false, false, null, DAMAGE_TYPE_UNIVERSAL, null)
endif
return false
endmethod
static method operator damageType takes nothing returns integer
return damageType_p[runInstanceCount_p]
endmethod
static method operator damageId takes nothing returns integer
return damageId_p[runInstanceCount_p]
endmethod
static method operator instance takes nothing returns integer
return instance_p[runInstanceCount_p]
endmethod
static method operator depth takes nothing returns integer
return runAttackCount_p
endmethod
private static delegate DamageEventProperties damageEventProperties = 0
private static method damage takes nothing returns boolean
local integer previousTargetId
local integer previousSourceId
local real previousAmount
//! runtextmacro optional ADV_DAMAGE_EVENT_LOC_BEFORE()
if (enabled and 0 != GetEventDamage()) then
/*
* Setup spell
*/
set runAttackCount_p = runAttackCount_p + 1
if (runAttackCount_p != runInstanceCount_p) then
set runInstanceCount_p = runInstanceCount_p + 1
endif
/*
* Store previous amounts
*/
set previousTargetId = targetId
set previousSourceId = sourceId
set previousAmount = amount
/*
* Update amounts to new amounts
*/
set targetId = GetUnitUserData(GetTriggerUnit())
set sourceId = GetUnitUserData(GetEventDamageSource())
set amount = GetEventDamage()
/*
* Fire event
*/
//! runtextmacro optional ADV_DAMAGE_EVENT_EXT()
call ANY.fire()
/*
* Restore previous amounts
*/
set targetId = previousTargetId
set sourceId = previousSourceId
set amount = previousAmount
//! runtextmacro optional ADV_DAMAGE_EVENT_LOC_AFTER()
/*
* Remove spell
*/
set damageType_p[runInstanceCount_p] = 0
set damageId_p[runInstanceCount_p] = 0
set instance_p[runInstanceCount_p] = 0
set runInstanceCount_p = runInstanceCount_p - 1
set runAttackCount_p = runAttackCount_p - 1
endif
return false
endmethod
private static method index takes nothing returns boolean
call UnitIndex(GetIndexedUnitId()).lock()
call DamageTriggerHeap.add(GetIndexedUnitId())
return false
endmethod
private static method deindex takes nothing returns boolean
call DamageTriggerHeap.remove(GetIndexedUnitId())
call UnitIndex(GetIndexedUnitId()).unlock()
return false
endmethod
private static method onInit takes nothing returns nothing
set enabled = true
set runInstanceCount_p = 0
set runAttackCount_p = 0
set ANY = PriorityEvent.create()
call RegisterUnitIndexEvent(Condition(function thistype.index), UnitIndexer.INDEX)
call RegisterUnitIndexEvent(Condition(function thistype.deindex), UnitIndexer.DEINDEX)
set damageCondition = Condition(function thistype.damage)
set targetId = 0
set sourceId = 0
set amount = 0
endmethod
endmodule
struct DamageEvent extends array
implement DamageEventMod
endstruct
module ExtendDamageEvent
static if AdvDamageEvent_p.onDamage_p_core.exists then
private static delegate AdvDamageEvent advDamageEvent = 0
else
private static delegate DamageEvent damageEvent = 0
endif
endmodule
module DamageEvent
implement ExtendDamageEvent
static if thistype.onDamage.exists then
private static method onDamage_p takes nothing returns boolean
static if thistype.filter.exists then
if (filter()) then
call onDamage()
endif
else
call onDamage()
endif
return false
endmethod
private static method onInit takes nothing returns nothing
static if thistype.PRIORITY.exists then
call DamageEvent.ANY.register(Condition(function thistype.onDamage_p), PRIORITY())
else
call DamageEvent.ANY.register(Condition(function thistype.onDamage_p), 0)
endif
endmethod
endif
endmodule
endlibrary
//TESH.scrollpos=66
//TESH.alwaysfold=0
//
// ___ _ __ __ _ ___ ____ _______________________________
// | \ /_\ / |/ | /_\ / _\| __| || D E A L I T , ||
// | |) / _ \ / / | / |/ _ \| |/|| __| || D E T E C T I T , ||
// |___/_/ \_/_/|__/|_|_/ \_\___/|____| || B L O C K I T . ||
// By Jesus4Lyf ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// v 1.0.5
// What is Damage?
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// Damage is a damage dealing, detection and blocking system. It implements
// all such functionality. It also provides a means to detect what type
// of damage was dealt, so long as all damage in your map is dealt using
// this system's deal damage functions (except for basic attacks).
//
// It is completely recursively defined, meaning if you deal damage on
// taking damage, the type detection and other features like blocking
// will not malfunction.
//
// How to implement?
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// Create a new trigger object called Damage, go to 'Edit -> Convert to
// Custom Text', and replace everything that's there with this script.
//
// At the top of the script, there is a '//! external ObjectMerger' line.
// Save your map, close your map, reopen your map, and then comment out this
// line. Damage is now implemented. This line creates a dummy ability used
// in the system in some circumstances with damage blocking.
//
// Functions:
// ¯¯¯¯¯¯¯¯¯¯¯¯
// function Damage_RegisterEvent takes trigger whichTrigger returns nothing
// - This registers a special "any unit takes damage" event.
// - This event supports dynamic trigger use.
// - Only triggers registered on this event may block damage.
// - Only fires when the damage dealt is not 0.
//
// function Damage_RegisterZeroEvent takes trigger whichTrigger returns nothing
// - The same as Damage_RegisterEvent, but for only zero damage events
// (which are excluded from Damage_RegisterEvent).
// - Note that getting the damage type may be unreliable, since spells
// like faerie fire trigger 0 damage, but it will count as an attack.
//
// function Damage_GetType takes nothing returns damagetype
// - This will get the type of damage dealt, like an event response,
// for when using a unit takes damage event (or the special event above).
//
// function Damage_IsPhysical takes nothing returns boolean
// function Damage_IsSpell takes nothing returns boolean
// function Damage_IsPure takes nothing returns boolean
// - Wrappers to simply check if Damage_GetType is certain types.
//
// function Damage_IsAttack takes nothing returns boolean
// - Checks if the damage is from a physical attack (so you can deal
// physical damage without it being registered as an actual attack).
//
// function Damage_Block takes real amount returns nothing
// function Damage_BlockAll takes nothing returns nothing
// - For use only with Damage_RegisterEvent.
// - Blocks 'amount' of the damage dealt.
// - Multiple blocks at once work correctly.
// - Blocking more than 100% of the damage will block 100% instead.
// - Damage_BlockAll blocks 100% of the damage being dealt.
//
// function Damage_EnableEvent takes boolean enable returns nothing
// - For disabling and re-enabling the special event.
// - Use it to deal damage which you do not want to be detected by
// the special event.
//
// function UnitDamageTargetEx takes lots of things returns boolean
// - Replaces UnitDamageTarget in your map, with the same arguments.
//
// function Damage_Physical takes unit source, unit target, real amount,
// attacktype whichType, boolean attack, boolean ranged returns boolean
// - A clean wrapper for physical damage.
// - 'attack' determines if this is to be treated as a real physical
// attack or just physical type damage.
// - 'ranged' determines if this is to be treated as a ranged or melee
// attack.
//
// function Damage_Spell takes unit source, unit target, real amount returns boolean
// - A clean wrapper for spell damage.
//
// function Damage_Pure takes unit source, unit target, real amount returns boolean
// - A clean wrapper for pure type damage (universal type, 100% damage).
//
// Thanks:
// ¯¯¯¯¯¯¯¯¯
// - Romek, for helping me find a better way to think about damage blocking.
//
library Damage uses AIDS, Event
//============================================================
///! external ObjectMerger w3a AIlz dprv anam "Life Bonus" ansf "(Damage System)" Ilif 1 500000 aite 0
globals
private constant integer LIFE_BONUS_ABIL='dprv'
endglobals
//============================================================
globals
private Event OnDamageEvent
private Event OnZeroDamageEvent
private boolean EventEnabled=true
endglobals
public function RegisterEvent takes trigger whichTrigger returns nothing
call OnDamageEvent.register(whichTrigger)
endfunction
public function RegisterZeroEvent takes trigger whichTrigger returns nothing
call OnZeroDamageEvent.register(whichTrigger)
endfunction
public function EnableEvent takes boolean enable returns nothing
set EventEnabled=enable
endfunction
//============================================================
globals
private integer TypeStackLevel=0
private damagetype array TypeStackValue
private boolean array TypeStackAttack
private real array ToBlock
endglobals
public function GetType takes nothing returns damagetype
return TypeStackValue[TypeStackLevel]
endfunction
public function IsAttack takes nothing returns boolean
return TypeStackAttack[TypeStackLevel]
endfunction
public function Block takes real amount returns nothing
set ToBlock[TypeStackLevel]=ToBlock[TypeStackLevel]+amount
endfunction
public function BlockAll takes nothing returns nothing
set ToBlock[TypeStackLevel]=ToBlock[TypeStackLevel]+GetEventDamage()
endfunction
//============================================================
globals
private integer BlockNum=0
private unit array BlockUnit
private real array BlockUnitLife
private real array BlockRedamage
private unit array BlockDamageSource
private timer BlockTimer=CreateTimer()
endglobals
//============================================================
globals
private unit array RemoveBoosted
private integer RemoveBoostedMax=0
private timer RemoveBoostedTimer=CreateTimer()
endglobals
globals//locals
private real BoostedLifeTemp
private unit BoostedLifeUnit
endglobals
private function RemoveBoostedTimerFunc takes nothing returns nothing
loop
exitwhen RemoveBoostedMax==0
set BoostedLifeUnit=RemoveBoosted[RemoveBoostedMax]
set BoostedLifeTemp=GetWidgetLife(BoostedLifeUnit)
call UnitRemoveAbility(BoostedLifeUnit,LIFE_BONUS_ABIL)
if BoostedLifeTemp>0.405 then
call SetWidgetLife(BoostedLifeUnit,BoostedLifeTemp)
endif
set RemoveBoostedMax=RemoveBoostedMax-1
endloop
endfunction
//============================================================
private keyword Detector // Darn, I actually had to do this. XD
globals//locals
private unit ForUnit
private real NextHealth
endglobals
private function OnDamageActions takes nothing returns boolean
if EventEnabled then
if GetEventDamage()==0. then
call OnZeroDamageEvent.fire()
else
call OnDamageEvent.fire()
endif
if ToBlock[TypeStackLevel]!=0. then
//====================================================
// Blocking
set ForUnit=GetTriggerUnit()
set NextHealth=GetEventDamage()
if ToBlock[TypeStackLevel]>=NextHealth then
set NextHealth=GetWidgetLife(ForUnit)+NextHealth
else
set NextHealth=GetWidgetLife(ForUnit)+ToBlock[TypeStackLevel]
endif
call SetWidgetLife(ForUnit,NextHealth)
if GetWidgetLife(ForUnit)<NextHealth then
// NextHealth is over max health.
call UnitAddAbility(ForUnit,LIFE_BONUS_ABIL)
call SetWidgetLife(ForUnit,NextHealth)
set RemoveBoostedMax=RemoveBoostedMax+1
set RemoveBoosted[RemoveBoostedMax]=ForUnit
call ResumeTimer(RemoveBoostedTimer)
endif
//====================================================
set ToBlock[TypeStackLevel]=0.
endif
endif
return false
endfunction
//============================================================
function UnitDamageTargetEx takes unit whichUnit, widget target, real amount, boolean attack, boolean ranged, attacktype attackType, damagetype damageType, weapontype weaponType returns boolean
local boolean result
set TypeStackLevel=TypeStackLevel+1
set TypeStackValue[TypeStackLevel]=damageType
set TypeStackAttack[TypeStackLevel]=attack
set result=UnitDamageTarget(whichUnit,target,amount,attack,ranged,attackType,damageType,weaponType)
set TypeStackLevel=TypeStackLevel-1
return result
endfunction
//! textmacro Damage__DealTypeFunc takes NAME, TYPE
public function $NAME$ takes unit source, unit target, real amount returns boolean
return UnitDamageTargetEx(source,target,amount,false,false,ATTACK_TYPE_NORMAL,$TYPE$,WEAPON_TYPE_WHOKNOWS)
endfunction
public function Is$NAME$ takes nothing returns boolean
return GetType()==$TYPE$
endfunction
//! endtextmacro
//! runtextmacro Damage__DealTypeFunc("Pure","DAMAGE_TYPE_UNIVERSAL")
//! runtextmacro Damage__DealTypeFunc("Spell","DAMAGE_TYPE_MAGIC")
// Uses different stuff, but works much the same way.
public function Physical takes unit source, unit target, real amount, attacktype whichType, boolean attack, boolean ranged returns boolean
return UnitDamageTargetEx(source,target,amount,attack,ranged,whichType,DAMAGE_TYPE_NORMAL,WEAPON_TYPE_WHOKNOWS)
endfunction
public function IsPhysical takes nothing returns boolean
return GetType()==DAMAGE_TYPE_NORMAL
endfunction
//============================================================
private struct Detector extends array // Uses AIDS.
//! runtextmacro AIDS()
private static conditionfunc ACTIONS_COND
private trigger t
private method AIDS_onCreate takes nothing returns nothing
set this.t=CreateTrigger()
call TriggerAddCondition(this.t,thistype.ACTIONS_COND)
call TriggerRegisterUnitEvent(this.t,this.unit,EVENT_UNIT_DAMAGED)
endmethod
private method AIDS_onDestroy takes nothing returns nothing
call DestroyTrigger(this.t)
endmethod
private static method AIDS_onInit takes nothing returns nothing
set thistype.ACTIONS_COND=Condition(function OnDamageActions)
endmethod
endstruct
//============================================================
private module InitModule
private static method onInit takes nothing returns nothing
local unit abilpreload=CreateUnit(Player(15),'uloc',0,0,0)
call UnitAddAbility(abilpreload,LIFE_BONUS_ABIL)
call RemoveUnit(abilpreload)
set abilpreload=null
set OnDamageEvent=Event.create()
set OnZeroDamageEvent=Event.create()
set TypeStackValue[TypeStackLevel]=DAMAGE_TYPE_NORMAL
set TypeStackAttack[TypeStackLevel]=true
call TimerStart(RemoveBoostedTimer,0.0,false,function RemoveBoostedTimerFunc)
endmethod
endmodule
private struct InitStruct extends array
implement InitModule
endstruct
endlibrary
//TESH.scrollpos=61
//TESH.alwaysfold=0
library Damage /* v2.0.7
____ _ _ _____ _____
| _ \ /\ | \ / | /\ / ___\ | ___|
| | | | / \ | \/ | / \ | / _ | __|
| |_| | / /\ \ | |\/| | / /\ \ | |_\ \ | |___
|____/ /_/ \_\ |_| |_| /_/ \_\ \_____/ |_____|
*/uses/* one of the following
*/ optional AIDS /* thehelper.net/forums/showthread.php/130752-Advanced-Indexing-Data-Storage
*/ optional UnitIndexer /* hiveworkshop.com/forums/jass-functions-413/system-unit-indexer-172090/
and
*/ LinkedListModule /* thehelper.net/forums/showthread.php/168775-LinkedListModule
Description:
Damage management, detection and manipulation. Allows full control of damage done
by units
Extensions:
DamageMod v1.0.7 - thehelper.net/forums/showthread.php/168198-Damage-Mod
DamageType v1.0.1 - thehelper.net/forums/showthread.php/168364-DamageType
DamageId v1.0.1 - thehelper.net/forums/showthread.php/168970-DamageId
Armor v1.0.0 - thehelper.net/forums/showthread.php/168282-Armor
RawDamage v1.0.5 - thehelper.net/forums/showthread.php/168257-RawDamage
AmplifyDamage v1.0.4 - thehelper.net/forums/showthread.php/168255-AmplifyDamage
DampenDamage v1.0.4 - thehelper.net/forums/showthread.php/168256-DampenDamage
Shield v1.0.6 - thehelper.net/forums/showthread.php/168203-Shield
Demo map:
mediafire.com/?o8hxybolawjeun5
Special thanks to:
-Nestharus (Damage library concept)
-PurgeandFire111 (original idea)
Important note:
If there is another damage system in your map, replace it with this one, otherwise
it will cause collision issues. This system provides all the useful damage tools
you'll ever need.
**********************************************************************/
globals
//*********************************************************************
// For damage event recycling upon deindexing. Resets the main damage
// event recognition trigger whenever that amount of units are
// deindexed.
private constant integer MAX_WASTED_UNITS = 20
//*********************************************************************
// An ability based off item life bonus that should add a very high
// amount of hp to the unit, the higher the better
constant integer ABILITY_LIFE_BONUS = 'DcLB'
endglobals
/**********************************************************************
*
* function RegisterGlobalDamage takes code func returns triggercondition
* - Runs after damage mods and unit damage events.
* function UnregisterGlobalDamage takes triggercondition whichCondition returns nothing
* - Unregister the function from handling damage
* function RegisterUnitDamageDealtEvent takes unit whichUnit, code func returns DamageDealtEvent
* function RegisterUnitDamageTakenEvent takes unit whichUnit, code func returns DamageTakenEvent
* - Wrappers for the DamageDealtEvent and DamageTakenEvent register methods
* function UnregisterUnitDamageDealtEvent takes DamageDealtEvent whichEvent returns nothing
* function UnregisterUnitDamageTakenEvent takes DamageTakenEvent whichEvent returns nothing
* - Wrappers for the DamageDealtEvent and DamageTakenEvent unregister methods
* function GetUnitDamageReduction takes unit whichUnit, attacktype attackType, damagetype damageType returns real
* - Returns the amount of damage the unit takes from a specific type
* - of damage. 1.0 means it takes 100% of the damage.
* function GetUnitArmor takes unit whichUnit returns real
* - Returns the unit's armor, losses it's accuracy when dealing with
* - negative armor.
*
***********************************************************************
*
* struct Damage
*
* - This struct manages all damage done and taken.
*
* readonly static real amount
* - Total damage dealt.
* readonly static real dealt
* - Actual damage received by the unit:
* - Takes into account prevented and added damage.
* readonly static real recount
* - Recount damage done by all units.
* readonly static unit target
* readonly static unit source
* - Event units.
* readonly static integer targetId
* readonly static integer sourceId
* - The custom value of those units.
* readonly integer data
* - The damage event attached data.
* static boolean enabled
* - If false the system stops recording damage.
* static method prevent takes real howMuch returns real
* - Prevents damage done, call only inside damage handling method.
* - Note: after this call the unit's max health changes.
* static method add takes real howMuch returns nothing
* - Increases damage done, damage dealt this way is pure, call only
* - inside damage handling methods.
*
**********************************************************************
*
* struct DamageDealtEvent and DamageTakenEvent
*
* - Both structs have the same API, they record the unit's damage dealt
* - or taken.
*
* integer data
* - You're able to attach and retreive data from instances.
* static method register takes integer unitId, code func returns thistype
* - Registers unit's damage through the given code.
* method unregister takes nothing returns nothing
* - Unregisters the given damage event.
*
**********************************************************************
*
* struct UnitDamage
*
* - This struct manages damage done by units, uses unit custom value as
* - instances.
*
* readonly real dealt
* readonly real taken
* - Recount of unit damage, doesn't take into account current damage.
* boolean enabled
* - If false then damage events by this unit wont fire.
* method reset takes nothing returns nothing
* - Clears all the damage done by the given unit.
*
*********************************************************************/
globals
private trigger globalTrig =CreateTrigger()
private integer currentData =0
private real array unitDamageDealt
private real array unitDamageTaken
endglobals
/*********************************************************************/
//! runtextmacro optional DAMAGE_TYPE_EX1()
//! runtextmacro optional DAMAGE_MOD_EX1()
//! runtextmacro optional DAMAGE_ID_EX1()
/*********************************************************************/
function RegisterGlobalDamage takes code func returns triggercondition
return TriggerAddCondition(globalTrig,Filter(func))
endfunction
function UnregisterGlobalDamage takes triggercondition whichCondition returns nothing
call TriggerRemoveCondition(globalTrig,whichCondition)
endfunction
/*********************************************************************/
struct UnitDamage extends array
boolean enabled
method operator dealt takes nothing returns real
return unitDamageDealt[this]
endmethod
method operator taken takes nothing returns real
return unitDamageTaken[this]
endmethod
method reset takes nothing returns nothing
set unitDamageDealt[this]=0
endmethod
endstruct
/*********************************************************************/
//! runtextmacro DAMAGE_EVENT_REGISTRATION_SETUP("Dealt")
//! runtextmacro DAMAGE_EVENT_REGISTRATION_SETUP("Taken")
//! textmacro DAMAGE_EVENT_REGISTRATION_SETUP takes NAME
struct Damage$NAME$Event extends array
implement LinkedList
//*********************************************************************
// The instance's corresponding unit id.
private thistype root
//*********************************************************************
// First instance from the list attached to an unit.
private thistype node
//*********************************************************************
// The instance's trigger to be evaluated.
private trigger trig
//*********************************************************************
// If false the event wont be fired.
boolean enabled
//*********************************************************************
// Value assigned by the user.
integer data
static method fire takes thistype unitId returns nothing
local thistype exit = unitId.node
local thistype this = exit
//*********************************************************************
// If node points to nowhere then the list is empty.
if this==0 then
return
endif
//*********************************************************************
// Iterate through the list firing all the triggers.
loop
set currentData=data
if enabled then
call TriggerEvaluate(trig)
endif
set this=next
exitwhen this==exit
endloop
endmethod
static method register takes thistype unitId, code func returns thistype
local thistype this
if unitId.node==0 then
set this=createNode()
set unitId.node=this
else
set this=allocate()
call unitId.node.insertNode(this)
endif
set root=unitId
if this.trig==null then
set trig=CreateTrigger()
else
call TriggerClearConditions(trig)
endif
set enabled=true
call TriggerAddCondition(trig,Filter(func))
return this
endmethod
method unregister takes nothing returns nothing
call this.removeNode()
if next==this then
set root.node=0
elseif this==root.node then
set root.node=next
endif
call this.deallocate()
endmethod
static method clear takes thistype unitId returns nothing
call unitId.node.flushNode()
set unitId.node=0
endmethod
endstruct
//! endtextmacro
/*********************************************************************/
private module DamageModule
readonly static real recount =0
readonly static real dealt =0
readonly static real amount =0
readonly static unit source =null
readonly static unit target =null
readonly static integer sourceId=0
readonly static integer targetId=0
//*********************************************************************
// Keeps track of the amount of wasted unit indexes.
private static integer count =0
//*********************************************************************
// Keeps track of the additional damage dealt through the Damage.add
// method, this damage is applied at the end of the evaluation to
// prevent the unit from dying.
private static real toAdd =0
//*********************************************************************
// The main trigger that detects all damage done ingame
private static trigger dmgTrig =CreateTrigger()
//*********************************************************************
// Timer used to remove the life bonus ability from units who had
// damage prevented to them.
private static timer after =CreateTimer()
//*********************************************************************
// Stack used for simultaneous damage prevention, stores all the units
// inside it and then it gets cleared.
private static boolean array stackIs
private static integer array stack
//*********************************************************************
// Linked list of all units indexed.
private static integer array next
private static integer array prev
private static method handleDamage takes nothing returns nothing
local real hp
local unit u
//*********************************************************************
// Loops through the stack removing the life bonus ability from units,
// after that the unit gets removed from the stack.
loop
exitwhen stack[0]==0
static if LIBRARY_UnitIndexer then
set u=GetUnitById(stack[0])
else
set u=GetIndexUnit(stack[0])
endif
set hp=GetWidgetLife(u)
call UnitRemoveAbility(u,ABILITY_LIFE_BONUS)
call SetWidgetLife(u,hp)
set stackIs[stack[0]]=false
set stack[0]=stack[stack[0]]
endloop
endmethod
static method prevent takes real howMuch returns real
local real hp=GetWidgetLife(target)
set howMuch=RMinBJ(howMuch,amount)
set dealt=RMaxBJ(dealt-howMuch,0)
if dealt>hp or howMuch==0 then
return howMuch
endif
//*********************************************************************
// This is where the stack for damage prevention gets created. Only
// adds the life bonus ability if needed (remember that the damage
// prevention comes before the damage is actually done, so if the unit
// has it's max hp then adding life to it would bug.)
if not(stackIs[targetId]) and (hp+howMuch>=GetUnitState(target,UNIT_STATE_MAX_LIFE)) then
set stack[targetId]=stack[0]
set stack[0]=targetId
set stackIs[targetId]=true
call UnitAddAbility(target,ABILITY_LIFE_BONUS)
endif
call SetWidgetLife(target,hp+howMuch)
return howMuch
endmethod
static method operator enabled= takes boolean state returns nothing
if state then
call EnableTrigger(dmgTrig)
else
call DisableTrigger(dmgTrig)
endif
endmethod
static method operator data takes nothing returns integer
return currentData
endmethod
//! runtextmacro optional DAMAGE_ID_EX2()
static method add takes real howMuch returns nothing
set dealt=dealt+howMuch
set toAdd=toAdd+howMuch
endmethod
//*********************************************************************
// First damage handler method of the entire system.
private static method onDamage takes nothing returns boolean
//*********************************************************************
// Stores the previous damage instance inside locals, at the end of
// the execution the values are reverted.
local real prevAmount =amount
local real prevDealt =dealt
local real prevAdd =toAdd
local unit prevSource =source
local unit prevTarget =target
local integer prevSourceId =sourceId
local integer prevTargetId =targetId
//! runtextmacro optional DAMAGE_ID_EX3()
set amount=GetEventDamage()
set source=GetEventDamageSource()
set target=GetTriggerUnit()
set sourceId=GetUnitUserData(source)
set targetId=GetUnitUserData(target)
set toAdd=0
//! runtextmacro optional DAMAGE_ID_EX4()
static if OVERKILL then
set dealt=amount
else
set dealt=RMinBJ(amount,GetWidgetLife(target))
endif
//! runtextmacro optional DAMAGE_TYPE_EX2()
//! runtextmacro optional DAMAGE_MOD_EX2()
//*********************************************************************
// Fires the trigger attached to the unit's custom value, each trigger
// with a number of conditions that are added on the Register calls.
if UnitDamage[sourceId].enabled then
call DamageDealtEvent.fire(sourceId)
endif
if UnitDamage[targetId].enabled then
call DamageTakenEvent.fire(targetId)
endif
call TriggerEvaluate(globalTrig)
//*********************************************************************
// Keeping track of all damage dealt and taken...
set unitDamageDealt[sourceId]=unitDamageDealt[sourceId]+dealt
set unitDamageTaken[targetId]=unitDamageTaken[targetId]+dealt
set recount=recount+dealt
//*********************************************************************
// Applies all missing damage left to be dealt.
if 0<toAdd then
set enabled=false
call UnitDamageTarget(Damage.source,Damage.target,toAdd,true,false,ATTACK_TYPE_CHAOS,DAMAGE_TYPE_UNIVERSAL,null)
set enabled=true
endif
//*********************************************************************
// Reverts the variables to their previous values
set amount=prevAmount
set dealt=prevDealt
set source=prevSource
set target=prevTarget
set sourceId=prevSourceId
set targetId=prevTargetId
set toAdd=prevAdd
//! runtextmacro optional DAMAGE_ID_EX5()
//*********************************************************************
// When amount equals to 0 as well as any other variable it means that
// no more damage is left to be done so it starts the prevention timer
// if stack isn't empty
if not(stack[0]==0) and amount==0 then
call TimerStart(after,0,false,function thistype.handleDamage)
endif
set prevSource=null
set prevTarget=null
return false
endmethod
private static method onIndex takes nothing returns boolean
local integer index
local unit indexUnit
static if LIBRARY_UnitIndexer then
set index=GetIndexedUnitId()
set indexUnit=GetIndexedUnit()
else
set index=AIDS_GetIndexOfEnteringUnit()
set indexUnit=AIDS_GetEnteringIndexUnit()
endif
//*********************************************************************
// Adds the unit to the list.
set prev[next[0]]=index
set next[index]=next[0]
set next[0]=index
set prev[index]=0
//! runtextmacro optional DAMAGE_ID_EX6()
set UnitDamage[index].enabled=true
call TriggerRegisterUnitEvent(dmgTrig,indexUnit,EVENT_UNIT_DAMAGED)
set indexUnit=null
return false
endmethod
private static method onDeIndex takes nothing returns boolean
local integer i = 0
local integer index
static if LIBRARY_UnitIndexer then
set index=GetIndexedUnitId()
else
set index=AIDS_GetDecayingIndex()
endif
//*********************************************************************
// Deletes the given unit from the list.
set next[prev[index]]=next[index]
set prev[next[index]]=prev[index]
//*********************************************************************
// In order to prevent a high amount of unused evets added to the
// trigger whenever plenty of units are deindexed it refreshes the
// trigger's events using a linked list of indexed units.
set count=count+1
if count==MAX_WASTED_UNITS then
call DestroyTrigger(dmgTrig)
set dmgTrig=CreateTrigger()
call TriggerAddCondition(dmgTrig,Filter(function thistype.onDamage))
loop
set i=next[i]
exitwhen i==0
static if LIBRARY_UnitIndexer then
call TriggerRegisterUnitEvent(dmgTrig,GetUnitById(i),EVENT_UNIT_DAMAGED)
else
call TriggerRegisterUnitEvent(dmgTrig,GetIndexUnit(i),EVENT_UNIT_DAMAGED)
endif
endloop
set count=0
endif
//*********************************************************************
// Prepares the triggers for the next unit.
call DamageDealtEvent.clear(index)
call DamageTakenEvent.clear(index)
return false
endmethod
private static method onInit takes nothing returns nothing
debug if GetObjectName(ABILITY_LIFE_BONUS)=="" then
debug call BJDebugMsg("ERROR: Life Bonus Ability doesn't exist")
debug return
debug endif
call TriggerAddCondition(dmgTrig,Filter(function thistype.onDamage))
//*********************************************************************
// Detection of units that enter the map.
static if LIBRARY_UnitIndexer then
call RegisterUnitIndexEvent(Condition(function thistype.onIndex),UnitIndexer.INDEX)
call RegisterUnitIndexEvent(Condition(function thistype.onDeIndex),UnitIndexer.DEINDEX)
else
call AIDS_RegisterOnEnter(Condition(function thistype.onIndex))
call AIDS_RegisterOnDeallocate(Condition(function thistype.onDeIndex))
endif
endmethod
endmodule
struct Damage extends array
//*********************************************************************
// Module initialization calls perform before any other.
implement DamageModule
endstruct
/*********************************************************************/
function RegisterUnitDamageDealtEvent takes unit whichUnit, code func returns DamageDealtEvent
return DamageDealtEvent.register(GetUnitUserData(whichUnit),func)
endfunction
function RegisterUnitDamageTakenEvent takes unit whichUnit, code func returns DamageTakenEvent
return DamageTakenEvent.register(GetUnitUserData(whichUnit),func)
endfunction
function UnregisterUnitDamageDealtEvent takes DamageDealtEvent whichEvent returns nothing
call whichEvent.unregister()
endfunction
function UnregisterUnitDamageTakenEvent takes DamageTakenEvent whichEvent returns nothing
call whichEvent.unregister()
endfunction
/*********************************************************************/
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=152
//TESH.alwaysfold=0
library UnitIndexer /* v4.0.2.6
*************************************************************************************
*
* 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/
*
************************************************************************************
*
* 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 constant integer ABILITIES_UNIT_INDEXER = 'A@!!'
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)]) 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
//
// _ ___ ___ ___ _______________________________________________
// /_\ |_ _| \/ __| || A D V A N C E D I N D E X I N G ||
// / _ \ | || |) \__ \ || A N D ||
// /_/ \_\___|___/|___/ || D A T A S T O R A G E ||
// By Jesus4Lyf ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// v 1.1.0
// What is AIDS?
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// AIDS assigns unique integers between 1 and 8191 to units which enter
// the map. These can be used for arrays and data attaching.
//
// AIDS also allows you to define structs which are created automatically
// when units enter the map, and filtering which units should be indexed
// as well as for which units these structs should be created.
//
// How to implement?
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// Simply create a new trigger object called AIDS, go to 'Edit -> Convert
// to Custom Text', and replace everything that's there with this script.
//
// Save the map, close it, reopen it, and then delete the "!" from the
// FAR left side of the next lines (so "external" will line up with this line):
///! external ObjectMerger w3a Adef AIDS anam "State Detection" ansf "(AIDS)" aart "" arac 0
//
// At the top of the script, there is a 'UnitIndexingFilter' constant
// function. If the function returns true for the unit, then that unit
// will be automatically indexed. Setting this to true will automatically
// index all units. Setting it to false will disable automatic indexing.
//
// Functions:
// ¯¯¯¯¯¯¯¯¯¯¯¯
// function GetUnitId takes unit u returns integer
// - This returns the index of an indexed unit. This will return 0
// if the unit has not been indexed.
// - This function inlines. It does not check if the unit needs an
// index. This function is for the speed freaks.
// - Always use this if 'UnitIndexingFilter' simply returns true.
//
// function GetUnitIndex takes unit u returns integer
// - This will return the index of a unit if it has one, or assign
// an index if the unit doesn't have one (and return the new index).
// - Use this if 'UnitIndexingFilter' doesn't return true.
//
// function GetIndexUnit takes integer index returns unit
// - This returns the unit which has been assigned the 'index'.
//
// AIDS Structs:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// - Insert: //! runtextmacro AIDS() at the top of a struct to make it
// an AIDS struct.
// - AIDS structs cannot be created or destroyed manually. Instead, they
// are automatically created when an appropriate unit enters the map.
// - You cannot give members default values in their declaration.
// (eg: private integer i=5 is not allowed)
// - You cannot use array members.
// - AIDS structs must "extend array". This will remove some unused
// functions and enforce the above so there will be no mistakes.
// - There are four optional methods you can use in AIDS structs:
// - AIDS_onCreate takes nothing returns nothing
// - This is called when the struct is 'created' for the unit.
// - In here you can assign members their default values, which
// you would usually assign in their declarations.
// (eg: set this.i=5)
// - AIDS_onDestroy takes nothing returns nothing
// - This is called when the struct is 'destroyed' for the unit.
// - This is your substitute to the normal onDestroy method.
// - AIDS_filter takes unit u returns boolean
// - This is similar to the constant filter in the main system.
// - Each unit that enters the map will be tested by each AIDS
// struct filter. If it returns true for that unit, that unit
// will be indexed if it was not already, the AIDS struct will
// have its AIDS_onCreate method called, and later have its
// AIDS_onDestroy method called when the index is recycled.
// - Not declaring this will use the default AIDS filter instead.
// - AIDS_onInit takes nothing returns nothing
// - This is because I stole your onInit function with my textmacro.
// - You can use '.unit' from any AIDS struct to get the unit for which
// the struct is for.
// - The structs id will be the units index, so getting the struct for
// a unit inlines to a single native call, and you can typecast between
// different AIDS structs. This is the premise of AIDS.
// - Never create or destroy AIDS structs directly.
// - You can call .AIDS_addLock() and AIDS_removeLock() to increase or
// decrease the lock level on the struct. If a struct's lock level is
// not 0, it will not be destroyed until it is reduced to 0. Locks just
// put off AIDS struct destruction in case you wish to attach to a timer
// or something which must expire before the struct data disappears.
// Hence, not freeing all locks will leak the struct (and index).
//
// PUI and AutoIndex:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// - AIDS includes the PUI textmacros and the AutoIndex module, because
// these systems are not compatible with AIDS but have valid and distinct
// uses.
// - The PUI textmacros are better to use for spells than AIDS structs,
// because they are not created for all units, just those targetted by
// the spell (or whatever else is necessary).
// - The AutoData module is good for very simple array syntax for data
// attachment (although I don't recommend that people actually use it,
// it's here mostly for compatability). Note that unlike the PUI textmacros,
// units must pass the AIDS filter in order for this module to work with
// them. This is exactly as the same as in AutoIndex itself (AutoIndex
// has a filter too).
//
// Thanks:
// ¯¯¯¯¯¯¯¯¯
// - Romek, for writing 90% of this user documentation, challenging my
// interface, doing some testing, suggesting improvements and inspiring
// me to re-do my code to include GetUnitIndex as non-inlining.
// - grim001, for writing the AutoData module, and AutoIndex. I used the
// on-enter-map method that he used. Full credits for the AutoData module.
// - Cohadar, for writing his PUI textmacros. Full credits to him for these,
// except for my slight optimisations for this system.
// Also, I have used an optimised version of his PeriodicRecycler from
// PUI in this system to avoid needing a RemoveUnitEx function.
// - Vexorian, for helping Cohadar on the PUI textmacro.
// - Larcenist, for suggesting the AIDS acronym. Originally he suggested
// 'Alternative Index Detection System', but obviously I came up with
// something better. In fact, I'd say it looks like the acronym was
// an accident. Kinda neat, don't you think? :P
//
// Final Notes:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// - With most systems, I wouldn't usually add substitutes for alternative
// systems. However, UnitData systems are an exception, because they
// are incompatible with eachother. Since using this system forbids
// people from using the real PUI or AutoIndex, and a lot of resources
// use either of these, it made sense not to break them all.
//
// - If this documentation confused you as to how to use the system, just
// leave everything as default and use GetUnitId everywhere.
//
// - To use this like PUI (if you don't like spamming indices) simply
// make the AIDS filter return false, and use GetUnitIndex.
//
library AIDS initializer InitAIDS
//==============================================================================
// Configurables
//
globals
private constant boolean USE_PERIODIC_RECYCLER = false
private constant real PERIOD = 0.03125 // Recycles 32 units/second max.
// Lower to be able to recycle faster.
// Only used if USE_PERIODIC_RECYCLER
// is set to true.
private constant integer LEAVE_DETECTION_ABILITY = 'AIDS'
endglobals
private function UnitIndexingFilter takes unit u returns boolean
return true
endfunction
//==============================================================================
// System code
//
globals
// The unit stored at an index.
private unit array IndexUnit
private integer array LockLevel
endglobals
//==============================================================================
globals
// Recycle stack
private integer array RecycledIndex
private integer MaxRecycledIndex = 0
// Previous highest index
private integer MaxIndex = 0
endglobals
//==============================================================================
globals
private integer array DecayingIndex
private integer MaxDecayingIndex=0
private integer DecayChecker=0
endglobals
globals
private timer UndefendTimer=CreateTimer()
private integer array UndefendIndex
private integer UndefendStackIndex=0
endglobals
globals
private integer array UndefendExpiringIndex
private integer UndefendExpiringIndexLevel=0
endglobals
//==============================================================================
globals
// The Add/Remove stack (or assign/recycle stack).
//
// Indexing can become recusive since units can be created on index
// assignment or deallocation.
// To support this, a stack is used to store the event response results.
private integer ARStackLevel=0
private integer array ARStackIndex
private unit array ARStackUnit
// A later discovery revealed that the Add/Remove stack did not need to be
// used for deallocation. The alternative used works fine...
endglobals
public constant function GetEnteringIndexUnit takes nothing returns unit
return ARStackUnit[ARStackLevel]
endfunction
public function GetIndexOfEnteringUnit takes nothing returns integer
// Called in AIDS structs when units do not pass the initial AIDS filter.
if ARStackIndex[ARStackLevel]==0 then
// Get new index, from recycler first, else new.
// Store the current index on the (new) top level of the AR stack.
if MaxRecycledIndex==0 then // Get new.
set MaxIndex=MaxIndex+1
set ARStackIndex[ARStackLevel]=MaxIndex
else // Get from recycle stack.
set ARStackIndex[ARStackLevel]=RecycledIndex[MaxRecycledIndex]
set MaxRecycledIndex=MaxRecycledIndex-1
endif
// Store index on unit.
call SetUnitUserData(ARStackUnit[ARStackLevel],ARStackIndex[ARStackLevel])
set IndexUnit[ARStackIndex[ARStackLevel]]=ARStackUnit[ARStackLevel]
// Add index to recycle list.
set MaxDecayingIndex=MaxDecayingIndex+1
set DecayingIndex[MaxDecayingIndex]=ARStackIndex[ARStackLevel]
endif
return ARStackIndex[ARStackLevel]
endfunction
public constant function GetIndexOfEnteringUnitAllocated takes nothing returns integer
// Called in AIDS structs when units have passed the initial AIDS filter.
return ARStackIndex[ARStackLevel]
endfunction
public constant function GetDecayingIndex takes nothing returns integer
static if USE_PERIODIC_RECYCLER then
return DecayingIndex[DecayChecker]
else
return UndefendExpiringIndex[UndefendExpiringIndexLevel]
endif
endfunction
//==============================================================================
globals
// For structs and such which need to do things on unit index assignment.
private trigger OnEnter=CreateTrigger()
// The same, but for when units pass the initial filter anyway.
private trigger OnEnterAllocated=CreateTrigger()
// For structs and such which need to do things on unit index deallocation.
private trigger OnDeallocate=CreateTrigger()
endglobals
public function RegisterOnEnter takes boolexpr b returns triggercondition
return TriggerAddCondition(OnEnter,b)
endfunction
public function RegisterOnEnterAllocated takes boolexpr b returns triggercondition
return TriggerAddCondition(OnEnterAllocated,b)
endfunction
public function RegisterOnDeallocate takes boolexpr b returns triggercondition
return TriggerAddCondition(OnDeallocate,b)
endfunction
//==============================================================================
function GetIndexUnit takes integer index returns unit
debug if index==0 then
debug call BJDebugMsg("|cFFFF0000Error using AIDS:|r Trying to get the unit of index 0.")
debug elseif IndexUnit[index]==null then
debug call BJDebugMsg("|cFFFF0000Error using AIDS:|r Trying to get the unit of unassigned index.")
debug endif
return IndexUnit[index]
endfunction
function GetUnitId takes unit u returns integer
debug if u==null then
debug call BJDebugMsg("|cFFFF0000Error using AIDS:|r Trying to get the id (inlines) of null unit.")
debug elseif GetUnitUserData(u)==0 then
debug call BJDebugMsg("|cFFFF0000Error using AIDS:|r Trying to use GetUnitId (inlines) when you should be using GetUnitIndex (unit didn't pass filter).")
debug endif
return GetUnitUserData(u)
endfunction
globals//locals
private integer getindex
endglobals
function GetUnitIndex takes unit u returns integer // Cannot be recursive.
debug if u==null then
debug call BJDebugMsg("|cFFFF0000Error using AIDS:|r Trying to get the index of null unit.")
debug endif
set getindex=GetUnitUserData(u)
if getindex==0 then
// Get new index, from recycler first, else new.
// Store the current index in getindex.
if MaxRecycledIndex==0 then // Get new.
set MaxIndex=MaxIndex+1
set getindex=MaxIndex
else // Get from recycle stack.
set getindex=RecycledIndex[MaxRecycledIndex]
set MaxRecycledIndex=MaxRecycledIndex-1
endif
// Store index on unit.
call SetUnitUserData(u,getindex)
set IndexUnit[getindex]=u
static if USE_PERIODIC_RECYCLER then
// Add index to recycle list.
set MaxDecayingIndex=MaxDecayingIndex+1
set DecayingIndex[MaxDecayingIndex]=getindex
else
// Add leave detection ability.
call UnitAddAbility(ARStackUnit[ARStackLevel],LEAVE_DETECTION_ABILITY)
call UnitMakeAbilityPermanent(ARStackUnit[ARStackLevel],true,LEAVE_DETECTION_ABILITY)
endif
// Do not fire things here. No AIDS structs will be made at this point.
endif
return getindex
endfunction
//==============================================================================
public function AddLock takes integer index returns nothing
set LockLevel[index]=LockLevel[index]+1
endfunction
public function RemoveLock takes integer index returns nothing
set LockLevel[index]=LockLevel[index]-1
static if not USE_PERIODIC_RECYCLER then
if GetUnitUserData(IndexUnit[index])==0 and LockLevel[index]==0 then
// Increment stack for recursion.
set UndefendExpiringIndexLevel=UndefendExpiringIndexLevel+1
set UndefendExpiringIndex[UndefendExpiringIndexLevel]=index
// Fire things.
call TriggerEvaluate(OnDeallocate)
// Decrement stack for recursion.
set UndefendExpiringIndexLevel=UndefendExpiringIndexLevel-1
// Add the index to the recycler stack.
set MaxRecycledIndex=MaxRecycledIndex+1
set RecycledIndex[MaxRecycledIndex]=index
// Null the unit.
set IndexUnit[index]=null
endif
endif
endfunction
//==============================================================================
static if USE_PERIODIC_RECYCLER then
private function PeriodicRecycler takes nothing returns nothing
if MaxDecayingIndex>0 then
set DecayChecker=DecayChecker+1
if DecayChecker>MaxDecayingIndex then
set DecayChecker=1
endif
if GetUnitUserData(IndexUnit[DecayingIndex[DecayChecker]])==0 then
if LockLevel[DecayingIndex[DecayChecker]]==0 then
// Fire things.
call TriggerEvaluate(OnDeallocate)
// Add the index to the recycler stack.
set MaxRecycledIndex=MaxRecycledIndex+1
set RecycledIndex[MaxRecycledIndex]=DecayingIndex[DecayChecker]
// Null the unit.
set IndexUnit[DecayingIndex[DecayChecker]]=null
// Remove index from decay list.
set DecayingIndex[DecayChecker]=DecayingIndex[MaxDecayingIndex]
set MaxDecayingIndex=MaxDecayingIndex-1
endif
endif
endif
endfunction
else
private function UndefendFilter takes nothing returns boolean
return IsUnitType(GetFilterUnit(),UNIT_TYPE_DEAD)
endfunction
private function OnUndefendTimer takes nothing returns nothing
loop
exitwhen UndefendStackIndex==0
set UndefendStackIndex=UndefendStackIndex-1
set UndefendExpiringIndex[0]=UndefendIndex[UndefendStackIndex]
if IndexUnit[UndefendExpiringIndex[0]]!=null then
if GetUnitUserData(IndexUnit[UndefendExpiringIndex[0]])==0 then
if LockLevel[UndefendExpiringIndex[0]]==0 then
// Fire things.
call TriggerEvaluate(OnDeallocate)
// Add the index to the recycler stack.
set MaxRecycledIndex=MaxRecycledIndex+1
set RecycledIndex[MaxRecycledIndex]=UndefendExpiringIndex[0]
// Null the unit.
set IndexUnit[UndefendExpiringIndex[0]]=null
endif
endif
endif
endloop
endfunction
globals//locals
private integer UndefendFilterIndex
endglobals
private function OnUndefend takes nothing returns boolean
if GetIssuedOrderId()==852056 then // If undefend then...
set UndefendFilterIndex=GetUnitUserData(GetOrderedUnit())
if UndefendIndex[UndefendStackIndex-1]!=UndefendFilterIndex then // Efficiency perk.
set UndefendIndex[UndefendStackIndex]=UndefendFilterIndex
set UndefendStackIndex=UndefendStackIndex+1
call TimerStart(UndefendTimer,0,false,function OnUndefendTimer)
endif
endif
return false
endfunction
endif
//==============================================================================
public function IndexEnum takes nothing returns boolean // Can be recursive...
// Start by adding another level on the AR stack (for recursion's sake).
set ARStackLevel=ARStackLevel+1
// Store the current unit on the (new) top level of the AR stack.
set ARStackUnit[ARStackLevel]=GetFilterUnit()
if GetUnitUserData(ARStackUnit[ARStackLevel])==0 then // Has not been indexed.
if UnitIndexingFilter(ARStackUnit[ARStackLevel]) then
// Get new index, from recycler first, else new.
// Store the current index on the (new) top level of the AR stack.
if MaxRecycledIndex==0 then // Get new.
set MaxIndex=MaxIndex+1
set ARStackIndex[ARStackLevel]=MaxIndex
else // Get from recycle stack.
set ARStackIndex[ARStackLevel]=RecycledIndex[MaxRecycledIndex]
set MaxRecycledIndex=MaxRecycledIndex-1
endif
// Store index on unit.
call SetUnitUserData(ARStackUnit[ARStackLevel],ARStackIndex[ARStackLevel])
set IndexUnit[ARStackIndex[ARStackLevel]]=ARStackUnit[ARStackLevel]
static if USE_PERIODIC_RECYCLER then
// Add index to recycle list.
set MaxDecayingIndex=MaxDecayingIndex+1
set DecayingIndex[MaxDecayingIndex]=ARStackIndex[ARStackLevel]
else
// Add leave detection ability.
call UnitAddAbility(ARStackUnit[ARStackLevel],LEAVE_DETECTION_ABILITY)
call UnitMakeAbilityPermanent(ARStackUnit[ARStackLevel],true,LEAVE_DETECTION_ABILITY)
endif
// Fire things.
call TriggerEvaluate(OnEnter)
else
// The unit did not pass the filters, so does not need to be auto indexed.
// However, for certain AIDS structs, it may still require indexing.
// These structs may index the unit on their creation.
// We flag that an index must be assigned by setting the current index to 0.
set ARStackIndex[ARStackLevel]=0
// Fire things.
call TriggerEvaluate(OnEnter)
endif
endif
// Decrement the stack.
set ARStackLevel=ARStackLevel-1
return false
endfunction
//==============================================================================
private function InitAIDS takes nothing returns nothing
local region r=CreateRegion()
local group g=CreateGroup()
local integer n=15
static if USE_PERIODIC_RECYCLER then
call TimerStart(UndefendTimer,PERIOD,true,function PeriodicRecycler)
else
local trigger t=CreateTrigger()
loop
call TriggerRegisterPlayerUnitEvent(t,Player(n),EVENT_PLAYER_UNIT_ISSUED_ORDER,Filter(function UndefendFilter))
call SetPlayerAbilityAvailable(Player(n),LEAVE_DETECTION_ABILITY,false)
// Capture "undefend" orders.
exitwhen n==0
set n=n-1
endloop
set n=15
call TriggerAddCondition(t,Filter(function OnUndefend))
set t=null
endif
// This must be done first, due to recursion. :)
call RegionAddRect(r,GetWorldBounds())
call TriggerRegisterEnterRegion(CreateTrigger(),r,Filter(function IndexEnum))
set r=null
loop
call GroupEnumUnitsOfPlayer(g,Player(n),Filter(function IndexEnum))
//Enum every non-filtered unit on the map during initialization and assign it a unique
//index. By using GroupEnumUnitsOfPlayer, even units with Locust can be detected.
exitwhen n==0
set n=n-1
endloop
call DestroyGroup(g)
set g=null
endfunction
//==============================================================================
public struct DEFAULT extends array
method AIDS_onCreate takes nothing returns nothing
endmethod
method AIDS_onDestroy takes nothing returns nothing
endmethod
static method AIDS_filter takes unit u returns boolean
return UnitIndexingFilter(u)
endmethod
static method AIDS_onInit takes nothing returns nothing
endmethod
endstruct
//===========================================================================
// Never create or destroy AIDS structs directly.
// Also, do not initialise members except by using the AIDS_onCreate method.
//===========================================================================
//! textmacro AIDS
// This magic line makes default methods get called which do nothing
// if the methods are otherwise undefined.
private static delegate AIDS_DEFAULT AIDS_DELEGATE=0
//-----------------------------------------------------------------------
// Gotta know whether or not to destroy on deallocation...
private boolean AIDS_instanciated
//-----------------------------------------------------------------------
static method operator[] takes unit whichUnit returns thistype
return GetUnitId(whichUnit)
endmethod
method operator unit takes nothing returns unit
// Allows structVar.unit to return the unit.
return GetIndexUnit(this)
endmethod
//-----------------------------------------------------------------------
method AIDS_addLock takes nothing returns nothing
call AIDS_AddLock(this)
endmethod
method AIDS_removeLock takes nothing returns nothing
call AIDS_RemoveLock(this)
endmethod
//-----------------------------------------------------------------------
private static method AIDS_onEnter takes nothing returns boolean
// At this point, the unit might not have been assigned an index.
if thistype.AIDS_filter(AIDS_GetEnteringIndexUnit()) then
// Flag it for destruction on deallocation.
set thistype(AIDS_GetIndexOfEnteringUnit()).AIDS_instanciated=true
// Can use inlining "Assigned" function now, as it must be assigned.
call thistype(AIDS_GetIndexOfEnteringUnitAllocated()).AIDS_onCreate()
endif
return false
endmethod
private static method AIDS_onEnterAllocated takes nothing returns boolean
// At this point, the unit must have been assigned an index.
if thistype.AIDS_filter(AIDS_GetEnteringIndexUnit()) then
// Flag it for destruction on deallocation. Slightly faster!
set thistype(AIDS_GetIndexOfEnteringUnitAllocated()).AIDS_instanciated=true
// Can use inlining "Assigned" function now, as it must be assigned.
call thistype(AIDS_GetIndexOfEnteringUnitAllocated()).AIDS_onCreate()
endif
return false
endmethod
private static method AIDS_onDeallocate takes nothing returns boolean
if thistype(AIDS_GetDecayingIndex()).AIDS_instanciated then
call thistype(AIDS_GetDecayingIndex()).AIDS_onDestroy()
// Unflag destruction on deallocation.
set thistype(AIDS_GetDecayingIndex()).AIDS_instanciated=false
endif
return false
endmethod
//-----------------------------------------------------------------------
private static method onInit takes nothing returns nothing
call AIDS_RegisterOnEnter(Filter(function thistype.AIDS_onEnter))
call AIDS_RegisterOnEnterAllocated(Filter(function thistype.AIDS_onEnterAllocated))
call AIDS_RegisterOnDeallocate(Filter(function thistype.AIDS_onDeallocate))
// Because I robbed you of your struct's onInit method.
call thistype.AIDS_onInit()
endmethod
//! endtextmacro
endlibrary
library PUI uses AIDS
//===========================================================================
// Allowed PUI_PROPERTY TYPES are: unit, integer, real, boolean, string
// Do NOT put handles that need to be destroyed here (timer, trigger, ...)
// Instead put them in a struct and use PUI textmacro
//===========================================================================
//! textmacro PUI_PROPERTY takes VISIBILITY, TYPE, NAME, DEFAULT
$VISIBILITY$ struct $NAME$
private static unit array pui_unit
private static $TYPE$ array pui_data
//-----------------------------------------------------------------------
// Returns default value when first time used
//-----------------------------------------------------------------------
static method operator[] takes unit whichUnit returns $TYPE$
local integer pui = GetUnitId(whichUnit) // Changed from GetUnitIndex.
if .pui_unit[pui] != whichUnit then
set .pui_unit[pui] = whichUnit
set .pui_data[pui] = $DEFAULT$
endif
return .pui_data[pui]
endmethod
//-----------------------------------------------------------------------
static method operator[]= takes unit whichUnit, $TYPE$ whichData returns nothing
local integer pui = GetUnitIndex(whichUnit)
set .pui_unit[pui] = whichUnit
set .pui_data[pui] = whichData
endmethod
endstruct
//! endtextmacro
//===========================================================================
// Never destroy PUI structs directly.
// Use .release() instead, will call .destroy()
//===========================================================================
//! textmacro PUI
private static unit array pui_unit
private static integer array pui_data
private static integer array pui_id
//-----------------------------------------------------------------------
// Returns zero if no struct is attached to unit
//-----------------------------------------------------------------------
static method operator[] takes unit whichUnit returns integer
local integer pui = GetUnitId(whichUnit) // Changed from GetUnitIndex.
// Switched the next two lines for optimisation.
if .pui_unit[pui] != whichUnit then
if .pui_data[pui] != 0 then
// recycled index detected
call .destroy(.pui_data[pui])
set .pui_unit[pui] = null
set .pui_data[pui] = 0
endif
endif
return .pui_data[pui]
endmethod
//-----------------------------------------------------------------------
// This will overwrite already attached struct if any
//-----------------------------------------------------------------------
static method operator[]= takes unit whichUnit, integer whichData returns nothing
local integer pui = GetUnitIndex(whichUnit)
if .pui_data[pui] != 0 then
call .destroy(.pui_data[pui])
endif
set .pui_unit[pui] = whichUnit
set .pui_data[pui] = whichData
set .pui_id[whichData] = pui
endmethod
//-----------------------------------------------------------------------
// If you do not call release struct will be destroyed when unit handle gets recycled
//-----------------------------------------------------------------------
method release takes nothing returns nothing
local integer pui= .pui_id[integer(this)]
call .destroy()
set .pui_unit[pui] = null
set .pui_data[pui] = 0
endmethod
//! endtextmacro
endlibrary
library AutoIndex uses AIDS
module AutoData
private static thistype array data
// Fixed up the below to use thsitype instead of integer.
static method operator []= takes unit u, thistype i returns nothing
set .data[GetUnitId(u)] = i //Just attaching a struct to the unit
endmethod //using the module's thistype array.
static method operator [] takes unit u returns thistype
return .data[GetUnitId(u)] //Just returning the attached struct.
endmethod
endmodule
endlibrary
//TESH.scrollpos=68
//TESH.alwaysfold=0
/***********************************************
*
* SoundTools
* v1.1.0.1
* By Magtheridon96
*
* (Special Thanks to Rising_Dusk)
*
* - Allows you to play sounds immediately after creating them.
* - Uses a sound recycler to increase efficiency and save RAM.
*
* API:
* ----
*
* - constant boolean DEFAULT_SOUND_STOPS_ON_LEAVE_RANGE
* - constant integer DEFAULT_SOUND_FADE_IN_RATE
* - constant integer DEFAULT_SOUND_FADE_OUT_RATE
* - constant string DEFAULT_SOUND_EAX_SETTINGS
*
* - struct Sound extends array
*
* - static method create takes string fileName, integer duration, boolean looping, boolean is3D returns thistype
* - Creates a sound struct given the filepath, the duration in milliseconds, whether it is looping or not, and whether it is 3D or not.
* - static method createEx takes string fileName, integer duration, boolean looping, boolean is3D, boolean stopOnExitRange, integer fadeIn, integer fadeOut, string eaxSetting returns thistype
* - In addition to static method create, this allows you to specificy whether the sound stops when the player leaves range, the fadeIn/fadeOut rates and the EAX Setting.
* - static method release takes sound s returns boolean
* - Releases a sound and throws it into the recycler. Also stops the sound.
*
* - method run takes nothing returns sound
* - Plays the sound.
* - method runUnit takes unit whichUnit returns sound
* - Plays the sound on a unit.
* - method runPoint takes real x, real y, real z returns sound
* - Plays the sound at a point.
* - method runPlayer takes player whichPlayer returns sound
* - Plays the sound for a player.
*
* - function NewSound takes string fileName, integer duration, boolean looping, boolean is3D returns Sound
* - Creates a sound struct given the filepath, the duration in milliseconds, whether it is looping or not, and whether it is 3D or not.
* - function NewSoundEx takes string fileName, integer duration, boolean looping, boolean is3D, boolean stop, integer fadeInRate, integer fadeOutRate, string eax returns Sound
* - In addition to static method create, this allows you to specificy whether the sound stops when the player leaves range, the fadeIn/fadeOut rates and the EAX Setting.
* - function ReleaseSound takes sound s returns boolean
* - Releases a sound and throws it into the recycler. Also stops the sound.
* - function RunSound takes Sound this returns sound
* - Plays the sound.
* - function RunSoundOnUnit takes Sound this, unit whichUnit returns sound
* - Plays the sound on a unit.
* - function RunSoundAtPoint takes Sound this, real x, real y, real z returns sound
* - Plays the sound at a point.
* - function RunSoundForPlayer takes Sound this, player p returns sound
* - Plays the sound for a player.
*
***********************************************/
library SoundTools requires Table, TimerUtils
// Configurables
globals
constant boolean DEFAULT_SOUND_STOPS_ON_LEAVE_RANGE = true
constant integer DEFAULT_SOUND_FADE_IN_RATE = 10
constant integer DEFAULT_SOUND_FADE_OUT_RATE = 10
constant string DEFAULT_SOUND_EAX_SETTINGS = "CombatSoundsEAX"
endglobals
globals
private constant integer SOUND_CHANNEL = 5
private constant integer SOUND_VOLUME = 127
private constant integer SOUND_PITCH = 1
private constant integer SOUND_MIN_DIST = 600
private constant integer SOUND_MAX_DIST = 10000
private constant integer SOUND_DIST_CUT = 3000
endglobals
// End Configuration
private module Init
private static method onInit takes nothing returns nothing
set tb = Table.create()
set pt = Table.create()
endmethod
endmodule
struct Sound extends array
private static Table tb
private static Table pt
private static integer index = 1
private static Table array stack
private static integer array count
private static string array file
private static integer array duration
private static boolean array looping
private static boolean array is3D
private static boolean array stopOnRange
private static integer array fadeIn
private static integer array fadeOut
private static string array eaxSetting
static method createEx takes string fileName, integer dur, boolean loopng, boolean isTD, boolean stop, integer fadeInRate, integer fadeOutRate, string eax returns thistype
local thistype this = index
set index = index + 1
set file[this] = fileName
set duration[this] = dur
set looping[this] = loopng
set is3D[this] = isTD
set stopOnRange[this] = stop
set fadeIn[this] = fadeInRate
set fadeOut[this] = fadeOutRate
set eaxSetting[this] = eax
set stack[this] = Table.create()
return this
endmethod
static method create takes string fileName, integer dur, boolean loopng, boolean isTD returns thistype
return createEx(fileName, dur, loopng, isTD, DEFAULT_SOUND_STOPS_ON_LEAVE_RANGE, DEFAULT_SOUND_FADE_IN_RATE, DEFAULT_SOUND_FADE_OUT_RATE, DEFAULT_SOUND_EAX_SETTINGS)
endmethod
private static sound snd
private method get takes nothing returns sound
if count[this] == 0 then
// Create a new sound using struct data
set snd = CreateSound(file[this], looping[this], is3D[this], stopOnRange[this], fadeIn[this], fadeOut[this], eaxSetting[this])
set pt[GetHandleId(snd)] = this
// Proper Configuration
call SetSoundDuration(snd, duration[this])
call SetSoundChannel(snd, SOUND_CHANNEL)
call SetSoundVolume(snd, SOUND_VOLUME)
call SetSoundPitch(snd, SOUND_PITCH)
// Proper 3D Sound Configuration
if is3D[this] then
call SetSoundDistances(snd, SOUND_MIN_DIST, SOUND_MAX_DIST)
call SetSoundDistanceCutoff(snd, SOUND_DIST_CUT)
call SetSoundConeAngles(snd, 0, 0, SOUND_VOLUME)
call SetSoundConeOrientation(snd, 0, 0, 0)
endif
return snd
endif
// Decrease stack count
set count[this] = count[this] - 1
return stack[this].sound[count[this]]
endmethod
private method push takes sound s returns nothing
set stack[this].sound[count[this]] = s
set count[this] = count[this] + 1
endmethod
private static method recycle takes nothing returns nothing
local timer t = GetExpiredTimer()
local sound s = tb.sound[GetHandleId(t)]
// Stop sound and throw it in the recycler
call StopSound(s, false, true)
call thistype(GetTimerData(t)).push(s)
call ReleaseTimer(t)
set t = null
set s = null
endmethod
private static integer array next
private static sound array media
private static method runSounds takes nothing returns nothing
local integer n = next[0]
local timer t
call ReleaseTimer(GetExpiredTimer())
loop
exitwhen n == 0
// Play the sound
call StartSound(media[n])
if not looping[n] then
set t = NewTimerEx(n)
set tb.sound[GetHandleId(t)] = media[n]
call TimerStart(t, duration[n] * 0.001, false, function thistype.recycle)
endif
set media[n] = null
set n = next[n]
endloop
set next[0] = 0
set t = null
endmethod
method run takes nothing returns sound
if next[0] == 0 then
call TimerStart(NewTimer(), 0, false, function thistype.runSounds)
endif
if media[this] == null then
set next[this] = next[0]
set next[0] = this
set media[this] = this.get()
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[SoundTools]Warning: Attempted to run the same sound twice.")
endif
return media[this]
endmethod
static method release takes sound s returns boolean
local integer id = GetHandleId(s)
// Safety Checks (You mad Nestharus?)
if s == null then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[SoundTools]Error: Attempted to release a null sound.")
return false
elseif pt[id] == 0 then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[SoundTools]Error: Attempted to release a sound not allocated by RunSound.")
return false
endif
// Stop the sound and recycle it.
call StopSound(s, false, true)
call thistype(pt[id]).push(s)
return true
endmethod
method runUnit takes unit whichUnit returns sound
set snd = this.run()
call AttachSoundToUnit(snd, whichUnit)
return snd
endmethod
method runPoint takes real x, real y, real z returns sound
set snd = this.run()
call SetSoundPosition(snd, x, y, z)
return snd
endmethod
method runPlayer takes player p returns sound
set snd = this.run()
if GetLocalPlayer() != p then
call SetSoundVolume(snd, 0)
endif
return snd
endmethod
implement Init
endstruct
function NewSoundEx takes string fileName, integer duration, boolean looping, boolean is3D, boolean stop, integer fadeInRate, integer fadeOutRate, string eax returns Sound
return Sound.createEx(fileName, duration, looping, is3D, stop, fadeInRate, fadeOutRate, eax)
endfunction
function NewSound takes string fileName, integer duration, boolean looping, boolean is3D returns Sound
return Sound.create(fileName, duration, looping, is3D)
endfunction
function RunSound takes Sound this returns sound
return this.run()
endfunction
function ReleaseSound takes sound s returns boolean
return Sound.release(s)
endfunction
function RunSoundOnUnit takes Sound this, unit whichUnit returns sound
return this.runUnit(whichUnit)
endfunction
function RunSoundAtPoint takes Sound this, real x, real y, real z returns sound
return this.runPoint(x, y, z)
endfunction
function RunSoundForPlayer takes Sound this, player p returns sound
return this.runPlayer(p)
endfunction
endlibrary
//TESH.scrollpos=288
//TESH.alwaysfold=0
library SoundUtils requires Stack, TimerUtils
//******************************************************************************
//* BY: Rising_Dusk
//*
//* Sounds are a very picky datatype in WC3. They have many quirks that one must
//* account for in order to use them, and simply using the internal WE Sound
//* Editor isn't enough because the sounds it makes can't be played multiple
//* times at once. 3-D sounds are also very tricky because there are different
//* WC3 sound options that a user can have activated where certain sounds will
//* or will not work. This library attempts to streamline the handling of sounds
//* so that it is less likely to confuse you or cause problems.
//*
//* The .mp3 format can be used for 3-D sounds, but there is one problem that
//* must be noted. If your computer supports the "Dolby Surround" sound option
//* in WC3 and you have it selected, then .mp3 files will work for 3-D sounds.
//* If you don't, however, they may not work depending on what you do have
//* selected and what is available for your computer. The .wav format works on
//* all possible settings, making them excellent for general use. This library
//* can interface with sounds of either type.
//*
//* Known issues with sounds that this library resolves:
//* - A given sound variable can only be played once at a time. In order to
//* play a sound type multiple times at once, you need multiple variables.
//* - A sound cannot be played at the same instant that it is created.
//*
//* The DefineSound function defines a sound type based on some basic parameters
//* the user provides. DefineSoundEx is available if the user wants control over
//* all possible parameters, though they won't have an impact most of the time.
//* The duration parameter for DefineSound and DefineSoundEx is in milliseconds,
//* which is consistent with Blizzard's natives. To get the duration of a given
//* sound, open up the WE's Sound Editor, navigate to your sound, and select
//* "Add as Sound." In doing so, it will show its duration in seconds. Multiply
//* that number by 1000 and use it as the duration argument.
//*
//* This library returns a sound variable with RunSound that you can change the
//* settings of using the standard JASS sound API. The library assigns default
//* values to the parameters for 2-D and 3-D sounds, that way they will run
//* without any further help.
//*
//* The library automatically allocates, runs, and recycles a sound when you
//* call RunSound. This library will not automatically recycle looping sounds,
//* so you will need to call ReleaseSound on the looping sound when you want it
//* to end.
//*
//******************************************************************************
//*
//* > function DefineSound takes string fileName, integer duration, ...
//* boolean looping, boolean is3D returns integer
//*
//* This function defines a sound type with a short list of parameters. The
//* returned integer serves as a SOUND_TYPE for running this type of sound at
//* any other point in a map.
//*
//* > function DefineSoundEx takes string fileName, integer duration, ...
//* boolean looping, boolean is3D, boolean stopwhenoutofrange, ...
//* integer fadeInRate, integer fadeOutRate, string eaxSetting ...
//* returns integer
//*
//* This function serves an identical purpose to DefineSound, but gives the user
//* full control over the entire list of parameters. Similar to DefineSound, the
//* returned integer serves as a SOUND_TYPE for running this type of sound.
//*
//* > function RunSound takes integer soundRef returns sound
//*
//* This function runs a sound with the parameters held within the soundRef
//* integer argument. The soundRef argument is the returned value of DefineSound
//* or DefineSoundEx.
//*
//* > function RunSoundOnUnit takes integer soundRef, unit whichUnit returns sound
//*
//* The same as RunSound, just this function runs a sound of a given type on a
//* specified unit.
//*
//* > function RunSoundAtPoint takes integer soundRef, real x, real y, real z returns sound
//*
//* The same as RunSound, just this function runs a sound of a given type at a
//* specified point in 3D space.
//*
//* > function RunSoundForPlayer takes integer soundRef, player p returns sound
//*
//* The same as RunSound, just this function runs a sound of a given type only
//* for the specified player.
//*
//* > function ReleaseSound takes sound s returns boolean
//*
//* This function need only be called on looping sounds. If a sound is not
//* looping, it will be released and recycled on its own. This function should
//* be used on looping sounds when you want them to end.
//*
//* Example usage:
//* set SOUND_TYPE = DefineSound("Sound\\Path.wav", 300, false, true)
//* call RunSound(SOUND_TYPE)
//* call RunSoundOnUnit(SOUND_TYPE, SomeUnit)
//* call RunSoundAtPoint(SOUND_TYPE, x, y, z)
//* call RunSoundForPlayer(SOUND_TYPE, Player(5))
//* call ReleaseSound(SomeLoopingSound)
//*
globals
private hashtable ht = InitHashtable() //Attach sound types to sounds
private hashtable st = InitHashtable() //Sound hashtable
private hashtable rt = InitHashtable() //Attach soundrecyclers to sounds
private hashtable kt = InitHashtable() //Attach StopSound data
endglobals
//Struct for each sound type
private struct soundhelper
//Stack associated to each struct
Stack sta
//Sound Settings for this sound type
string fileName = ""
integer duration = 0
boolean looping = false
boolean is3D = false
boolean stopwhenoutofrange = false
integer fadeInRate = 0
integer fadeOutRate = 0
string eaxSetting = ""
static method create takes string fileName, integer duration, boolean looping, boolean is3D, boolean stopwhenoutofrange, integer fadeInRate, integer fadeOutRate, string eaxSetting returns soundhelper
local soundhelper sh = soundhelper.allocate()
//Load the parameters so the sound can be created later as necessary
set sh.fileName = fileName
set sh.duration = duration
set sh.looping = looping
set sh.is3D = is3D
set sh.stopwhenoutofrange = stopwhenoutofrange
set sh.fadeInRate = fadeInRate
set sh.fadeOutRate = fadeOutRate
set sh.eaxSetting = eaxSetting
//Create the stack for the struct
set sh.sta = Stack.create()
return sh
endmethod
endstruct
//Struct for holding data for the sound recycling
private struct soundrecycler
timer t = null
sound s = null
integer sh = 0
boolean stopped = false //Only gets used if StopSound is called on a new sound
static method create takes sound whichSound, integer soundRef returns soundrecycler
local soundrecycler sr = soundrecycler.allocate()
set sr.t = NewTimer()
set sr.s = whichSound
set sr.sh = soundRef
call SetTimerData(sr.t, integer(sr))
//Hook the value to the soundRef and whichSound
call SaveInteger(rt, soundRef, GetHandleId(whichSound), integer(sr))
return sr
endmethod
private method onDestroy takes nothing returns nothing
call RemoveSavedInteger(rt, .sh, GetHandleId(.s))
call ReleaseTimer(.t)
endmethod
endstruct
//******************************************************************************
private function HookStopSound takes sound soundHandle, boolean killWhenDone, boolean fadeOut returns nothing
local integer id = GetHandleId(soundHandle)
local integer soundRef = 0
local soundrecycler sr = 0
if HaveSavedInteger(ht, 0, id) then //Sound is from stacks
set soundRef = LoadInteger(ht, 0, id)
if HaveSavedInteger(rt, soundRef, id) then //Sound has a recycler
set sr = soundrecycler(LoadInteger(rt, soundRef, id))
set sr.stopped = true
endif
if killWhenDone then
debug call BJDebugMsg(SCOPE_PREFIX+"Warning: (StopSound) Destroying a sound in the stack")
endif
endif
endfunction
hook StopSound HookStopSound
private function HookKillSoundWhenDone takes sound soundHandle returns nothing
if HaveSavedInteger(ht, 0, GetHandleId(soundHandle)) then
call BJDebugMsg(SCOPE_PREFIX+"Warning: (KillSoundWhenDone) Destroying a sound in the stack")
endif
endfunction
debug hook KillSoundWhenDone HookKillSoundWhenDone
//******************************************************************************
function DefineSoundEx takes string fileName, integer duration, boolean looping, boolean is3D, boolean stopwhenoutofrange, integer fadeInRate, integer fadeOutRate, string eaxSetting returns integer
return integer(soundhelper.create(fileName, duration, looping, is3D, stopwhenoutofrange, fadeInRate, fadeOutRate, eaxSetting))
endfunction
function DefineSound takes string fileName, integer duration, boolean looping, boolean is3D returns integer
return DefineSoundEx(fileName, duration, looping, is3D, true, 10, 10, "CombatSoundsEAX")
endfunction
function ReleaseSound takes sound s returns boolean
local integer id = GetHandleId(s)
local integer soundRef = 0
local soundhelper sh = 0
local soundrecycler sr = 0
if s == null then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Cannot recycle a null sound")
return false
elseif not HaveSavedInteger(ht, 0, id) then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Cannot recycle a sound not allocated by RunSound")
return false
endif
set soundRef = LoadInteger(ht, 0, id)
set sh = soundhelper(soundRef)
call StopSound(s, false, true) //Stop the sound
call sh.sta.push(id) //Return it to the stack
call SaveSoundHandle(st, soundRef, id, s) //Save it to hashtable
if not sh.looping then
//soundrecycler only exists for non-looping sounds
set sr = soundrecycler(LoadInteger(rt, soundRef, id))
call sr.destroy() //Destroy recycler helper
endif
return true
endfunction
private function Recycle takes nothing returns nothing
local soundrecycler sr = soundrecycler(GetTimerData(GetExpiredTimer()))
local soundhelper sh = soundhelper(sr.sh)
local integer id = GetHandleId(sr.s)
call StopSound(sr.s, false, true) //Stop the sound
call sh.sta.push(id) //Return it to the stack
call SaveSoundHandle(st, integer(sh), id, sr.s) //Save it to hashtable
call sr.destroy() //Destroy recycler helper
endfunction
private function Run takes nothing returns nothing
local soundrecycler sr = soundrecycler(GetTimerData(GetExpiredTimer()))
local soundhelper sh = soundhelper(sr.sh)
if not sr.stopped then
call StartSound(sr.s) //Play sound here
endif
if not sh.looping and not sr.stopped then
call TimerStart(sr.t, sh.duration*0.001, false, function Recycle)
else
call sr.destroy()
endif
endfunction
function RunSound takes integer soundRef returns sound
local sound s = null
local integer i = 0
local soundhelper sh = soundhelper(soundRef)
local soundrecycler sr = 0
if soundRef <= 0 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Cannot run sound of undefined type")
return null
endif
//Check if the stack is empty
if sh.sta.peek() == Stack.EMPTY then
//Create a new sound for the stack
set s = CreateSound(sh.fileName, sh.looping, sh.is3D, sh.stopwhenoutofrange, sh.fadeInRate, sh.fadeOutRate, sh.eaxSetting)
//Attach the type to the sound for future reference
call SaveInteger(ht, 0, GetHandleId(s), integer(sh))
call SetSoundDuration(s, sh.duration)
//Stuff that must be performed immediately upon creation of sounds
call SetSoundChannel(s, 5)
call SetSoundVolume(s, 127)
call SetSoundPitch(s, 1.)
if sh.is3D then
//These are settings necessary for 3-D sounds to function properly
//You can change them at will outside of this function
call SetSoundDistances(s, 600., 10000.)
call SetSoundDistanceCutoff(s, 3000.)
call SetSoundConeAngles(s, 0., 0., 127)
call SetSoundConeOrientation(s, 0., 0., 0.)
endif
//Start sound after a delay because it was created here
set sr = soundrecycler.create(s, soundRef)
call TimerStart(sr.t, 0.001, false, function Run)
else
//Allocate a sound from the stack
set i = sh.sta.pop()
if not HaveSavedHandle(st, soundRef, i) then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: No sound in given stack member")
return null
endif
set s = LoadSoundHandle(st, soundRef, i)
call RemoveSavedInteger(st, soundRef, i)
call SetSoundVolume(s, 127) //Start volume at max
//Start it here since it wasn't created here
call StartSound(s)
//Recycle the sound in a timer callback after it's finished if nonlooping
if not sh.looping then
set sr = soundrecycler.create(s, soundRef)
call TimerStart(sr.t, sh.duration*0.001, false, function Recycle)
endif
endif
return s
endfunction
function RunSoundOnUnit takes integer soundRef, unit whichUnit returns sound
local sound s = RunSound(soundRef)
call AttachSoundToUnit(s, whichUnit)
return s
endfunction
function RunSoundAtPoint takes integer soundRef, real x, real y, real z returns sound
local sound s = RunSound(soundRef)
call SetSoundPosition(s, x, y, z)
return s
endfunction
function RunSoundForPlayer takes integer soundRef, player p returns sound
local sound s = RunSound(soundRef)
if GetLocalPlayer() != p then
call SetSoundVolume(s, 0)
else
call SetSoundVolume(s, 127)
endif
return s
endfunction
endlibrary
//TESH.scrollpos=110
//TESH.alwaysfold=0
library Stack
//*****************************************************************
//* STACK
//*
//* written by: Anitarf
//*
//* This is an efficient implementation of a stack in vJass. Since
//* it is based on a linked list, I decided to add common list
//* methods to the data structure so it can function both as
//* a stack and a simple linked list.
//*
//* As a linked list, it has less functionality than Ammorth's
//* LinkedList, but is considerably faster. Note only that most of
//* the list methods have O(n) time complexity so they may not be
//* suitable for operations on very large lists, however due to
//* their simplicity the list would need to be really large for
//* this to become a problem.
//*
//* All stack methods are of course O(1) and as fast as possible.
//* If you just need a stack, this is definitely the best choice.
//*
//* set s=Stack.create() - Instanceates a new Stack object.
//* call s.destroy() - Destroys the Stack.
//*
//* Stack syntax:
//* call s.push(123) - Pushes the value 123 on the stack.
//* A stack may contain multiple
//* instances of the same value.
//* set i=s.peek() - Reads the top value of the stack
//* and stores it to the variable i.
//* set i=s.pop() - Removes the top value from the stack
//* and stores it to the variable i.
//* s.peek()==Stack.EMPTY - Checks if the stack is empty.
//*
//* List syntax:
//* call s.add(123) - Adds the value 123 to the list.
//* A list may contain multiple
//* instances of the same value.
//* s.size - The total number of values on the list.
//* s.contains(123) - Checks if the value 123 is on the list.
//* set n=s.count(123) - Stores the number of times the value
//* 123 is on the list to the variable n.
//* call s.remove(123) - Removes one instance of the value 123
//* from the list. Returns false if
//* the value was not found on the list.
//* call s.purge(123) - Removes all instances of the value 123
//* from the list. Returns the number of
//* values that were removed.
//* set i=s.first - Reads the first value from the list
//* and stores it to the variable i.
//* set i=s.random - Reads a random value from the list
//* and stores it to the variable i.
//* set s2=s.copy() - Makes a copy of the list and stores
//* it to the variable s2.
//* call s.enum(Func,b) - Calls function Func for all values
//* on the list. The function must follow
//* the Enum function interface.
//* b is a boolean value, if it is true
//* then the values will be enumerated
//* top to bottom, if false then bottom
//* to top.
//*****************************************************************
public function interface Enum takes integer value returns nothing
struct Stack
// Use a totally random number here, the more improbable someone uses it, the better.
// This is the value that is returned by .pop and .peek methods and .first and .random operators when called on an empty stack.
public static constant integer EMPTY=0x28829022
// End of calibration.
readonly integer size = 0
private integer top = 0
private static integer free = 1
private static integer array next
private static integer array value
method push takes integer i returns nothing
// Get an index from the list of free indexes.
local integer n=Stack.free
set Stack.free=Stack.next[n]
// Extend the list of free indexes if needed.
if Stack.free==0 then
set Stack.free=n+1
endif
// Store the value to the index.
set Stack.value[n]=i
// Add index to the top of the stack.
set Stack.next[n]=.top
set .top=n
set .size=.size+1
endmethod
method pop takes nothing returns integer
// Get the top index of stack.
local integer n=.top
// Safety check in case a user pops an empty stack.
if n==0 then
debug call BJDebugMsg("stack warning: .pop called on an empty stack!")
return Stack.EMPTY
endif
// Remove the top index from stack.
set .top=Stack.next[n]
set .size=.size-1
// Add the index to the list of free indexes.
set Stack.next[n]=Stack.free
set Stack.free=n
// Return the value.
return Stack.value[n]
endmethod
method peek takes nothing returns integer
// Read the value of the top index.
return Stack.value[.top]
endmethod
method add takes integer value returns nothing
call .push(value)
endmethod
method contains takes integer value returns boolean
// Get the first index of the list.
local integer i=.top
// Search through the list.
loop
// Stop the search when the end of the list is reached.
exitwhen i==0
// Stop the search if the value is found.
if Stack.value[i]==value then
return true
endif
// Get the next index of the list.
set i=Stack.next[i]
endloop
return false
endmethod
method count takes integer value returns integer
local integer count=0
// Get the first index of the list.
local integer i=.top
// Search through the list.
loop
// Stop the search when the end of the list is reached.
exitwhen i==0
// Increase the count if the value is found.
if Stack.value[i]==value then
set count=count+1
endif
// Get the next index of the list.
set i=Stack.next[i]
endloop
return count
endmethod
method operator first takes nothing returns integer
return .peek()
endmethod
method operator random takes nothing returns integer
local integer r=GetRandomInt(1,.size)
// Get the first index of the list.
local integer i=.top
// Loop through the list.
loop
// Stop the loop after a random amount of repeats.
set r=r-1
exitwhen r==0 or i==0
// Get the next index of the list.
set i=Stack.next[i]
endloop
return Stack.value[i]
endmethod
method remove takes integer value returns boolean
// Get the first index of the list.
local integer i1=.top
local integer i2
// Check if the first index holds the value.
if Stack.value[i1]==value then
call .pop()
return true
endif
// Search through the rest of the list.
loop
set i2=Stack.next[i1]
// Stop the search when the end of the list is reached.
exitwhen i2==0
// Check if the next index holds the value.
if Stack.value[i2]==value then
// Remove the index from the stack.
set Stack.next[i1]=Stack.next[i2]
// Add the removed index to the list of free indexes.
set Stack.next[i2]=Stack.free
set Stack.free=i2
set .size=.size-1
return true
endif
set i1=i2
endloop
return false
endmethod
method purge takes integer value returns integer
local integer count=0
local integer i1
local integer i2
// If the first index holds the value, pop it.
loop
// If the list is empty, return.
if .top==0 then
return count
endif
// Repeat until the first index doesn't hold the value.
exitwhen Stack.value[.top]!=value
call .pop()
set count=count+1
endloop
// Get the first index of the list.
set i1=.top
// Search through the rest of the list.
loop
set i2=Stack.next[i1]
// Stop the search when the end of the list is reached.
exitwhen i2==0
// Check if the next index holds the value.
if Stack.value[i2]==value then
// Remove the index from the stack.
set Stack.next[i1]=Stack.next[i2]
// Add the removed index to the list of free indexes.
set Stack.next[i2]=Stack.free
set Stack.free=i2
set .size=.size-1
set count=count+1
else
set i1=i2
endif
endloop
return count
endmethod
method enum takes Enum f, boolean top2bottom returns nothing
local integer array value
// Get the first index of the list.
local integer i1=.top
local integer i2=0
// Populate the array.
loop
exitwhen i1==0
set value[i2]=Stack.value[i1]
set i2=i2+1
set i1=Stack.next[i1]
endloop
// Call the Enum function for each value in the array.
set i1=i2-1
loop
exitwhen i2==0
set i2=i2-1
// Enumerate in which direction?
if top2bottom then
call f.evaluate(value[i1-i2])
else
call f.evaluate(value[i2])
endif
endloop
endmethod
method copy takes nothing returns Stack
local Stack that=Stack.create()
// Get the first index of the list.
local integer i1=.top
local integer i2
// Add a dummy index to the top of the new list.
call that.push(0)
set i2=that.top
loop
exitwhen i1==0
// Get an index from the list of free indexes and add it at the end of the list.
set Stack.next[i2]=Stack.free
set i2=Stack.next[i2]
set Stack.free=Stack.next[i2]
// Extend the list of free indexes if needed.
if Stack.free==0 then
set Stack.free=i2+1
endif
// Copy the value to the new index.
set Stack.value[i2]=Stack.value[i1]
set i1=Stack.next[i1]
endloop
// End the new list correctly.
set Stack.next[i2]=0
// Remove the dummy index.
call that.pop()
// Copy the size value to the new list.
set that.size=this.size
return that
endmethod
method onDestroy takes nothing returns nothing
local integer n
// Remove all remaining indexes from the stack.
loop
// Get the top index.
set n=.top
exitwhen n==0
// Remove it from the stack.
set .top=Stack.next[n]
// Add it to the list of free indexes.
set Stack.next[n]=Stack.free
set Stack.free=n
endloop
endmethod
static method onInit takes nothing returns nothing
// Store the EMPTY value to index 0 to make .peek inline friendly.
set Stack.value[0]=Stack.EMPTY
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library 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=152
//TESH.alwaysfold=0
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ~~ Event ~~ By Jesus4Lyf ~~ Version 1.04 ~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// What is Event?
// - Event simulates Warcraft III events. They can be created,
// registered for, fired and also destroyed.
// - Event, therefore, can also be used like a trigger "group".
// - This was created when there was an influx of event style systems
// emerging that could really benefit from a standardised custom
// events snippet. Many users were trying to achieve the same thing
// and making the same kind of errors. This snippet aims to solve that.
//
// Functions:
// - Event.create() --> Creates a new Event.
// - .destroy() --> Destroys an Event.
// - .fire() --> Fires all triggers which have been
// registered on this Event.
// - .register(trigger) --> Registers another trigger on this Event.
// - .unregister(trigger) --> Unregisters a trigger from this Event.
//
// Details:
// - Event is extremely efficient and lightweight.
// - It is safe to use with dynamic triggers.
// - Internally, it is just a linked list. Very simple.
//
// How to import:
// - Create a trigger named Event.
// - Convert it to custom text and replace the whole trigger text with this.
//
// Thanks:
// - Builder Bob for the trigger destroy detection method.
// - Azlier for inspiring this by ripping off my dodgier code.
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
library Event
///////////////
// EventRegs //
////////////////////////////////////////////////////////////////////////////
// For reading this far, you can learn one thing more.
// Unlike normal Warcraft III events, you can attach to Event registries.
//
// Event Registries are registrations of one trigger on one event.
// These cannot be created or destroyed, just attached to.
//
// It is VERY efficient for loading and saving data.
//
// Functions:
// - set eventReg.data = someStruct --> Store data.
// - eventReg.data --> Retreive data.
// - Event.getTriggeringEventReg() --> Get the triggering EventReg.
// - eventReg.destroy() --> Undo this registration.
//
private keyword destroyNode
struct EventReg extends array
integer data
method clear takes nothing returns nothing
set this.data=0
endmethod
method destroy takes nothing returns nothing
call Event(this).destroyNode()
endmethod
endstruct
private module Stack
static thistype top=0
static method increment takes nothing returns nothing
set thistype.top=thistype(thistype.top+1)
endmethod
static method decrement takes nothing returns nothing
set thistype.top=thistype(thistype.top-1)
endmethod
endmodule
private struct EventStack extends array
implement Stack
Event current
endstruct
struct Event
private trigger trig
private thistype next
private thistype prev
static method getTriggeringEventReg takes nothing returns EventReg
return EventStack.top.current
endmethod
static method create takes nothing returns Event
local Event this=Event.allocate()
set this.next=this
set this.prev=this
return this
endmethod
private static trigger currentTrigger
method fire takes nothing returns nothing
local thistype curr=this.next
call EventStack.increment()
loop
exitwhen curr==this
set thistype.currentTrigger=curr.trig
if IsTriggerEnabled(thistype.currentTrigger) then
set EventStack.top.current=curr
if TriggerEvaluate(thistype.currentTrigger) then
call TriggerExecute(thistype.currentTrigger)
endif
else
call EnableTrigger(thistype.currentTrigger) // Was trigger destroyed?
if IsTriggerEnabled(thistype.currentTrigger) then
call DisableTrigger(thistype.currentTrigger)
else // If trigger destroyed...
set curr.next.prev=curr.prev
set curr.prev.next=curr.next
call curr.deallocate()
endif
endif
set curr=curr.next
endloop
call EventStack.decrement()
endmethod
method register takes trigger t returns EventReg
local Event new=Event.allocate()
set new.prev=this.prev
set this.prev.next=new
set this.prev=new
set new.next=this
set new.trig=t
call EventReg(new).clear()
return new
endmethod
method destroyNode takes nothing returns nothing // called on EventReg
set this.prev.next=this.next
set this.next.prev=this.prev
call this.deallocate()
endmethod
method unregister takes trigger t returns nothing
local thistype curr=this.next
loop
exitwhen curr==this
if curr.trig==t then
set curr.next.prev=curr.prev
set curr.prev.next=curr.next
call curr.deallocate()
return
endif
set curr=curr.next
endloop
endmethod
method destroy takes nothing returns nothing
local thistype curr=this.next
loop
call curr.deallocate()
exitwhen curr==this
set curr=curr.next
endloop
endmethod
method chainDestroy takes nothing returns nothing
call this.destroy() // backwards compatability.
endmethod
endstruct
/////////////////////////////////////////////////////
// Demonstration Functions & Alternative Interface //
////////////////////////////////////////////////////////////////////////////
// What this would look like in normal WC3 style JASS (should all inline).
//
function CreateEvent takes nothing returns Event
return Event.create()
endfunction
function DestroyEvent takes Event whichEvent returns nothing
call whichEvent.chainDestroy()
endfunction
function FireEvent takes Event whichEvent returns nothing
call whichEvent.fire()
endfunction
function TriggerRegisterEvent takes trigger whichTrigger, Event whichEvent returns EventReg
return whichEvent.register(whichTrigger)
endfunction
// And for EventRegs...
function SetEventRegData takes EventReg whichEventReg, integer data returns nothing
set whichEventReg.data=data
endfunction
function GetEventRegData takes EventReg whichEventReg returns integer
return whichEventReg.data
endfunction
function GetTriggeringEventReg takes nothing returns integer
return Event.getTriggeringEventReg()
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library 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 PriorityEvent /* v2.0.0.1
*************************************************************************************
*
* Creates events that fire given a priority. A higher priority means that those
* events will fire first. A priority of 0 means that those events will fire last.
*
* Priority events can only be created at map init
* Code can only be registered to priority events at map init
*
*************************************************************************************
*
* */uses/*
*
* */ AVL /* hiveworkshop.com/forums/jass-resources-412/snippet-avl-tree-203168/
*
************************************************************************************
*
* struct PriorityEvent extends array
*
* static method create takes nothing returns thistype
* method register takes boolexpr func, integer priority returns nothing
* method fire takes nothing returns nothing
*
************************************************************************************/
private struct PriorityEventTree extends array
method lessThan takes thistype value returns boolean
return integer(this) < integer(value)
endmethod
method greaterThan takes thistype value returns boolean
return integer(this) > integer(value)
endmethod
implement AVL
endstruct
private module PriorityEventMod
private static integer instanceCount = 0
/*
* A queue of code registered with the same priority
*/
private thistype next_p
private thistype last_p
private thistype first_p
/*
* The priorities are stored in the tree list
*/
/*
* Iterate from 0 to count to go over all created events
*/
private static PriorityEventTree count = 0
private static PriorityEventTree array tree
/*
* This is a temporary trigger to store all code of the same priority
* Once the game has started, all code will be merged on to one trigger
*/
private trigger event
/*
* Need to store the code in order to merge all it all on to one trigger
*/
private boolexpr code
/*
* All code is merged on this
*/
private trigger allEvent
/*
* Has the code all been merged?
*/
private static boolean merged = false
static method create takes nothing returns thistype
local thistype this
debug if (merged) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"Priority Event Error: Can Only Create Events On Game Init")
debug set this = 1/0
debug endif
/*
* Allocate new event
*/
set this = PriorityEventTree.create()
/*
* Add to array for merging later
*/
set tree[count] = this
set count = count + 1
/*
* Create the merging trigger
*/
set thistype(count).allEvent = CreateTrigger()
return count
endmethod
method register takes boolexpr func, integer priority returns nothing
local thistype node
debug if (merged) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"Priority Event Error: Can Only Register Code On Game Init")
debug set node = 1/0
debug endif
/*
* Allocate a new node to store the function
*/
set node = instanceCount + 1
set instanceCount = node
set node.code = func
/*
* Retrieve the priority. This will act as the pointer to
* the queue that the node will be added to.
*/
set this = PriorityEventTree(this).add(priority)
if (null == event) then
/*
* If the queue hasn't been created yet, create it
*/
set event = CreateTrigger()
set first_p = node
set last_p = node
else
/*
* Add node to queue
*/
set last_p.next_p = node
set last_p = node
endif
call TriggerAddCondition(event, func)
endmethod
method fire takes nothing returns nothing
if (merged) then
/*
* If the code has all been merged (game started), evaluate the trigger that contains all code
*/
call TriggerEvaluate(allEvent)
else
/*
* If the code hasn't been merged yet, evaluate all of the triggers along the priority queue
*/
loop
set this = PriorityEventTree(this).prev
exitwhen PriorityEventTree(this).head
call TriggerEvaluate(event)
endloop
endif
endmethod
/*
* This is called when the game starts. It merges all of the registered code
* for each event on to single triggers to improve performance
*/
private static method merge takes nothing returns nothing
local thistype this
local integer current = count
local PriorityEventTree priority
local thistype node
set merged = true
/*
* Iterate over all events
*/
loop
exitwhen 0 == current
set current = current - 1
set this = tree[current]
/*
* Iterate over all priorities
*/
set priority = this
loop
set priority = priority.prev
exitwhen priority.head
/*
* Clean up temporary priority event trigger
*/
call TriggerClearConditions(thistype(priority).event)
call DestroyTrigger(thistype(priority).event)
set thistype(priority).event = null
/*
* Iterate over all registered code on the priority trigger
*/
set node = thistype(priority).first_p
loop
exitwhen 0 == node
/*
* Add to main trigger
*/
call TriggerAddCondition(allEvent, node.code)
set node = node.next_p
endloop
endloop
endloop
call DestroyTimer(GetExpiredTimer())
endmethod
private static method onInit takes nothing returns nothing
call TimerStart(CreateTimer(), 0, false, function thistype.merge)
endmethod
endmodule
struct PriorityEvent extends array
implement PriorityEventMod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library BinaryHeap /* v4.0.0.0
*************************************************************************************
*
* Binary Heap
*
************************************************************************************
*
* Interface:
* private static method compare takes thistype value1, thistype value2 returns boolean
* - < for minimum heap
* - > for maximum heap
*
* static readonly thistype root
* - The node with the smallest/biggest value
* readonly thistype node
* - Node stored within heap position (array[heapPosition] = node)
* readonly thistype heap
* - Heap position of node (array[heapPosition] = node)
* static readonly integer size
* - Size of binary heap
* readonly integer value
* - Sorted value (the value of the node)
*
* method modify takes integer sortValue returns nothing
* - Modifies the value of the node
*
* static method insert takes integer sortValue returns thistype
* - Inserts a new node into the heap and returns it
* - Assigns that node the passed in value
* method delete takes nothing returns nothing
* - Deletes node from heap
*
* static method clear takes nothing returns nothing
* - Clears the heap
*
************************************************************************************/
module BinaryHeap
readonly static integer size = 0
readonly thistype node //node
private static thistype instanceCount = 0 //node instance count
private static thistype array recycler //node recycler
readonly thistype value
readonly thistype heap
static method operator root takes nothing returns thistype
return thistype(1).node
endmethod
static method allocate takes thistype value returns thistype
local thistype this = recycler[0]
if (0 == this) then
set this = instanceCount + 1
set instanceCount = this
else
set recycler[0] = recycler[this]
endif
set this.value = value
set node.heap = 0
return this
endmethod
method deallocate takes nothing returns nothing
set recycler[this]=recycler[0]
set recycler[0]=this
endmethod
private method link takes thistype heapPosition returns nothing
set heapPosition.node = this
set heap = heapPosition
endmethod
private method bubbleUp takes nothing returns nothing
local thistype value = this.value
local thistype heapPosition = heap
local thistype parent
/*
* Bubble node up
*/
loop
set parent = heapPosition/2
exitwhen (0 == parent or compare(parent.node.value, value))
set heapPosition.node = parent.node
set heapPosition.node.heap = heapPosition
set heapPosition = parent
endloop
/*
* Update pointers
*/
call link(heapPosition)
endmethod
private method bubbleDown takes nothing returns nothing
local thistype value = this.value
local thistype heapPosition = heap
local thistype left
local thistype right
/*
* Bubble node down
*/
loop
set left = heapPosition*2
set right = left + 1
exitwhen (0 == left.node or compare(value, left.node.value)) and (0 == right.node or compare(value, right.node.value))
if (0 == right.node.value or (0 != left.node and compare(left.node.value, right.node.value))) then
/*
* Go left
*/
set heapPosition.node = left.node
set heapPosition.node.heap = heapPosition
set heapPosition = left
else
/*
* Go right
*/
set heapPosition.node = right.node
set heapPosition.node.heap = heapPosition
set heapPosition = right
endif
endloop
/*
* Update pointers
*/
call link(heapPosition)
endmethod
method modify takes integer value returns nothing
set this.value = value
/*
* Bubble node into correct position
*/
call bubbleUp()
call bubbleDown()
endmethod
static method insert takes thistype value returns thistype
local thistype heapPosition
local thistype this
/*
* Allocate new node
*/
set this = allocate(value)
/*
* Increase heap size
*/
set heapPosition = size + 1
set size = heapPosition
/*
* Store node in last heap position
*/
call link(heapPosition)
/*
* Bubble node into correct position
*/
call bubbleUp()
return this
endmethod
method delete takes nothing returns nothing
local thistype lastNode
debug if (0 == size) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"Attempted To Delete Node From Empty Heap")
debug set this = 1/0
debug endif
/*
* Deallocate node
*/
call deallocate()
/*
* Remove last node from last position
*/
set lastNode = thistype(size).node
set thistype(size).node = 0
set size = size - 1
if (lastNode != node) then
/*
* Put last node in deallocated node's position
*/
call lastNode.link(heap)
/*
* Bubble into correct spot
*/
call lastNode.bubbleUp()
call lastNode.bubbleDown()
endif
endmethod
static method clear takes nothing returns nothing
set size = 0
set recycler[0] = 0
set instanceCount = 0
set thistype(1).node = 0
endmethod
endmodule
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
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 Table /* made by Bribe, special thanks to Vexorian & Nestharus, version 3.1.0.1
One map, one hashtable. Welcome to NewTable 3.1
This library was originally called NewTable so it didn't conflict with
the API of Table by Vexorian. However, the damage is done and it's too
late to change the library name now. To help with damage control, I
have provided an extension library called TableBC, which bridges all
the functionality of Vexorian's Table except for 2-D string arrays &
the ".flush(integer)" method. I use ".flush()" to flush a child hash-
table, because I wanted the API in NewTable to reflect the API of real
hashtables (I thought this would be more intuitive).
API
------------
struct Table
| static method create takes nothing returns Table
| create a new Table
|
| method destroy takes nothing returns nothing
| destroy it
|
| method flush takes nothing returns nothing
| flush all stored values inside of it
|
| method remove takes integer key returns nothing
| remove the value at index "key"
|
| method operator []= takes integer key, $TYPE$ value returns nothing
| assign "value" to index "key"
|
| method operator [] takes integer key returns $TYPE$
| load the value at index "key"
|
| method has takes integer key returns boolean
| whether or not the key was assigned
|
----------------
struct TableArray
| static method operator [] takes integer array_size returns TableArray
| create a new array of Tables of size "array_size"
|
| method destroy takes nothing returns nothing
| destroy it
|
| method flush takes nothing returns nothing
| flush and destroy it
|
| method operator size takes nothing returns integer
| returns the size of the TableArray
|
| method operator [] takes integer key returns Table
| returns a Table accessible exclusively to index "key"
*/
globals
private integer less = 0 //Index generation for TableArrays (below 0).
private integer more = 8190 //Index generation for Tables.
//Configure it if you use more than 8190 "key" variables in your map (this will never happen though).
private hashtable ht = InitHashtable()
private key sizeK
private key listK
endglobals
private struct dex extends array
static method operator size takes nothing returns Table
return sizeK
endmethod
static method operator list takes nothing returns Table
return listK
endmethod
endstruct
private struct handles extends array
method has takes integer key returns boolean
return HaveSavedHandle(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSavedHandle(ht, this, key)
endmethod
endstruct
private struct agents extends array
method operator []= takes integer key, agent value returns nothing
call SaveAgentHandle(ht, this, key, value)
endmethod
endstruct
//! textmacro NEW_ARRAY_BASIC takes SUPER, FUNC, TYPE
private struct $TYPE$s extends array
method operator [] takes integer key returns $TYPE$
return Load$FUNC$(ht, this, key)
endmethod
method operator []= takes integer key, $TYPE$ value returns nothing
call Save$FUNC$(ht, this, key, value)
endmethod
method has takes integer key returns boolean
return HaveSaved$SUPER$(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSaved$SUPER$(ht, this, key)
endmethod
endstruct
private module $TYPE$m
method operator $TYPE$ takes nothing returns $TYPE$s
return this
endmethod
endmodule
//! endtextmacro
//! textmacro NEW_ARRAY takes FUNC, TYPE
private struct $TYPE$s extends array
method operator [] takes integer key returns $TYPE$
return Load$FUNC$Handle(ht, this, key)
endmethod
method operator []= takes integer key, $TYPE$ value returns nothing
call Save$FUNC$Handle(ht, this, key, value)
endmethod
endstruct
private module $TYPE$m
method operator $TYPE$ takes nothing returns $TYPE$s
return this
endmethod
endmodule
//! endtextmacro
//Run these textmacros to include the entire hashtable API as wrappers.
//Don't be intimidated by the number of macros - Vexorian's map optimizer is
//supposed to kill functions which inline (all of these functions inline).
//! runtextmacro NEW_ARRAY_BASIC("Real", "Real", "real")
//! runtextmacro NEW_ARRAY_BASIC("Boolean", "Boolean", "boolean")
//! runtextmacro NEW_ARRAY_BASIC("String", "Str", "string")
//! runtextmacro NEW_ARRAY("Player", "player")
//! runtextmacro NEW_ARRAY("Widget", "widget")
//! runtextmacro NEW_ARRAY("Destructable", "destructable")
//! runtextmacro NEW_ARRAY("Item", "item")
//! runtextmacro NEW_ARRAY("Unit", "unit")
//! runtextmacro NEW_ARRAY("Ability", "ability")
//! runtextmacro NEW_ARRAY("Timer", "timer")
//! runtextmacro NEW_ARRAY("Trigger", "trigger")
//! runtextmacro NEW_ARRAY("TriggerCondition", "triggercondition")
//! runtextmacro NEW_ARRAY("TriggerAction", "triggeraction")
//! runtextmacro NEW_ARRAY("TriggerEvent", "event")
//! runtextmacro NEW_ARRAY("Force", "force")
//! runtextmacro NEW_ARRAY("Group", "group")
//! runtextmacro NEW_ARRAY("Location", "location")
//! runtextmacro NEW_ARRAY("Rect", "rect")
//! runtextmacro NEW_ARRAY("BooleanExpr", "boolexpr")
//! runtextmacro NEW_ARRAY("Sound", "sound")
//! runtextmacro NEW_ARRAY("Effect", "effect")
//! runtextmacro NEW_ARRAY("UnitPool", "unitpool")
//! runtextmacro NEW_ARRAY("ItemPool", "itempool")
//! runtextmacro NEW_ARRAY("Quest", "quest")
//! runtextmacro NEW_ARRAY("QuestItem", "questitem")
//! runtextmacro NEW_ARRAY("DefeatCondition", "defeatcondition")
//! runtextmacro NEW_ARRAY("TimerDialog", "timerdialog")
//! runtextmacro NEW_ARRAY("Leaderboard", "leaderboard")
//! runtextmacro NEW_ARRAY("Multiboard", "multiboard")
//! runtextmacro NEW_ARRAY("MultiboardItem", "multiboarditem")
//! runtextmacro NEW_ARRAY("Trackable", "trackable")
//! runtextmacro NEW_ARRAY("Dialog", "dialog")
//! runtextmacro NEW_ARRAY("Button", "button")
//! runtextmacro NEW_ARRAY("TextTag", "texttag")
//! runtextmacro NEW_ARRAY("Lightning", "lightning")
//! runtextmacro NEW_ARRAY("Image", "image")
//! runtextmacro NEW_ARRAY("Ubersplat", "ubersplat")
//! runtextmacro NEW_ARRAY("Region", "region")
//! runtextmacro NEW_ARRAY("FogState", "fogstate")
//! runtextmacro NEW_ARRAY("FogModifier", "fogmodifier")
//! runtextmacro NEW_ARRAY("Hashtable", "hashtable")
struct Table extends array
// Implement modules for intuitive syntax (tb.handle; tb.unit; etc.)
implement realm
implement booleanm
implement stringm
implement playerm
implement widgetm
implement destructablem
implement itemm
implement unitm
implement abilitym
implement timerm
implement triggerm
implement triggerconditionm
implement triggeractionm
implement eventm
implement forcem
implement groupm
implement locationm
implement rectm
implement boolexprm
implement soundm
implement effectm
implement unitpoolm
implement itempoolm
implement questm
implement questitemm
implement defeatconditionm
implement timerdialogm
implement leaderboardm
implement multiboardm
implement multiboarditemm
implement trackablem
implement dialogm
implement buttonm
implement texttagm
implement lightningm
implement imagem
implement ubersplatm
implement regionm
implement fogstatem
implement fogmodifierm
implement hashtablem
method operator handle takes nothing returns handles
return this
endmethod
method operator agent takes nothing returns agents
return this
endmethod
//set this = tb[GetSpellAbilityId()]
method operator [] takes integer key returns Table
return LoadInteger(ht, this, key)
endmethod
//set tb[389034] = 8192
method operator []= takes integer key, Table tb returns nothing
call SaveInteger(ht, this, key, tb)
endmethod
//set b = tb.has(2493223)
method has takes integer key returns boolean
return HaveSavedInteger(ht, this, key)
endmethod
//call tb.remove(294080)
method remove takes integer key returns nothing
call RemoveSavedInteger(ht, this, key)
endmethod
//Remove all data from a Table instance
method flush takes nothing returns nothing
call FlushChildHashtable(ht, this)
endmethod
//local Table tb = Table.create()
static method create takes nothing returns Table
local Table this = dex.list[0]
if this == 0 then
set this = more + 1
set more = this
else
set dex.list[0] = dex.list[this]
call dex.list.remove(this) //Clear hashed memory
endif
debug set dex.list[this] = -1
return this
endmethod
// Removes all data from a Table instance and recycles its index.
//
// call tb.destroy()
//
method destroy takes nothing returns nothing
debug if dex.list[this] != -1 then
debug call BJDebugMsg("Table Error: Tried to double-free instance: " + I2S(this))
debug return
debug endif
call this.flush()
set dex.list[this] = dex.list[0]
set dex.list[0] = this
endmethod
//! runtextmacro optional TABLE_BC_METHODS()
endstruct
//! runtextmacro optional TABLE_BC_STRUCTS()
struct TableArray extends array
//Returns a new TableArray to do your bidding. Simply use:
//
// local TableArray ta = TableArray[array_size]
//
static method operator [] takes integer array_size returns TableArray
local Table tb = dex.size[array_size] //Get the unique recycle list for this array size
local TableArray this = tb[0] //The last-destroyed TableArray that had this array size
debug if array_size <= 0 then
debug call BJDebugMsg("TypeError: Invalid specified TableArray size: " + I2S(array_size))
debug return 0
debug endif
if this == 0 then
set this = less - array_size
set less = this
else
set tb[0] = tb[this] //Set the last destroyed to the last-last destroyed
call tb.remove(this) //Clear hashed memory
endif
set dex.size[this] = array_size //This remembers the array size
return this
endmethod
//Returns the size of the TableArray
method operator size takes nothing returns integer
return dex.size[this]
endmethod
//This magic method enables two-dimensional[array][syntax] for Tables,
//similar to the two-dimensional utility provided by hashtables them-
//selves.
//
//ta[integer a].unit[integer b] = unit u
//ta[integer a][integer c] = integer d
//
//Inline-friendly when not running in debug mode
//
method operator [] takes integer key returns Table
static if DEBUG_MODE then
local integer i = this.size
if i == 0 then
call BJDebugMsg("IndexError: Tried to get key from invalid TableArray instance: " + I2S(this))
return 0
elseif key < 0 or key >= i then
call BJDebugMsg("IndexError: Tried to get key [" + I2S(key) + "] from outside TableArray bounds: " + I2S(i))
return 0
endif
endif
return this + key
endmethod
//Destroys a TableArray without flushing it; I assume you call .flush()
//if you want it flushed too. This is a public method so that you don't
//have to loop through all TableArray indices to flush them if you don't
//need to (ie. if you were flushing all child-keys as you used them).
//
method destroy takes nothing returns nothing
local Table tb = dex.size[this.size]
debug if this.size == 0 then
debug call BJDebugMsg("TypeError: Tried to destroy an invalid TableArray: " + I2S(this))
debug return
debug endif
if tb == 0 then
//Create a Table to index recycled instances with their array size
set tb = Table.create()
set dex.size[this.size] = tb
endif
call dex.size.remove(this) //Clear the array size from hash memory
set tb[this] = tb[0]
set tb[0] = this
endmethod
private static Table tempTable
private static integer tempEnd
//Avoids hitting the op limit
private static method clean takes nothing returns nothing
local Table tb = .tempTable
local integer end = tb + 0x1000
if end < .tempEnd then
set .tempTable = end
call ForForce(bj_FORCE_PLAYER[0], function thistype.clean)
else
set end = .tempEnd
endif
loop
call tb.flush()
set tb = tb + 1
exitwhen tb == end
endloop
endmethod
//Flushes the TableArray and also destroys it. Doesn't get any more
//similar to the FlushParentHashtable native than this.
//
method flush takes nothing returns nothing
debug if this.size == 0 then
debug call BJDebugMsg("TypeError: Tried to flush an invalid TableArray instance: " + I2S(this))
debug return
debug endif
set .tempTable = this
set .tempEnd = this + this.size
call ForForce(bj_FORCE_PLAYER[0], function thistype.clean)
call this.destroy()
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library AVL /* v1.1.0.6
*************************************************************************************
*
* An AVL Tree where all nodes are connected by an AVL tree, a linked list, and
* are referenced by a hashtable.
*
*************************************************************************************
*
* */uses/*
*
* */ Table /* hiveworkshop.com/forums/jass-functions-413/snippet-new-table-188084/
*
************************************************************************************
*
* module AVLTree
*
* Interface:
* method lessThan takes thistype value returns boolean
* method greaterThan takes thistype value returns boolean
*
* readonly thistype tree
* - Tree pointer. Accessible from any node on the tree.
* readonly thistype root
* - Root of two children (if root is 0, it's the tree's root)
* readonly thistype left
* readonly thistype right
* readonly thistype down (tree pointer only)
* - The tree has only 1 child, the root
*
* readonly thistype next
* readonly thistype prev
* readonly boolean head
*
* readonly thistype value
* - Value stored in node
*
* static method create takes nothing returns thistype
* method destroy takes nothing returns nothing
*
* method search takes thistype value returns thistype
* - Returns the node containing the value
* - If the value didn't exist, it will return 0
* method searchClose takes thistype value, boolean low returns thistype
* - Searches for the best match to the value.
* -
* - Low = True
* - Search for the closest value <= to the target value
* -
* - Low = False
* - Search for the closest value >= to the target value
*
* method has takes thistype value returns boolean
* - Returns true if a node contains the value
*
* method add takes thistype value returns thistype
* - Returns new node containing added value. If the value
* - was already in the tree, it returns the node that already
* - contained that value.
* method delete takes nothing returns nothing
* - Deletes node
*
* -> Delete the value 15 from the tree if it exists
* -> call search(15).delete()
*
* method clear takes nothing returns thistype
* - Clears the tree of all nodes
* - Returns tree pointer (just in case some node in the tree was passed in rather than the tree)
*
************************************************************************************/
module AVL
private static Table array table //for O(1) searches on specific values
private static thistype c=0 //instance count
private static thistype array b //root
private static thistype array l //left
private static thistype array r //right
private static integer array h //height
private static thistype array p //parent
private static thistype array v //value
private static integer array nn //next node
private static integer array pn //prev node
private static integer array ro //root
method operator tree takes nothing returns thistype
return ro[this]
endmethod
method operator root takes nothing returns thistype
return p[this]
endmethod
method operator down takes nothing returns thistype
return b[this]
endmethod
method operator left takes nothing returns thistype
return l[this]
endmethod
method operator right takes nothing returns thistype
return r[this]
endmethod
method operator value takes nothing returns thistype
return v[this]
endmethod
method operator next takes nothing returns thistype
return nn[this]
endmethod
method operator prev takes nothing returns thistype
return pn[this]
endmethod
method operator head takes nothing returns boolean
return 0==p[this]
endmethod
private method getHeight takes nothing returns integer
//return the bigger leaf height
if (h[l[this]]>h[r[this]]) then
return h[l[this]]+1
endif
return h[r[this]]+1
endmethod
private method updateParent takes integer n returns nothing
//only update the parent of a target leaf if that leaf isn't the original leaf
if (n!=this) then
//update the parent point to
//first leaf
if (0==p[p[this]]) then
set b[p[this]]=n
//left
elseif (l[p[this]]==this) then
set l[p[this]]=n
//right
else
set r[p[this]]=n
endif
//update the leaf point back
//if the leaf isn't null, update the leaf's parent
if (0!=n) then
set p[n]=p[this]
endif
endif
endmethod
private method finishRotate takes thistype n returns nothing
//this code is identical in rotateLeft and rotateRight, so it has
//been abstracted to a method
call updateParent(n)
set p[this]=n
set h[this]=getHeight()
set h[n]=n.getHeight()
endmethod
private method rotateLeft takes nothing returns thistype
local thistype n=r[this]
set r[this]=l[n]
set p[l[n]]=this
set l[n]=this
call finishRotate(n)
return n
endmethod
private method rotateRight takes nothing returns thistype
local thistype n=l[this]
set l[this]=r[n]
set p[r[n]]=this
set r[n]=this
call finishRotate(n)
return n
endmethod
//return the difference between the left and right leaf heights
private method getBalanceFactor takes nothing returns integer
return h[l[this]]-h[r[this]]
endmethod
private static method allocate takes nothing returns thistype
local integer n
if (0==nn[0]) then
set n=c+1
set c=n
else
set n=nn[0] //notice that the recycler uses the next pointer
//the reason it is used is for fast clear/destroy and to save
//a variable
set nn[0]=nn[n]
endif
set l[n]=0 //left leaf
set r[n]=0 //right leaf
set b[n]=0 //down leaf (first node of tree)
set h[n]=1 //height (a node will always have at least a height of 1 for itself)
return n
endmethod
static method create takes nothing returns thistype
local integer n=allocate()
set p[n]=0 //the parent of the tree node is 0
//initialize tree next and prev
set nn[n]=n
set pn[n]=n
//tree value table for O(1) searches on specific values
set table[n]=Table.create()
//tree root is itself (allows one to pass any node from the tree into the methods)
set ro[n]=n
return n
endmethod
//balance from the current node up to the root O(log n)
//balancing is rotations wherever rotations need to be done
private method balance takes nothing returns nothing
local integer f
loop
exitwhen 0==p[this]
set h[this]=getHeight()
set f=getBalanceFactor()
if (2==f) then
if (-1==l[this].getBalanceFactor()) then
call l[this].rotateLeft()
endif
set this=rotateRight()
return
elseif (-2==f) then
if (1==r[this].getBalanceFactor()) then
call r[this].rotateRight()
endif
set this=rotateLeft()
return
endif
set this=p[this]
endloop
endmethod
//goes to the very bottom of a node (for deletion)
private method getBottom takes nothing returns thistype
if (0!=r[this]) then
if (0!=l[this]) then
set this=r[this]
loop
exitwhen 0==l[this]
set this=l[this]
endloop
return this
else
return r[this]
endif
elseif (0!=l[this]) then
return l[this]
endif
return this
endmethod
method search takes thistype val returns thistype
return table[ro[this]][val]
endmethod
method has takes thistype val returns boolean
return table[ro[this]].has(val)
endmethod
method searchClose takes thistype val, boolean low returns thistype
local thistype n
//retrieve tree
set this=ro[this]
//if tree is empty, return 0
if (0==b[this]) then
return 0
endif
//check to see if the node exists in the tree and return it if it does
set n=table[this][val]
if (0!=n) then
return n
endif
//perform a standard tree search for the value to the bottom of the tree
//will always be at most 1 off from the best match
set this=b[this]
loop
if (val.lessThan(v[this])) then
exitwhen 0==l[this]
set this=l[this]
else
exitwhen 0==r[this]
set this=r[this]
endif
endloop
//look at the found value's neighbors on the linked list
if (low) then
//shift down if greater than
if (v[this].greaterThan(val)) then
set this=prev
endif
//return 0 if node wasn't found
if (0==p[this] or v[this].greaterThan(val)) then
return 0
endif
else
//shift up if less than
if (v[this].lessThan(val)) then
set this=next
endif
//return 0 if node wasn't found
if (0==p[this] or v[this].lessThan(val)) then
return 0
endif
endif
return this
endmethod
method add takes thistype val returns thistype
local thistype n
//check if the tree already has the value in it
set this=ro[this]
set n=table[this][val]
//if the tree doesn't have the value in it, add the value
if (0==n) then
set n=this
set this=allocate()
set ro[this]=n //store tree into leaf
set table[n][val]=this //store leaf into value table
set v[this]=val //store value into leaf
//if the tree is empty
if (0==b[n]) then
set b[n]=this //place as first node
set p[this]=n //parent of first node is tree
//add to list
set nn[this]=n
set pn[this]=n
set nn[n]=this
set pn[n]=this
else
//go to the first node in the tree
set n=b[n]
//go to the bottom of the tree with search algorithm
loop
if (val.lessThan(v[n])) then
exitwhen 0==l[n]
set n=l[n]
else
exitwhen 0==r[n]
set n=r[n]
endif
endloop
//add leaf to tree
set p[this]=n
if (val.lessThan(v[n])) then
set l[n]=this
else
set r[n]=this
endif
//update the height of the parent
set h[n]=n.getHeight()
//balance from the parent upwards
call p[n].balance()
//add leaf to list
if (v[n].greaterThan(v[this])) then
set n=pn[n]
endif
set nn[this]=nn[n]
set pn[this]=n
set pn[nn[n]]=this
set nn[n]=this
endif
return this
endif
return n
endmethod
method delete takes nothing returns nothing
local thistype n
local thistype y
//if the leaf to be deleted isn't 0 and the leaf isn't the tree
if (0 != this and 0 != p[this]) then
//remove the leaf from the value table
call table[ro[this]].remove(v[this])
set n=getBottom() //retrieve the bottom leaf
set y=p[n] //store the parent here for balancing later
//move the found leaf into the deleted leaf's position
call n.updateParent(0)
call updateParent(n)
if (this!=n) then
set l[n]=l[this]
set p[l[n]]=n
set r[n]=r[this]
set p[r[n]]=n
set p[n]=p[this]
set h[n]=h[this]
endif
//balance from the found leaf's old parent upwards
call y.balance()
//remove deleted leaf from list
set nn[pn[this]]=nn[this]
set pn[nn[this]]=pn[this]
set nn[this]=nn[0]
set nn[0]=this
endif
endmethod
method clear takes nothing returns thistype
//quick clear
set this=ro[this]
if (nn[this] != this) then
set nn[pn[this]]=nn[0]
set nn[0]=nn[this]
set nn[this]=this
set pn[this]=this
set b[this] = 0
call table[this].flush()
endif
return this
endmethod
method destroy takes nothing returns nothing
//quick destroy
set this=ro[this]
set nn[pn[this]]=nn[0]
set nn[0]=this
call table[this].destroy()
endmethod
endmodule
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/**************************************************************
*
* RegisterPlayerUnitEvent
* v5.1.0.0
* By Magtheridon96
*
* I would like to give a special thanks to Bribe, azlier
* and BBQ for improving this library. For modularity, it only
* supports player unit events.
*
* Functions passed to RegisterPlayerUnitEvent must either
* return a boolean (false) or nothing. (Which is a Pro)
*
* Warning:
* --------
*
* - Don't use TriggerSleepAction inside registered code.
* - Don't destroy a trigger unless you really know what you're doing.
*
* API:
* ----
*
* - function RegisterPlayerUnitEvent takes playerunitevent whichEvent, code whichFunction returns nothing
* - Registers code that will execute when an event fires.
* - function RegisterPlayerUnitEventForPlayer takes playerunitevent whichEvent, code whichFunction, player whichPlayer returns nothing
* - Registers code that will execute when an event fires for a certain player.
* - function GetPlayerUnitEventTrigger takes playerunitevent whichEvent returns trigger
* - Returns the trigger corresponding to ALL functions of a playerunitevent.
*
**************************************************************/
library RegisterPlayerUnitEvent // Special Thanks to Bribe and azlier
globals
private trigger array t
endglobals
function RegisterPlayerUnitEvent takes playerunitevent p, code c returns nothing
local integer i = GetHandleId(p)
local integer k = 15
if t[i] == null then
set t[i] = CreateTrigger()
loop
call TriggerRegisterPlayerUnitEvent(t[i], Player(k), p, null)
exitwhen k == 0
set k = k - 1
endloop
endif
call TriggerAddCondition(t[i], Filter(c))
endfunction
function RegisterPlayerUnitEventForPlayer takes playerunitevent p, code c, player pl returns nothing
local integer i = 260 + 16 * GetHandleId(p) + GetPlayerId(pl)
if t[i] == null then
set t[i] = CreateTrigger()
call TriggerRegisterPlayerUnitEvent(t[i], pl, p, null)
endif
call TriggerAddCondition(t[i], Filter(c))
endfunction
function GetPlayerUnitEventTrigger takes playerunitevent p returns trigger
return t[GetHandleId(p)]
endfunction
endlibrary
//TESH.scrollpos=288
//TESH.alwaysfold=0
struct TestMap extends array
static unit hero = null
static real camDist = 0.
static timer camDistLocker = CreateTimer()
static real camX = 0.
static real camY = 0.
static timer camXYLocker = CreateTimer()
static trigger restartTrig = CreateTrigger()
static integer continue = 1
static method tryCrash takes nothing returns integer
return 1/continue
endmethod
static method print takes string s returns nothing
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 5, s)
endmethod
static method printEx takes string s, real d returns nothing
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, d, s)
endmethod
static method wait takes real time returns nothing
call TriggerSleepAction(time)
endmethod
static method randomEnemy takes nothing returns player
return Player(GetRandomInt(3,5))
endmethod
static method randomAlly takes nothing returns player
return Player(GetRandomInt(1,2))
endmethod
static method makeEnemy takes real x, real y returns unit
return CreateUnit(randomEnemy(), 'Hamg', x, y, 270)
endmethod
static method makeAlly takes real x, real y returns unit
return CreateUnit(randomAlly(), 'Hmkg', x, y, 270)
endmethod
static method makeStrongAlly takes real x, real y returns unit
return CreateUnit(randomAlly(), 'H000', x, y, 270)
endmethod
static method makeStrongEnemy takes real x, real y returns unit
return CreateUnit(randomEnemy(), 'H000', x, y, 270)
endmethod
static method damage takes unit source, unit target, real amount returns nothing
call UnitDamageTarget(source, target, amount, false, false, null, null, null)
endmethod
static method ally takes player p1, player p2 returns nothing
call SetPlayerAlliance(p1, p2, ALLIANCE_PASSIVE, true)
call SetPlayerAlliance(p1, p2, ALLIANCE_HELP_REQUEST, true)
call SetPlayerAlliance(p1, p2, ALLIANCE_HELP_RESPONSE, true)
call SetPlayerAlliance(p1, p2, ALLIANCE_SHARED_XP, true)
call SetPlayerAlliance(p1, p2, ALLIANCE_SHARED_SPELLS, true)
call SetPlayerAlliance(p2, p1, ALLIANCE_PASSIVE, true)
call SetPlayerAlliance(p2, p1, ALLIANCE_HELP_REQUEST, true)
call SetPlayerAlliance(p2, p1, ALLIANCE_HELP_RESPONSE, true)
call SetPlayerAlliance(p2, p1, ALLIANCE_SHARED_XP, true)
call SetPlayerAlliance(p2, p1, ALLIANCE_SHARED_SPELLS, true)
call SetPlayerAlliance(p1, p2, ALLIANCE_SHARED_VISION, true)
call SetPlayerAlliance(p2, p1, ALLIANCE_SHARED_VISION, true)
call SetPlayerAlliance(p1, p2, ALLIANCE_SHARED_VISION, false)
call SetPlayerAlliance(p2, p1, ALLIANCE_SHARED_VISION, false)
call SetPlayerAlliance(p1, p2, ALLIANCE_SHARED_CONTROL, false)
call SetPlayerAlliance(p2, p1, ALLIANCE_SHARED_CONTROL, false)
call SetPlayerAlliance(p1, p2, ALLIANCE_SHARED_ADVANCED_CONTROL, false)
call SetPlayerAlliance(p2, p1, ALLIANCE_SHARED_ADVANCED_CONTROL, false)
endmethod
static method giveVision takes nothing returns nothing
call FogModifierStart(CreateFogModifierRect(Player(0),FOG_OF_WAR_VISIBLE,bj_mapInitialPlayableArea,true,true))
endmethod
static method lockDist takes nothing returns nothing
call SetCameraField(CAMERA_FIELD_TARGET_DISTANCE, camDist, 0)
endmethod
static method lockXY takes nothing returns nothing
call SetCameraPosition(camX, camY)
endmethod
static method lockCamDistance takes real distance returns nothing
set camDist = distance
call TimerStart(camDistLocker, 0.05, true, function thistype.lockDist)
endmethod
static method lockCamXY takes real x, real y returns nothing
set camX = x
set camY = y
call TimerStart(camXYLocker, 0.05, true, function thistype.lockXY)
endmethod
static method unlockCamDistance takes nothing returns nothing
call PauseTimer(camDistLocker)
endmethod
static method unlockCamXY takes nothing returns nothing
call PauseTimer(camXYLocker)
endmethod
static method revive takes nothing returns nothing
call ReviveHero(hero, GetUnitX(hero), GetUnitY(hero), false)
endmethod
static multiboard board = null
static method setTitle takes integer col, string val returns nothing
call MultiboardSetItemValue(MultiboardGetItem(board, 0, col), val)
endmethod
static method setBox takes integer col, integer val returns nothing
call MultiboardSetItemValue(MultiboardGetItem(board, 1, col), I2S(val))
endmethod
static method multiboardUpdate takes nothing returns nothing
if board==null then
set board=CreateMultiboard()
call MultiboardClear(board)
call MultiboardSetColumnCount(board, 15)
call MultiboardSetRowCount(board, 2)
call MultiboardSetItemsStyle(board, true, false)
call MultiboardDisplay(board, true)
call MultiboardMinimize(board, false)
call MultiboardSetItemsWidth(board, 0.02)
call setTitle(0, "K")
call setTitle(1, "Dt")
call setTitle(2, "A")
call setTitle(3, "Dn")
call setTitle(4, "S")
call setTitle(5, "St")
call setTitle(6, "S3")
call setTitle(7, "S4")
call setTitle(8, "S5")
call setTitle(9, "Mt")
call setTitle(10, "M2")
call setTitle(11, "M3")
call setTitle(12, "M4")
call setTitle(13, "M5")
call setTitle(14, "CB")
endif
call setBox(0, StreakSystem.getKillsById(0))
call setBox(1, StreakSystem.getDeathsById(0))
call setBox(2, StreakSystem.getAssistsById(0))
call setBox(3, StreakSystem.getDeniesById(0))
call setBox(4, StreakSystem.getSuicidesById(0))
call setBox(5, StreakSystem.getStreaksById(0))
call setBox(6, StreakSystem.getStreaksOfTypeById(0,3))
call setBox(7, StreakSystem.getStreaksOfTypeById(0,4))
call setBox(8, StreakSystem.getStreaksOfTypeById(0,5))
call setBox(9, StreakSystem.getMultikillsById(0))
call setBox(10, StreakSystem.getMultikillsOfTypeById(0,2))
call setBox(11, StreakSystem.getMultikillsOfTypeById(0,3))
call setBox(12, StreakSystem.getMultikillsOfTypeById(0,4))
call setBox(13, StreakSystem.getMultikillsOfTypeById(0,5))
call setBox(14, StreakSystem.getCombobreakersById(0))
endmethod
static method startMultiboard takes nothing returns nothing
call TimerStart(CreateTimer(), 0.01, true, function thistype.multiboardUpdate)
endmethod
static method createLeEnemies takes nothing returns nothing
if GetRandomInt(0,1) == 0 then
call makeEnemy(GetRandomReal(-1000,1000), GetRandomReal(-1000,1000))
else
call makeAlly(GetRandomReal(-1000,1000), GetRandomReal(-1000,1000))
endif
endmethod
static boolean running = false
private static method stop takes nothing returns nothing
local unit u
set continue = 0
call DestroyTrigger(restartTrig)
call GroupEnumUnitsInRange(bj_lastCreatedGroup, 0, 0, 9001, null)
loop
set u = FirstOfGroup(bj_lastCreatedGroup)
exitwhen u == null
call GroupRemoveUnit(bj_lastCreatedGroup, u)
call RemoveUnit(u)
endloop
call CreateUnit(Player(0), 'H000', 0, 0, 0)
call TimerStart(CreateTimer(), 1, true, function thistype.createLeEnemies)
endmethod
private static method do takes nothing returns nothing
local integer i = 0
local unit u
local unit v
local unit u1
local unit u2
local unit u3
local unit u4
if not running then
set running = true
set hero = CreateUnit(Player(0), 'H000', 0, 0, 270)
call wait(2)
call print("Testing suicide.")
call tryCrash()
call wait(1)
call tryCrash()
call damage(hero, hero, 100000)
call wait(2)
call tryCrash()
call print("Testing Denial.")
call revive()
call wait(1)
call tryCrash()
set u = makeAlly(0, -256)
call IssueTargetOrder(hero, "attack", u)
call wait(2)
call RemoveUnit(u)
call tryCrash()
call print("Testing Streaks and Multikills.")
loop
set u = makeEnemy(0, -256)
call IssueTargetOrder(hero, "attack", u)
call wait(3)
call tryCrash()
call RemoveUnit(u)
set i = i + 1
exitwhen i == 5
endloop
call print("Testing Combobreaker")
call PauseUnit(hero, true)
set u = makeStrongEnemy(0, -256)
call IssueTargetOrder(hero, "attack", u)
call wait(5)
call RemoveUnit(u)
call tryCrash()
call print("Testing Assists.")
call ReviveHero(hero, 0, 0, false)
call PauseUnit(hero, true)
set u = makeAlly(256, 0)
set v = makeEnemy(0, -256)
call IssueTargetOrder(u, "attack", v)
call wait(2)
call tryCrash()
call PauseUnit(hero, false)
call IssueTargetOrder(hero, "attack", v)
call wait(3)
call tryCrash()
call RemoveUnit(u)
call RemoveUnit(v)
call print("Testing Assists Again.")
call PauseUnit(hero, true)
set u1 = makeAlly(0,0)
set u2 = makeAlly(0,0)
set u3 = makeAlly(0,0)
set u4 = makeAlly(0,0)
set v = makeEnemy(0, -256)
call IssueTargetOrder(u1, "attack", v)
call IssueTargetOrder(u2, "attack", v)
call IssueTargetOrder(u3, "attack", v)
call IssueTargetOrder(u4, "attack", v)
call wait(3)
call tryCrash()
call PauseUnit(hero, false)
call IssueTargetOrder(hero, "attack", v)
call wait(2)
call tryCrash()
call RemoveUnit(u1)
call RemoveUnit(u2)
call RemoveUnit(u3)
call RemoveUnit(u4)
call RemoveUnit(v)
call print("Random Tests")
call print("Watch Carefully")
set u = makeStrongAlly(256,256)
call SetUnitTimeScale(u, 0.3)
set u = makeStrongAlly(-256,256)
call SetUnitTimeScale(u, 0.3)
set u = makeStrongEnemy(-256,-256)
call SetUnitTimeScale(u, 0.3)
set u = makeStrongEnemy(256,-256)
call SetUnitTimeScale(u, 0.3)
set u = makeEnemy(0,0)
call SetUnitTimeScale(u, 0.3)
set u = makeAlly(0,0)
call SetUnitTimeScale(u, 0.3)
set u = makeEnemy(0,0)
call SetUnitTimeScale(u, 0.3)
set u = makeAlly(0,0)
call SetUnitTimeScale(u, 0.3)
call wait(6)
call tryCrash()
call GroupEnumUnitsInRange(bj_lastCreatedGroup, 0, 0, 8000, null)
loop
set u = FirstOfGroup(bj_lastCreatedGroup)
exitwhen u == null
call GroupRemoveUnit(bj_lastCreatedGroup, u)
call RemoveUnit(u)
endloop
set running = false
endif
endmethod
static method run takes nothing returns nothing
local trigger t
call startMultiboard()
call giveVision()
call lockCamDistance(2500)
call lockCamXY(0,0)
call ally(Player(0), Player(1))
call ally(Player(0), Player(2))
call ally(Player(1), Player(2))
call ally(Player(4), Player(5))
call ally(Player(4), Player(6))
call ally(Player(5), Player(6))
call do()
call TriggerRegisterPlayerChatEvent(restartTrig, Player(0), "restart", true)
call TriggerAddAction(restartTrig, function thistype.do)
call DestroyTrigger(GetTriggeringTrigger())
call ClearTextMessages()
call printEx("Type 'restart' to restart the entire demonstration.", 60)
set t = CreateTrigger()
call TriggerRegisterPlayerChatEvent(t, Player(0), "stop", true)
call TriggerAddAction(t, function thistype.stop)
call printEx("Type 'stop' to start the user trial. You can type 'stop' while a restart has been done and it will skip to the user trial just fine", 60)
set t = null
endmethod
private static method config takes nothing returns nothing
//Configuration:
call StreakSystem.setFirstBloodSound("Sound\\Interface\\GoodJob.wav", 2548)
call StreakSystem.setComboBreakerSound("Sound\\Interface\\GoodJob.wav", 2548)
call StreakSystem.setMinimumStreak(3)
call StreakSystem.newStreak("|cff80ff80killing spree|r", "is on a |cff80ff80killing spree|r!", "Sound\\Interface\\GoodJob.wav", 2548)
call StreakSystem.newStreak("|cffff80c0dominating|r", "is |cffff80c0dominating|r!", "Sound\\Interface\\GoodJob.wav", 2548)
call StreakSystem.newStreak("|cffffff80mega-kill|r", "has a |cffffff80mega-kill|r!", "Sound\\Interface\\GoodJob.wav", 2548)
call StreakSystem.newStreak("|cffff8000unstoppable|r", "is |cffff8000unstoppable|r!", "Sound\\Interface\\GoodJob.wav", 2548)
call StreakSystem.newStreak("|cff00ffa2wicked sick|r", "is |cff00ffa2wicked sick|r!", "Sound\\Interface\\GoodJob.wav", 2548)
call StreakSystem.newStreak("|cffff0080monster kill|r", "has a |cffff0080monster kill|r!", "Sound\\Interface\\GoodJob.wav", 2548)
call StreakSystem.newStreak("|cffff0000GODLIKE|r", "is |cffff0000GODLIKE|r!", "Sound\\Interface\\GoodJob.wav", 2548)
call StreakSystem.newStreak("|cffffa500beyond Godlike|r", "is |cffffa500beyond Godlike. SOMEONE KILL HIM|r!", "Sound\\Interface\\GoodJob.wav", 2548)
call StreakSystem.newMultikill("got a |cff0028ffDouble Kill|r!!", "Sound\\Interface\\GoodJob.wav", 2548)
call StreakSystem.newMultikill("got a |cff40ff40Triple Kill|r!!!", "Sound\\Interface\\GoodJob.wav", 2548)
call StreakSystem.newMultikill("got an |cff00bfffUltra-Kill|r!!!!", "Sound\\Interface\\GoodJob.wav", 2548)
call StreakSystem.newMultikill("is on a |cff00ced1Rampage|r!!!!!", "Sound\\Interface\\GoodJob.wav", 2548)
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterPlayerChatEvent(t, Player(0), "start", true)
call TriggerAddAction(t, function thistype.run)
call config()
set t = null
call print("Type 'start' to begin.")
endmethod
endstruct