• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[JASS] Need help with a "Locust Swarm" like concept for a system !

Status
Not open for further replies.
Level 4
Joined
Jan 29, 2007
Messages
98
Ok, so I'm trying to make a system which will work like the "Locust Swarm" ability, meaning that some creatures will come out of the caster, and "circulate" him within a specific range...
(By "circulate" I mean they will move around randomly until they spot an enemy etc... You know how I mean ;))

So, this is what I have atm:
JASS:
library EntityCol requires AutoIndex
    
    globals
        private constant integer MAX_ENTITIES = 20
    endglobals

    /*struct Entity
    
        unit Entity
        unit Owner
        
        static method create takes unit owner, integer amount, real x, real y, integer unitId returns Entity
            local Entity a = Entity.allocate()
            local player p = GetOwningPlayer( owner )
            
            
            set a.Owner = owner
            
            set a.Entity = CreateUnit( p, unitId, x, y, bj_UNIT_FACING )
            
            return a
        endmethod
        
    endstruct*/
    
    struct EntityCollection// extends Entity
    
        //group Entities = CreateGroup()
        unit array Entity [ MAX_ENTITIES ]
        unit Caster
        
        integer array ID [ MAX_ENTITIES ]
        
        static method create takes unit caster, integer number, integer unitId, real radius returns thistype
            local real x = GetUnitX( caster )
            local real y = GetUnitY( caster )
            local thistype this = thistype.allocate(/* caster, number, x, y, unitId */)
            local integer loops = 1
            local player p = GetOwningPlayer( caster )
            local real x2 = x + GetRandomReal( x - radius, x + radius )
            local real y2 = y + GetRandomReal( y - radius, y + radius )
            
            set this.Caster = caster
            
            loop
            exitwhen loops >= number
            
                set Entity[ ID[ loops ]] = CreateUnit( p, unitId, x, y, bj_UNIT_FACING )
                
                set ID[loops] = GetUnitId( caster )
                
                call IssuePointOrder( Entity[GetUnitId( caster )], "move", x2, y2 )
                //call GroupAddUnit( d.Entities, u )
                
            endloop
            
            return this
        endmethod
        
    endstruct

endlibrary

And as you can see, I had a struct for the "Entity Collection", meaning the whole group on entities (Locusts) a unit "owns", but also a struct for each entity...

I could not get this to work, because I thought i'd need to extend the EntityCollection struct, and I didn't know how that works :(

I haven't yet tested what I have now, but I'd believe it wouldn't work very well...

And also, as you can see by the unit arrays and integer arrays, I've tried to "link" the entities of a specific cast to their caster, but I'm very unsure if I've done it correctly :S

And, I think I still need to extend EntityCollection... (Or I at least want to have a stand-alone struct for each entity so you can change their flying height and many more features for them ;))

Anyone want to help me change it so it'll work as intended ? :S

(Note: I searched the forums TheHelper.net, WC3C.net and TheHiveWorkshop.com before I started working on this and I couldn't find anything like this anywhere :S If you know of a system like this, please let me know so I don't waste my time with this :D)

If you need more information, or you think I've been unclear on anything please let me know and I'll fix it :D
 
Last edited:
Level 13
Joined
Nov 22, 2006
Messages
1,260
If you're not sure how extending structs work, you can just read that part in the manual a couple of times, maybe it'll start to get clearer.

Anyways, this is a pretty complicated thing to do :D. I made the same thing, but I used Vexorian's xe system, which made things a whole lot easier. I can PM you the map if it's helpful in any way.

There are couple of things to note:
  • I think you created an infinite loop in the create method
  • you didn't name the struct anything :)
  • in this case, moving units with IssuePointOrder is not a good way to go, you should move it with triggers or something
  • with this system, you'll probably have to create a timer for each locust and you'll have to check many stuff periodically (not that I see a better solution :p)
  • I don't think naming both the struct and the unit array "Entity" is a good idea, such a name conflict probably causes problems
  • I don't think you can use variables when setting the array size in a struct (at least it didn't work for me, but maybe they fixed it, I don't know :p), but you can use variables when setting array size in the globals block
That's all I can think of right now.

EDIT: Also, I noticed the way you're trying to access the Entity and ID arrays. It won't work like that, you're acting as if they're global (and the compiler probably things you're trying to do something with the struct called Entity). You should do something like:

set this.Entity[ this.ID[ loops ]] = CreateUnit( p, unitId, x, y, bj_UNIT_FACING )


So basically just prefix them with a this. :p
 
Level 4
Joined
Jan 29, 2007
Messages
98
I think you created an infinite loop in the create method

Hehe, yeah XD
Well, I haven't really tested the code yet, so XD
But will fix ;)

you didn't name the struct anything :)

Hehe, wierd ! XD
Will change too then :p

in this case, moving units with IssuePointOrder is not a good way to go, you should move it with triggers or something

Ok then...

with this system, you'll probably have to create a timer for each locust and you'll have to check many stuff periodically (not that I see a better solution :p)

Ok, I'll try to accomplish that then ;)

I don't think naming both the struct and the unit array "Entity" is a good idea, such a name conflict probably causes problems

Yeah, I know... I just couldn't come up with another name XD
(I have zero imagination...)

I don't think you can use variables when setting the array size in a struct (at least it didn't work for me, but maybe they fixed it, I don't know :p), but you can use variables when setting array size in the globals block

Well, yes it works... It's just that the variable can't have like A + B in it, it needs a static number, like 20 or 10 or so...

EDIT: Also, I noticed the way you're trying to access the Entity and ID arrays. It won't work like that, you're acting as if they're global (and the compiler probably things you're trying to do something with the struct called Entity). You should do something like:

set this.Entity[ this.ID[ loops ]] = CreateUnit( p, unitId, x, y, bj_UNIT_FACING )

So basically just prefix them with a this. :p

I believe that Vex fixed that in a late update, because this whole code actually compiles XD
But, it's better for readability like what you said, so I'll change it ;)

Thanks sooo much !!!

+Rep ! ;)
 
Level 13
Joined
Nov 22, 2006
Messages
1,260
Ok then...

I realized I may have been a bit unclear with that "moving with triggers" phrase, because IssuePointOrder is also triggering. What I meant is moving the unit a bit at a time (a common thing to do in jass), so setting up a timer that runs every 0.04 seconds or so would do. With that same timer you can check other necessary stuff.

Yeah, I know... I just couldn't come up with another name XD
(I have zero imagination...)

Me too. But this isn't about imagination, this is about compiling :), so even an Entity1 would do, as long as it's not the same.

I believe that Vex fixed that in a late update, because this whole code actually compiles XD
But, it's better for readability like what you said, so I'll change it ;)

Vex fixed it?? But that was never a problem, it's perfectly logical, I don't get it... If you have a global and a struct variable both named "Entity", how will the compiler know which one you want to use? Giving the same name to a global and a struct variable was never a problem, because the struct variable gets some fancy prefixes anyways... I'm confused :)

It would help me if you'd explain what struct should serve what purpose. That way I can know what you want.
 
Level 4
Joined
Jan 29, 2007
Messages
98
I realized I may have been a bit unclear with that "moving with triggers" phrase, because IssuePointOrder is also triggering. What I meant is moving the unit a bit at a time (a common thing to do in jass), so setting up a timer that runs every 0.04 seconds or so would do. With that same timer you can check other necessary stuff.

Yeah, I understood that... :p

But you have any suggestions on what to check for here ? :eek:

Vex fixed it?? But that was never a problem, it's perfectly logical, I don't get it... If you have a global and a struct variable both named "Entity", how will the compiler know which one you want to use? Giving the same name to a global and a struct variable was never a problem, because the struct variable gets some fancy prefixes anyways... I'm confused :)

No, I meant that you don't have to put this. before struct members...
I know that it'll conflict with globals, but maybe that'll give an error ? :eek:
I haven't checked it, I just saw it in the changelog at the Jasshelper thread on wc3c.net... I'll check again ;)

It would help me if you'd explain what struct should serve what purpose. That way I can know what you want.

Well, I'm not really sure what you mean, but the first struct , "Entity", should be keeping track of all aspects of each individual entity, meaning that it'll have their height, and such stored in it...

And the second struct, "EntityCollection", should just keep track of them as a group, and like control other stuff (I might not need this one, but I made it anyways XD)...

Hope that is what you wanted to know :S
 
Level 13
Joined
Nov 22, 2006
Messages
1,260
But you have any suggestions on what to check for here ? :eek:

For example, their position. In order for those "creatures" to turn around the caster, you have to check whether the reach a certain destination, and then make them turn.

No, I meant that you don't have to put this. before struct members...
I know that it'll conflict with globals, but maybe that'll give an error ? :eek:
I haven't checked it, I just saw it in the changelog at the Jasshelper thread on wc3c.net... I'll check again ;)

Well, I don't know what to say, so I'm not gonna say anything :).

Well, I'm not really sure what you mean, but the first struct , "Entity", should be keeping track of all aspects of each individual entity, meaning that it'll have their height, and such stored in it...

And the second struct, "EntityCollection", should just keep track of them as a group, and like control other stuff (I might not need this one, but I made it anyways XD)...

Hope that is what you wanted to know :S

Yes, that is what I wanted to know. In that case, you don't need any struct extending any other struct. These are some basic things you should have that I could think of at the moment:

JASS:
globals
    private constant integer MAX_ENTITIES = 20
    private constant integer TIM_INTERVAL=0.04 //the timer interval, I just like putting
                                               //every value as a constant
endglobals

struct Entity
    unit u=null //the "creature" unit
    unit c=null //the caster all of the creatures will circulate around
    timer t=null
    
    static method create takes unit c returns Entity
        local Entity this=Entity.allocate()  //using thistype instead of Enity works fine as well
        
        set this.t=NewTimer() //this is if you have Vexorian's TimerUtils implemented
                              //(which I strongly suggest), if not, use CreateTimer()
        set this.c=c
        
        call SetTimerData(this.t, this) //a TimerUtils funciton, if you don't want to use it
                                        //you have to get another attaching system
        call TimerStart(this.t, TIM_INTERVAL, true, function Entity.Execute)
        
        return this
    endmethod
    
    static method Execute takes nothing returns nothing
        local timer t=GetExpiredTimer()
        local Entity this=GetTimerData(t)
        
        //this function will move the unit
        //and do other necessary stuff if needed
        
        // you can refer to the properties through this
    endmethod
    
    method onDestroy takes nothing returns nothing
        //some cleaning here, for example killing the unit, destroying the timer...
    endmethod
endstruct

struct EntityCollection
    Entity array [MAX_ENTITIES] //this will hold all the creatures you got
    
    //some methods here that manipulate the Entity structs alltogether
endstruct

Excuse me if I put a semicolon (;) on the end of a line or two, that's because I'm learning a language that requires that :p.

Btw, there is a way to not use a timer attaching system at all for this code, but it's a little more complex. Though it's much more efficient.
 
Last edited:
Level 4
Joined
Jan 29, 2007
Messages
98
0.A.0.0
* . or this. are not required anymore to use members. Note that this may cause issues if for some (incredibly weird) reason you try to use global variables from a method of a struct that has variables of the same name. To disable this feature, you can add [noimplicitthis] to jasshelper.conf.
* Improved the syntax error when you place a function inside a struct.
* Code values might get implicitly casted to boolexpr in some occasions, specifically, when using them as arguments for natives/bjfunc that take boolexpr. More cases will get added when type safety gets on its way for more stuff...
* Zinc: Added anonymous functions, but they cannot use locals from their parent (yet).
* Zinc: Fixed a crash that could happen when the zinc input is much smaller than the vJass output.
* Zinc: Fixed a couple of missing ; mistakes in the examples.

That's a quote from the Jasshelper Changelog...

Yes, that is what I wanted to know. In that case, you don't need any struct extending any other struct. These are some basic things you should have that I could think of at the moment:

Ok, thanks ! :D

I'll look through that code later ;)

Excuse me if I put a semicolon (;) on the end of a line or two, that's because I'm learning a language that requires that :p.

Haha XD It's ok... C++ ? :eek:

Btw, there is a way to not use a timer attaching system at all for this code, but it's a little more complex. Though it's much more efficient.

xe ? :O

Or what do you mean ?
 
Level 13
Joined
Nov 22, 2006
Messages
1,260
That's a quote from the Jasshelper Changelog...

It's ok then, I guess. Might be useful.

Haha XD It's ok... C++ ? :eek:

JavaScript :)

xe ? :O

Or what do you mean ?

Xe simplifies the whole movement thing, but what I meant was that you can use a single timer that will loop through 20 Entity structs (instead of 20 timers each managing a single struct). This is possible because the timer interval is very small, so there is actually no big difference (no noticeable difference, that is).
 
Level 4
Joined
Jan 29, 2007
Messages
98
Ok, I'll try it ;)

But, how would I go about acessing the "entities" (The units themselves) if I'm using something like this (This is without implementing the "single-timer-per-collection" thing :p):
JASS:
library EntityCol requires TimerUtils
    
    globals
        private constant integer MAX_ENTITIES = 20      // Is what it says, the maximum amount of "entities" which will be attached to a single unit at any time !
        
        private constant real MOVE_INTERVAL = 0.03125   // Interval of the "entities" movement...
        private constant real DMG_INTERVAL = 1.0        // Interval of the entities damaging...
    endglobals

    struct Entity
        real duration = 0.
    
        timer array t [ 2 ]

        unit u = null //the "creature" unit
        unit c = null //the caster that all of the creatures will circulate around

        static method create takes unit c, integer entityID returns Entity
            local thistype this = thistype.allocate()
            local real x = GetUnitX( c )
            local real y = GetUnitY( c )
            local player p = GetOwningPlayer( c )

            set this.t[ 0 ] = NewTimer() // Some TimerUtils magic ! ;)
            set this.t[ 1 ] = NewTimer() // Some TimerUtils magic ! ;)
            set this.c = c          // The owner of this "entity" !
            set this.u = CreateUnit( p, entityID, x, y, bj_UNIT_FACING )

            call SetTimerData( this.t[ 0 ], this ) // TimerUtils' magic again ! :D
            call TimerStart( this.t[ 0 ], TIMER_INTERVAL, true, function thistype.Move ) // Putting the TimerUtils' magic to use :P
            
            call SetTimerData( this.t[ 1 ], this ) // TimerUtils' magic again ! :D
            call TimerStart( this.t[ 1 ], TIMER_INTERVAL, true, function thistype.Damage ) // Putting the TimerUtils' magic to use :P
            
            set p = null

            return this
        endmethod

        static method Move takes nothing returns nothing
            local timer t = GetExpiredTimer()
            local thistype this = GetTimerData( t )
            
            set this.duration = this.duration + MOVE_INTERVAL
            
            set t = null
        endmethod
        
        static method Damage takes nothing returns nothing
            local timer t = GetExpiredTimer()
            local thistype this = GetTimerData( t )
        endmethod

        method onDestroy takes nothing returns nothing
            //some cleaning here, for example killing the unit, destroying the timer...
        endmethod
        
    endstruct

    struct EntityCollection
        Entity array e [ MAX_ENTITIES ] // All "entities" are here...
        
        //static method create 
    endstruct

endlibrary

I mean, can I just somehow do e[ 0 ].Move.execute() ? :S
 
Level 4
Joined
Jan 29, 2007
Messages
98
Ok, so I have this now:
JASS:
library EntityCol requires TimerUtils
    
    globals
        private constant integer MAX_ENTITIES = 100     // Is what it says, the maximum amount of "entities" which will be attached to a single unit at any time !
        
        private constant real MOVE_INTERVAL = 0.03125   // Interval of the "entities" movement...
        private constant real DMG_INTERVAL = 1.0        // Interval of the entities damaging...
        
        private constant integer CROW_ID = 'Arav'       // "Storm Crow Form" ability's ID, for height-changing purposes... Shouldn't need changing :D
        private constant integer LOCUST_ID = 'Aloc'     // "Locust" ability's ID... Shouldn't need changing either ;)
    endglobals

    struct Entity
        real angle = 0.0        // Current angle of movement for the "entity"
        real currentX = 0.0     // Current X position of the "entity"
        real currentY = 0.0     // Current Y position of the "entity"
        real radius = 0.0       // The range from the caster 
        real casterX = 0.0
        real casterY = 0.0
    
        timer array t [ 2 ]

        unit u = null // The "entity" unit...
        unit c = null // The caster that all of the creatures will circulate around...

        static method create takes unit c, integer entityID, real height, real radius returns Entity
            local thistype this = thistype.allocate()
            local player p = GetOwningPlayer( c )

            set this.currentX = GetUnitX( c )
            set this.currentY = GetUnitY( c )
            set this.casterX = GetUnitX( c )
            set this.casterY = GetUnitY( c )
            set this.t[ 0 ] = NewTimer() // Some TimerUtils magic ! ;)
            set this.t[ 1 ] = NewTimer() // Some TimerUtils magic ! ;)
            set this.c = c          // The owner of this "entity" !
            set this.u = CreateUnit( p, entityID, currentX, currentY, bj_UNIT_FACING ) // Creating the "entity"
            set this.angle = GetRandomReal( 0.0, 360.0 )
            set this.radius = radius
            
            //call DisplayTextToPlayer( Player( 0 ), 0.0, 0.0, R2S( this.currentX ) )
            //call DisplayTextToPlayer( Player( 0 ), 0.0, 0.0, R2S( this.currentY ) )
            
            call UnitAddAbility( this.u, CROW_ID )
            call UnitRemoveAbility( this.u, CROW_ID )
            call SetUnitFlyHeight( this.u, height, 0.0 )
            call UnitAddAbility( this.u, LOCUST_ID )
            
            call SetTimerData( this.t[ 0 ], this ) // TimerUtils' magic again ! :D
            call TimerStart( this.t[ 0 ], MOVE_INTERVAL, true, function thistype.Move ) // Putting the TimerUtils' magic to use :P
            
            call SetTimerData( this.t[ 1 ], this ) // TimerUtils' magic again ! :D
            call TimerStart( this.t[ 1 ], DMG_INTERVAL, true, function thistype.Damage ) // Putting the TimerUtils' magic to use :P
            
            set p = null

            return this
        endmethod

        static method Move takes nothing returns nothing
            local timer t = GetExpiredTimer()
            local thistype this = GetTimerData( t )
            local real x
            local real y
            local real distance
            
            set this.angle = GetRandomReal( this.angle - 30.0, this.angle + 30.0 )
            set this.casterX = GetUnitX( this.c )
            set this.casterY = GetUnitY( this.c )
            
            set x = currentX + 5.0 * Cos( this.angle * ( bj_PI / 180.0 ) )
            set y = currentY + 5.0 * Sin( this.angle * ( bj_PI / 180.0 ) )
            set distance = SquareRoot( ( ( casterX - x ) * ( casterX - x ) ) + ( ( casterY - y ) * ( casterY - y ) ) )
            
            //call DisplayTextToPlayer( Player( 0 ), 0.0, 0.0, R2S( distance ) )
            //call DisplayTextToPlayer( Player( 0 ), 0.0, 0.0, R2S( x ) )
            //call DisplayTextToPlayer( Player( 0 ), 0.0, 0.0, R2S( y ) )
            
            if distance > this.radius then
            
                loop
                    exitwhen distance < this.radius
                        set this.angle = this.angle - GetRandomReal( 0.0, 360.0 )
                        set this.casterX = GetUnitX( this.c )
                        set this.casterY = GetUnitY( this.c )
                        
                        set x = currentX + 5.0 * Cos( this.angle * ( bj_PI / 180.0 ) )
                        set y = currentY + 5.0 * Sin( this.angle * ( bj_PI / 180.0 ) )
                        set distance = SquareRoot( ( ( casterX - x ) * ( casterX - x ) ) + ( ( casterY - y ) * ( casterY - y ) ) )
                endloop
                
            endif
            
            call SetUnitX( this.u, x )
            call SetUnitY( this.u, y )
            
            set this.currentX = x
            set this.currentY = y
            
            set t = null
        endmethod
        
        static method Damage takes nothing returns nothing
            local timer t = GetExpiredTimer()
            local thistype this = GetTimerData( t )
        endmethod

        method onDestroy takes nothing returns nothing
            //some cleaning here, for example killing the unit, destroying the timer...
        endmethod
        
    endstruct

    struct EntityCollection
        Entity array e [ MAX_ENTITIES ] // All "entities" are here...
        
        real duration = 0.
        
        static method create takes unit caster, integer amount, integer entityID, real entityHeight, real maxRadius, real duration returns thistype
            local thistype this = thistype.allocate()
            local integer i = 1
            
            loop
                exitwhen i == amount
                
                set this.e[ i ] = Entity.create( caster, entityID, entityHeight, maxRadius )
                
                set i = i + 1
            endloop
            
            //set this.duration = duration + MOVE_INTERVAL
            
            return this
        endmethod
        
    endstruct

endlibrary

And this actually works just fine, accept for a few flaws...

  1. As soon as I move the caster, just a little, it'll start lagging and freeze WC3
  2. There is no duration for it yet XD
  3. I think it's unecessary to use 2 timers for each "entity", is there some way to maybe use 2 timers per "EntityCollection" ?
  4. How the whole concept of the "damage-dealing" should be, as I'm using a different timer for that...
  5. A lot of more stuff, which I'll have to deal wth after the above is done XD

Anyone have any idea of how to fix these problems ? :S
 
Status
Not open for further replies.
Top