System Spawn Units at Point Over Time (SUPOT)

Discussion in 'Systems and Snippets' started by Prometheus, Aug 7, 2008.

  1. Prometheus

    Prometheus Everything is mutable; nothing is sacred Staff Member

    Ratings:
    +592 / 0 / -0
    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 >= 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("Unit has been created!")
                                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("The instance number " + I2S(instance) + " of SUPOT has been stopped!")
        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("No Spawn ID!")
                return
            endif
            set supot(instance).sid = newUnitID
            debug call BJDebugMsg("A different unit is now being spawned for SUPOT instance number " + I2S(instance))
        endfunction
        
        public function ChangeSpawnTime takes integer instance, real newSpawnTime returns nothing
            if newSpawnTime <= 0 then
                debug call BJDebugMsg("Invalid Spawn Interval!")
                return
            endif
            set supot(instance).interval = newSpawnTime
            debug call BJDebugMsg("Spawn time has been changed for SUPOT instance number " + I2S(instance))
        endfunction
        
        public function ChangePlayer takes integer instance, player newPlayer returns nothing
            if newPlayer == null then
                debug call BJDebugMsg("Player Not Found!")
                return
            endif
            set supot(instance).p = newPlayer
            debug call BJDebugMsg("Units are now being spawned for a different player under SUPOT instance number " + I2S(instance))
        endfunction
        
        public function ChangeSpawnAmount takes integer instance, integer newNumber returns nothing
            if newNumber <= 0 then
                debug call BJDebugMsg("Incorrect spawn number specified!")
                return
            endif
            set supot(instance).num = newNumber
            debug call BJDebugMsg("A different number of units are being spawned for SUPOT instance number " + I2S(instance))
        endfunction
        
        public function ChangeLocation takes integer instance, real newX, real newY returns nothing
            if newX > MAXX or newX < MINX then
                debug call BJDebugMsg("Impossible Spawn Location X Given!")
                return
            elseif newY > MAXY or newY < MINY then
                debug call BJDebugMsg("Unusable Spawn Location Y Given!")
                return
            endif
            set supot(instance).x = newX
            set supot(instance).y = newY
            debug call BJDebugMsg("The spawn location has been changed for SUPOT instance number " + 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("No Spawn ID!")
                return 0
            elseif number <= 0 then
                debug call BJDebugMsg("Invalid Spawn Amount Specified!")
                return 0
            elseif interval <= 0 then
                debug call BJDebugMsg("Invalid Spawn Time Interval Recieved!")
                return 0
            elseif x > MAXX or x < MINX then
                debug call BJDebugMsg("Impossible Spawn Location X Given!")
                return 0
            elseif y > MAXY or y < MINY then
                debug call BJDebugMsg("Unusable Spawn Location Y Given!")
                return 0
            elseif p == null then
                debug call BJDebugMsg("Spawn Player Not Found!")
                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("A spawn has started under instance id "+I2S(N)+"!")
            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
     

    Attached Files:

    • Like Like x 10
  2. Flare

    Flare Stops copies me! Staff Member

    Ratings:
    +662 / 0 / -0
    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
     
    • Like Like x 1
  3. Prometheus

    Prometheus Everything is mutable; nothing is sacred Staff Member

    Ratings:
    +592 / 0 / -0
    I took your ideas and used them. Thanks.
     
  4. quraji

    quraji zap

    Ratings:
    +143 / 0 / -0
    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.
     
  5. Prometheus

    Prometheus Everything is mutable; nothing is sacred Staff Member

    Ratings:
    +592 / 0 / -0
    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.
     
  6. demotry241

    demotry241 Don't Ever Categorize Yourself.

    Ratings:
    +104 / 0 / -0
    The fact that the name of this system is SUPOT is :thup:

    it will be good for AoS.
     
  7. Prometheus

    Prometheus Everything is mutable; nothing is sacred Staff Member

    Ratings:
    +592 / 0 / -0
    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.
     
  8. manofsteel

    manofsteel New Member

    Ratings:
    +36 / 0 / -0
    Cool system :thup:

    +rep
     
  9. Ghostwind

    Ghostwind o________o

    Ratings:
    +171 / 0 / -0
    Nice system. :thup:

    [/repwhore]
     
    • Like Like x 1
  10. Prometheus

    Prometheus Everything is mutable; nothing is sacred Staff Member

    Ratings:
    +592 / 0 / -0
    Added ABCT into the first post.
     
  11. Artificial

    Artificial Without Intelligence

    Ratings:
    +325 / 0 / -0
    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('hfoo', 1, 1, Location(0, 0), i, Player(0))
        call TriggerSleepAction(5)
        call SUPOT_Stop(i)
        call SUPOT_Start('hpea', 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.
     
    • Like Like x 1
  12. Prometheus

    Prometheus Everything is mutable; nothing is sacred Staff Member

    Ratings:
    +592 / 0 / -0
    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.
     
  13. Flare

    Flare Stops copies me! Staff Member

    Ratings:
    +662 / 0 / -0
    Hmmm... why exactly did you specify an array size for your supot array? You don't need to specify a size for struct arrays, only arrayed struct members i.e.

    JASS:
    struct temp
      unit array unitarray[50]
    endstruct
    
    globals
      temp array gdata
    endglobals
     
  14. Prometheus

    Prometheus Everything is mutable; nothing is sacred Staff Member

    Ratings:
    +592 / 0 / -0
    Update - 3.3
     
  15. emjlr3

    emjlr3 Change can be a good thing Staff Member

    Ratings:
    +396 / 0 / -0
    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
     
  16. Uberplayer

    Uberplayer -

    Ratings:
    +454 / 0 / -0
    I think a loop could handle this, so no need for structs and whatever :).
     
  17. Flare

    Flare Stops copies me! Staff Member

    Ratings:
    +662 / 0 / -0
    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)
     
  18. Prometheus

    Prometheus Everything is mutable; nothing is sacred Staff Member

    Ratings:
    +592 / 0 / -0
    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.


    Doubtful?

    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
     
  19. Flare

    Flare Stops copies me! Staff Member

    Ratings:
    +662 / 0 / -0
    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've never really worked with it
      set data.spawnType = newType
    endfunction

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

    ??? What?

    JASS:
    //         local <struct name> <local name> = <struct name>.create()
    //         set <local name> = 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)
     
  20. emjlr3

    emjlr3 Change can be a good thing Staff Member

    Ratings:
    +396 / 0 / -0
    i had alreayd relayed all that to him after he posted over im
     

Share This Page