• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Automatic Creep Revival

Level 4
Joined
Feb 18, 2008
Messages
69
i1mgrl.png[

v2.2

2jzbqa.png
v2.2 - 5.14.2010
- Removed some unnecessary nulling​

v2.1 - 5.10.2010
- You can now set time of first appear
- Respawn time now has min and max time of respawn​

v2.0 - 5.8.2010
- Removed [MAX_CREEP], using linked list now, thanks to all who helped and forced me to :)
- There's effect on creeps revive now, you can change it in globals​

v1.5 - 5.5.2010
- Fixed "fixed" bug :D
- Now definitely works properly​

v1.4 - 5.3.2010
- Fixed major bug causing system to not work properly​

v1.3 - 4.19.2010
- Now does all stuff in enum function
- MAX_CREEPS increased to 8191, not like you ever going to need that high number
- struct is now not private, could not compile​

v1.2 - 4.18.2010
- Now uses only one global group for all enum calls​

v1.1 - 4.14.2010
- Scope changed to library​

v1.0a - 4.13.2010
- Fixed an error causing system not to compile​

v1.0 - 4.13.2010

- First release​

33o4mpv.png
I remade an old version of creep revival, now using structs, has cleaner code, a way to filter units before storing as creeps.​

dmd7dd.png

6ozti8.png
  • Make new trigger.
  • Rename it to Automatic Creep Revival or whatever you want.
  • Edit -> Convert to custom text.
  • Replace the whole trigger with the code.
  • Edit configurables if needed.
You don't have to do anything else! Just put creeps on map! You don't have to set any variables or call any functions! It will do all job for you!

k1b6tj.png
JASS:
//  |==========================================|
//  |******************************************|
//  |*****    AUTOMATIC CREEP REVIVAL     *****|
//  |***     ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯     ****|
//  |**              by Furby               ***|
//  |*****             v2.2               *****|
//  |******************************************|
//  |            - Requirements -              |
//  |               TimerUtils                 |
//  |                 Table                    |
//  |******************************************|
//  |                Credits:                  |
//  |             - Jesus4Lyf -                |
//  |      telling me to use linked list       |
//  |                                          |
//  | - Viikuna, kingkingyyk3, SerraAvenger -  |
//  |   helping me understand linked lists     |
//  |                                          |
//  |******************************************|
//  |        more info at thehelper.net        |
//  |==========================================|
library AutomaticCreepRevival requires TimerUtils, Table
    
    globals
        private constant real   INIT_SPAWN       = 60. // At what time of game will creeps firstly appear
        private constant real   RESPAWN_TIME_MIN = 30. // Minimal time between death of last creep of camp and reviving
        private constant real   RESPAWN_TIME_MAX = 30. // Maximal time between death of last creep of camp and reviving
        private constant real   BASE_MAXRADIUS   = 700. // Maximum radius of camp
        private constant string REVIVE_EFFECT    = "Abilities\\Spells\\Human\\Polymorph\\PolyMorphDoneGround.mdl" // Path of revive effect
        private constant string REVIVE_ATTACH    = "origin" // Path of attach point
    endglobals
    
    // If this function will return true, unit will be stored as creep if not, unit will be ignored
    private function ValidCreep takes unit whatCreep returns boolean
        return /*
        */GetOwningPlayer(whatCreep)                 == Player(PLAYER_NEUTRAL_AGGRESSIVE) and /*
        */IsUnitType(whatCreep, UNIT_TYPE_STRUCTURE) == false                             and /*
        */IsUnitType(whatCreep, UNIT_TYPE_ANCIENT)   == false                           //and
    endfunction
    
    // = = = = = = = = = = = = = = = = = = = = = = = = = = = = 
    //  DO NOT TOUCH BELOW UNLESS YOU KNOW WHAT'RE YOU DOING!
    // = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    
    globals
        private group ALL = CreateGroup()
        private group G = CreateGroup()
        private CreepCamp CAMP = 0
        private CreepUnit UNIT = 0
    endglobals
    
    struct CreepUnit
        unit    creep      // Actual creep units
        integer creepOwner // Id of the creep's owner
        integer creepType  // Type of the creep (raw code)
        real    creepX     // X position of the creep
        real    creepY     // Y position of the creep
        real    creepAng   // Angle of the creep
        static HandleTable table
        CreepUnit next
    endstruct
    
    struct CreepCamp // Struct instances are basicly creep camps
        CreepUnit first
        
        private static method onRevive takes nothing returns nothing
            local timer t = GetExpiredTimer()
            local thistype this = thistype(GetTimerData(t))
            local CreepUnit mob = this.first
            loop
                exitwhen(mob.next == mob)
                set mob = mob.next
                call ShowUnit(mob.creep, true)
                call DestroyEffect(AddSpecialEffectTarget(REVIVE_EFFECT, mob.creep, REVIVE_ATTACH))
            endloop
            call ReleaseTimer(t)
        endmethod
        
        private static method onDeath takes nothing returns boolean
            local unit dead = GetTriggerUnit()
            local thistype this = thistype(CreepUnit.table[dead])
            local CreepUnit mob = 0
            local boolean startTimer = true
            local timer t
            if this != 0 then
                call CreepUnit.table.flush(dead)
                set mob = this.first
                loop
                    exitwhen(mob.next == mob)
                    set mob = mob.next
                    if mob.creep == dead then
                        set mob.creep = CreateUnit(Player(mob.creepOwner), mob.creepType, mob.creepX, mob.creepY, mob.creepAng)
                        set CreepUnit.table[mob.creep] = this
                        call ShowUnit(mob.creep, false)
                    endif
                    if IsUnitHidden(mob.creep) == false then
                        set startTimer = false
                    endif
                endloop
                if startTimer then
                    set t = NewTimer()
                    call TimerStart(t, GetRandomReal(RESPAWN_TIME_MIN, RESPAWN_TIME_MAX), false, function thistype.onRevive)
                    call SetTimerData(t, this)
                endif
            endif
            set dead = null
            return false
        endmethod
        
        private static method saveCreeps takes nothing returns boolean
            local unit filter = GetFilterUnit()
            local CreepUnit another
            if ValidCreep(filter) and CreepUnit.table.exists(filter) == false then
                set CreepUnit.table[filter] = CAMP
                set another = CreepUnit.create()
                set UNIT.next = another
                set UNIT = another
                set UNIT.creep = filter
                set UNIT.creepOwner = GetPlayerId(GetOwningPlayer(filter))
                set UNIT.creepType = GetUnitTypeId(filter)
                set UNIT.creepX = GetUnitX(filter)
                set UNIT.creepY = GetUnitY(filter)
                set UNIT.creepAng = GetUnitFacing(filter)
                call ShowUnit(UNIT.creep, false)
            endif
            return false
        endmethod
        
        private static method saveCamps takes nothing returns boolean
            local unit filter = GetFilterUnit()
            local real x = GetUnitX(filter)
            local real y = GetUnitY(filter)
            local integer i
            local boolean result = false
            if ValidCreep(filter) and CreepUnit.table.exists(filter) == false then
                set CAMP = thistype.create()
                set UNIT = CreepUnit.create()
                set CAMP.first = UNIT
                call GroupEnumUnitsInRange(G, x, y, BASE_MAXRADIUS, Condition(function thistype.saveCreeps))
                set UNIT.next = UNIT
                set result = true
            elseif CreepUnit.table.exists(filter) then
                set result = true
            endif
            set filter = null
            return result
        endmethod
        
        private static method showUnits takes nothing returns nothing
            call ShowUnit(GetEnumUnit(), true)
        endmethod
        
        private static method firstSpawn takes nothing returns boolean
            call ForGroup(ALL, function thistype.showUnits)
            call DestroyGroup(ALL)
            set ALL = null
            return false
        endmethod
        
        private static method onInit takes nothing returns nothing 
            local trigger t = CreateTrigger()
            set CreepUnit.table = HandleTable.create()
            call GroupEnumUnitsInRect(ALL, bj_mapInitialPlayableArea, Condition(function thistype.saveCamps))
            call TriggerRegisterTimerEvent(t, INIT_SPAWN, false)
            call TriggerAddCondition(t, Condition(function thistype.firstSpawn))
            set t = CreateTrigger()
            call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
            call TriggerAddCondition(t, Condition(function thistype.onDeath))
            call DestroyGroup(G)
            set G = null
        endmethod
    endstruct
endlibrary

10dh2si.png
  • Does it respawn the whole camp of creeps at once?
    - Yes, all units of camp at once.
  • But I want random spawns!
    - Alt + F4
 
Last edited:
Level 8
Joined
Oct 3, 2008
Messages
367
Ah, ze code is veddy cleanly organized and documented.

This is good, but there are some things that could be tweaked.

There are some unnecessary variables being nulled. Timers you get from NewTimer don't need nulling, and that trigger in the initializer doesn't need nulling. That's all I spotted on this subject.

Also, you shouldn't attach to units via Table. A unit indexer is a better choice, although it would require users to get one. With current vJass technology, you can optionally require any popular unit indexer and if it fails, resort to Table usage. This makes everyone happy.
 
Level 4
Joined
Feb 18, 2008
Messages
69
Also, you shouldn't attach to units via Table. A unit indexer is a better choice, although it would require users to get one. With current vJass technology, you can optionally require any popular unit indexer and if it fails, resort to Table usage. This makes everyone happy.

Hm, and how with indexer(I'd use AIDS)? CreepCamp array.. CampUnit[index of unit] = camp of unit.. but that would need a lot of looping at initialization.
 
Top