1. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  2. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still havn't received your rank award? Then please contact the administration.
    Dismiss Notice
  3. We have recently started the 16th edition of the Mini Mapping Contest. The theme is mini RPG. Do check it out and have fun.
    Dismiss Notice
  4. Choose your ride to damnation in the 5th Special Effect Contest Poll.
    Dismiss Notice
  5. The winners of the 13th Techtree Contest have been announced!
    Dismiss Notice
  6. The 13th Music Contest Poll has begun! Vote for the best tracks in this symphony of frost and flame.
    Dismiss Notice
  7. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

[JASS] The Next Spawn System

Discussion in 'Triggers & Scripts' started by Nestharus, Dec 13, 2010.

  1. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,146
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    Yes... I'm working on a new Spawn : O.


    For those of you who don't know what Spawn was, it was a system that allowed one to periodically spawn units (the original system was incredibly complicated, was 2000+ lines of code, and had a 15 page manual to go with it).


    The new Spawn is much simpler. In fact, it is split into two different systems, these being SimpleSpawns and Spawns.


    The normal method for spawning is enumerating every x seconds and creating a unit if a unit type id is right (like create a footmen at all barracks on the map ('hfoo' at 'hbar')).


    The next method that people use, which is more optimal, is using timer loops and unit indexing to iterate through all units on a list (iterate through list rather than enumerate).


    The problem with the above is that spawns start at different periods. Furthermore, it disallows tech upgrades for players and specific units.


    SimpleSpawn creates a timer for each specific spawn so that the timers can be paused and modified. It also uses elapsed times when a time is changed so that the timer isn't simply reset. This means that if a timer went for 15 seconds out of 30 and had 15 seconds left, changing it to 18 seconds will make it have 3 seconds left instead. A normal system would make it have 18 seconds remaining (reset).

    SimpleSpawn uses a combination of unit type id properties, player unit type id properties, and specific spawn properties to manipulate spawns. Properties that reals/integers (like count and time) are stacked upon each other (unit type id time + player unit type id time + spawn time = total time).

    Updating spawn time on a global level updates the spawn time of all spawns matching its criteria. For example, modifying the spawn time of barracks owned by player 0 will iterate through all spawners owned by player 0 of type barracks and modify their spawn time.

    For example
    Code (vJASS):

    set Spawn['hbar'].time = 10
    set Spawn['hbar'].spawns['hfoo'].count = 3 //add footman x3
    set Spawn['hbar'].spawns['ewsp'].count = 1 //add wisp x1
    set Spawn['hbar'].spawns['hfoo'].count = 0 //remove footman
    set Spawn['hbar'].player[0].spawns['hfoo'].count = 2 //add two footmen to player 0 only
    set Player[0].spawns.time = 5 //add 5 seconds to all spawns of player 0

    //actual spawn time
    time = Spawn['hbar']+Player['hbar']+SpecificSpawn['hbar']

    //time is updated on initial set
    local real t = SpecificSpawn['hbar']+Spawn['hbar'].time+Player['hbar'].time
    set t = t-TimerGetElapsed(timer)
    if (t < 0) then
        set t = 0
    endif
    call TimerStart(timer, t, false, function DoSpawn)
    if (not enabled)
        call PauseTimer(timer)
    endif
     

    Updating a value like a boolean on a global level will only apply itself to spawns that derive itself from the same level. For example, if the player enabled value for barracks is not set (meaning barracks don't spawn), enabled for barracks will apply itself to all barracks owned by that player that don't have their specific values set.

    For example
    Code (vJASS):

    set Spawn['hbar'].enabled = true

    //background if statements in enumeration
    if (player['hbar'].enabled.used == false) then
       if (specificSpawn.enabled.used == false) then
           set specificSpawn.enabled = true
       endif
    endif
     


    Custom code works the same way. If code is null, it uses default spawning, which just places x spawns at a spawner.



    Advanced Spawning works in a very different fashion. With Advanced Spawning, each spawn is specifically created and given a position (a position being any agent with coordinates). Advanced Spawning also has stacked fields, meaning that changes to its properties like time can be undone. This is useful when transferring a spawn from one position to another and then wanting to undo the change (like a stealing spawn ability).

    The stacked fields are grouped into two different stacks-
    Position Stack (x, y, z, etc)
    Stat Stack (time, count, spawn type, etc)

    Spawns can also have multiple positions and positions can have multiple spawns.

    Advanced Spawning also has special macros for the intense spawning loops involved.




    Both Advanced Spawning and Simple Spawning keep track of all spawns via a set of groups.

    1. Global spawn group, split by types
    2. Global spawn group 2, split by types and players
    3. Specific spawn group, split by spawners/positions
    4. Instantaneous spawn groups, descending by time and organized by specific spawners/positions, types and players, and types

    In this way, spawned units can be manipulated at any point in the game (for example, tracking a group of unit's ages for possible life/death or giving units of a base bonuses when that base is upgraded, or etc, who knows)


    Archiving and Spawn Tracking are extra features and can be disabled.



    I am simply posting these designs up to see what people think (what they want me to add, etc) : ). The code for the two systems should be done soon.

    Woo, now I have 60000 projects going + end of the semester exams/projects : D.

    /muahahahahas


    edit
    Can actually merge advanced and simple spawning ; O.

    edit
    further ideas for API (while background code may be coded to a degree, the API hasn't been set ^^)
    Code (vJASS):

    //Global Spawning Manipulation that spawns may or may not use
    origin['hbar'].spawn['hfoo'].count = 3
    origin['hbar'].player[0].spawn['hfoo'].count = 3
    player[0].spawn['hfoo'].count = 3
    spawn['hfoo'].count = 3
    origin['hbar'].player[0].spawn['hfoo'].count = 3
    origin.spawn[0].count = 3 //set spawn via origin

    //Specific Spawn Manipulation
    spawn.position[position] = 3
    spawn.type['hfoo'].count = 2
    spawn.type['hfoo'].share(spawn2)

    //soft/hard is when a spawn shares its stats with another spawn (may be specific stats)
    //this is like spawn1 giving a spawntype to another spawn, or increasing another spawn's spawn count

    //soft relies on spawn1 position integrity
    //hard does not rely on spawn1 position integrity
    spawn.soft[spawn2] = 1 //soft, x1
    spawn.soft.position[position] = 1 //soft, x1
    spawn.soft[spawn2] = 0
    spawn.hard[spawn3] = 2 //hard, x2

    //Lending
    spawn.lend.position = pos //stack position
    spawn.lend.position.revert()
    spawn.lend.position.base()

    //Mimicing
    spawn.mimic = spawnToMimic
    spawn.mimic.count = spawnToMimic.count
    spawn.mimic.revert() //undo mimic
    spawn.mimic.base() //stop mimicing
     
     
    Last edited: Dec 13, 2010
  2. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,426
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    This is like what... your 3rd time creating a spawn system? :p

    Oh well, at least the interface looks a lot cooler than before. Still, I don't know why you are so obsessed with spawn systems. ;D
     
  3. Lambdadelta

    Lambdadelta

    Joined:
    Jul 6, 2009
    Messages:
    721
    Resources:
    1
    Maps:
    1
    Resources:
    1
    +1.. Insane Nestharus is insane ;o ~! --Runs away-- ... don't hit me on MSN :|
     
  4. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,146
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    ->This is like what... your 3rd time creating a spawn system? :p

    Fourth actually : )

    If you notice by API, I have learned much ^_^.

    Spawn System is about the hardest type of system I've ever worked on, and each revision is just as hard as the previous ones to code (if not harder) : P.

    The new Spawn system uses Position script and Stacked Fields script, so all of the older ideas were much easier to code. However, this new and glorious/wonder API is going to be a serious pain O-o.
     
  5. Anachron

    Anachron

    Joined:
    Sep 9, 2007
    Messages:
    6,221
    Resources:
    66
    Icons:
    49
    Packs:
    2
    Tools:
    1
    Maps:
    3
    Spells:
    9
    Tutorials:
    1
    JASS:
    1
    Resources:
    66
    Actually instead doing this

    Code (vJASS):

    set Spawn['hbar'].time = 10
    set Spawn['hbar'].spawns['hfoo'].count = 3 //add footman x3
    set Spawn['hbar'].spawns['ewsp'].count = 1 //add wisp x1
    set Spawn['hbar'].spawns['hfoo'].count = 0 //remove footman
    set Spawn['hbar'].player[0].spawns['hfoo'].count = 2 //add two footmen to player 0 only
    set Player[0].spawns.time = 5 //add 5 seconds to all spawns of player 0
     

    You should do this

    Code (vJASS):

    local unit structure = /* get unit */ null
    set Spawn[structure].time = 10
    set Spawn[structure].spawns['hfoo'].count = 3 //add footman x3
    set Spawn[structure].spawns['ewsp'].count = 1 //add wisp x1
    set Spawn[structure].spawns['hfoo'].count = 0 //remove footman
    set Spawn[structure].player[1].spawns['hfoo'].count = 2 //add two footmen to player 1 only
    set Spawn[structure].time = 5 //add 5 seconds to all spawns of player 0
    set Spawn[structure].player = Player(0) //normal owner
     
     
  6. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,146
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    Er.. that wouldn't work in the slightest... that specific portion deals with units of a type id, meaning that no struct is ever actually instantiated by the mapper >.>.

    Specific spawns can be instantiated/manipulated sure, as well as specific positions, but not global ;o. Global are instantiated as units are created.
     
  7. Anachron

    Anachron

    Joined:
    Sep 9, 2007
    Messages:
    6,221
    Resources:
    66
    Icons:
    49
    Packs:
    2
    Tools:
    1
    Maps:
    3
    Spells:
    9
    Tutorials:
    1
    JASS:
    1
    Resources:
    66
    Then make it work. Its possible.

    You can catch the units within an method operator [] and get their handle-ids and store them as ids.

    It would be a lot better to not bound a spawn to a unittype but a specific unit. Also make spawns be able to have a easily changeable player.
     
  8. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,146
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    Er... you don't get it...

    Spawns are manipulated 6 different ways

    Global (all)
    Global 2 (unit type)
    Global 3 (player)
    Global 4 (player unit type)
    Local (specific position)
    Local 2 (specific spawn)


    .... like I said before, what you mentioned is already part of the system... unless you are saying that all of the global things should be removed, which isn't going to happen : P.
     
  9. Anachron

    Anachron

    Joined:
    Sep 9, 2007
    Messages:
    6,221
    Resources:
    66
    Icons:
    49
    Packs:
    2
    Tools:
    1
    Maps:
    3
    Spells:
    9
    Tutorials:
    1
    JASS:
    1
    Resources:
    66
    But I am still missing Local (handle id/unit)
     
  10. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,146
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    Position.... is the handle id/unit..

    a position is anything with coordinates, so this does include units.
     
  11. Anachron

    Anachron

    Joined:
    Sep 9, 2007
    Messages:
    6,221
    Resources:
    66
    Icons:
    49
    Packs:
    2
    Tools:
    1
    Maps:
    3
    Spells:
    9
    Tutorials:
    1
    JASS:
    1
    Resources:
    66
    Alright, so I missunderstood. Good then. I vote for approving :p
     
  12. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,146
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
  13. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,146
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    Look at code so far, lol

    and yes, I am achieving the syntax I set out to do.

    If you look through, you're going to see a load of hashtables. The overall system might end up using 20 hashtables. The first Spawn was sloppily written and generated like 5 hashtables per spawn, but this one will use 20 for entire system hopefully ; ).

    And yes, these hashtables are condensed as much as possible. I do hashing and tons of merging. The numbers start getting up to around (2^32)/4 in size. The many hashtables prevent collisions as well.

    The count/time in the global stuff go through groups so as to updated all of the units that the player's own. Keep in mind that I will update this to run through a hashtable loop of positions so that they will effect all spawns.

    The global stuff will work with all positions, but it will only be automatically enabled for units for obvious reasons (no viable way for me to make it auto run as items/destructables are placed and locations are created ^_^).

    Now, I'm still trying to figure out a good way to work the singles API in with the globals, singles being like-
    Origin[positionStruct].spawns['hfoo'].count = 4

    thus adding 4 footmen spawns to the origin

    Spawns can also be created from scratch and sent to the origin
    Origin[positionStruct].add(spawn)

    These will run on separate timers and will not sync up with other spawns, but it allows you to easily add bonuses or special techs or w/e.

    I'm also getting rid of the idea of lending and mimicing as that can easily be achieved outside of the system.

    I am adding a bonus property. This bonus property will be a multiplier for spawns. It is useful if spawns are shared, but you don't want the spawns spawning at the actual owner (set bonus to 0 for owner, meaning it won't effect the other origins sharing in the spawn).

    I'm also changing it from a single position to a set of positions.

    Woo hoo for the hardest system to make ever ; D. This really is the hardest type of system I've ever worked on ^_^. Lucky for me this is my fourth time working on a Spawn system, so I'm getting experience ; P.

    The spawn system will probably just include 2 loop text macros that you can put inside of your function. With these, you can easily set up the spawns in whatever positions you want to set them up in. Alternatively, you can just not pass in custom code and it'll spawn with default code.

    In the below code, you'll see groups. These groups are for updating via global settings.

    Code (vJASS):

    library Spawn uses UnitIndexer, Position
        globals
            private hashtable groups = InitHashtable()
            private hashtable unitTypeTable = InitHashtable()
            private integer unitTypeCount = 0
            private integer array runit
            private hashtable rtable = InitHashtable()
           
            private constant integer TIME = 0
            private constant integer COUNT = 1
            private constant integer INDEX = 2
            private constant integer PLAYER = 3
            private constant integer TYPE = 4
            private constant integer TYPE_2 = 5
        endglobals
       
        private keyword OnIndex
        private keyword OnDeindex
        private module init
            private static method onInit takes nothing returns nothing
                call OnUnitIndex(Condition(function OnIndex))
                call OnUnitDeindex(Condition(function OnDeindex))
            endmethod
        endmodule
       
        globals
            private real enumTime = 0
            private integer enumCount = 0
        endglobals
       
        private function EnumTime takes nothing returns nothing
        endfunction
       
        private function EnumCount takes nothing returns nothing
        endfunction
       
        private struct GlobalSpawns extends array
            /*
            public static hashtable table = InitHashtable()
           
            public method operator count takes nothing returns integer
                if (this > 0) then
                    return counts
                endif
                return LoadInteger(addSpawnTable, this, SPAWN_COUNT)
            endmethod
           
            public method operator count= takes integer i returns nothing
                set counts = i
            endmethod
           
            public method operator [] takes integer id returns thistype
                local integer spawnType = LoadInteger(addSpawnTable, id, 0)
                if (spawnType == 0) then
                    set unitTypeCount = unitTypeCount + 1
                    call SaveInteger(addSpawnTable, id, 0, unitTypeCount)
                    call SaveGroupHandle(groups, unitTypeCount, 0, CreateGroup())
                    set spawnType = unitTypeCount
                    if (this < 0) then
                        set spawnType = -spawnType
                    endif
                endif
                return this*8193+spawnType
            endmethod
            */

           
            public method operator group takes nothing returns group
                return LoadGroupHandle(groups, this, 2)
            endmethod
        endstruct
       
        private struct GlobalPlayerSpawns extends array
            private static hashtable table = InitHashtable()
           
            public method operator count takes nothing returns integer
                return LoadInteger(table, this, COUNT)
            endmethod
           
            public method operator count= takes integer c returns nothing
                local integer c2 = LoadInteger(table, this, COUNT)
                if (c2 != c) then
                    call SaveInteger(table, this, COUNT, c)
                    set c2 = enumCount
                    set enumCount = c
                    call ForGroup(LoadGroupHandle(groups, this, 2), function EnumCount)
                    set enumCount = c2
                endif
            endmethod
           
            public method operator time takes nothing returns real
                return LoadReal(table, this, TIME)
            endmethod
           
            public method operator time= takes real r returns nothing
                local real r2 = LoadReal(table, this, TIME)
                if (r2 != r) then
                    call SaveReal(table, this, TIME, r)
                    set r2 = enumTime
                    set enumTime = r
                    call ForGroup(LoadGroupHandle(groups, this, 2), function EnumTime)
                    set enumTime = r2
                endif
            endmethod
           
            public method operator [] takes integer unitTypeId returns thistype
                local integer k = LoadInteger(unitTypeTable, unitTypeId, 0)
                local integer i
                if (k == 0) then
                    set unitTypeCount = unitTypeCount + 1
                    call SaveInteger(unitTypeTable, unitTypeId, 0, unitTypeCount)
                    set runit[unitTypeCount] = unitTypeId
                    call SaveGroupHandle(groups, unitTypeCount, 0, CreateGroup())
                    set k = unitTypeCount
                endif
                set i = this*8192+k
               
                if (not HaveSavedHandle(groups, i, 2)) then
                    call SaveGroupHandle(groups, i, 2, CreateGroup())
                    call SaveInteger(rtable, i, TYPE_2, k)
                    call SaveInteger(rtable, i, TYPE, LoadInteger(rtable, this, TYPE))
                    call SaveInteger(rtable, i, PLAYER, LoadInteger(rtable, this, PLAYER))
                endif
               
                return i
            endmethod
        endstruct
       
        private struct GlobalPlayers extends array
            public method operator [] takes integer playerId returns thistype
                local integer i = this*16+playerId
                if (not HaveSavedHandle(groups, i, 1)) then
                    call SaveInteger(rtable, i, TYPE, this)
                    call SaveInteger(rtable, i, PLAYER, playerId)
                    call SaveGroupHandle(groups, i, 1, CreateGroup())
                endif
                return i
            endmethod
           
            public method operator group takes nothing returns group
                return LoadGroupHandle(groups, this, 1)
            endmethod
           
            public method operator spawns takes nothing returns GlobalPlayerSpawns
                return this
            endmethod
           
            /*
            public method operator count takes nothing returns integer
               
            endmethod
           
            public method operator count= takes integer i returns nothing
                call SaveInteger(addSpawnTable, this, SPAWN_COUNT, i)
            endmethod
           
            public method operator time takes nothing returns real
                return LoadReal(addSpawnTable, this, SPAWN_TIME)
            endmethod
           
            public method operator time= takes real r returns nothing
                call SaveReal(addSpawnTable, this, SPAWN_TIME, r)
            endmethod
            */

        endstruct
       
        private struct Origins extends array
            public static hashtable table = InitHashtable()
           
            public method operator group takes nothing returns group
                return LoadGroupHandle(groups, this, 0)
            endmethod
           
            public static method operator [] takes integer typeId returns thistype
                local thistype this = LoadInteger(unitTypeTable, typeId, 0)
                if (this == 0) then
                    set unitTypeCount = unitTypeCount + 1
                    call SaveInteger(unitTypeTable, typeId, 0, unitTypeCount)
                    set runit[unitTypeCount] = typeId
                    call SaveGroupHandle(groups, unitTypeCount, 0, CreateGroup())
                    return unitTypeCount
                endif
                return this
            endmethod
           
            public method operator spawns takes nothing returns GlobalSpawns
                return this
            endmethod
           
            public method operator player takes nothing returns GlobalPlayers
                return this
            endmethod
        endstruct
       
        private struct Spawns extends array
            public static hashtable table = InitHashtable()
        endstruct
       
        struct Spawn extends array
           
        endstruct
       
        private function OnDeindex takes nothing returns boolean
            /*
            local Origin o = Origin[GetUnitTypeId(GetIndexedUnit())]
            call GroupRemoveUnit(LoadGroupHandle(groups, o, 0), GetIndexedUnit())
            set o = o.player[GetPlayerId(GetOwningPlayer(GetIndexedUnit()))]
            call GroupRemoveUnit(LoadGroupHandle(groups, o, 0), GetIndexedUnit())
            */

            return false
        endfunction
       
        private function OnIndex takes nothing returns boolean
            /*
            local Origin o = Origin[GetUnitTypeId(GetIndexedUnit())]
            call GroupAddUnit(LoadGroupHandle(groups, o, 0), GetIndexedUnit())
            set o = o.player[GetPlayerId(GetOwningPlayer(GetIndexedUnit()))]
            call GroupAddUnit(LoadGroupHandle(groups, o, 0), GetIndexedUnit())
            */

            return false
        endfunction
       
        private struct inits extends array
            implement init
        endstruct
    endlibrary

    function test takes nothing returns nothing
        //set Origin['hbar'].spawns['hfoo'].count = 5
        //set Origin['hbar'].player[0].
    endfunction
     
     
    Last edited: Dec 21, 2010
  14. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,146
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    So, lately I've been thinking about spawn limits: how many spawns a specific spawner can have on the map at any given time. Furthermore, I've been thinking about using spawn limits of other objects.

    With the map I may or may not be working on (which may or may not be using Spawn), I'd probably need that ; ).

    It would be something built on top of this but anyways.. my idea was that spawn maximums would be based on commanders on the map. Each player would have x amount of troops off the map. The rate they place troops on the map are based on their spawn points. The amount of troops they can have on the map at one time are based on how many troops their top commanders can lead as well as their supplies. No supplies = starving = no morale. A poor commander = hard cap.

    Now, with this layout, I also think it would be possible to successfully create an RTSRPG (RTSMMORPG as well).

    Thoughts?

    And yes, I'm working on Spawn + Lua crap + Morale + Troop Command + keeping up with anime + etc, so dev on everything is going slow ; P.
     
  15. modafoka

    modafoka

    Joined:
    Aug 30, 2005
    Messages:
    162
    Resources:
    0
    Resources:
    0
    thinking on finishing this anytime soon?
     
  16. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,146
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
    Eventually, but I got a lot on my plate. At the moment I'm writing tutorials and accompanying systems for save/load codes ;P. I also have to update a slew of systems. I'm also working with T2D =). I have some of the code for this written up in my System Library map thingie, but don't know when I'll finish it.

    If you really need it, and possibly others, I could prioritize it : ).