Zeige Ergebnis 1 bis 8 von 8

Thema: Spawnsystem

  1. #1
    Benutzerbild von Skater
    Registriert seit
    Apr 2006
    Ort
    ••••••••†
    BNet Account
    Skaterl
    Beiträge
    620

    Spawnsystem

    Spawnsystem/Movesystem~

    Jo, wer kennt nicht die Qual bei z.B. einer AoS die Creepwege zu erstellen.
    Mit diesem System will ich euch helfen die Qual zu reduzieren.

    Ich habe versucht die API möglichst einfach zu halten (nochmal danke an The-Red-OrK :> ).

    code:
    Code:
    library Spawn initializer init uses UnitIndexingUtils
    
        public keyword node
    
        globals
            private node array nodes
            private trigger noorder
        endglobals
        
        public function interface NodeCallback takes unit u returns nothing
        
        private function Cond takes nothing returns boolean
            return nodes[GetUnitId(GetTriggerUnit())].enter==GetTriggeringRegion()
        endfunction
        
        private function Enter takes nothing returns nothing
            local unit u=GetTriggerUnit()
            local integer i=GetUnitId(u)
            local node n=nodes[i].next
            if n!=0x0then
                call IssuePointOrder(u, "attack", n.x, n.y)
                set nodes[i]=n
            endif
            if n.cb!=0 then
                call n.cb.execute(u)
            endif
            set u=null
        endfunction
        
        public struct node
            static trigger register
            node first
            real x
            real y
            real r
            
            region enter
            node next
            NodeCallback cb
            
            private static method newNode takes real x, real y, real r, NodeCallback cb returns node
                local node this=node.allocate()
                local rect c=Rect(x-r, y-r, x+r, y+r)
                set .x=x
                set .y=y
                set .r=r
                set .enter=CreateRegion()
                set .cb=cb
                call RegionAddRect(.enter, c) //too lazy to remove the leak~
                call TriggerRegisterEnterRegion(.register, .enter, null)
                
                //DEBUG!!!
                debug if bj_enumDestructableCenter==null then
                debug     set bj_enumDestructableCenter=Location(0, 0)
                debug endif
                debug call AddSpecialEffect("UI\\Feedback\\RallyPoint\\RallyPoint.mdl", x, y)
                debug call MoveLocation(bj_enumDestructableCenter, x, y)
                debug call CreateTextTagLocBJ("( "+I2S(R2I(x))+" | "+I2S(R2I(y))+" )", bj_enumDestructableCenter, 100, 10, 0xFF, 0xFF, 0xFF, 0x00)
                debug call SetTextTagVisibility(bj_lastCreatedTextTag, true)
                //DEBUG!!!
                
                call RemoveRect(c)
                set c=null
                return this
            endmethod
            
            static method start takes real x, real y, real r returns node
                local node n=node.newNode(x, y, r, 0)
                set n.first=n
                return n
            endmethod
            
            method add takes real x, real y, real r returns node
                local node n=node.newNode(x, y, r, 0)
                set n.first=this.first
                set .next=n
                return n
            endmethod
            
            method exit takes real x, real y, real r, NodeCallback cb returns node
                local node n=node.newNode(x, y, r, cb)
                set .next=n
                return .first
            endmethod
            
            //since you cant have code arrays, you have to
            //give this method the callback code
            //  (untested...)
            method reverse takes nothing returns node
                local node array a
                local node d
                local node x=this
                local integer n=0
                
                loop
                    exitwhen x==0
                    set a[n]=x
                    set n=n+1
                    set x=x.next
                endloop
                set n = n - 1
                set d=node.start(a[n].x, a[n].y, a[n].r)
                set n=n-1
                loop
                    exitwhen n==0
                    set d=d.add(a[n].x, a[n].y, a[n].r)
                    set n=n-1
                endloop
                set d=d.exit(a[n].x, a[n].y, a[n].r, a[n].cb)
                return d
    
                return 0
            endmethod
    
            
            method spawn takes player p, integer id, integer n returns nothing
                local unit u
                loop
                exitwhen n==0
                    set u=CreateUnit(p, id, .x, .y, 0)
                    set nodes[GetUnitId(u)]=this
                    set n=n-1
                endloop
                set u=null
            endmethod
        endstruct
        
        private function NoOrderCond takes nothing returns boolean
            return nodes[GetUnitId(GetTriggerUnit())]!=0
        endfunction
        
        private function NoOrderAction takes nothing returns nothing
            local unit u=GetTriggerUnit()
            local node n=nodes[GetUnitId(u)]
            
            if n.next==0 then
                call IssuePointOrder(GetTriggerUnit(), "attack", n.x, n.y)
            else
                call IssuePointOrder(GetTriggerUnit(), "attack", n.next.x, n.next.y)
            endif
            
            set u=null
        endfunction
        
        public function cleanUnit takes unit u returns nothing
            //hoffentlich stimmt alles~
            set nodes[GetUnitUserData(u)]=0
        endfunction
    
        
        private function init takes nothing returns nothing       
            set noorder=CreateTrigger()
            call TriggerRegisterAnyUnitEventBJ(noorder, EVENT_PLAYER_UNIT_ISSUED_ORDER)
            call TriggerAddCondition(noorder, Condition(function NoOrderCond))
            call TriggerAddAction(noorder, function NoOrderAction)
            set node.register=CreateTrigger()
            call TriggerAddCondition(node.register, Condition(function Cond))
            call TriggerAddAction(node.register, function Enter)
        endfunction
    
    endlibrary
    
    
    

    Documentation:
    method start takes real x, real y, real r returns node

    Diese Funktion muss als erstes aufgerufen werden.
    x und y representieren die Koordinaten.
    r ist die Grösse, in der dann die Einheiten registriert werden (kein Kreis, sondern ein Quadrat wird erstellt)

    Diese Methode kann man nun folgendermaßen aufrufen:
    Code:
    local Spawn_node s=Spawn_node.start(0, 0, 200)
    method add takes real x, real y, real r returns node

    Die Parameter sind genau die gleichen wie bei start(). Das coole ist, dass man, da add() auch "node" zurückgibt,
    alle add()'s hintereinaderreihen kann:

    Code:
    local Spawn_node s=Spawn_node.start(0, 0, 200).add(700, 500, 200).add(1500, 300, 200)
    
    Man kann natürlich auch jeweils add() einzeln aufrufen:
    
    local Spawn_node s=Spawn_node.start(0, 0, 200)
    set s=s.add(700, 500, 200)
    set s=s.add(1500, 300, 200)
    
    Das macht genau das gleiche.
    VORSICHT: Man darf add() nur auf eine Spawn_node aufrufen, die via start() erstellt wurde.

    method exit takes real x, real y, real r, code cb returns node

    x,y und r sind wie immer die gleichen parameter wie sonst.
    cb gibt nun an, welche Funktion aufgerufen wird, wenn eine Einheir den letzen Knoten bestimmt.
    Soll nicht aufgerufen werden, so übergibt man einfach null.

    exit() muss aufgerufen werden!
    Dies kann wie bei add() direkt passieren oder teilweise:

    Code:
    local Spawn_node s=Spawn_node.start(0, 0, 200).add(700, 500, 200).add(1500, 300, 200).exit(1800, 700, 200, function UnitEndFunction)
    
    oder
    
    local Spawn_node s=Spawn_node.start(0, 0, 200)
    set s=s.add(700, 500, 200)
    set s=s.add(1500, 300, 200)
    set s=s.exit(1800, 700, 200, function UnitEndFunction)
    method reverse takes code c returns node

    Die Funktion ist für die ganz faulen
    Sie kehr die komplette Liste aus Wegpunkten um und gibt den Startpunkt aus; da WarCraft keine Code-Arrays zulässt, muss man auch hier wieder
    die Funktion angeben, die aufgerufen werden soll, wenn die Einheit den letzen Wegpunkt erreicht.

    local Spawn_node s=Spawn_node.start(0, 0, 200).add(700, 500, 200).add(1500, 300, 200).exit(1800, 700, 200, function UnitEndFunction)
    local Spawn_node s2=s.reverse(function UnitEndFunction)

    VORSICHT: Diese Funktion ist noch relativ ungetestet.

    method spawn takes player p, integer id, integer n returns nothing

    Diese Methode erstellt nun für den Spieler "p", "n" Einheiten des Types "id".
    Diese Einheiten laufen dann schön die Wegpunkte ab, und attackieren alles, was ihnen dabei begegnet und sich anzugreifen lohnt.

    Code:
    local Spawn_node s=Spawn_node.start(0, 0, 200).add(700, 500, 200).add(1500, 300, 200).exit(1800, 700, 200, null)
    local Spawn_node s2=s.reverse(null)
    
    call s.spawn(Player(0), 'hfoo', 3)
    call s2.spawn(Player(1), 'hfoo', 3)
    Das war schon alles an methoden.

    Damit jedoch die Einheiten wissen, wohin sie zu laufen haben, benutzt ich sogenanntes "Unit-indexing", was besagt, dass jede Einheit
    eine individuelle Id bekommt.
    Hier ist mein System (falls ihr schon soetwas benutzt, baut die Funktionen einfach um. Einfach UserData_Id durch den Namen eurer Unit-indexing-funktion
    ersetzen (oder auch andere funktioen, wie gamecache)):

    code:
    Code:
    library UserData
    
        globals
            private integer Stack_N=0
            private integer array Stack_Save
            private integer N=0
        endglobals
    
        public function Remove takes unit u returns nothing
            local integer i=GetUnitUserData(u)
            if not(i==0) then
                if Stack_N>8189 then
                    debug call BJDebugMsg("Stack UnitUserData is full!")
                    return
                endif
                set Stack_N=Stack_N+1
                set Stack_Save[Stack_N]=i
            debug else
                debug call BJDebugMsg("Unit user data was empty")
            endif
            call SetUnitUserData(u, 0)
            set u=null
        endfunction
        
        public function Id takes unit u returns integer
            local integer i=GetUnitUserData(u)
            if i==0 then
                if Stack_N>0 then
                    set i=Stack_Save[Stack_N]
                    set Stack_N=Stack_N-1
                else
                    if N<8189 then
                        set N=N+1
                        set i=N
                    debug else
                        debug call BJDebugMsg("no free space for a new unit index")
                    endif
                endif
                call SetUnitUserData(u, i)
            endif
    
            set u=null
            return i
        endfunction
    
    endlibrary
    Zudem wäre noch zu beachten, dass man die Indexe auch wieder freigeben muss, sowie die vom Spawnsystem benuten array auf 0 setzen; dafür reicht auch folgender
    GUI-Trigger (hier nur Pseudocode):

    E: Unit dies
    B: eure Bedingungen wie z.B. Dying Unit ist kein Held oder so
    A:
    Customscript: call Spawn_cleanUnit(GetTriggerUnit())
    //wichtig: erst cleanUnit aufrufen, dann erst UserData removen
    Customscript: call UserData_Remove(GetTriggerUnit())

    Falls sich irgendwo Fehler eingeschlichen haben, dann meldet mir das bitte, und ich werde es fixxen.

    Solltet ihr Fragen haben, so stellt sie mir bitte.

    Changelog:
    Code:
    30.09.2009
     1.24b Version (eher ungetestet...)
    07.01.2009:
      Code verbessert.
      (hoffentlich hab ich den richtigen Code hochgeladen :o )
    P.s.: Bitte gebt mir Credits :>
    P.P.s: Das System wird schon von so tollen Leute wie an anXieTy und The-Red-OrK benutzt
    Are you #gp39f.tmp?

    Mein Spawnsystem macht das Leben einfacher.

    Mein Multiboard-Tutorial: schaut euch es an.

  2. #2
    Benutzerbild von jacobson
    Registriert seit
    Mär 2003
    Ort
    Berlin
    BNet Account
    nexXus]
    Beiträge
    1.943
    Dann bin ich mal der Erste der seinen Senf hier abgibt.
    Sieht sehr gut und sauber aus. Der Nutzen ist zwar eher begrenzt aber einige Möglichkeiten der Ingame-korrektur sind doch für manche sehr Verlockend. Ich find deine reverse Funktion sehr erotisch. Ich bin zwar kein großer vJass Freund aber hier ist der Nutzen unverkennbar.

    P.P.s: Das System wird schon von so tollen Leute wie an anXieTy und The-Red-OrK benutzt
    Ich musste lachen Kann mir vorstellen das die den Nutzen erkannt haben und fänds lustig wenn die nochmal posten wie es sich als Dritter so anwenden lässt.

    good work
    moar suuucking powa than a HUUKA in a vacuum o:

  3. #3
    Benutzerbild von RedOrK
    Registriert seit
    Sep 2006
    Ort
    Nicht weit genug weg
    BNet Account
    The-Red-OrK
    Beiträge
    712
    Ich nutze das sys ja einige Zeit bzw war auch bei der Implementierung teilweise anwesend.

    Alles in allen kann ich sagen das es wenn man es verstanden hat sehr einfach anzuwenden ist. Ich habe komplexe wege mit jeweils über 20 wegpunkten in 5 minuten pro weg geschafft.
    Hätte ich für jeden punkt ein rect und einen eigenen Trigger erstellt, hätte das sicherlich mehrere stunden gedauert. So war es eine Sache von 20 Minuten.
    Bugs sind nur in der damals ungetestet Reverse Funktion aufgetaucht. Allerdings läuft seit einer kleinen Korrektur, die schon in der hier Veröffentlichten Version eingebaut ist(hoffe ich), alles absolut perfekt.

    Auch halte ich es für fast unmöglich ein schnelleres System zu diesem zweck zu schreiben.

    Und ich habe immer noch die Hoffnung das sich so auch hier auf inwc Unit Indexing durchsetzt, das die ganzen zweifelhaften gamecache Konstruktionen langsam aber sicher verschwinden.

    e: auch sich kreuzende wege sind kein problem
    Ich musste lachen Kann mir vorstellen das die den Nutzen erkannt haben
    verstehe ich jetzt nicht was daran lustig ist; würde aber gerne mitlachen; klär mich einer auf

  4. #4
    Benutzerbild von Seshiro
    Registriert seit
    Sep 2007
    BNet Account
    ImTheUnknown
    Beiträge
    3.413
    ICh muss sagen, dass es ein sehr schönes System ist,
    ich benutzte es jetzt auch in meiner map, es gibt nur ein Problem:
    Man darf die range niemals kleiner als 350 halten, sonst kann es sein, dass die units nicht laufen!

    Greez

    Can you dig it?

  5. #5
    Benutzerbild von RedOrK
    Registriert seit
    Sep 2006
    Ort
    Nicht weit genug weg
    BNet Account
    The-Red-OrK
    Beiträge
    712
    Kann ich nicht bestätigen.
    Ich habe in meinem AoS eine range von 200 und funktioniert absolut perfekt. (Hab bei 25xSpeed 10 Minuten laufen gelassen und es ging ohne bugs.

    Ich halte es noch für eine gute Neuerung es einstellbar zu machen, dass die Spawns nicht genau zum Zielpunkt gehen sondern nur einen Zufälligen punkte in der Range als Ziel auswählen.

  6. #6
    Benutzerbild von Seshiro
    Registriert seit
    Sep 2007
    BNet Account
    ImTheUnknown
    Beiträge
    3.413
    Bei mir hat es bei zu großen Spawns gebugged, des hast du sogar mitgekriegt glaub, ich bei maptest, da sind immer bei 4 spawns gestuckt!

    Greez

    Can you dig it?

  7. #7
    Metroi_hunter
    Guest
    Bei mir stuckt nichts mit 200 range! Das system ist echt super...
    Erspart ne Menge Arbeit

    Nur leider funktioniert "reverse" nicht so richtig.
    Die Units werden erstellt an dem Endpunkt (neuem Startpunkt) nur laufen sie nicht weiter. o.O

    Mfg
    Metroi_Hunter

  8. #8
    Benutzerbild von RedOrK
    Registriert seit
    Sep 2006
    Ort
    Nicht weit genug weg
    BNet Account
    The-Red-OrK
    Beiträge
    712
    Hab grade mal die aktuellste version getestet; bei mir funktioniert die reverse method auch nicht.

    Nun stelle ich hier mal die methode rein die ich nutzte. Diese funktionierte in meinem (alten) aos problemlos.
    Code:
            method reverse takes code c returns node
                local node array a
                local node d
                local node x = this
                local integer n=0
                loop
                    exitwhen integer(x) > 0x100000 or x == 0
                    set a[n]=x
                    set n=n+1
                    set x=x.next
                endloop
                set n = n - 1
                set d=node.start(a[n].x, a[n].y, a[n].range)
                set n=n-1
                loop
                    exitwhen n==0
                    set d=d.add(a[n].x, a[n].y, a[n].range)
                    set n=n-1
                endloop
                set d=d.exit(a[n].x, a[n].y, a[n].range, c)
                return d
            endmethod
    So wie ich das auf die schnelle sehe fehlt in skas version ein set n = n - 1 zwischen den loops.
    (muss allerdings sagen das bei mit r range heißt und das ganze nicht 100% kompatibel ist; ska könnte das ja mal schnell zusammenbasteln)
    To be done

Forumregeln

  • Es ist dir nicht erlaubt, neue Themen zu verfassen.
  • Es ist dir nicht erlaubt, auf Beiträge zu antworten.
  • Es ist dir nicht erlaubt, Anhänge hochzuladen.
  • Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.
  •