• 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.

[Solved] Random ports and unit containers

Status
Not open for further replies.
Ok, so I have a building that periodically creates 1 unit at X angle every Y seconds, up to 5 units. If one of those units die, a new one takes its place after Y seconds. The angle will always be 360° / Maximum Unit Count, so in this case, 72°. What angles get used is random, so a unit might be appearing at 72°, then at 288°, and then at 144°, etc.

I'm deliberating how to tackle this. I made that system with fake 2D arrays for my TC11 submission (the Fabricator. click here for a video), but making it random doesn't seem to work as GetRandomInt() doesn't seem able to cycle indefinitely. Eg:
JASS:
loop
    set RandomInt = GetRandomInt(1, Max_Units)
    set angle = ((6.28319 / Max_Units) * RandomInt)
    set x = GetUnitX(Building) + Cos(angle) * PortsRadius
    set y = GetUnitY(Building) + Sin(angle) * PortsRadius
    if Unit[Building_ID * Max_Units + Current_Unit_Count] == null then
        set Unit[Building_ID * Max_Units + Current_Unit_Count] == CreateUnit(player, unittype, x, y, Rad2Deg(angle))
    endif
endloop
When I did that, sometimes the system would stop at two or three units, unable to randomise past a certain point, which leads me to believe the randomization is not actually random at all.

I need a smart person to tell me how to do this :/

Now, as far as containers are concerned, their not strictly needed for this system, but something of the like can always come in handy in the future. Usually, I'd just use unit groups, but I've had problems with that in the past and was also advised to use LinkedLists over unit groups. Sadly, I'm at a complete loss when it comes to LinkedList. I've looked at Hashtables, but these confound me as well and I have no idea what I'm flushing or what is being bound to what???

Does anyone have an alternative, or perhaps can explain to me how to get LinkedLists to work?
 
Last edited:
Linked list or hashtables would only help make your system less complicated to work with, it wouldn't help with your GetRandomInt() problem.

...which leads me to believe the randomization is not actually random at all.
Do you have "Use Fixed Random Seed" selected in Preferences?


On a side note, what is the purpose of RandomInt in this snippet you posted?
 
Linked list or hashtables would only help make your system less complicated to work with, it wouldn't help with your GetRandomInt() problem.
Yes, that is true, I'm just lousy at explaining myself :p I asked this mostly about finding a better alternative to unit groups.

Do you have "Use Fixed Random Seed" selected in Preferences?
I do. I'm assuming that's what prevents true randomisation?

On a side note, what is the purpose of RandomInt in this snippet you posted?
Man, I'm really distracted today, lol. Lemme fix that snippet a sec...
 
Last edited:
Hmm, that didn't fix the old bug I was having. The building should generate up to 4 units, but most stop at 2 and some stop at 3. It doesn't matter how often I restart the test, the results are the exact same. Turning off the 'Used Fixed Random Seed' does make the game pick different integers, but the building still stop short of the maximum value, so the randomisation wasn't the problem after all :/

Here's the code:


JASS:
function Generate_Husk takes nothing returns boolean
 
    local integer iLoop = 0
    local integer iRecycle = 0
    local integer id = 0
    local integer PortNumber = 0
    local integer Calculated_ID = 0
    local integer Husk_ID = 0
    local real x = 0.
    local real y = 0.
    local real angle = 0.
    local boolean b = false
 
    loop
        set iLoop = iLoop + 1
    
        set id = GetUnitUserData(UNI_Fabricator[iLoop])
    
        if UNI_HuskCount[id] < UNI_HuskPorts[id] then
            set UNI_HuskInterval[id] = UNI_HuskInterval[id] + 1
            if UNI_HuskInterval[id] >= UNI_HuskIntervalLimit[id] then
            
                set UNI_HuskInterval[id] = 0
                set UNI_HuskCount[id] = UNI_HuskCount[id] + 1
            
                loop
                    set PortNumber = GetRandomInt(1, UNI_HuskPorts[id])//PortNumber + 1
                    /*if PortNumber > UNI_HuskPorts[id] then
                        set PortNumber = 1
                    endif*/
                    set Calculated_ID = id * UNI_HUSK_PORT_HARD_LIMIT + PortNumber
                    if UNI_EmptyHusk[Calculated_ID] == null then
                        set angle = ((6.28319 / UNI_HuskPorts[id]) * PortNumber) - UNI_FabricatorAngleOffset[id]
                        set x = GetUnitX(UNI_Fabricator[iLoop]) + Cos(angle) * UNI_HuskRadius[id]
                        set y = GetUnitY(UNI_Fabricator[iLoop]) + Sin(angle) * UNI_HuskRadius[id]
                        set UNI_EmptyHusk[Calculated_ID] = CreateUnit(GetOwningPlayer(UNI_Fabricator[iLoop]), UNI_HuskType[id], x, y, Rad2Deg(angle))
                        call DestroyEffect(AddSpecialEffect(UNI_HUSK_BIRTH_EFFECT_1, x + 16, y + 16))
                        call DestroyEffect(AddSpecialEffect(UNI_HUSK_BIRTH_EFFECT_2, x + 16, y + 16))
                        call SetUnitAnimation(UNI_EmptyHusk[Calculated_ID], "stand")
                        call SetUnitTimeScale(UNI_EmptyHusk[Calculated_ID], 0.)
                        call UnitAddAbility(UNI_EmptyHusk[Calculated_ID], 'mact')
                        call UnitAddAbility(UNI_EmptyHusk[Calculated_ID], 'FX02')
                        set Husk_ID = GetUnitUserData(UNI_EmptyHusk[Calculated_ID])
                        set UNI_FabricatorIndex[Husk_ID] = id
                        set UNI_HuskSlotIndex[Husk_ID] = Calculated_ID
                        set b = true
                    endif
                    exitwhen b
                endloop
            
            endif
        endif
    
        if not UnitAlive(UNI_Fabricator[iLoop]) then
            set iRecycle = iLoop
            loop
                set UNI_Fabricator[iRecycle] = UNI_Fabricator[iRecycle + 1]
                exitwhen iRecycle == UNI_TotalFabricators
                set iRecycle = iRecycle + 1
            endloop
            set UNI_TotalFabricators = UNI_TotalFabricators - 1
            if UNI_TotalFabricators == 0 then
                call DisableTrigger(gg_trg_Generate_Husk)
            endif
        endif
    
        exitwhen iLoop == UNI_TotalFabricators
    endloop
 
    return false
endfunction

//===========================================================================
function InitTrig_Generate_Husk takes nothing returns nothing
    set gg_trg_Generate_Husk = CreateTrigger()
    call DisableTrigger( gg_trg_Generate_Husk )
    call TriggerRegisterTimerEvent( gg_trg_Generate_Husk, 0.1, true )
    call TriggerAddCondition( gg_trg_Generate_Husk, function Generate_Husk )
endfunction

This is the base function that sets up the Fabricator:
JASS:
function SetAsFabricator takes unit u, integer id, integer portnum, integer husktype, real portradius, real angleoffset, integer limit returns nothing
    set UNI_TotalFabricators                    = UNI_TotalFabricators + 1
    set UNI_Fabricator[UNI_TotalFabricators]    = u
    set UNI_FabricatorAngleOffset[id]           = angleoffset * bj_DEGTORAD
    set UNI_HuskCount[id]                       = 0
    set UNI_HuskPorts[id]                       = portnum
    set UNI_HuskType[id]                        = husktype
    set UNI_HuskRadius[id]                      = portradius
    set UNI_HuskInterval[id]                    = 0
    set UNI_HuskIntervalLimit[id]               = limit
    if not IsTriggerEnabled(gg_trg_Generate_Husk) then
        call EnableTrigger(gg_trg_Generate_Husk)
    endif
endfunction
 
Last edited:
Does it work the first time or does it never create the 4 units? On a side note, it's hard to debug without knowing what the variables represent in your system :p it would be easier if you can post a test map so I can add debug messages and such. If you aren't comfortable with it, then add debug messages checking how many times the loop runs + when it creates a unit.
 
Right. Triggers concerned are Generate Husk under Fabricators and onIndex Map Start under Indexing. I've preplaced 4 Fabricators for easy testing, but you might want to tweak some values to make it go faster. Search for //Fabricator in the onIndex Map Start trigger and you see the call SetAsFrabricator() function call. The last value (200) is the time.
http://www.hiveworkshop.com/pastebin/76900e6b6acd6f537eb4125c9951611a8535/
 
Level 13
Joined
Nov 7, 2014
Messages
571
This seems to somewhat work:

JASS:
library Factory

globals
    private constant integer MAX_ANGLES = 16
endglobals

struct Factory
    unit uu // the factory
    real px // the position of the factory
    real py //
    player pp // the factory owner

    integer ruc // the number of units that remain to be created by the factory
    real array angles[MAX_ANGLES] // the angles at which the factory can create its units
    real dist // the distance from the factory that units are placed when they are created

    real create_time // how much time it takes the factory to create a unit, in seconds
    timer tmr
    static thistype array from_tmr_id

    integer unit_type_id // the type of unit the factory will be creating

    static code create_unit_handler
    static code on_created_unit_death_handler
    private static method onInit takes nothing returns nothing
        call ExecuteFunc("s__" + "thistype" + "_set_handlers")
    endmethod

    static method create takes integer p, integer factory_id, real x, real y, real facing, integer max_units, integer unit_type_id, real dist, real create_time returns thistype
        local thistype this = allocate()
        local integer i
        local real angle
        local real angle_inc

        set this.pp = Player(p)
        set this.uu = CreateUnit(this.pp, factory_id, x, y, facing)
        set this.px = x
        set this.py = y

        set this.ruc = max_units
        set angle = 0
        set angle_inc = 360.0 / max_units
        set i = 0
        loop
            exitwhen i >= max_units

            set this.angles[i] = angle
            set angle = angle + angle_inc

            set i = i + 1
        endloop
        set this.dist = dist

        set this.create_time = create_time
        if this.tmr == null then
            set this.tmr = CreateTimer()
        endif
        call TimerStart(this.tmr, create_time, true, create_unit_handler)
        set from_tmr_id[GetHandleId(this.tmr) - 0x00100000] = this

        set this.unit_type_id = unit_type_id

        return this
    endmethod

    method get_random_angle takes nothing returns real
        local integer r
        local real angle

        set this.ruc = this.ruc - 1

        set r = GetRandomInt(0, this.ruc)
        set angle = this.angles[r]
        set this.angles[r] = this.angles[this.ruc]
        set this.angles[this.ruc] = angle

        return angle
    endmethod

    static method create_unit takes nothing returns nothing
        local thistype this = from_tmr_id[GetHandleId(GetExpiredTimer()) - 0x00100000]
        local real random_angle
        local real ux
        local real uy
        local unit created_uu
        local FactoryUnit created_u

        if this.ruc <= 0 then
            call PauseTimer(this.tmr)
            return
        endif

        set random_angle = this.get_random_angle()
        set ux = this.px + this.dist * Cos(random_angle * bj_DEGTORAD)
        set uy = this.py + this.dist * Sin(random_angle * bj_DEGTORAD)
        set created_uu = CreateUnit(this.pp, this.unit_type_id, ux, uy, random_angle)

        // just for testing
        call SetUnitState(created_uu, UNIT_STATE_LIFE, 1.0)

        set created_u = FactoryUnit.create()
        set created_u.uu = created_uu
        set created_u.factory = this
        set created_u.trg = CreateTrigger()
        call TriggerRegisterUnitEvent(created_u.trg, created_uu, EVENT_UNIT_DEATH)
        call TriggerAddAction(created_u.trg, on_created_unit_death_handler)
        set created_u.angle = random_angle

        call SetUnitUserData(created_uu, created_u)

        set created_uu = null
    endmethod

    method return_angle takes real angle returns nothing
        local integer i = 0
        local real tmp

        set i = 0
        loop
            exitwhen this.angles[i] == angle
            set i = i + 1
        endloop

        set tmp = this.angles[this.ruc]
        set this.angles[this.ruc] = this.angles[i]
        set this.angles[i] = tmp

        set this.ruc = this.ruc + 1
    endmethod

    static method on_created_unit_death takes nothing returns nothing
        local FactoryUnit u = GetUnitUserData(GetTriggerUnit())
        local Factory f = u.factory
        local real tmp
        local integer i

        call f.return_angle(u.angle)
        if f.ruc == 1 then
            call TimerStart(f.tmr, f.create_time, true, create_unit_handler)
        endif

        call DestroyTrigger(u.trg)
        set u.uu = null
        set u.trg = null
        call u.destroy()
    endmethod

    static method set_handlers takes nothing returns nothing
        set create_unit_handler = function thistype.create_unit
        set on_created_unit_death_handler = function thistype.on_created_unit_death
    endmethod
endstruct

struct FactoryUnit
    unit uu
    Factory factory // the factory that created this unit
    trigger trg
    real angle // the angle this unit was created at
endstruct

endlibrary

JASS:
library FactoryDemo initializer init requires Factory

globals
    private Factory factory
endglobals

private function init takes nothing returns nothing
    set factory = Factory.create(/*
        integer factory_owner: */ 0, /*
        integer factory_id: */ 'harm', /*
        real factory_x: */ 0.0, /*
        real factory_y: */ 0.0, /*
        real factory_facing: */ 0.0, /*
        integer max_units: */ 5, /*
        integer unit_type_id: */ 'hfoo', /*
        real distance_from_factory: */ 256.0, /*
        real creation_time: */ 3.0 /*
    */)
endfunction

endlibrary
 
Holy crap that works like magic. I really need to learn how to use structs, they seem soooo useful :(

I'm wondering, though, if I could push my luck and request some adjustments to this? My factories are buildable, so it would be nice if the creation was not integrated into the code but can be set afterwards. I'm also planning to use a beam to build the units (visual pizazz, if you will), so that means catching the start of the cycle as well as the end.

Regardless, grazie mille.
 
Level 24
Joined
Aug 1, 2013
Messages
4,658
Why the hell is that onInit() function not below the setHandlers() function?
Why the hell do you not use camel case function and field names?

I would put the "if f.ruc == 1 then" inside the returnAngle() funtion.
Also, you dont need the unit reference in the FactoryUnit struct.
 
Level 13
Joined
Nov 7, 2014
Messages
571
Why the hell is that onInit() function not below the set_handlers() function?
Because I like to order functions/methods in the order they are going to be called in time (chronological order), although its kind of annoying to do this in Jass, I find it more readable (reading the script top to bottom, rather than reading it bottom to top).
(1)onInit -> (2)Factory.create -> (3)factory.create_unit -> (4)factory_unit.on_death

Why the hell do you not use camel case function and field names?
I find underscores more readable and because jass-doesn't-allow-kebab-case, so underscores are the next best thing in my opinion.

@Aniki you can use the .name member to get the full name of a function for use in ExecuteFunc(). JASSHelper includes it specifically for that purpose.
I could use it if jasshelper didn't generate any "bonus stuff".
 
I'm wondering, though, if I could push my luck and request some adjustments to this? My factories are buildable, so it would be nice if the creation was not integrated into the code but can be set afterwards. I'm also planning to use a beam to build the units (visual pizazz, if you will), so that means catching the start of the cycle as well as the end.
Sooooo... pretty please? :3
 
Level 13
Joined
Nov 7, 2014
Messages
571
How does JassHelper generating bonus stuff prevent you from using it? It doesn't, you just won't use it for no good reason.

Here's a good reason: when one gets a compiler error, they would not have to skip through piles of "bonus stuff" like:
JASS:
function sc___prototype1_execute takes integer i returns nothing

    call TriggerExecute(st___prototype1[i])
endfunction
function sc___prototype1_evaluate takes integer i returns nothing

    call TriggerEvaluate(st___prototype1[i])

endfunction

function sa___prototype1_foo takes nothing returns boolean

    call BJDebugMsg("function")
    call BJDebugMsg("foo")
    call BJDebugMsg("got")
    call BJDebugMsg("called")
    return true
endfunction
when they want to debug something or see the output of jasshelper, for example.
 
Apologies for necromancy, but I've finally gotten round to trying to modify Aniki's code to fit with what I want to do. I've more or less managed part of it, but I'm running into some issues: for one, not all units get created. I set the value to 5 but it stops at 3 for some reason and then pauses on 4th 'husk' unit. The main changes I made were to make the factory create a husk unit (I might just replace that with a special effect eventually) for the duration of the creation time, and they replace the husk with the unit proper. Additionally, a lightning effect connects the dummy to the factory. It's all for aesthetic reasons.

JASS:
library Factory

globals
    private constant integer MAX_ANGLES = 16
endglobals

struct Factory
    unit uu // the factory
    unit husk // the husk unit that occupies the port before the actual unit is created
    integer husktype
    real ux
    real uy
    real uz
    real uangle
 
    lightning uln //the lightning effect that connects to the active port
    string ln_string //lightning effect string
 
    real px // the position of the factory
    real py //
    real pz //
    player pp // the factory owner

    integer ruc // the number of units that remain to be created by the factory
    real array angles[MAX_ANGLES] // the angles at which the factory can create its units
    real dist // the distance from the factory that units are placed when they are created

    integer create_time // how much time it takes the factory to create a unit, in seconds
    integer downtime // the downtime between creation of units
    integer timer_time //
    boolean create_phase //identifies if it's create_time or downtime
    timer tmr
    static thistype array from_tmr_id

    integer unit_type_id // the type of unit the factory will be creating

    static code create_unit_handler
    static code on_created_unit_death_handler
    private static method onInit takes nothing returns nothing
        call ExecuteFunc("s__" + "thistype" + "_set_handlers")
    endmethod

 
    method get_random_angle takes nothing returns real
        local integer r
        local real angle

        set this.ruc = this.ruc - 1

        set r = GetRandomInt(0, this.ruc)
        set angle = this.angles[r]
        set this.angles[r] = this.angles[this.ruc]
        set this.angles[this.ruc] = angle

        return angle
    endmethod
 
 
    static method create takes integer p, unit factory_unit, real coll_correct, integer max_units, integer husk_type_id, integer unit_type_id, real dist, integer create_time, integer downtime, string ln_fx, real p_height, real u_height returns thistype
        local thistype this = allocate()
        local integer i
        local real angle
        local real angle_inc

        set this.pp = Player(p)
        set this.uu = factory_unit
        set this.px = GetUnitX(factory_unit) + coll_correct
        set this.py = GetUnitY(factory_unit) + coll_correct
        set this.pz = p_height //for lightning effect

        set this.ruc = max_units
        set angle = 0
        set angle_inc = 360.0 / max_units
        set i = 0
        loop
            exitwhen i >= max_units

            set this.angles[i] = angle
            set angle = angle + angle_inc

            set i = i + 1
        endloop
        set this.dist = dist
        set this.husktype = husk_type_id
    
        set this.uangle = this.get_random_angle()
        set this.ux = this.px + this.dist * Cos(this.uangle * bj_DEGTORAD)
        set this.uy = this.py + this.dist * Sin(this.uangle * bj_DEGTORAD)
        set this.uz = u_height //for lightning effect
        //set this.husk = CreateUnit(this.pp, this.husktype, this.ux, this.uy, this.uangle)
        //call SetUnitPathing(this.husk, false)
        //call UnitRemoveAbility(this.husk, 'Amov')
    
        if ln_fx != null then
            set this.ln_string = ln_fx
            //set this.uln = AddLightningEx(this.ln_string, true, this.px, this.py, this.pz, this.ux, this.uy, this.uz)
        endif
    
        set this.create_time = create_time
        set this.timer_time = downtime
        set this.downtime = downtime
        set this.create_phase = false
        if this.tmr == null then
            set this.tmr = CreateTimer()
        endif
        call TimerStart(this.tmr, .03125, true, create_unit_handler)
        set from_tmr_id[GetHandleId(this.tmr) - 0x00100000] = this

        set this.unit_type_id = unit_type_id

        return this
    endmethod
 

    static method create_unit takes nothing returns nothing
        local thistype this = from_tmr_id[GetHandleId(GetExpiredTimer()) - 0x00100000]
        local unit created_uu
        local FactoryUnit created_u

        if this.ruc <= 0 then
            call PauseTimer(this.tmr)
            return
        endif
    
        set this.timer_time = this.timer_time - 1
        if this.timer_time < 1 then
            if this.create_phase then
                if this.uln != null then
                    call DestroyLightning(this.uln)
                    set this.uln = null
                endif
                call UnitApplyTimedLife(this.husk, 'BTLF', .01)
                set this.husk = null
            
                set created_uu = CreateUnit(this.pp, this.unit_type_id, ux, uy, this.uangle)
            
                // just for testing
                call SetUnitState(created_uu, UNIT_STATE_LIFE, 1.0)
            
                set created_u = FactoryUnit.create()
                set created_u.uu = created_uu
                set created_u.factory = this
                set created_u.trg = CreateTrigger()
                call TriggerRegisterUnitEvent(created_u.trg, created_uu, EVENT_UNIT_DEATH)
                call TriggerAddAction(created_u.trg, on_created_unit_death_handler)
                set created_u.angle = this.uangle
            
                call SetUnitUserData(created_uu, created_u)
            
                set created_uu = null
            
                set this.timer_time = this.downtime //after unit creation, set timer to downtime.
                set this.create_phase = false
            else
                if this.husk == null then
                    set this.uangle = this.get_random_angle()
                    set this.ux = this.px + this.dist * Cos(this.uangle * bj_DEGTORAD)
                    set this.uy = this.py + this.dist * Sin(this.uangle * bj_DEGTORAD)
                    set this.husk = CreateUnit(this.pp, this.husktype, ux, uy, this.uangle)
                    //call UnitRemoveAbility(this.husk, 'Amov')
                    call SetUnitPathing(this.husk, false)
                
                    if this.ln_string != null then
                        set this.uln = AddLightningEx(this.ln_string, true, this.px, this.py, this.pz, this.ux, this.uy, this.uz)
                    endif
                endif
            
                set this.timer_time = this.create_time
                set this.create_phase = true
            endif
        endif
    endmethod

 
    method return_angle takes real angle returns nothing
        local integer i = 0
        local real tmp

        set i = 0
        loop
            exitwhen this.angles[i] == angle
            set i = i + 1
        endloop

        set tmp = this.angles[this.ruc]
        set this.angles[this.ruc] = this.angles[i]
        set this.angles[i] = tmp

        set this.ruc = this.ruc + 1
    endmethod

 
    static method on_created_unit_death takes nothing returns nothing
        local FactoryUnit u = GetUnitUserData(GetTriggerUnit())
        local Factory f = u.factory
        local real tmp
        local integer i

        call f.return_angle(u.angle)
        if f.ruc == 1 then
            call TimerStart(f.tmr, f.create_time, true, create_unit_handler)
        endif

        call DestroyTrigger(u.trg)
        set u.uu = null
        set u.trg = null
        call u.destroy()
    endmethod

 
    static method set_handlers takes nothing returns nothing
        set create_unit_handler = function thistype.create_unit
        set on_created_unit_death_handler = function thistype.on_created_unit_death
    endmethod
endstruct

struct FactoryUnit
    unit uu
    Factory factory // the factory that created this unit
    trigger trg
    real angle // the angle this unit was created at
endstruct

endlibrary
JASS:
library Factory

globals
    private constant integer MAX_ANGLES = 16
endglobals

struct Factory
    unit uu // the factory
    unit husk // the husk unit that occupies the port before the actual unit is created
    integer husktype
    real ux
    real uy
    real uz
    real uangle
   
    lightning uln //the lightning effect that connects to the active port
    string ln_string //lightning effect string
   
    real px // the position of the factory
    real py //
    real pz //
    player pp // the factory owner

    integer ruc // the number of units that remain to be created by the factory
    real array angles[MAX_ANGLES] // the angles at which the factory can create its units
    real dist // the distance from the factory that units are placed when they are created

    integer create_time // how much time it takes the factory to create a unit, in seconds
    integer downtime // the downtime between creation of units
    integer timer_time //
    boolean create_phase //identifies if it's create_time or downtime
    timer tmr
    static thistype array from_tmr_id

    integer unit_type_id // the type of unit the factory will be creating

    static code create_unit_handler
    static code on_created_unit_death_handler
    private static method onInit takes nothing returns nothing
        call ExecuteFunc("s__" + "thistype" + "_set_handlers")
    endmethod

   
    method get_random_angle takes nothing returns real
        local integer r
        local real angle

        set this.ruc = this.ruc - 1
        call BJDebugMsg("ruc is reduced to " + I2S(this.ruc))

        set r = GetRandomInt(0, this.ruc)
        set angle = this.angles[r]
        set this.angles[r] = this.angles[this.ruc]
        set this.angles[this.ruc] = angle

        return angle
    endmethod
   
   
    static method create takes integer p, unit factory_unit, real coll_correct, integer max_units, integer husk_type_id, integer unit_type_id, real dist, integer create_time, integer downtime, string ln_fx, real p_height, real u_height returns thistype
        local thistype this = allocate()
        local integer i
        local real angle
        local real angle_inc

        set this.pp = Player(p)
        set this.uu = factory_unit
        set this.px = GetUnitX(factory_unit) + coll_correct
        set this.py = GetUnitY(factory_unit) + coll_correct
        set this.pz = p_height //for lightning effect

        set this.ruc = max_units
        set angle = 0
        set angle_inc = 360.0 / max_units
        set i = 0
        loop
            exitwhen i >= max_units

            set this.angles[i] = angle
            set angle = angle + angle_inc

            set i = i + 1
        endloop
        set this.dist = dist
        set this.husktype = husk_type_id
       
        if ln_fx != null then
            set this.ln_string = ln_fx
        endif
       
        set this.create_time = create_time
        set this.timer_time = downtime
        set this.downtime = downtime
        set this.create_phase = false
        if this.tmr == null then
            set this.tmr = CreateTimer()
        endif
        call TimerStart(this.tmr, .03125, true, create_unit_handler)
        set from_tmr_id[GetHandleId(this.tmr) - 0x00100000] = this

        set this.unit_type_id = unit_type_id

        return this
    endmethod
   

    static method create_unit takes nothing returns nothing
        local thistype this = from_tmr_id[GetHandleId(GetExpiredTimer()) - 0x00100000]
        local unit created_uu
        local FactoryUnit created_u
       
        set this.timer_time = this.timer_time - 1
        if this.timer_time < 1 then
            if this.create_phase then
                if this.uln != null then
                    call DestroyLightning(this.uln)
                    set this.uln = null
                endif
                call UnitApplyTimedLife(this.husk, 'BTLF', .01)
                set this.husk = null
               
                set created_uu = CreateUnit(this.pp, this.unit_type_id, this.ux, this.uy, this.uangle)
               
                // just for testing
                call SetUnitState(created_uu, UNIT_STATE_LIFE, 1.0)
               
                set created_u = FactoryUnit.create()
                set created_u.uu = created_uu
                set created_u.factory = this
                set created_u.trg = CreateTrigger()
                call TriggerRegisterUnitEvent(created_u.trg, created_uu, EVENT_UNIT_DEATH)
                call TriggerAddAction(created_u.trg, on_created_unit_death_handler)
                set created_u.angle = this.uangle
               
                call SetUnitUserData(created_uu, created_u)
               
                set created_uu = null
               
                set this.timer_time = this.downtime //after unit creation, set timer to downtime.
                set this.create_phase = false
                       
                if this.ruc <= 0 then
                    call PauseTimer(this.tmr)
                    return
                endif
            else
                if this.husk == null then
                    set this.uangle = this.get_random_angle()
                    set this.ux = this.px + this.dist * Cos(this.uangle * bj_DEGTORAD)
                    set this.uy = this.py + this.dist * Sin(this.uangle * bj_DEGTORAD)
                    set this.husk = CreateUnit(this.pp, this.husktype, this.ux, this.uy, this.uangle)
                    call UnitRemoveAbility(this.husk, 'Amov')
                    call SetUnitPathing(this.husk, false)
                   
                    if this.ln_string != null then
                        set this.uln = AddLightningEx(this.ln_string, true, this.px, this.py, this.pz, this.ux, this.uy, this.uz)
                    endif
                endif
               
                set this.timer_time = this.create_time
                set this.create_phase = true
            endif
        endif
       
    endmethod

   
    method return_angle takes real angle returns nothing
        local integer i = 0
        local real tmp

        set i = 0
        loop
            exitwhen this.angles[i] == angle
            set i = i + 1
        endloop

        set tmp = this.angles[this.ruc]
        set this.angles[this.ruc] = this.angles[i]
        set this.angles[i] = tmp

        set this.ruc = this.ruc + 1
        call BJDebugMsg("ruc is increased to " + I2S(this.ruc))
    endmethod

   
    static method on_created_unit_death takes nothing returns nothing
        local FactoryUnit u = GetUnitUserData(GetTriggerUnit())
        local Factory f = u.factory
        local real tmp
        local integer i

        call f.return_angle(u.angle)
        if f.ruc == 1 then
            call TimerStart(f.tmr, .03125, true, create_unit_handler)
        endif

        call DestroyTrigger(u.trg)
        set u.uu = null
        set u.trg = null
        call u.destroy()
    endmethod

   
    static method set_handlers takes nothing returns nothing
        set create_unit_handler = function thistype.create_unit
        set on_created_unit_death_handler = function thistype.on_created_unit_death
    endmethod
endstruct

struct FactoryUnit
    unit uu
    Factory factory // the factory that created this unit
    trigger trg
    real angle // the angle this unit was created at
endstruct

endlibrary
 
Last edited:
Status
Not open for further replies.
Top