• 🏆 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!

[JASS] CreepSpot System

Status
Not open for further replies.
Hi guys, I need a creep system for my project and so I decided to look here for one. However I feel bad because there is only 1 which is old and outdated so I decided to create my own system.

What is a CreepSpot System?
- Well, a creep spot is a spot with creeps. When the creeps die, you can use my system to revive them after X seconds in a periodic cycle, it is a respawn creep system.

Why use my system?
1 - Create CreepSpots with only 1 line of code! After that you can forget about them, the system will make all the job for you.
2 - Works for all kinds of units from all kinds of players.
3 - When creating the creeps their facing degree is the same.
4 - The initial position of the creeps is always the same as well
5 - The system will prevent the respawn of creeps if nearby units are near and you can change this condition very easily.
6 - Unlike Dota this system won't bug at all! With my system this movie would never happen:
YouTube - DotA jungling tricks
Meaning? As long as 1 unit of the group remains alive, there will be no respawn.
7 - You can customize your creep spawns to many levels, including the refresh rate, and the verification range.
8 - You can create effects when the creeps are created, this eye-candy feature is also very easy to customize.

This system requires TimerUtils.

So far this is what I have. I tested it and I don't think it bugs under any occasion, so I feel confident enough to say that this system is quite stable, however is what I call "static" respawn system. What does this mean? Well, it basically means that you can't change the types and numbers of units that spawn under a creep spot.

History:

Version 1.0:
- Initial release to the foruns to ask for oppinions and suggestions.

Version 1.1 and 1.2:
- Fixed and improved a few sections of the code and added new functionalities.

Version 1.3:
- Greatly improved the efficiency of the code and now it is a lot easier to understand.
- Replace groups with arrays that now keep all needed information about the units.
- Now when teh creeps spawn there can be effects for eye candy purposes.
- Added and fixed other stuff.

Version 1.4:
- Added change log to keep track of changes.
- Added a debug message that lets the user know if something is not well configured.
- Added GUI example and improved the test map.

Version 1.5:
- Added new functionalities to the system and changed its name, it is no longer static.

Version 1.51:
- Added a new functionality, "getCreepGroup".
- Fixed a bug with the restartSpawnTimer method.
- Added Element of Water to the credits.
- Fixed a bug with addUnit method, now it works properly.
- Added a new functionality, "removeUnit".
- Added Documentation that explains the system.

Version 1.6:
- Added a new method addGroup.
- Added a new method removeGroup.
- Added a new method killAll.
- Added a new method setUnitPosition.
- Improved the documentation.
- Change the name of the system, the structure is no longer public. Now newbies will
find it easier to use the system.

Version 1.7:
- Added new method addNum.
- Added new method removeNum.
- Changed the name of the constructor to createFromRect.
- Added a new constructor called createFromGroup.
- Improved the AddUnit method, now when a unit is created it also plays the effect.
- Improved the documentation, special thx to "Water Elemental".
- Improved the system so now it doesnt use the bj_lastCreatedUnit variable.
- Added setRefresh method.
- Added setRange method.


How to use? Well, using this system can't get any easier, you only need 1 line and all the job is done.

If you are lazy and don't want the hassle of reading all my system or importing the map, here is the documentation:

This "CreepSpot" system is a system designed to allow map makers to create creep
respawn regions as easily and in the most userfriendly way possible, requiring very
little effort. This system is based on the notion of an object and its use is quite
similar to the use of an object in Java, allowing you to create a variable for each
CreepSpot which you can modify very easily using the methods below. The system has
many methods and it is advisable to know each one of them so you can use the system
in the best possible way:

static method createFromRect takes rect place, real rate, real range,
string bornEff returns CreepSpot

This method creates a CreepSpot variable from an already created rect in the map.
It simply saves all units inside the rect and after that it will spawn them as a group
of creeps in a periodic fashion. For more information see the sample trigger.

static method createFromGroup takes group g, real rate, real range,
string bornEff returns CreepSpot

This is another create method. It creates a CreepSpot variable from an already
created group of units. Note that the units MUST ALREADY BE PLACED ON THE MAP before
you can call this method in a safe and sane way. This method allows the creation of
dynamic CreepSpots anywhere at anytime during the game and will also respawn
the creeps of the group according to their original positions and angles periodicaly.

method onDestroy takes nothing returns nothing

Well, I dont know if this will be useful, but it is a good idea to have it.
If you no longer want the CreepSpot alive, just destroy it to release resources.

method addUnit takes player owner, integer unitId, real unitX, real unitY,
real unitFace returns unit

This method allows you to add a single unit to the creepSpot. This can be
useful if you want to increase the number of creeps with time or depending on some
other event.

method removeUnit takes unit who returns nothing

As well as there is a method to add units, it makes sense to create one to remove
them. This method removes a unit from the CreepSpot group. It kills the unit and does
not show its death animation, just like removing it. A removed unit will not spawn
again, unless it is added again.

method getCreepGroup takes nothing returns group

This method returns a group with all the units of a creep spot. This is usefull,
you can use this to select all creeps of the group and then order them to do something or
if you want, or to add/remove something for them. Just use your imagination.

method pauseSpawnTimer takes nothing returns nothing
method restartSpawnTimer takes nothing returns nothing

These methods pause or re-start the timer that controls the spawns. This
functionality can be usefull if for some reason you want to make a pause.

method addGroup takes group g returns group

This method adds a group of units to the creepSpot. Please note that the units
'must already be created and placed on the map', else bugs will occur. There is no way
for me to check if a unit has a valid X or Y, so the user must be careful with this
functionality. This method returns a group with all units of the creepSpot, after the
addition is complete.

method removeGroup takes group g returns group

If we can add groups of units, than we should also be able to remove groups of
units. This method removes units in a group from the creepSpot and returns a group
with the remaining units.

method killAll takes nothing returns nothing

Simply kills all creeps of a CreepSpot =P Just wanted to add this for fun lol.

method setUnitPosition takes unit who, real newX, real newY, real newFacing
returns nothing

This method changes the position of a unit in the creepSpot.

method addNum takes integer num, player owner, integer unitIds, boolean randomPos
returns nothing

This method adds a number of units with the same id to a creep spot and immediatly
spawns them.

method removeNum takes integer num, integer unitIds returns nothing

This method removes a number of units with the same id from a creep spot. The
units are simply removed and wont spawn again. The units are removed in a random way.

method setRefresh takes real newVal returns nothing
method setRange takes real newVal returns nothing

This methods allow the user to change the range of already created CreepSpots.
Note that the modificated variables will not be applied immediatly, but only the next
spawn of the units. Meaning? Well basically it means that if you create a CreepSpot
with a certain value, say A and B, for range and rate, and after that you change the
values using this methods, that the timer will only start using the new values after
the first spawn.



JASS:
//===========================================================================
//A system that allows the user to have creep spots in the easier possible 
//way using only 1 line.
//
//Requires TimerUtils
//
//@author Flame_Phoenix 
//
//@credits
//- Deaod, for helping me with a detail
//- Element of Water, for many ideas and suggestions
//
//@version 1.7
//===========================================================================
library CreepSys initializer Init requires TimerUtils
//===========================================================================
//=============================SETUP START===================================
//===========================================================================
    globals
        private constant real VERIFY = 5.   //if the creep spot has intruders preventing the spawn of the creeps, we make a check every 5. secs
        private constant integer MAX_UNITS_NUMBER = 5 //the maximum amount of units a creep spot can have
    endglobals

    private function ValidUnit takes unit who returns boolean
    //Here we select the units that will PREVENT the spawning of our creeps. 
    //In this case, if a unit which owner is not a passive player is inside the 
    //range of the CreepSpot, than the creeps will NOT re-spawn again.
        return GetWidgetLife(who) > 0.405 and GetOwningPlayer(who) !=  Player(PLAYER_NEUTRAL_PASSIVE)
    endfunction
//===========================================================================
//=============================SETUP END=====================================
//===========================================================================

    globals
        private group checkGroup
        private boolexpr checkConds
        private group initGroup
    endglobals
//===========================================================================
    private function RangeConds takes nothing returns boolean
        return ValidUnit(GetFilterUnit())
    endfunction
//===========================================================================    
    struct CreepSpot
        rect spawnPlace
        real refresh
        real checkRange
        timer t
        real array spawnX[MAX_UNITS_NUMBER]
        real array spawnY[MAX_UNITS_NUMBER]
        real array facing[MAX_UNITS_NUMBER]
        unit array units[MAX_UNITS_NUMBER]
        integer creepNum
        string bornEffect
        
        //recursive method
        static method SpawnUnits takes nothing returns nothing
            local CreepSpot data = CreepSpot(GetTimerData(GetExpiredTimer())) 
            local integer i
            local integer alive = data.creepNum
            local unit created
            
            call PauseTimer(data.t)
            
            //here we update the number of creeps belonging to the creep spot that
            //are alive
            set i = 0
            loop
                exitwhen(i == data.creepNum)
                if GetWidgetLife(data.units[i]) < 0.405 then
                    set alive = alive - 1
                endif
                set i = i + 1
            endloop
            
            call GroupEnumUnitsInRange(checkGroup, GetRectCenterX(data.spawnPlace), GetRectCenterY(data.spawnPlace), data.checkRange, checkConds)
            if CountUnitsInGroup(checkGroup) == 0 and alive == 0 then
                call GroupClear(checkGroup)
               
                //in this loop I create the new spawn which is exactly like the one 
                //before it
                set i = 0
                loop
                    exitwhen(i == data.creepNum)
                    set created = CreateUnit(GetOwningPlayer(data.units[i]), GetUnitTypeId(data.units[i]), data.spawnX[i], data.spawnY[i], data.facing[i])
                    set data.units[i] = created
                    call DestroyEffect(AddSpecialEffectTarget(data.bornEffect, data.units[i], "origin"))
                    set i = i + 1
                endloop
                
                //start the timer!
                call SetTimerData(data.t, integer(data))
                call TimerStart(data.t, data.refresh, true, function CreepSpot.SpawnUnits)
            else
                call GroupClear(checkGroup)
                
                //wait VERIFY secs
                call SetTimerData(data.t, integer(data))
                call TimerStart(data.t, VERIFY, true, function CreepSpot.SpawnUnits)
            endif
        endmethod
        
        method setRefresh takes real newVal returns nothing
            set .refresh = newVal
        endmethod
        
        method setRange takes real newVal returns nothing
            set .checkRange = newVal
        endmethod
        
        method setUnitPosition takes unit who, real newX, real newY, real newFacing returns nothing
            local integer i = 0
            
            loop 
                exitwhen(i == .creepNum)
                if .units[i] == who then
                    set .spawnX[i] = newX
                    set .spawnY[i] = newY
                    set .facing[i] = newFacing
                endif
                set i = i + 1
            endloop
        endmethod
        
        //pauses the timer that spawns the units
        method pauseSpawnTimer takes nothing returns nothing
            call PauseTimer(.t)
        endmethod
        
        //restarts the timer that spawns the units
        method restartSpawnTimer takes nothing returns nothing
            call TimerStart(.t, .refresh, true, function CreepSpot.SpawnUnits)
        endmethod
        
        //returns a group with the units of the CreepSpot
        method getCreepGroup takes nothing returns group
            local group ret = CreateGroup()
            local integer i = 0
            
            loop
                exitwhen(i == .creepNum)
                call GroupAddUnit(ret, .units[i])
                set i = i + 1
            endloop
            
            return ret
        endmethod
        
        //removes a unit from the group
        method removeUnit takes unit who returns nothing
            local integer i = 0
            local integer k
            
            loop 
                exitwhen(i == .creepNum)
                if .units[i] == who then
                    
                    //here we kill the unit without people seeing it
                    call ShowUnit(.units[i], false)
                    call KillUnit(.units[i])
                    
                    //now to remove it from the array... 
                    //we move all indexes after this unit 1 spot behind
                    set k = i
                    loop
                        exitwhen(k == .creepNum)
                        
                        set .units[k] = .units[i + 1] 
                        set .spawnX[k] = .spawnX[i + 1] 
                        set .spawnY[k] = .spawnY[i + 1] 
                        set .facing[k] = .facing[i + 1] 
                        
                        set k = k + 1
                    endloop
                    
                endif
                set i = i + 1
            endloop
            
            //we have -1 unit now
            set .creepNum = .creepNum - 1
        endmethod
        
        //adds a unit to the CreepSpot 
        method addUnit takes player owner, integer unitId, real unitX, real unitY, real unitFace returns unit
            //increase the counter
            set .creepNum = .creepNum + 1
            
            //safety check
            if .creepNum > MAX_UNITS_NUMBER then
                call BJDebugMsg("The addUnit call has passed the MAX_UNITS_NUMBER variable. Update this variable to accomodate the changes.")
            endif
            
            //add the unit to the last position of the array
            set .spawnX[.creepNum-1] = unitX
            set .spawnY[.creepNum-1] = unitY
            set .facing[.creepNum-1] = unitFace
            set .units[.creepNum-1] = CreateUnit(owner, unitId, unitX, unitY, unitFace)
            call DestroyEffect(AddSpecialEffectTarget(.bornEffect, .units[.creepNum-1], "origin"))
            
            return .units[.creepNum-1]
        endmethod
        
        method removeNum takes integer num, integer unitIds returns nothing
            local integer i = 0
            local integer k = 0
            
            if (.creepNum - num < 0) then
                call BJDebugMsg("The removeNum call failed, you are removing too many untis.")
            endif
            
            loop
                exitwhen(i == .creepNum)
                
                if GetUnitTypeId(.units[i]) == unitIds and k <= num then
                    call .removeUnit(.units[i])
                    set k = k + 1
                endif
                
                set i = i + 1
            endloop
        endmethod
        
        method addNum takes integer num, player owner, integer unitIds, boolean randomPos returns nothing
            local integer i = 0
            
            if (.creepNum + num > MAX_UNITS_NUMBER) then
                call BJDebugMsg("The addNum call has passed the MAX_UNITS_NUMBER variable. Update this variable to accomodate the changes.")
            endif
            
            loop
                exitwhen(i == num)
                
                if randomPos then
                    call .addUnit(owner, unitIds, GetRandomReal(GetRectMinX(.spawnPlace), GetRectMaxX(.spawnPlace)), GetRandomReal(GetRectMinY(.spawnPlace), GetRectMaxY(.spawnPlace)), 0.)
                else
                    call .addUnit(owner, unitIds, GetRectCenterX(.spawnPlace), GetRectCenterY(.spawnPlace), 0.)
                endif
                
                set i = i + 1
            endloop
        endmethod
        
        method killAll takes nothing returns nothing
            local integer i = 0
            
            loop
                exitwhen(i == .creepNum)
                call KillUnit(.units[i])
                set i = i + 1
            endloop
        endmethod
        
        method removeGroup takes group g returns group 
            local unit f 
            local group ret = CreateGroup()
            local integer i = 0
            
            //here we remove the units of this group from the creepSpot
            loop
                set f = FirstOfGroup(g)
                exitwhen(f == null)
                call GroupRemoveUnit(g, f)
                
                call .removeUnit(f)
            endloop
            
            //here we add all units of the creepSpot to the 
            //group ret
            loop
                exitwhen(i == .creepNum)
                call GroupAddUnit(ret, .units[i])
                set i = i + 1
            endloop
            
            //now we return the group with all units
            return ret
        endmethod
        
        method addGroup takes group g returns group
            local unit f 
            local group ret = CreateGroup()
            local integer i = 0
            
            if .creepNum + CountUnitsInGroup(g) > MAX_UNITS_NUMBER then
                call BJDebugMsg("Cannot add group to CreepSpot, it passes the maximum number of units allowed.")
            endif
            
            //here we add all units of the creepSpot to the 
            //group ret
            loop
                exitwhen(i == .creepNum)
                call GroupAddUnit(ret, .units[i])
                set i = i + 1
            endloop
            
            //here we remove units from group g and add them to the array and 
            //to the return group
            loop
                set f = FirstOfGroup(g)
                exitwhen(f == null)
                call GroupRemoveUnit(g, f)
                
                call .addUnit(GetOwningPlayer(f), GetUnitTypeId(f), GetUnitX(f), GetUnitY(f), GetUnitFacing(f))
                
                call GroupAddUnit(ret, f)
            endloop
            
            //now we return the group with all units
            return ret
        endmethod
        
        static method createFromGroup takes group g, real rate, real range, string bornEff returns CreepSpot
            local CreepSpot data = CreepSpot.allocate()
            local integer i = 0
            local unit f
            local real maxX 
            local real minX
            local real maxY
            local real minY
            
            //setting our members
            set data.refresh = rate
            set data.checkRange = range
            set data.bornEffect = bornEff
            set data.creepNum = CountUnitsInGroup(g)
            
            //now we save information about the units
            loop
                set f = FirstOfGroup(g)
                exitwhen(f == null)
                call GroupRemoveUnit(g, f)
                
                set data.units[i] = f
                set data.spawnX[i] = GetUnitX(f)
                set data.spawnY[i] = GetUnitY(f)
                set data.facing[i] = GetUnitFacing(f)
                
                set i = i + 1
            endloop
            
            //now we search for the X and Y values to create our rect
            set i = 0
            set maxX = data.spawnX[i]
            set minX = data.spawnX[i]
            set maxY = data.spawnY[i]
            set minY = data.spawnY[i]
            loop
                exitwhen(i == data.creepNum)
                
                if maxX < data.spawnX[i] then
                    set maxX = data.spawnX[i]
                endif
                
                if minX > data.spawnX[i] then
                    set minX = data.spawnX[i]
                endif
                
                if maxY < data.spawnY[i] then
                    set maxY = data.spawnY[i]
                endif
                
                if minY > data.spawnY[i] then
                    set minY = data.spawnY[i]
                endif
                
                set i = i + 1
            endloop
            
            //and now create the rect!
            set data.spawnPlace = Rect(minX, minY, maxX, maxY)
            
            //now we start the timer and make everything run!
            set data.t = NewTimer()
            
            //start the timer!
            call SetTimerData(data.t, integer(data))
            call TimerStart(data.t, data.refresh, true, function CreepSpot.SpawnUnits)
            
            return data
        endmethod
        
        static method createFromRect takes rect place, real rate, real range, string bornEff returns CreepSpot
            local CreepSpot data = CreepSpot.allocate()
            local integer i = 0
            local unit f
            
            //setting our members
            set data.spawnPlace = place
            set data.refresh = rate
            set data.checkRange = range
            set data.bornEffect = bornEff
            set data.t = NewTimer()
            
            //adding the creeps in the region to teh group
            call GroupEnumUnitsInRect(initGroup, data.spawnPlace, null)
            set data.creepNum = CountUnitsInGroup(initGroup)
            
            if data.creepNum > MAX_UNITS_NUMBER then
                call BJDebugMsg("Please update the MAX_UNITS_NUMBER variable, there is a creep spot with more units than it should.")
            endif
            
            //now we add the units to the array so we can keep track of their data
            loop
                set f = FirstOfGroup(initGroup)
                exitwhen(i == data.creepNum)
                call GroupRemoveUnit(initGroup, f)
                
                set data.units[i] = f
                set data.spawnX[i] = GetUnitX(f)
                set data.spawnY[i] = GetUnitY(f)
                set data.facing[i] = GetUnitFacing(f)
                
                set i = i + 1
            endloop
        
            //start the timer!
            call SetTimerData(data.t, integer(data))
            call TimerStart(data.t, data.refresh, true, function CreepSpot.SpawnUnits)

            return data
        endmethod
        
        method onDestroy takes nothing returns nothing
            local integer i = 0
            
            //killing the creeps
            loop
                exitwhen(i == .creepNum)
                call KillUnit(.units[i])
                set .units[i] = null
                set i = i + 1
            endloop
            
            //fixing other stuff
            call ReleaseTimer(.t)
        endmethod
    endstruct
//===========================================================================
    private function Init takes nothing returns nothing
        //setting our globals 
        set checkConds = Condition(function RangeConds)
        set checkGroup = CreateGroup()
        set initGroup = CreateGroup()
    endfunction
endlibrary

Ok guys, I am posting this here for suggestions and bug reports. If you think there is something I should improve, go ahead and make a post.
 

Attachments

  • CreepSpawnSys 1.7.w3x
    60.5 KB · Views: 65
Last edited:
uhm, why post this here, and not at the submission thread?
I want some suggestions and opinions before I submit a final version of this xD

Looks nice :p But you should really make it more user friendly, example: You need to call on the struct, make a function for easy use
My idea is to have an object of type "CreepSpot" just like you have an integer or a real. This object manages himself without any help and can be local, global, be inside an array, anything, just like a variable.
With functions such a functionality would be impossible and besides, it wouldn't fit to the concept of object =P
I suppose I could remove "public" keyword from the structure, but this is a rule I have for myself, systems always use public =P
About parameters, they would be the same for a function, so I would achieve nothing at all.
I am sorry, I won't change it into a function, I believe users will find this easy to use and understand as well. =D

Thx for the comment =D
 
I think it looks quite good, but you should have more options, eg creating a CreepSpot from a unit group, or creating x units of type x in region x and assigning them to a CreepSpot.

About what dynasti said, I think something like the below would be more appropriate than what you have at the moment, and will still allow you to keep your whole object thing going. You also shouldn't have it public, because why would someone use more than one CreepSpot system in their map?

JASS:
function CreepSpotCreate takes rect place, real rate, real range, string bornEff returns StaticCreepSpot
 
I think it looks quite good, but you should have more options, eg creating a CreepSpot from a unit group, or creating x units of type x in region x and assigning them to a CreepSpot.
Elaborate plz.

About what dynasti said, I think something like the below would be more appropriate than what you have at the moment, and will still allow you to keep your whole object thing going. You also shouldn't have it public, because why would someone use more than one CreepSpot system in their map?
I won't change it into a function, your example is totally anti-modular, my head hearts just of thinking of it xD
And don't ask me why people would use more than 1 CreepSpot struct in their maps, afaik it can be anything besides a system, like a name for a spell or something else. This way I make sure nothing bad happens. However I agree it looks complicated, (although it isn't) I will try asking some advice to some people to have more opinions.
 
Elaborate plz.

You should make it so that you can create a creep spot out of a unit group, rather than from units in a region. You should also make it so you can create a creep spot by passing the number and type of creeps you want. And you should be able to add units/unit groups to the spot later.

I won't change it into a function, your example is totally anti-modular, my head hearts just of thinking of it xD
And don't ask me why people would use more than 1 CreepSpot struct in their maps, afaik it can be anything besides a system, like a name for a spell or something else. This way I make sure nothing bad happens. However I agree it looks complicated, (although it isn't) I will try asking some advice to some people to have more opinions.
Either be a complete purist object-orientated programmer (and I remember when you hated timers, let alone vJass), or make it easily usable by people who don't understand the concept of structs. Also, why is that entirely anti-modular? It's just a function that calls your create function...

What if someone makes another spell or system called CreepSys? What then? It's not like you have a name no-one else is ever going to use for a spell or system. It's just as likely someone will make another "CreepSys" as "CreepSpot", therfore you may as well just call your whole system "CreepSpot" to avoid confusion...
 
Either be a complete purist object-orientated programmer (and I remember when you hated timers, let alone vJass)
When it makes sense to have objects instead of functions, I use objects, and therefore methods. See me as a purist object-oriented programmer if you want.
Seriously, even if you make no idea what a struct is, you can use the system very easily. I just require you to know the abstract meaning of a variable. (If using variables is too much complicated for you, then you shouldn't use my system)

You should make it so that you can create a creep spot out of a unit group
(...)You should also make it so you can create a creep spot by passing the number and type of creeps you want.
Not that simple. I will still need a rect (to spawn the units) as well as their positions and facing angles. Unfortunately JASS can't pass arrays between functions nor structures, so I have limited idea on how to do this. Besides, passing an array would be quite hard for the user, I just have no idea on how to make this easy for him.

And you should be able to add units/unit groups to the spot later.
To add units to a group would seem a good idea. In fact, why add groups if you can add units one by one? I believe this would be rather very easy for any user to do. I will see if it is feasible to add this functionality.

What if someone makes another spell or system called CreepSys? What then? It's not like you have a name no-one else is ever going to use for a spell or system. It's just as likely someone will make another "CreepSys" as "CreepSpot", therfore you may as well just call your whole system "CreepSpot" to avoid confusion...
Dude, I said I was going to consider it ... jeesssss...
 
When it makes sense to have objects instead of functions, I use objects, and therefore methods. See me as a purist object-oriented programmer if you want.
Seriously, even if you make no idea what a struct is, you can use the system very easily. I just require you to know the abstract meaning of a variable. (If using variables is too much complicated for you, then you shouldn't use my system)
OK then, leave it as it is. But I bet you'd get a lot less questions about how to use it if you changed it.

Not that simple. I will still need a rect (to spawn the units) as well as their positions and facing angles. Unfortunately JASS can't pass arrays between functions nor structures, so I have limited idea on how to do this. Besides, passing an array would be quite hard for the user, I just have no idea on how to make this easy for him.
1. Create the rect based on the positions of the units. You can get the positions and facing angles by looping through the group either with ForGroup or with a normal loop... this means you don't have to precreate a region for your creeps to respawn in.

2. I don't know what you think I meant, but you understood me wrong. What I meant was, you should have another create function, which takes two integers - number of creeps to create and then respawn in the future, and the other which defines what kind of creeps to create, as well as all of the other parameters. This would allow the user to create a CreepSpot without having previously placed all of the units in a region.

Dude, I said I was going to consider it ... jeesssss...
Sorry, my eyes skipped over the part where you said that.
 
OK then, leave it as it is. But I bet you'd get a lot less questions about how to use it if you changed it.
Wermm, if after my examples you don't know how to use this system, than you don't know JASS at all.

2. I don't know what you think I meant, but you understood me wrong. What I meant was, you should have another create function, which takes two integers - number of creeps to create and then respawn in the future, and the other which defines what kind of creeps to create, as well as all of the other parameters. This would allow the user to create a CreepSpot without having previously placed all of the units in a region.
About point 1 that is how version 1.0 of the system worked ... in fact this version still has a "smell" and some of it's parts work that way as well.
About point two, I really don't get the idea, you description seems to be contradictory in some sections. Can you exemplify your mid with some piece of code please?

Anyway, changed name and new functionality added, now you can add units to the system!
 
Wermm, if after my examples you don't know how to use this system, than you don't know JASS at all.
Hehe. The thing is, i bet a lot of people would agree with me on that point - Dynasti already did.

About point 1 that is how version 1.0 of the system worked ... in fact this version still has a "smell" and some of it's parts work that way as well.
Then why not add another create function which does exactly that, and have that function as well as the rect one?

About point two, I really don't get the idea, you description seems to be contradictory in some sections. Can you exemplify your mid with some piece of code please?
3 create functions would be nice (I'll make them methods since you seem to be intent on keeping it that way...)
JASS:
//The one you already have, still useful
static method createRect takes rect place, real rate, real range, string bornEff returns CreepSpot
    //stuff
endmethod
//One which takes a unit group
static method createGroup takes group units, real rate, real range, string bornEff returns CreepSpot
    //loop through the group, adding each unit's position/angle to the struct, create the
    //rect after based on the maximum and minimum positions of units
endmethod
//One which takes a number / id
static method createNum takes rect place, integer number, integer unit_id, real rate, real range, string bornEff returns CreepSpot
    //loop from 0 to number, creating a unit of type unit_id each time you loop at a 
    //random position/angle within the rect. add each unit to the CreepSpot
endmethod
 
Hehe. The thing is, i bet a lot of people would agree with me on that point - Dynasti already did.
I am not changing the structure of the system because 2 people say I should. In wc3c no1 complains xD

Then why not add another create function which does exactly that, and have that function as well as the rect one?
Wermm, I am not sure if a structure can have 2 create methods xD

Ok, now about your examples:
JASS:
//One which takes a unit group
static method createGroup takes group units, real rate, real range, string bornEff returns CreepSpot
    //loop through the group, adding each unit's position/angle to the struct, create the
    //rect after based on the maximum and minimum positions of units
endmethod
Ok, so I take a unit group. Then I save all units (as usual) and create a rect based on their max position? I would have to choose 4 points based on the units ... this may be quite hard ... Besides the units already have to be placed (so they have an X and Y) so I don't get the point of adding this functionality, since it is basically going to be the same as the previous one.

JASS:
static method createNum takes rect place, integer number, integer unit_id, real rate, real range, string bornEff returns CreepSpot
    //loop from 0 to number, creating a unit of type unit_id each time you loop at a
    //random position/angle within the rect. add each unit to the CreepSpot
endmethod
This is quite bad, this way I can only create spawns of 1 type of unit. The main idea would be to allow the user to add several types of units, however that doesn't seen to be possible.
Besides this can be made using the new "addUnit" method that I created and a loop.
A correct use of "addUnit" will also allow the user to add a number of units with any type to a creep spawn after its creation.

See this example:
JASS:
scope CreateCreeps initializer Init
    private function Actions takes nothing returns nothing
        local integer i = 0   


        //I assume the rect is empty. This way I will use "addUnit" method to add units to the group
        local CreepSys_CreepSpot humans = CreepSys_CreepSpot.create(gg_rct_Region1, 15., 400., "Objects\\Spawnmodels\\NightElf\\NEDeathSmall\\NEDeathSmall.mdl")
       
        //we add 3 peasants to the humans creep spot 
        loop
            exitwhen(i == 3)
            call humans.addUnit(Player(PLAYER_NEUTRAL_PASSIVE), 'hpea', GetRectCenterX(gg_rct_Region1), GetRectCenterY(gg_rct_Region1), 280.)
            set i = i + 1
       endloop

        //we add 4 foots to the humans creep spot ... 
        set i = 0
        loop
            exitwhen(i == 4)
            call humans.addUnit(Player(PLAYER_NEUTRAL_PASSIVE), 'hfoot', GetRectCenterX(gg_rct_Region1), GetRectCenterY(gg_rct_Region1), 280.)
            set i = i + 1
       endloop
    endfunction
//===========================================================================
    private function Init takes nothing returns nothing
        local trigger CreateCreepsTrg = CreateTrigger( )
        call TriggerRegisterTimerEvent(CreateCreepsTrg, 0.1, false)
        call TriggerAddAction(CreateCreepsTrg, function Actions )
    endfunction
endscope

This simulates the last constructor you suggested with a few loops and nothing else.
 
I am not changing the structure of the system because 2 people say I should. In wc3c no1 complains xD
Well they're all mad at wc3 campaigns :p. and if more people saw this thread, probably more people would agree with me, but leave it as it is if you really want to.

Wermm, I am not sure if a structure can have 2 create methods xD
A create method doesn't have to be called "create".

Ok, so I take a unit group. Then I save all units (as usual) and create a rect based on their max position? I would have to choose 4 points based on the units ... this may be quite hard ... Besides the units already have to be placed (so they have an X and Y) so I don't get the point of adding this functionality, since it is basically going to be the same as the previous one.
It's not hard creating the rect based on where the units are...

JASS:
function CreateRect takes group g returns rect
    local unit u
    local real array points
    local real x
    local real y
    local group g2
    if g == null then
        return null
    endif
    set u = FirstOfGroup(g)
    if u == null then
        return null
    endif
    set g2 = CreateGroup()
    call GroupAddGroup(g2, g)
    set u = FirstOfGroup(g2)
    set x = GetUnitX(u)
    set y = GetUnitY(u)
    set points[0] = x
    set points[1] = y
    set points[2] = x
    set points[3] = y
    call GroupRemoveUnit(g2, u)
    loop
        set u = FirstOfGroup(g2)
        exitwhen u == null
        set x = GetUnitX(u)
        set y = GetUnitY(u)
        if x < points[0] then
            set points[0] = x
        elseif x > points[2] then
            set points[2] = x
        endif
        if y < points[1] then
            set points[1] = y
        elseif y > points[3] then
            set points[3] = y
        endif
        call GroupRemoveUnit(g2, u)
    endloop
    call DestroyGroup(g2)
    set g2 = null
endfunction
I just knocked that function up now. Use it if you want, or inline it into your loop where you assign all of your struct's properties to maximise efficiency.

I just think it will be better this way because then you won't have to worry about creating a rect if you want to use the system.

This is quite bad, this way I can only create spawns of 1 type of unit. The main idea would be to allow the user to add several types of units, however that doesn't seen to be possible.
Besides this can be made using the new "addUnit" method that I created and a loop.
A correct use of "addUnit" will also allow the user to add a number of units with any type to a creep spawn after its creation.
Maybe make an "addNum" method then? Or "addGroup"?
 
It's not hard creating the rect based on where the units are...
I appreciate your enthusiasm, however I still lack a defensive argument for the following flaw:
Besides the units already have to be placed (so they have an X and Y) so I don't get the point of adding this functionality, since it is basically going to be the same as the previous one.
Basically, imo, this serves no purpose because when I create the CreepSpot, the units already have to be placed on the map, just like in the demo I have.
I just can't see a use for it, sry =S Like, what would a user do with this thing?

Maybe make an "addNum" method then? Or "addGroup"?
Please define: "addNum".
Also, "addGroup" doesn't make much sense for me, why would I want to add a group to an already existing group? Isn't it easier for the user to add a unit at a time?

Anyway, although I won't use your posts as much as I did in the past, your comments are giving me many ideas, and I thank you for that.
rep++ for the effort. Version 1.51 will have your name on credits, keep on the good job.

PS: THW doesn't let me give rep+p to you, sry =S
 
I appreciate your enthusiasm, however I still lack a defensive argument for the following flaw:
Quote:
Besides the units already have to be placed (so they have an X and Y) so I don't get the point of adding this functionality, since it is basically going to be the same as the previous one.

Basically, imo, this serves no purpose because when I create the CreepSpot, the units already have to be placed on the map, just like in the demo I have.
I just can't see a use for it, sry =S Like, what would a user do with this thing?
And my argument is: you can still create the units easily with triggers. Rects, however, are harder to create with triggers while maintaining a sense of position.

Please define: "addNum".
Also, "addGroup" doesn't make much sense for me, why would I want to add a group to an already existing group? Isn't it easier for the user to add a unit at a time?
addNum is basically what I said "createNum" should do, except it adds to an already created one. Basically a quick and userfriendly alternative to the user looping every time they want to add a few new units to the creep spot.

"addGroup" would allow you to add a unit group to a creep spot. I think it would be useful. You may not. You decide.

Anyway, although I won't use your posts as much as I did in the past, your comments are giving me many ideas, and I thank you for that.
rep++ for the effort. Version 1.51 will have your name on credits, keep on the good job.

PS: THW doesn't let me give rep+p to you, sry =S
Thanks. You can't rep me now because you already repped me recently. Have some rep back anyways for making cool spells&systems :D.
 
Basically a quick and userfriendly alternative to the user looping every time they want to add a few new units to the creep spot.
Mmm, seems a good idea, I now start to understand you. Well, I guess one more method wouldn't hurt xD

"addGroup" would allow you to add a unit group to a creep spot. I think it would be useful. You may not. You decide.
Mmm, which one do you think the user would like better ?
addGroup, AddNum or both? They will do the same thing in the end =P

Thanks. You can't rep me now because you already repped me recently. Have some rep back anyways for making cool spells&systems :D.
Thx for the feedback, this is the first system I will release to the public, and I hope people like it enough to use it xD

Anyway, I m considering many of your suggestions.
As for now, bump, version 1.51 is released, please see change log for more information.

PS: Also added "Documentation" it gives a small idea about what you can do with the system.
 
Mmm, which one do you think the user would like better ?
addGroup, AddNum or both? They will do the same thing in the end =P
I think you should include both. AddNum will allow the user to add more than one unit without creating them manually, while addGroup will allow the user to add several units they have already created to the creep spot during the game.
 
I think you should include both. AddNum will allow the user to add more than one unit without creating them manually, while addGroup will allow the user to add several units they have already created to the creep spot during the game.
Ok, I will likely add something related to this functionalities until the end of the day. Stay tun for more updates.
Btw, what did you think of the documentation?

EDIT EDIT EDIT

Bump, ok improved to version 1.6 and add lots of new functionalities that I think the user will like.

AddNum will allow the user to add more than one unit without creating them manually
I still have a problem with this, so if then user doesn't create them, it means they have no valid X and Y positions (because they are not created yet). Should I just add them to the center of the rect or calculate a random position in the rect? (to make it look more natural)
 
Last edited:
Hmm is bumping allowed within 2 hours of posting? I thought it was 2 days.

Btw, what did you think of the documentation?
It's good, but it needs better English :p. I see you are still lacking alternate create methods...

I still have a problem with this, so if then user doesn't create them, it means they have no valid X and Y positions (because they are not created yet). Should I just add them to the center of the rect or calculate a random position in the rect? (to make it look more natural)
Make the function take a boolean - randomPositions maybe, and maybe also an x and y which don't take effect if randomPositions is true?
 
Hmm is bumping allowed within 2 hours of posting? I thought it was 2 days.
I think it is allowed when it is about major updates (as it is the case) but I am not 100% sure. I will merge the posts.

It's good, but it needs better English :p.
Well, English is not my natural language =P
You can correct it if you want, to better English. However, having in mind I can't rep++ you and that you already are in credits, I am afraid you won't win anything =S

I see you are still lacking alternate create methods...
If I add many create methods, I fear I will make it "complicated" for the user =S

Make the function take a boolean - randomPositions maybe, and maybe also an x and y which don't take effect if randomPositions is true?
Hã ?
You mean:
JASS:
method addNum takes integer number, integer unitId, boolean randomPos returns group
In the end it returns the group with all the units. You mean this kinda code correct?
 
Well, English is not my natural language =P
You can correct it if you want, to better English. However, having in mind I can't rep++ you and that you already are in credits, I am afraid you won't win anything =S
I don't have much else to do, so I might do that a bit later today.

If I add many create methods, I fear I will make it "complicated" for the user =S
You could keep that create method as it is, no name changing, but just add a createGroup method as I suggested before. As I said, if I were to use this system, I'd rather not have to create a rect for each creep spot. I'd want to create and dynsamically add them to unit groups.

Hã ?
You mean:



In the end it returns the group with all the units. You mean this kinda code correct?
Yeah, but why does it need to return a group?
 
Level 17
Joined
Jun 17, 2007
Messages
1,433
JASS:
set bj_lastCreatedUnit = CreateUnit(GetOwningPlayer(data.units[i]), GetUnitTypeId(data.units[i]), data.spawnX[i], data.spawnY[i], data.facing[i])
set data.units[i] = bj_lastCreatedUnit
That could conflict with other systems/triggers that use bj_lastCreatedUnit. You should use your own global instead.
 
I don't have much else to do, so I might do that a bit later today.
yeey! xD

You could keep that create method as it is, no name changing, but just add a createGroup method as I suggested before. As I said, if I were to use this system, I'd rather not have to create a rect for each creep spot. I'd want to create and dynsamically add them to unit groups.
I see ...

Yeah, but why does it need to return a group?
For ease of use.

That could conflict with other systems/triggers that use bj_lastCreatedUnit. You should use your own global instead.
No offense but, is this a joke?
I mean seriously, following you logic GUI maps would be doomed to fail into the evilness of strange bugs.
I think I will require some proof of that, besides, the code is running speed.light in that section, so I don't think I will have many problems.
 
Level 18
Joined
Oct 18, 2007
Messages
930
JASS:
set bj_lastCreatedUnit = CreateUnit(GetOwningPlayer(data.units[i]), GetUnitTypeId(data.units[i]), data.spawnX[i], data.spawnY[i], data.facing[i])
set data.units[i] = bj_lastCreatedUnit
That could conflict with other systems/triggers that use bj_lastCreatedUnit. You should use your own global instead.

Without doing that GUI users cannot get the last created unit, duh!
 
Without doing that GUI users cannot get the last created unit, duh!
GUI maps are a pain in the ass for the computer. You create a unit wait 30 seconds, doing stuff there (like creating more units) and then when you end, you kill your created unit using the bj and everything works fine. If it works for GUI it works for vJASS. I agree with Dynasti.
 
Ok, I've translated the intro into "proper English", working on the rest of it right now...

This "CreepSpot" system is a system designed to allow map makers to create creep respawn regions as easily and in the most userfriendly way possible, requiring very little effort. This system uses objects, allowing you to create a variable for each CreepSpot which you can modify very easily using the methods below. The system has many methods and it is advisable to know each one of them so you can use the system is the best possible way:
 
Level 17
Joined
Jun 17, 2007
Messages
1,433
yeey! xD


I see ...


For ease of use.


No offense but, is this a joke?
I mean seriously, following you logic GUI maps would be doomed to fail into the evilness of strange bugs.
I think I will require some proof of that, besides, the code is running speed.light in that section, so I don't think I will have many problems.
It most definitely could. Your substituting it for a global var. If any system or trigger did the same and used any sort of wait, then you will have problems. Besides, it's just bad practice. It's similar to using things like bj_forLoopAIndex in place of an integer.
 
and used any sort of wait, then you will have problems. Besides, it's just bad practice. It's similar to using things like bj_forLoopAIndex in place of an integer.
1 - MUI problems happen for GUI codes that do NOT use bj variables
2 - Since when it is bad practice ? I never heard such a thing

Anyway, having in mind I don't need to use that bj definitely, I will use a local to make all happy.

I will try to add the methods water ele asked me to, so I may release new version soon as well.

EDIT EDIT EDIT

Just to warn that I will release a new version the next few days.

EDIT EDIT EDIT

Ok guys, I am not sure if 48 hours passed (I am not counting, but for me time runs like hell so ... xD ) here is the new version 1.7 of the system.
I hope people enjoy it, it has many modifications and it will make all people and critics happy (or at least that is what I dream of).

Anyway, I adivise W_Ele to read the documention and/or the History of version 1.7

Btw, almost forgot, Bump xD
 
Last edited:
Actually, less than 24 hours have passed :S
I am happy to see I manged to work fast =D

Glad to see you included that alternate create method! Looks real good now, I think it's easily ready to upload to wc3campaigns, or whatever you feel you have to do before you upload it to the hive :D.
actually I will upload it first for THW, this is my first system and I want it approved hard =P
Well, if you are out of suggestions then I guess I am ready, having in mind you are mainly the only gone giving suggestions =P
I will leave this here more 24h, to give people some time. Thx for the suggestions btw !
 
Status
Not open for further replies.
Top