Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
A simple Creep Respawn System tailored for RPG maps. It does not requires SetUnitUserData and GetUnitUserData. As a result, it is compatible with any existing unit indexer.
private constant real CREEP_DEFAULT_RESPAWN_DURATION
Default creep revival duration.
JASS:
private constant real CREEP_BOSS_RESPAWN_DURATION
Boss revival duration.
JASS:
private constant real CREEP_MINI_BOSS_RESPAWN_DURATION
Mini boss revival duration.
JASS:
private constant boolean ALLOW_FADE_IN
Enable fade in effect for revived creeps.
JASS:
private constant integer ALPHA_INCREMENT
Increment for transparency effect.
JASS:
function AddCreepRespawn takes unit whichCreep returns integer
Include a unit to the Creep Respawn System. The function returns the Creep Index associated with that unit.
JASS:
function AddCreepRespawnEx takes unit whichCreep, real duration returns integer
Include a unit to the Creep Respawn System with a custom duration. The function returns the Creep Index associated with that unit.
JASS:
function SetCreepRespawn takes unit whichCreep, boolean enable returns boolean
Toggleable creep respawning. The function returns whether it succeeded or failed.
1.31+
Older patch
JASS:
library CreepRespawn initializer onInit /* v3.0.2
*************************************************************************************
*
* A simple Creep Respawn System tailored for RPG maps. It does not requires
* SetUnitUserData and GetUnitUserData. As a result, it is compatible with any
* existing unit indexer.
*
*************************************************************************************
*
* */ requires /*
*
* */ CTL /* https://github.com/nestharus/JASS/blob/master/jass/Systems/ConstantTimerLoop32/script.j
*
*
* */ optional /*
*
* */ RegisterPlayerUnitEvent /* https://www.hiveworkshop.com/threads/snippet-registerevent-pack.250266/
*
*
*************************************************************************************
*
* NOTE: If you’re using an older version of Warcraft 3, change the values of
* trn.red, trn.green, trn.blue, and trn.alphaMax to 255. The drawback is
* that you can't save and load the RGB color of the creep.
*/
//! textmacro CREEP_RESPAWN_TRANSPARENCY
set trn.red = BlzGetUnitIntegerField(I[creepIndex], UNIT_IF_TINTING_COLOR_RED)
set trn.green = BlzGetUnitIntegerField(I[creepIndex], UNIT_IF_TINTING_COLOR_GREEN)
set trn.blue = BlzGetUnitIntegerField(I[creepIndex], UNIT_IF_TINTING_COLOR_BLUE)
set trn.alphaMax = BlzGetUnitIntegerField(I[creepIndex], UNIT_IF_TINTING_COLOR_ALPHA)
//! endtextmacro
/************************************************************************************
*
* Configurations
*
************************************************************************************/
globals
// Owner
private constant player CREEP_OWNER = Player(PLAYER_NEUTRAL_AGGRESSIVE)
// Revive Special Effect
private constant string REVIVE_EFFECT = "Abilities\\Spells\\Human\\Resurrect\\ResurrectTarget.mdl"
// Mini Boss and Boss Detector
private constant integer CREEP_BOSS_DETECTOR = 'A000'
private constant integer CREEP_MINI_BOSS_DETECTOR = 'A001'
// Creep Revival Durations
private constant real CREEP_DEFAULT_RESPAWN_DURATION = 10
private constant real CREEP_BOSS_RESPAWN_DURATION = 30
private constant real CREEP_MINI_BOSS_RESPAWN_DURATION = 15
// Fade In
private constant boolean ALLOW_FADE_IN = true
private constant integer ALPHA_INCREMENT = 5
/************************************************************************************
*
* API
* ---------
*
* function AddCreepRespawn takes unit whichCreep returns integer
* - Include a unit to the Creep Respawn System. The function returns the Creep
* Index associated with that unit.
*
* function AddCreepRespawnEx takes unit whichCreep, real duration returns integer
* - Include a unit to the Creep Respawn System with a custom duration. The
* function returns the Creep Index associated with that unit.
*
* function SetCreepRespawn takes unit whichCreep, boolean enable returns boolean
* - Toggleable creep respawning. The function returns whether it succeeded or
* failed.
*
*************************************************************************************
*
* Ignore these below.
*
************************************************************************************/
private integer CREEP_INDEXED_COUNT = 0
private real array X // X Coord
private real array Y // Y Coord
private real array F // Unit Facing
private integer array U // Unit Type Id
private real array D // Duration
private unit array I // Creep
private boolean array B // Enable Revival
endglobals
private function getCreepDuration takes unit whichCreep returns real
if GetUnitAbilityLevel(whichCreep, CREEP_BOSS_DETECTOR) > 0 then
return CREEP_BOSS_RESPAWN_DURATION
elseif GetUnitAbilityLevel(whichCreep, CREEP_MINI_BOSS_DETECTOR) > 0 then
return CREEP_MINI_BOSS_RESPAWN_DURATION
else
return CREEP_DEFAULT_RESPAWN_DURATION
endif
endfunction
private function storeCreepData takes unit whichCreep, real duration returns integer
if GetOwningPlayer(whichCreep) == CREEP_OWNER then
set CREEP_INDEXED_COUNT = CREEP_INDEXED_COUNT + 1
set X[CREEP_INDEXED_COUNT] = GetUnitX(whichCreep)
set Y[CREEP_INDEXED_COUNT] = GetUnitY(whichCreep)
set F[CREEP_INDEXED_COUNT] = GetUnitFacing(whichCreep)
set U[CREEP_INDEXED_COUNT] = GetUnitTypeId(whichCreep)
set D[CREEP_INDEXED_COUNT] = duration
set I[CREEP_INDEXED_COUNT] = whichCreep
set B[CREEP_INDEXED_COUNT] = true
call SetUnitUseFood(whichCreep, false)
static if DEBUG_MODE then
debug call AddSpecialEffect("buildings\\other\\CircleOfPower\\CircleOfPower.mdl", X[CREEP_INDEXED_COUNT], Y[CREEP_INDEXED_COUNT])
endif
return CREEP_INDEXED_COUNT
endif
return 0
endfunction
private function findCreepDataIndex takes unit whichCreep returns integer
local integer count = 0
loop
exitwhen count == CREEP_INDEXED_COUNT
set count = count + 1
if I[count] == whichCreep then
return count
endif
endloop
return 0
endfunction
function AddCreepRespawn takes unit whichCreep returns integer
return storeCreepData(whichCreep, getCreepDuration(whichCreep))
endfunction
function AddCreepRespawnEx takes unit whichCreep, real duration returns integer
return storeCreepData(whichCreep, duration)
endfunction
function SetCreepRespawn takes unit whichCreep, boolean enable returns boolean
local integer creepIndex
if GetOwningPlayer(whichCreep) == CREEP_OWNER then
set creepIndex = findCreepDataIndex(whichCreep)
if creepIndex > 0 then
set B[creepIndex] = enable
return true
endif
endif
return false
endfunction
static if ALLOW_FADE_IN then
private struct transparent extends array
integer red
integer blue
integer green
integer alpha
integer alphaMax
unit unit
implement CTLExpire
if this.alpha >= this.alphaMax then
call SetUnitVertexColor(this.unit, this.red, this.green, this.blue, this.alphaMax)
set this.red = 0
set this.blue = 0
set this.green = 0
set this.alpha = 0
set this.alphaMax = 0
set this.unit = null
call this.destroy()
else
set this.alpha = this.alpha + ALPHA_INCREMENT
call SetUnitVertexColor(this.unit, this.red, this.green, this.blue, this.alpha)
endif
implement CTLEnd
endstruct
endif
private struct creepRevival extends array
real duration
integer creepData
integer uId
implement CTL
local real x
local real y
local real f
local integer creepIndex
static if ALLOW_FADE_IN then
local transparent trn
endif
implement CTLExpire
set creepIndex = this.creepData
if B[creepIndex] then
if this.duration <= 0 then
// Retrieve the X and Y coordinates as well as the unit’s facing direction.
set x = X[creepIndex]
set y = Y[creepIndex]
set f = F[creepIndex]
if IsUnitType(I[creepIndex], UNIT_TYPE_HERO) then
// Revive the Creep Hero
call ReviveHero(I[creepIndex], x, y, false)
call DestroyEffect(AddSpecialEffectTarget(REVIVE_EFFECT, I[creepIndex], "origin"))
else
// Substitute the new unit with the old unit.
set I[creepIndex] = CreateUnit(CREEP_OWNER, this.uId, x, y, f)
call DestroyEffect(AddSpecialEffectTarget(REVIVE_EFFECT, I[creepIndex], "origin"))
endif
static if ALLOW_FADE_IN then
set trn = transparent.create()
set trn.unit = I[creepIndex]
set trn.alpha = 0
//! runtextmacro CREEP_RESPAWN_TRANSPARENCY()
call SetUnitVertexColor(trn.unit, trn.red, trn.green, trn.blue, trn.alpha)
endif
// Data cleanup
set this.duration = 0
set this.creepData = 0
set this.uId = 0
call destroy()
else
set this.duration = this.duration - 0.031250000
endif
else
// Creep is no longer revivable.
set this.duration = 0
set this.creepData = 0
set this.uId = 0
call destroy()
endif
implement CTLEnd
endstruct
static if LIBRARY_RegisterPlayerUnitEvent then
private function onDeath takes nothing returns nothing
else
private function onDeath_ takes nothing returns boolean
endif
local unit Creep = GetTriggerUnit()
local integer creepId
local creepRevival this
if GetOwningPlayer(Creep) == CREEP_OWNER then
set creepId = findCreepDataIndex(Creep)
if creepId > 0 then
set this = creepRevival.create()
set this.duration = D[creepId]
set this.uId = U[creepId]
set this.creepData = creepId
endif
endif
set Creep = null
static if not LIBRARY_RegisterPlayerUnitEvent then
return false
endif
endfunction
private function registerCreepData takes nothing returns nothing
call AddCreepRespawn(GetFilterUnit())
endfunction
private function initRegisterCreepData takes nothing returns nothing
// Register all created creeps
call GroupEnumUnitsOfPlayer(bj_lastCreatedGroup, CREEP_OWNER, Filter(function registerCreepData))
endfunction
private function onInit takes nothing returns nothing
static if LIBRARY_RegisterPlayerUnitEvent then
call RegisterPlayerUnitEvent(CREEP_OWNER, EVENT_PLAYER_UNIT_DEATH, function onDeath)
else
local trigger trg = CreateTrigger()
call TriggerRegisterPlayerUnitEvent(trg, CREEP_OWNER, EVENT_PLAYER_UNIT_DEATH, null)
call TriggerAddCondition(trg, Filter(function onDeath_))
endif
call initRegisterCreepData()
endfunction
endlibrary
⚠️ IMPORTANT NOTE ⚠️
If you’re using an older version of Warcraft 3, change the values of trn.red, trn.green, trn.blue, and trn.alphaMax to 255. The drawback is that you can't save and load the RGB color of the creep.
JASS:
library CreepRespawn initializer onInit /* v3.0.2
*************************************************************************************
*
* A simple Creep Respawn System tailored for RPG maps. It does not requires
* SetUnitUserData and GetUnitUserData. As a result, it is compatible with any
* existing unit indexer.
*
*************************************************************************************
*
* */ requires /*
*
* */ CTL /* https://github.com/nestharus/JASS/blob/master/jass/Systems/ConstantTimerLoop32/script.j
*
*
* */ optional /*
*
* */ RegisterPlayerUnitEvent /* https://www.hiveworkshop.com/threads/snippet-registerevent-pack.250266/
*
*
*************************************************************************************
*
* NOTE: If you’re using an older version of Warcraft 3, change the values of
* trn.red, trn.green, trn.blue, and trn.alphaMax to 255. The drawback is
* that you can't save and load the RGB color of the creep.
*/
//! textmacro CREEP_RESPAWN_TRANSPARENCY
set trn.red = 255
set trn.green = 255
set trn.blue = 255
set trn.alphaMax = 255
//! endtextmacro
/************************************************************************************
*
* Configurations
*
************************************************************************************/
globals
// Owner
private constant player CREEP_OWNER = Player(PLAYER_NEUTRAL_AGGRESSIVE)
// Revive Special Effect
private constant string REVIVE_EFFECT = "Abilities\\Spells\\Human\\Resurrect\\ResurrectTarget.mdl"
// Mini Boss and Boss Detector
private constant integer CREEP_BOSS_DETECTOR = 'A000'
private constant integer CREEP_MINI_BOSS_DETECTOR = 'A001'
// Creep Revival Durations
private constant real CREEP_DEFAULT_RESPAWN_DURATION = 10
private constant real CREEP_BOSS_RESPAWN_DURATION = 30
private constant real CREEP_MINI_BOSS_RESPAWN_DURATION = 15
// Fade In
private constant boolean ALLOW_FADE_IN = true
private constant integer ALPHA_INCREMENT = 5
/************************************************************************************
*
* API
* ---------
*
* function AddCreepRespawn takes unit whichCreep returns integer
* - Include a unit to the Creep Respawn System. The function returns the Creep
* Index associated with that unit.
*
* function AddCreepRespawnEx takes unit whichCreep, real duration returns integer
* - Include a unit to the Creep Respawn System with a custom duration. The
* function returns the Creep Index associated with that unit.
*
* function SetCreepRespawn takes unit whichCreep, boolean enable returns boolean
* - Toggleable creep respawning. The function returns whether it succeeded or
* failed.
*
*************************************************************************************
*
* Ignore these below.
*
************************************************************************************/
private integer CREEP_INDEXED_COUNT = 0
private real array X // X Coord
private real array Y // Y Coord
private real array F // Unit Facing
private integer array U // Unit Type Id
private real array D // Duration
private unit array I // Creep
private boolean array B // Enable Revival
endglobals
private function getCreepDuration takes unit whichCreep returns real
if GetUnitAbilityLevel(whichCreep, CREEP_BOSS_DETECTOR) > 0 then
return CREEP_BOSS_RESPAWN_DURATION
elseif GetUnitAbilityLevel(whichCreep, CREEP_MINI_BOSS_DETECTOR) > 0 then
return CREEP_MINI_BOSS_RESPAWN_DURATION
else
return CREEP_DEFAULT_RESPAWN_DURATION
endif
endfunction
private function storeCreepData takes unit whichCreep, real duration returns integer
if GetOwningPlayer(whichCreep) == CREEP_OWNER then
set CREEP_INDEXED_COUNT = CREEP_INDEXED_COUNT + 1
set X[CREEP_INDEXED_COUNT] = GetUnitX(whichCreep)
set Y[CREEP_INDEXED_COUNT] = GetUnitY(whichCreep)
set F[CREEP_INDEXED_COUNT] = GetUnitFacing(whichCreep)
set U[CREEP_INDEXED_COUNT] = GetUnitTypeId(whichCreep)
set D[CREEP_INDEXED_COUNT] = duration
set I[CREEP_INDEXED_COUNT] = whichCreep
set B[CREEP_INDEXED_COUNT] = true
call SetUnitUseFood(whichCreep, false)
static if DEBUG_MODE then
debug call AddSpecialEffect("buildings\\other\\CircleOfPower\\CircleOfPower.mdl", X[CREEP_INDEXED_COUNT], Y[CREEP_INDEXED_COUNT])
endif
return CREEP_INDEXED_COUNT
endif
return 0
endfunction
private function findCreepDataIndex takes unit whichCreep returns integer
local integer count = 0
loop
exitwhen count == CREEP_INDEXED_COUNT
set count = count + 1
if I[count] == whichCreep then
return count
endif
endloop
return 0
endfunction
function AddCreepRespawn takes unit whichCreep returns integer
return storeCreepData(whichCreep, getCreepDuration(whichCreep))
endfunction
function AddCreepRespawnEx takes unit whichCreep, real duration returns integer
return storeCreepData(whichCreep, duration)
endfunction
function SetCreepRespawn takes unit whichCreep, boolean enable returns boolean
local integer creepIndex
if GetOwningPlayer(whichCreep) == CREEP_OWNER then
set creepIndex = findCreepDataIndex(whichCreep)
if creepIndex > 0 then
set B[creepIndex] = enable
return true
endif
endif
return false
endfunction
static if ALLOW_FADE_IN then
private struct transparent extends array
integer red
integer blue
integer green
integer alpha
integer alphaMax
unit unit
implement CTLExpire
if this.alpha >= this.alphaMax then
call SetUnitVertexColor(this.unit, this.red, this.green, this.blue, this.alphaMax)
set this.red = 0
set this.blue = 0
set this.green = 0
set this.alpha = 0
set this.alphaMax = 0
set this.unit = null
call this.destroy()
else
set this.alpha = this.alpha + ALPHA_INCREMENT
call SetUnitVertexColor(this.unit, this.red, this.green, this.blue, this.alpha)
endif
implement CTLEnd
endstruct
endif
private struct creepRevival extends array
real duration
integer creepData
integer uId
implement CTL
local real x
local real y
local real f
local integer creepIndex
static if ALLOW_FADE_IN then
local transparent trn
endif
implement CTLExpire
set creepIndex = this.creepData
if B[creepIndex] then
if this.duration <= 0 then
// Retrieve the X and Y coordinates as well as the unit’s facing direction.
set x = X[creepIndex]
set y = Y[creepIndex]
set f = F[creepIndex]
if IsUnitType(I[creepIndex], UNIT_TYPE_HERO) then
// Revive the Creep Hero
call ReviveHero(I[creepIndex], x, y, false)
call DestroyEffect(AddSpecialEffectTarget(REVIVE_EFFECT, I[creepIndex], "origin"))
else
// Substitute the new unit with the old unit.
set I[creepIndex] = CreateUnit(CREEP_OWNER, this.uId, x, y, f)
call DestroyEffect(AddSpecialEffectTarget(REVIVE_EFFECT, I[creepIndex], "origin"))
endif
static if ALLOW_FADE_IN then
set trn = transparent.create()
set trn.unit = I[creepIndex]
set trn.alpha = 0
//! runtextmacro CREEP_RESPAWN_TRANSPARENCY()
call SetUnitVertexColor(trn.unit, trn.red, trn.green, trn.blue, trn.alpha)
endif
// Data cleanup
set this.duration = 0
set this.creepData = 0
set this.uId = 0
call destroy()
else
set this.duration = this.duration - 0.031250000
endif
else
// Creep is no longer revivable.
set this.duration = 0
set this.creepData = 0
set this.uId = 0
call destroy()
endif
implement CTLEnd
endstruct
static if LIBRARY_RegisterPlayerUnitEvent then
private function onDeath takes nothing returns nothing
else
private function onDeath_ takes nothing returns boolean
endif
local unit Creep = GetTriggerUnit()
local integer creepId
local creepRevival this
if GetOwningPlayer(Creep) == CREEP_OWNER then
set creepId = findCreepDataIndex(Creep)
if creepId > 0 then
set this = creepRevival.create()
set this.duration = D[creepId]
set this.uId = U[creepId]
set this.creepData = creepId
endif
endif
set Creep = null
static if not LIBRARY_RegisterPlayerUnitEvent then
return false
endif
endfunction
private function registerCreepData takes nothing returns nothing
call AddCreepRespawn(GetFilterUnit())
endfunction
private function initRegisterCreepData takes nothing returns nothing
// Register all created creeps
call GroupEnumUnitsOfPlayer(bj_lastCreatedGroup, CREEP_OWNER, Filter(function registerCreepData))
endfunction
private function onInit takes nothing returns nothing
static if LIBRARY_RegisterPlayerUnitEvent then
call RegisterPlayerUnitEvent(CREEP_OWNER, EVENT_PLAYER_UNIT_DEATH, function onDeath)
else
local trigger trg = CreateTrigger()
call TriggerRegisterPlayerUnitEvent(trg, CREEP_OWNER, EVENT_PLAYER_UNIT_DEATH, null)
call TriggerAddCondition(trg, Filter(function onDeath_))
endif
call initRegisterCreepData()
endfunction
endlibrary
Changelog
v3.0.2
Replaced //! novjass with //! textmacro CREEP_RESPAWN_TRANSPARENCY in order to easily access the trn values.
Removed the NewTable library. Use a global variable array instead.
Since it has its own unit indexing, it can no longer rely on SetUnitUserData() and GetUnitUserData(). Consequently, it is compatible with any existing unit indexer.
v2.0
Replaced TimerUtils library with CTL (ConstantTimerLoop) for better performance.
Changed library to scope.
Temporarily removed UnitDex; Unit Indexer will be added soon.
Don't have my Ph.D. in cryptography yet, so I unfortunately don't quite understand how the CTL library works. :pcry: Out of interest, can you explain "Replaced TimerUtils library with CTL (ConstantTimerLoop) for better performance"? What exactly...
Please review the Resource Submission Rules regarding documentation requirements. Specifically, your documentation is missing:
What are the specific features/limitations?
How do I import/get it working? Do I have to add units manually or are they added automatically?
How does the API work? What do I do with the integer returned by AddCreepRespawn etc.?
A well-designed system on first glance. We'll have to work our way through the long list of pending resources, then we can do a full review and approve it.
Don't have my Ph.D. in cryptography yet, so I unfortunately don't quite understand how the CTL library works. Out of interest, can you explain "Replaced TimerUtils library with CTL (ConstantTimerLoop) for better performance"? What exactly makes CTL more performant? How was it handled before?
The documentation you wrote on the hive page definitely helps, but I would still ask you to elaborate on some of the things that are unclear.
You're automatically adding creeps that are present on map initialization to your system, but not ones that get created later. This is not explained anywhere. I think an option to toggle this behavior would be a nice addition. Automatically adding newly created units could be done with a unit-enters-map trigger.
Your functions return the creep index, so I would assume there is an API function that accepts an integer instead of a unit, which could be useful, since the unit gets replaced by a new one. But there's no way to make use of that integer with your system.
Some more thoughts:
You're limiting the user to declare one player that owns all the creeps, but someone might have multiple factions with creeps and doesn't want to be limited in such a way. An option to turn this off would be nice or the ability to use the AddCreepRespawn function to register units owned by other players.
I recommend using a hashtable here to improve performance:
JASS:
private function findCreepDataIndex takes unit whichCreep returns integer
local integer count = 0
loop
exitwhen count == CREEP_INDEXED_COUNT
set count = count + 1
if I[count] == whichCreep then
return count
endif
endloop
return 0
endfunction
Is the RegisterPlayerUnitEvent inclusion necessary? You're just using it to execute the same code as in the block without it, but in a more convoluted way. I don't quite see the point of including a library to save writing two lines of code total, especially if you're then using more lines of code to make the library optional. It's just needless confusion.
JASS:
static if LIBRARY_RegisterPlayerUnitEvent then
private function onDeath takes nothing returns nothing
else
private function onDeath_ takes nothing returns nothing
endif
Is this for older Wc3 versions? You do not need to return booleans in conditionfuncs anymore. Idk when it was changed.
This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
By continuing to use this site, you are consenting to our use of cookies.