System Spawn Units at Point Over Time (SUPOT)

Prometheus

Everything is mutable; nothing is sacred
Reaction score
589
This can be used for anything, though I don't think it would be good for a TD. Implementation instructions are in the SUPOT trigger.

Code
JASS:
//==============================================================================
//  SUPOT - Spawn Units at Point Over Time by kc102 AKA R34P3R - v6.6
//==============================================================================
//
//  PURPOSE OF SUPOT:
//       * Spawning units at a point with ease~
//
//  PROS: 
//       * Makes spawning units for anything easy.
//
//  CONS:
//       * Limited to 8191 instances running at a time.
//
//  FUNCTIONS:
//       * SUPOT_Start(player PlayerToSpawnFor, integer UnitTypeID, integer NumberOfUnits, real TimePerSpawn, real Spawn Location X, real Spawn Location Y)
//         Starts the spawning.
//
//       * SUPOT_StartEx(player PlayerToSpawnFor, integer UnitTypeID, integer NumberOfUnits, real TimePerSpawn, location SpawnLocation)
//         Starts the spawning using a location, meant for eGUI use.
//
//       * SUPOT_Stop(integer Instance ID)
//         Stops the spawning for that ID.
//         
//       * SUPOT_Pause(integer Instance ID)
//         Pauses the spawning for that ID.
//
//       * SUPOT_UnPause(integer Instance ID)
//         Unpauses the spawning for that ID.
//         
//       * SUPOT_ChangeUnitID(integer Instance ID, integer newUnitID)
//         Changes the unit-type spawned for that ID.
//         
//       * SUPOT_ChangeSpawnTime(integer Instance ID, real newSpawnTime)
//         Changes the interval between spawning for that ID.
//         
//       * SUPOT_ChangePlayer(integer Instance ID, player newPlayer)
//         Changes the player that the unit is spawned for, for that ID.
//         
//       * SUPOT_ChangeSpawnAmount(integer Instance ID, integer newSpawnAmount)
//         Changes the number of units spawned per interval, for that ID.
//         
//       * SUPOT_ChangeLocation(integer Instance ID, real newX, real newY)
//         Changes the location of the spawning, for that ID.
//
//       * GetLastSpawned(integer Instance)
//         This function returns a group with the units made
//         in the last run of that SUPOT instance
//
//       * GetLastInstance()
//         This function is for eGUI mainly, but anyone can use it.
//         Since each start returns an integer, this basically takes that integer
//         and allows you to set it to a variable after starting SUPOT.
//
//       * The start function returns an integer, so make sure to call it properly.
//         To stop something, call the stop function with the integer you set to call
//         the start function, same with the other functions.
//
//  HALPS:
//       * Report any bugs or odd happennings please.
//       * Assigning a single integer to the function twice will result in
//         you only having once instance of SUPOT on that integer.
//
//  REQUIREMENTS:
//       * NewGen v5 and above (there might be some problems with older NewGen's)
//
//
//  HOW TO IMPORT:
//       * Copy this code into your map.
//==============================================================================//
//                      Change Log                                              //
//                      1.0 - Release                                           //
//                      1.0 to 3.3 - Stuff                                      //
//                      4.0 - Major Code Overhaul, Self-Sufficient              //
//                      4.5 - Added modification functions, inproved code       //
//                      4.6 - Safeties added to mod functions                   //
//                      4.7 - Added more safeties, fixed a few others           //
//                      4.8 - Added a pause function                            //
//                      4.9 - Added function so the unit can do stuff           //
//                      4.10 - Fixed a pause function error                     //
//                      5.0 - Made struct SUPOT open                            //
//                      5.1 - Readme fix                                        //
//                      5.2 - Global supot variable is now private              //
//                      5.3 - All spawned units are now returned VIA group      //
//                      5.4 - Lst changed to Last <Blame Uberplayer>            //
//                      5.5 - Slight code changes                               //
//                      5.6 - Major Code Fixes                                  //
//                      5.7 - Modifed Pause function, UnPause added             //
//                      5.75 - SUPOT Public                                     //
//                      5.8 - Major & Minor Code Fixes                          //
//                      5.9 - Group Last is now an arrayed group                //
//                      5.10 - Code Improvements                                //
//                      6.0 - Code Improvments                                  //
//                      6.1 - Major Flaw Overcome thanks to Artificial          //
//                      6.2 - Safety Improvments & Inlining                     //
//                      6.3 - DoubleFree & Invalid X/Y values fixed             //
//                      6.4 - Improved for eGUI, code touchups                  //
//                      6.5 - *glares at Sevion* eGUI improvment                //
//                      6.55 - Fixed extra space on 5.7                         //
//                      6.6 - Added in StartEx for location use and eGUI        //
//==============================================================================//
//==============================================================================//
//                       ~<||§||Credits||§||>~                                  //
//                             Uberplayer                                       //
//                             Flare                                            //
//                             emjlr3                                           //
//                             Themis                                           //
//                             Dusk                                             //
//                           --Artificial--                                     //
//                             Gwypaas                                          //
//                             Romek                                            //
//                             Sevion                                           //
//==============================================================================//

library SUPOT initializer Init
    struct supot
        integer sid = 0
        integer num = 0
        
        player p = null
        
        real x
        real y
        real interval = 0
        real ticker = 0
        
        boolean paused = false
        
        method onDestroy takes nothing returns nothing
            set .interval = 0
            set .x = 0
            set .y = 0
            set .p = null
            set .num = 0
            set .sid = 0
        endmethod
    endstruct

    globals
        private constant real TIMER_INT = 0.25
        
        private integer N = 0
        
        private timer T = CreateTimer()
        
        private group array Last
  
        public supot array spawn
        
        private real MINX
        private real MAXX
        private real MINY
        private real MAXY
        
        integer bj_LastCreatedInstance
    endglobals
    
    function GetLastSpawned takes integer instance returns group 
        if Last[instance] != null then
            return Last[instance]
        endif
        debug call BJDebugMsg(I2S(instance) + " has no units running on that instance.") 
        return null
    endfunction
    
    private function DoSpawn takes nothing returns nothing
        local supot a
        local unit u
        local integer i = N
        local integer j = 0
        loop
        exitwhen i == 0
            set a = spawn<i>
            if a == 0 then
            else
                if not a.paused == true then
                    set a.ticker = a.ticker + TIMER_INT
                    if a.ticker &gt;= a.interval then
                        set a.ticker = a.ticker - a.interval
                        call GroupClear(Last<i>)
                        loop
                        exitwhen j == a.num
                            set u = CreateUnit(a.p, a.sid, a.x, a.y, bj_UNIT_FACING)
                            call GroupAddUnit(Last<i>,u)
                            debug call BJDebugMsg(&quot;Unit has been created!&quot;)
                            set u = null
                            set j = j + 1
                        endloop
                        set j = 0
                    endif
                endif
                if a.num == 0 and a.interval != 0 then
                    call a.destroy()
                    call DestroyGroup(Last<i>)
                endif
            endif
            set i = i - 1
        endloop
        set u = null
        set j = 0
    endfunction

    public function Stop takes integer instance returns nothing
        set supot(instance).num = 0
        debug call BJDebugMsg(&quot;The instance number &quot; + I2S(instance) + &quot; of SUPOT has been stopped!&quot;)
    endfunction
    
    public function Pause takes integer instance returns nothing
        set supot(instance).paused = true
    endfunction
    
    public function UnPause takes integer instance returns nothing
        set supot(instance).paused = false
    endfunction
    
    public function ChangeUnitID takes integer instance, integer newUnitID returns nothing
        if newUnitID == 0 then
            debug call BJDebugMsg(&quot;No Spawn ID!&quot;)
            return
        endif
        set supot(instance).sid = newUnitID
        debug call BJDebugMsg(&quot;A different unit is now being spawned for SUPOT instance number &quot; + I2S(instance))
    endfunction
    
    public function ChangeSpawnTime takes integer instance, real newSpawnTime returns nothing
        if newSpawnTime &lt;= 0 then
            debug call BJDebugMsg(&quot;Invalid Spawn Interval!&quot;)
            return
        endif
        set supot(instance).interval = newSpawnTime
        debug call BJDebugMsg(&quot;Spawn time has been changed for SUPOT instance number &quot; + I2S(instance))
    endfunction
    
    public function ChangePlayer takes integer instance, player newPlayer returns nothing
        if newPlayer == null then
            debug call BJDebugMsg(&quot;Player Not Found!&quot;)
            return
        endif
        set supot(instance).p = newPlayer
        debug call BJDebugMsg(&quot;Units are now being spawned for a different player under SUPOT instance number &quot; + I2S(instance))
    endfunction
    
    public function ChangeSpawnAmount takes integer instance, integer newNumber returns nothing
        if newNumber &lt;= 0 then
            debug call BJDebugMsg(&quot;Incorrect spawn number specified!&quot;)
            return
        endif
        set supot(instance).num = newNumber
        debug call BJDebugMsg(&quot;A different number of units are being spawned for SUPOT instance number &quot; + I2S(instance))
    endfunction
    
    public function ChangeLocation takes integer instance, real newX, real newY returns nothing
        if newX &gt; MAXX or newX &lt; MINX then
            debug call BJDebugMsg(&quot;Impossible Spawn Location X Given!&quot;)
            return
        elseif newY &gt; MAXY or newY &lt; MINY then
            debug call BJDebugMsg(&quot;Unusable Spawn Location Y Given!&quot;)
            return
        endif
        set supot(instance).x = newX
        set supot(instance).y = newY
        debug call BJDebugMsg(&quot;The spawn location has been changed for SUPOT instance number &quot; + I2S(instance))
    endfunction
    
    public function GetLastInstance takes nothing returns integer
        return bj_LastCreatedInstance
        //For eGUI users, so they can modify the function.
    endfunction
    
    public function Start takes player p, integer uid, integer number, real interval, real x, real y returns integer
        local supot a
        if uid == 0 then
            debug call BJDebugMsg(&quot;No Spawn ID!&quot;)
            return 0
        elseif number &lt;= 0 then
            debug call BJDebugMsg(&quot;Invalid Spawn Amount Specified!&quot;)
            return 0
        elseif interval &lt;= 0 then
            debug call BJDebugMsg(&quot;Invalid Spawn Time Interval Recieved!&quot;)
            return 0
        elseif x &gt; MAXX or x &lt; MINX then
            debug call BJDebugMsg(&quot;Impossible Spawn Location X Given!&quot;)
            return 0
        elseif y &gt; MAXY or y &lt; MINY then
            debug call BJDebugMsg(&quot;Unusable Spawn Location Y Given!&quot;)
            return 0
        elseif p == null then
            debug call BJDebugMsg(&quot;Spawn Player Not Found!&quot;)
            return 0
        endif
        set a = supot.create()
        set a.sid = uid
        set a.p = p
        set a.num = number
        set a.interval = interval
        set a.x = x
        set a.y = y
        debug call BJDebugMsg(R2S(MINX) + R2S(MAXX) + R2S(MINY) + R2S(MAXY))
        if N == 0 then
            call TimerStart(T, TIMER_INT, true, function DoSpawn)
        endif
        set N = N + 1
        debug call BJDebugMsg(&quot;A spawn has started under instance id &quot;+I2S(N)+&quot;!&quot;)
        if spawn[N].num != 0 then
            set N = 1
            loop
            exitwhen spawn[N].num == 0
                set N = N+1
            endloop
        endif
        set spawn[N] = a
        if Last[N] == null then
            set Last[N] = CreateGroup()
        endif
        set bj_LastCreatedInstance = integer(a)
        return integer(a)
    endfunction
    
    public function StartEx takes player p, integer uid, integer number, real interval, location l returns integer
        return Start(p,uid,number,interval,GetLocationX(l),GetLocationY(l))
    endfunction
    
    private function Init takes nothing returns nothing
        //Damned bj_mapInitialPlayableArea...
        set MINX = GetRectMinX(bj_mapInitialPlayableArea)
        set MAXX = GetRectMaxX(bj_mapInitialPlayableArea)
        set MINY = GetRectMinY(bj_mapInitialPlayableArea)
        set MAXY = GetRectMaxY(bj_mapInitialPlayableArea)
    endfunction
endlibrary</i></i></i></i>
 

Attachments

  • SUPOT.w3m
    20.3 KB · Views: 434

Flare

Stops copies me!
Reaction score
662
Cool concept (can't see why you would think it wouldn't be good for a TD, I'd say it'd be great if you expanded it slightly to take a destination as well since it would get rid of the problem of lag on unit spawning)

Why don't you just put everything in the struct? Would probably be alot easier :p

And why not use XY's instead of locations? It'd get rid of the leak removal issue, and you can create a GUI friendly function that takes a location, but calls the main function i.e.

JASS:
function StartXY takes ... returns nothing
...
endfunction

function StartLoc takes ... returns nothing
call StartXY (..., GetLocationX (whichLoc), GetLocationY (whichLoc))
endfunction
 

quraji

zap
Reaction score
144
Um...so it spawns units? Fancy..

No offense but I could make this myself in the time it would take to find and implement this. And it's not even stand-alone :eek:

I hope someone proves me wrong and finds this useful.
 

Prometheus

Everything is mutable; nothing is sacred
Reaction score
589
Its a single function. I think this would make doing upgrades and such extremely easy.
E: research
A: call SUPOT_Stop(0)
call SUPOT_Start()


Edit: Now @ v1.8.
 

demotry241

Don't Ever Categorize Yourself.
Reaction score
105
The fact that the name of this system is SUPOT is :thup:

it will be good for AoS.
 

Prometheus

Everything is mutable; nothing is sacred
Reaction score
589
My orignial idea was that this could be used for a footies.
[del]Reminder: Allow users to set the number of units spawned.[/del]

Added.
 

Artificial

Without Intelligence
Reaction score
326
I found a bug for you. :) Or at least I don't think it's supposed to work like this...

When you stop a spawn and start a new one with the same ID right after that one, it doesn't stop the old instance of the spawn, but keeps it going. :p I'm not too good with verbally explaining this, so I'll show you the function I tested it with.
JASS:
function Trig_Test_Uno_Actions takes nothing returns nothing
    local integer i = 0
    call SUPOT_Start(&#039;hfoo&#039;, 1, 1, Location(0, 0), i, Player(0))
    call TriggerSleepAction(5)
    call SUPOT_Stop(i)
    call SUPOT_Start(&#039;hpea&#039;, 1, 1, Location(0, 0), i, Player(1))
endfunction
After I stopped the old one and started the new one, it spawned 1 peasant and 1 footman for blue each second.

Why not change this:
JASS:
local supot dat = supot.create()
to this:
JASS:
local supot dat = supot(uqid)
Then you could make the spawn boolean (and the other globals) a struct member, and the problem would be solved (after modifying some parts of the code to work with the new way, ofc.). :p

And in case you're not going to go this way: you're never destroying the struct instances.
 

Prometheus

Everything is mutable; nothing is sacred
Reaction score
589
Thanks for pointing that out. I've updated the code to 3.0.
There have been a lot of changes the the code is a lot more struct orientated. The unique ID is still in use because of the way the code stops.
Thank you Artificial for point out the bug, and Flare for helping with some code.
 

emjlr3

Change can be a good thing
Reaction score
395
a lot of those inputs ppl could screw up and insert something negative, the odds of them being null is slim to nnone, I doubt the map would compile in that case

taking a loc is so GUI

I say you destroy structs when not in use, free up some memory

using 1 global timer would remove the need for ABCT and optimize it

more descriptive names would help ppl like me read it and figure out what everything means - even after going through the readme it was confusing

also, returning he new scruct, as opposd ot requiring a unique id would remove that need, and allow ppl to just "struct.destroy" when they needed to kill it
 
Reaction score
456
I think a loop could handle this, so no need for structs and whatever :).
 

Flare

Stops copies me!
Reaction score
662
Just thought of some ideas :D

1) A function that allowed for the modification of the unit-type being spawned - would be very handy if a person wanted a continous stream of units that alternated every X seconds (like Enfo's TS, Seelenjagd, Abyss Gates, or just about any other Team Survival) e.g.
Code:
Every X seconds of game-time

-

Custom script: call SUPOT_UpdateId (uniqueId, newSpawn)

2) Same as above, but for spawn point, number, and owner

3) Usage of coordinates instead of locations, and make a GUI friendly function, that calls the coordinate function using
JASS:
public function StartLoc takes ..., location spawnLoc, ... returns nothing
call StartXY (... , GetLocationX (spawnLoc), GetLocationY (spawnLoc), ... )
endfunction


4) As emjlr said, negative inputs :p

5) If you go by emjlr's suggestion of using a single timer, would be cool to allow for a spawn interval modification (again, would be fun to see in a Team Survival map if you wanted to double the spawn rate for your enemy for a period of time)
 

Prometheus

Everything is mutable; nothing is sacred
Reaction score
589
Just thought of some ideas :D

1) A function that allowed for the modification of the unit-type being spawned - would be very handy if a person wanted a continous stream of units that alternated every X seconds (like Enfo's TS, Seelenjagd, Abyss Gates, or just about any other Team Survival) e.g.
Code:
Every X seconds of game-time

-

Custom script: call SUPOT_UpdateId (uniqueId, newSpawn)

2) Same as above, but for spawn point, number, and owner

3) Usage of coordinates instead of locations, and make a GUI friendly function, that calls the coordinate function using
JASS:
public function StartLoc takes ..., location spawnLoc, ... returns nothing
call StartXY (... , GetLocationX (spawnLoc), GetLocationY (spawnLoc), ... )
endfunction


4) As emjlr said, negative inputs :p

5) If you go by emjlr's suggestion of using a single timer, would be cool to allow for a spawn interval modification (again, would be fun to see in a Team Survival map if you wanted to double the spawn rate for your enemy for a period of time)

1. I'm going to make this return structs so you can change anything, easily.
2. See 1.
3. Already been added to the latest V.
4. See bottom.


I think a loop could handle this, so no need for structs and whatever :).
Doubtful?

a lot of those inputs ppl could screw up and insert something negative, the odds of them being null is slim to nnone, I doubt the map would compile in that case

taking a loc is so GUI

I say you destroy structs when not in use, free up some memory

using 1 global timer would remove the need for ABCT and optimize it

more descriptive names would help ppl like me read it and figure out what everything means - even after going through the readme it was confusing

also, returning he new scruct, as opposd ot requiring a unique id would remove that need, and allow ppl to just "struct.destroy" when they needed to kill it

I'm going to make this run on 1 global timer, modding names a lil. Planning to return a struct. Removing the uqid part, removing the negative issue.



Updated to v4.0!
Apparently the map is a .w3m now, RoC users can open it now. xD
 

Flare

Stops copies me!
Reaction score
662
I'm going to make this return structs so you can change anything, easily.
Returning the struct can lead to abuses by the end-user, since they will then have access to .destroy (struct itself may be private, but as long as you have access to a struct var, you have access to .destroy) and as a result, they can break shit :p You could return an integer (rather than 'returns supot'), then people wouldn't have direct access to the struct, but you could have modification stuff like
JASS:
function AlterSpawnType takes integer index, integer newType returns nothing
  local supot data = supot(index) //Not sure if you need to do the cast, I&#039;ve never really worked with it
  set data.spawnType = newType
endfunction

(and why did you bold structs in your sentence xD)

Doubtful?
??? What?

JASS:
//         local &lt;struct name&gt; &lt;local name&gt; = &lt;struct name&gt;.create()
//         set &lt;local name&gt; = SUPOT_StartSpawn(integer UnitTypeID, integer NumberOfUnits, real TimePerSpawn, real Spawn Location X, real Spawn Location Y,  player PlayerToSpawnFor)

Hmmm... why would you .create, and then assign it to SUPOT_Start? That's creating a struct instance, then losing the reference to it in favour of a non-existant struct (since, as far as I know, struct instances aren't magically allocated if you don't .create them)

The supot struct is private, so how are people supposed to set a struct var to it (even if they make a seemingly* identical struct, it won't be the same, so they can't modify anything)

Ahm... does the StartSpawnBJ function even work? It's not doing anything other than returning x[num] (and it's not incrementing num either, so you could have overwritten instances)

*It may be indentical in name and members, but public/private syntax will differentiate them

(Can't test the map right now, but based on the above, I can't see how alot of this will even work with latest version)
 

emjlr3

Change can be a good thing
Reaction score
395
i had alreayd relayed all that to him after he posted over im
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Staff online

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top