• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[JASS] Last Order Tracker for Advanced Build

Status
Not open for further replies.
I've been working on an Advanced Build system for a while now. I've tried various approaches, all of which have their own flaws and advantages.

The two best ways to achieve it that I've found so far are either disabling/enabling tech tree items, or replacing the worker unit.
This is done by firing a trigger when custom Build abilities are used (per race/build type), doing the necessary actions, and then firing the Build order.

You can find my example of the former here (GUI). The biggest flaws with this method are that you can't queue build orders for more than one building type at a time, all orders are cancelled each time you switch building type, and custom tech trees are ignored.

With the second method, all orders are cancelled when switching building type, and carried resources are lost. The biggest advantage is that you can still queue different types of buildings at one time (but not with one worker).


I've been writing some JASS to retrieve the cancelled orders from the deleted/ unit. So far it's working quite well, but I've still got some kinks to work out, if possible:

-Gold/Lumber are lost: need a way to save the amount of resources carried
-The repair animation isn't played when already repairing, and clicking the already active build button
-Selecting across screen is now divided between basic and advanced workers
-auto-stop after order finished isn't saved (will have to track manually)
-can only queue builder orders by using the real Build button :(


Here's the code (NOTE: the ReplaceWorkerWJ and Advanced_Build functions were written by StrategyMaster from War2:2ndGen):


Global constants, vars, funcs
JASS:
globals
    //Worker Constant Data
    constant integer cgm_ADVPEASANT = 'hpe2' //Advanced Peasant
    constant integer cgm_ADVPEON = 'ope2' //Advanced Peon
    constant integer cgm_PEASANT = 'hpea' //Peasant
    constant integer cgm_PEON = 'opeo' //Peon
    constant integer cgm_HADVBUILD = 'A001' //Human Advanced Build
    constant integer cgm_HSTDBUILD = 'A002' //Human Basic Build
    constant integer cgm_OADVBUILD = 'A000' //Orc Advanced Build
    constant integer cgm_OSTDBUILD = 'A003' //Orc Basic Build
    
    //Order Constant Data
    constant string OBJECT_TARGET = "object"
    constant string POINT_TARGET = "point"
    constant string BUILD_TARGET = "build"
    constant string NOTARGET_TARGET = "notarget"
    constant string NO_TARGET = "none"
    constant integer BUILD_ORDER = 851994
    constant integer OSTD_ORDER = 852513
    constant integer OADV_ORDER = 852512
    constant integer HSTD_ORDER = 852590
    constant integer HADV_ORDER = 852589
    constant integer HOLD_ORDER = 851993
    constant integer REPAIRON_ORDER = 852025
    constant integer REPAIROFF_ORDER = 852026
    
    //Order Track Data
    integer array trackOrders //tracked order strings
    string array trackOrderTypes //tracked order types
    widget array trackTargets //tracked targets
    location array trackPoints //tracked points
    integer unitCount = 0 //number of units being tracked
endglobals

//INDEX HANDLING
function GetUnitIndex takes unit orderedUnit, unit deletedUnit returns integer
    local integer unitIndex = 0

    //GET INDEX
    if(deletedUnit == null) then
        // SET VARS
        set unitIndex = GetUnitUserData(orderedUnit)
        
        //NO INDEX -> NEW
        if ( unitIndex == 0 ) then
            set unitCount = ( unitCount + 1 )
            set unitIndex = unitCount
            call SetUnitUserData(orderedUnit, unitIndex)
        endif
        
    //TRANSFER INDEX
    else
        // SET VARS
        set unitIndex = GetUnitUserData(deletedUnit)

        //NULL INDEX -> SKIP
        if(unitIndex != 0) then
            //TRANSFER UNIT INDEX
            call SetUnitUserData(orderedUnit, unitIndex)
        endif
    endif
    
    //OFFSET AND RETURN
    return ( unitIndex - 1 )
endfunction

//CONDITIONS
function Trig_Worker_Order_Tracker_Conditions takes nothing returns boolean
    //SET VARS
    local integer unitTypeId = GetUnitTypeId(GetTriggerUnit())
    local integer orderId = GetIssuedOrderId()
    
    //IGNORE NON-WORKERS
     if(unitTypeId != cgm_PEON and unitTypeId != cgm_ADVPEON and unitTypeId != cgm_PEASANT and unitTypeId != cgm_ADVPEASANT) then
        return false
        
    //ignore all orders that shouldnt be tracked        
    //IGNORE HUMAN BUILD ABIL
     elseif( orderId == HADV_ORDER or orderId == HSTD_ORDER ) then
        return false
        
    //INGORE ORC BUILD ABIL
     elseif( orderId == OADV_ORDER or orderId == OSTD_ORDER ) then
        return false
    
    //IGNORE BUILD ORDER
     elseif( orderId == BUILD_ORDER ) then
        return false
        
    //IGNORE HOLD POSITION
     elseif( orderId == HOLD_ORDER ) then
        return false

    //IGNORE REPAIR TOGGLE
     elseif( orderId == REPAIRON_ORDER or orderId == REPAIROFF_ORDER ) then
        return false
    
    //CONTINUE
    else
        return true

    endif        
endfunction


Build set switcher
JASS:
//REPLACE WORKER
function ReplaceWorkerWJ takes unit builder, integer newUnitId returns unit
    local player p = GetOwningPlayer(builder) //player
    local real x = GetUnitX(builder) //x coord
    local real y = GetUnitY(builder) //y coord
    local real f = GetUnitFacing(builder) //angle
    local real r = GetUnitState(builder, UNIT_STATE_LIFE) //state

    //replace and return unit
    call RemoveUnit(builder)
    set builder = CreateUnit(p, newUnitId, x, y, f)
    call SetUnitState(builder, UNIT_STATE_LIFE, r)
    set p = null
    return builder
endfunction

//RETRIEVE ORDERS
function Retrieve_Orders takes unit deletedUnit, unit orderedUnit returns nothing
    local integer unitIndex = GetUnitIndex(orderedUnit, deletedUnit)

    call DisplayTextToForce( GetPlayersAll(), ( "Order Retrieve: " + OrderId2StringBJ(trackOrders[unitIndex]) ) )
    call DisplayTextToForce( GetPlayersAll(), ( "Order Type Retrieve: " + trackOrderTypes[unitIndex] ) )
    
    //VALID ORDERS
    if(unitIndex != -1 and trackOrders[unitIndex] != 0 and trackOrderTypes[unitIndex] != NO_TARGET) then
        //TARGET ORDER
        if(trackOrderTypes[unitIndex] == OBJECT_TARGET) then
            call IssueTargetOrderById(orderedUnit, trackOrders[unitIndex], trackTargets[unitIndex])
        
        //POINT ORDER
        elseif(trackOrderTypes[unitIndex] == POINT_TARGET) then
            call IssuePointOrderByIdLoc(orderedUnit, trackOrders[unitIndex], trackPoints[unitIndex])
        
        //BUILD ORDER
        elseif(trackOrderTypes[unitIndex] == BUILD_TARGET) then
            call IssueBuildOrderById(orderedUnit, trackOrders[unitIndex], GetLocationX(trackPoints[unitIndex]), GetLocationY(trackPoints[unitIndex]))
      
        //NOTARGET ORDER
        elseif(trackOrderTypes[unitIndex] == NOTARGET_TARGET) then
            call IssueImmediateOrderById(orderedUnit, trackOrders[unitIndex])
            
        endif
        
    endif
endfunction

//BUILD SET SWITCH
function Advanced_Build takes unit caster, integer buildtype returns nothing
    //VARS
    local integer new_builder_id = 0
    local unit new_builder = null
    local player p = GetOwningPlayer(caster)
    
    //SET NEW BUILDER ID
    if buildtype == cgm_HADVBUILD then
        set new_builder_id = cgm_ADVPEASANT
    elseif buildtype == cgm_OADVBUILD then
        set new_builder_id = cgm_ADVPEON
    elseif buildtype == cgm_HSTDBUILD then
        set new_builder_id = cgm_PEASANT
    else
        set new_builder_id = cgm_PEON
    endif
   
    //REPLACE WORKER
    if GetUnitTypeId(caster) != new_builder_id then
        set new_builder = ReplaceWorkerWJ( caster, new_builder_id )
        call SelectUnitAddForPlayer( new_builder, p )

    //ALREADY CORRECT WORKER
    else
        set new_builder = caster
    endif
    
    //OPEN BUILD MENU
    call IssueImmediateOrderById( new_builder, BUILD_ORDER )
    //ISSUE PREVIOUS ORDERS
    call Retrieve_Orders(caster, new_builder)
    
    set new_builder = null
    set p = null

endfunction

function Trig_Worker_Advanced_Build_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == cgm_HADVBUILD or GetSpellAbilityId() == cgm_HSTDBUILD or GetSpellAbilityId() == cgm_OADVBUILD or GetSpellAbilityId() == cgm_OSTDBUILD
endfunction

function Trig_Worker_Advanced_Build_Actions takes nothing returns nothing
    call Advanced_Build( GetSpellAbilityUnit(), GetSpellAbilityId() )
endfunction

//===========================================================================
function InitTrig_Worker_Advanced_Build takes nothing returns nothing
    set gg_trg_Worker_Advanced_Build = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Worker_Advanced_Build, EVENT_PLAYER_UNIT_SPELL_CAST )
    call TriggerAddCondition( gg_trg_Worker_Advanced_Build, Condition( function Trig_Worker_Advanced_Build_Conditions ) )
    call TriggerAddAction( gg_trg_Worker_Advanced_Build, function Trig_Worker_Advanced_Build_Actions )
endfunction


Keep track of orders with object target
JASS:
//OBJECT TARGET TRACK
function Order_Tracker_Target takes unit orderedUnit, integer orderId returns nothing
    local integer unitIndex = GetUnitIndex(orderedUnit, null)
    local widget currentTarget = GetOrderTarget()
    
        
    if(unitIndex != -1 and orderId != 0 and currentTarget != null) then
        //SAVE ORDER
        set trackOrders[unitIndex] = orderId
        set trackTargets[unitIndex] = currentTarget
        set trackOrderTypes[unitIndex] = OBJECT_TARGET
        call DisplayTextToForce( GetPlayersAll(), ( "Order Tracker (Target): " + OrderId2StringBJ(trackOrders[unitIndex]) ) )
                
    //UNKNOWN TARGET
    else
        set trackOrders[unitIndex] = 0
        set trackOrderTypes[unitIndex] = NO_TARGET
    
    endif

    set currentTarget = null
endfunction

//TARGET ACTIONS
function Trig_Worker_Order_Tracker_Target_Actions takes nothing returns nothing
    call Order_Tracker_Target( GetTriggerUnit(), GetIssuedOrderId() )
endfunction

//===========================================================================
function InitTrig_Worker_Order_Tracker_Target takes nothing returns nothing
    set gg_trg_Worker_Order_Tracker_Target = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Worker_Order_Tracker_Target, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER )
    call TriggerAddCondition( gg_trg_Worker_Order_Tracker_Target, Condition( function Trig_Worker_Order_Tracker_Conditions ) )
    call TriggerAddAction( gg_trg_Worker_Order_Tracker_Target, function Trig_Worker_Order_Tracker_Target_Actions )
endfunction


Keep track of orders with point target
JASS:
//POINT TARGET TRACK
function Order_Tracker_Point takes unit orderedUnit, integer orderId returns nothing
    local integer unitIndex = GetUnitIndex(orderedUnit, null)
    local string currentOrders = OrderId2String(orderId)
    local string currentUnitType = UnitId2String(orderId)
    
    //VALID ORDER
    if(unitIndex != -1 and (currentOrders != null or currentUnitType != null)) then
        // SAVE TARGET DATA
        set trackOrders[unitIndex] = orderId
        set trackPoints[unitIndex] = GetOrderPointLoc()
    
        // POINT ORDER
        if(currentOrders != null) then
            //SAVE ORDER TYPE
            set trackOrderTypes[unitIndex] = POINT_TARGET
            call DisplayTextToForce( GetPlayersAll(), ( "Order Tracker (Point): " + currentOrders ) )
            
        // BUILD ORDER
        elseif(currentUnitType != null) then
            // SAVE ORDER TYPE
            set trackOrderTypes[unitIndex] = BUILD_TARGET
            call DisplayTextToForce( GetPlayersAll(), ( "Order Tracker (Build): " + currentUnitType ) )
        endif
    
    //INVALID ORDER
    else
        set trackOrders[unitIndex] = 0
        set trackOrderTypes[unitIndex] = NO_TARGET
    endif
    
endfunction

//POINT ACTIONS
function Trig_Worker_Order_Tracker_Point_Actions takes nothing returns nothing
    call Order_Tracker_Point( GetTriggerUnit(), GetIssuedOrderId() )
endfunction

//===========================================================================
function InitTrig_Worker_Order_Tracker_Point takes nothing returns nothing
    set gg_trg_Worker_Order_Tracker_Point = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Worker_Order_Tracker_Point, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER )
    call TriggerAddCondition( gg_trg_Worker_Order_Tracker_Point, Condition( function Trig_Worker_Order_Tracker_Conditions ) )
    call TriggerAddAction( gg_trg_Worker_Order_Tracker_Point, function Trig_Worker_Order_Tracker_Point_Actions )
endfunction


Keep track of orders with no target
JASS:
//NO TARGET TRACK
function Order_Tracker_NoTarget takes unit orderedUnit, integer orderId returns nothing
    local integer unitIndex = GetUnitIndex(orderedUnit, null)

    //VALID ORDER//NOT STOP
    if(unitIndex != -1 and orderId != 0 and orderId != 851972) then
        // SAVE ORDER
        set trackOrders[unitIndex] = orderId
        set trackOrderTypes[unitIndex] = NOTARGET_TARGET
        
    else
        // NO ORDER
        set trackOrders[unitIndex] = 0
        set trackOrderTypes[unitIndex] = NO_TARGET
    endif

    call DisplayTextToForce( GetPlayersAll(), ( "Order Tracker (NoTarget): " + OrderId2StringBJ(trackOrders[unitIndex]) ) )
    
endfunction

//NO TARGET ACTIONS
function Trig_Worker_Order_Tracker_NoTarget_Actions takes nothing returns nothing
    call Order_Tracker_NoTarget( GetTriggerUnit(), GetIssuedOrderId() )
endfunction

//===========================================================================
function InitTrig_Worker_Order_Tracker_NoTarget takes nothing returns nothing
    set gg_trg_Worker_Order_Tracker_NoTarget = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Worker_Order_Tracker_NoTarget, EVENT_PLAYER_UNIT_ISSUED_ORDER )
    call TriggerAddCondition( gg_trg_Worker_Order_Tracker_NoTarget, Condition( function Trig_Worker_Order_Tracker_Conditions ) )
    call TriggerAddAction( gg_trg_Worker_Order_Tracker_NoTarget, function Trig_Worker_Order_Tracker_NoTarget_Actions )
endfunction

I'll update this post as I work on it, any feedback/suggestions would be very appreciated :)
 

Attachments

  • Advanced Build Test 2.w3x
    30 KB · Views: 62
Last edited:
Level 29
Joined
Oct 24, 2012
Messages
6,543
Use hidden tags.
Get rid of the BJs in your scripts. ( The red text) Check out my tutorial Converting GUI to efficient JASS ( its in my sig)

For the gold and lumber that is lost you can use my resources it gets items and units gold and lumber costs. It also gets food costs for units.

-auto-stop after order finished isn't saved

for the above try pausing unit, order it to stop / cancel, then unpause unit.

-can only queue builder orders by using the real Build button :(

Try checking using the units build order. This may require using a unit group and looping through but I'm not really sure. Here is a link to the order ids.
http://www.thehelper.net/threads/order-ids.148097/

Can you explain your other problems a bit more so i can try to help.

Edit: Also notice that all local handles have to be nulled.
JASS:
function ReplaceWorkerWJ takes unit builder, integer newUnitId returns unit
    local player p = GetOwningPlayer(builder) //player
    local real x = GetUnitX(builder) //x coord
    local real y = GetUnitY(builder) //y coord
    local real f = GetUnitFacing(builder) //angle
    local unit newUnit //new unit
    local real r = GetUnitState(builder, UNIT_STATE_LIFE) //state

    //replace and return unit
    call RemoveUnit(builder)
    set newUnit = CreateUnit(p, newUnitId, x, y, f)
    call SetUnitState(newUnit, UNIT_STATE_LIFE, r)
    set p = null
    return newUnit
endfunction
JASS:
function ReplaceWorkerWJ takes unit builder, integer newUnitId returns unit
    local player p = GetOwningPlayer(builder) //player
    local real x = GetUnitX(builder) //x coord
    local real y = GetUnitY(builder) //y coord
    local real f = GetUnitFacing(builder) //angle
    local real r = GetUnitState(builder, UNIT_STATE_LIFE) //state

    //replace and return unit
    call RemoveUnit(builder)
    set builder = CreateUnit(p, newUnitId, x, y, f)
    call SetUnitState(builder, UNIT_STATE_LIFE, r)
    set p = null
    return builder
endfunction
I reuse the unit variable that does not leak so that it won't leak in a local in your script.
 
Level 7
Joined
Mar 6, 2006
Messages
282
Have you thought about building the advanced structures with those abilties that build stuff, like "Build Tiny Farm". You could put them all in a spellbook and just build with those, then trigger the resource deductions.

Another idea is to base each building from Channel, and make it have a Targeting Image. The downside is, you can't see the building on your cursor when you're placing it, you'd just see that AoE image.

This one is more complex and probably has problems, but:

Another idea is to not replace the worker at all, but when you click to build an advanced structure, it selects a temporary worker that can build those structures. Then once a structure is placed to build (catch it with the event Point Order), order the REAL worker to move to that spot, and make a dummy worker build the structure instead, and remove the real worker so it looks like he's building it.
 
Thanks for the feedback!

Use hidden tags.
Get rid of the BJs in your scripts. ( The red text) Check out my tutorial Converting GUI to efficient JASS ( its in my sig)
I've left a few that add necessary functionality (like TriggerRegisterAnyUnitEventBJ), is that alright or should I loop through the players (in that example) myself?

For the gold and lumber that is lost you can use my resources it gets items and units gold and lumber costs. It also gets food costs for units.


Try checking using the units build order. This may require using a unit group and looping through but I'm not really sure. Here is a link to the order ids.
http://www.thehelper.net/threads/order-ids.148097/
Thanks, I'll check it out soon!

-auto-stop after order finished isn't saved

for the above try pausing unit, order it to stop / cancel, then unpause unit.
I don't know how the catch the event in the first place, it doesn't seem to fire any. Only manual or triggered Stop orders do.


Edit: Also notice that all local handles have to be nulled. I reuse the unit variable that does not leak so that it won't leak in a local in your script.
I'm confused, why don't x, y, f, r have to be nulled?
Edit: hold on, I guess handles are things like player, unit, etc.?

Other problems:

-The repair animation isn't played when already repairing, and clicking the already active build button:
When I use the Build ability for the Build set which is already activated, while the worker is repairing, the repair animation will stop.
I'm guessing this can be fixed by resetting the unit's animation, or queueing the repair animation at the appropriate time, I haven't tried yet.

-Selecting across screen is now divided between basic and advanced workers:
There are now two different unit-types: Peasant/Peon and Advanced Peasant/Peon.
As a result, when you double-click (or ctrl+click) a unit (select across screen), only either the basic or the advanced workers are selected, instead of all workers.

-switch building style for peon when is constructing is delayed until peon cancels/finishes construction:
When a peon is constructing a building (i.e. it's inside the building), when you use the Build ability, it will be delayed until the peon is free again.
I've noticed that the same happens when any worker is inside a gold mine.


Thanks!


Edit:
Have you thought about building the advanced structures with those abilties that build stuff, like "Build Tiny Farm". You could put them all in a spellbook and just build with those, then trigger the resource deductions.

Another idea is to base each building from Channel, and make it have a Targeting Image. The downside is, you can't see the building on your cursor when you're placing it, you'd just see that AoE image.

This one is more complex and probably has problems, but:

Another idea is to not replace the worker at all, but when you click to build an advanced structure, it selects a temporary worker that can build those structures. Then once a structure is placed to build (catch it with the event Point Order), order the REAL worker to move to that spot, and make a dummy worker build the structure instead, and remove the real worker so it looks like he's building it.

I was actually doing the Build Tiny X thing just now. I had it working perfectly, by triggering a Build Structure order when a certain Build Tiny spell was cast.
Then I added more buildings to the Spell Book, and I noticed that every spell just builds the last one in the list, even without triggers :( I have no idea why.

It would be perfect otherwise.

I'm not sure using Channel like that has any particular advantages, but I'll keep it in mind.

The third one sounds interesting, it could avoid many of the other problems.
I could create an invisible worker, move it to the build location, order the real worker to move that spot, and then order the hidden worker to build.
For humans I'd then just have to remove the invisible one and order the real one to "repair". For orcs I'd just hide the real worker, wait for construction to be finished/cancelled, and then swap the two.


Edit: there's something weird with my installation of WC3. Even stock maps have something weird happening at Init, it skips press key event and I can see a move order being executed on the map right at the start, even though nothing is being ordered. o_O
 
Level 7
Joined
Mar 6, 2006
Messages
282
Reals don't need to be nulled.

it skips press key event

What do you mean by that?

I can see a move order being executed on the map right at the start

That's just what happens when you start a game. It's not an order, it's just the cursor model being preloaded or something (guess). It always happens at (0,0) coordinates at game start.


Edit:

I was actually doing the Build Tiny X thing just now. I had it working perfectly, by triggering a Build Structure order when a certain Build Tiny spell was cast.
Then I added more buildings to the Spell Book, and I noticed that every spell just builds the last one in the list, even without triggers :( I have no idea why.

If the "Build Tiny Structure" thing is working, then I'd definitely do that over the third option.

For your problem:

Make sure you're setting "Data - Unit Created (per player race) - " to the corresponding structure in each spell.

You may need to base each building off a different "Build Tiny" spell. Although you can't see it, they probably have the same order ID, which is why they all build the same thing (because it keeps casting the same spell).

So you have 8 different "Build Tiny" spells to base each building from:

Altar
Barracks
Blacksmith
Castle
Farm
Scout Tower
Great Hall
Lumber Mill
 
Last edited:
What do you mean by that?

That's just what happens when you start a game. It's not an order, it's just the cursor model being preloaded or something (guess). It always happens at (0,0) coordinates at game start.

Normally before a game starts, you have to press a key to continue. For some reason, it skips right to the game since today. Or am I being stupid and is that only in multiplayer games and campaigns :D
Never the noticed the cursor thing before today :p


If the "Build Tiny Structure" thing is working, then I'd definitely do that over the third option.

For your problem:

Make sure you're setting "Data - Unit Created (per player race) - " to the corresponding structure in each spell.

You may need to base each building off a different "Build Tiny" spell. Although you can't see it, they probably have the same order ID, which is why they all build the same thing (because it keeps casting the same spell).

Yeah, it would definitely be the best solution, the only drawbacks are that there's no gold/lumber icons in the tooltip (unless there's a way to do that?), and no Building image thing when queueing build orders.

As for the problem, yeah, I made sure to set the Units created 4x for each spell, and set a different order String.
But even with different spells, it doesn't seem to be working. I'm thinking it has something to do with the spellbook, as it's normally an item ability.


Edit: updated OP, code is much shorter, cleaner, more efficient now
@deathismyfriend, I'm not sure which of your resources I can use? I don't see anything that gets units' carried resources.
I'll probably go a bit deeper into your suggestions tomorrow, fix some more stuff
i'll also try the Build Tiny X thing again, and if it doesnt work the invisible unit thing, tired now :D

thanks for the help!
 
Last edited:
Level 29
Joined
Oct 24, 2012
Messages
6,543
I was referring to this. http://www.hiveworkshop.com/forums/...0-3-a-245280/?prev=status=p&u=deathismyfriend

It gets items and units gold / lumber cost. It also get the food cost from units.

This BJ is fine. TriggerRegisterAnyUnitEventBJ
The others should be removed.

The variables that are handles are all variables except reals, integers, booleans, strings.

Reals don't need to be nulled. Players don't either actually.

Player variable do need to be nulled.
 
Level 7
Joined
Mar 6, 2006
Messages
282
Normally before a game starts, you have to press a key to continue. For some reason, it skips right to the game since today. Or am I being stupid and is that only in multiplayer games and campaigns :D

Correct!

Also, after a quick google, it looks like you can never have more than one "Build Tiny" since they all use the same order ID.

I stole this from another thread though and tested it in your map, seems to work good.

- in trigs, disable your 'Worker Advanced Build'
- in object editor, for peon, reset the 'Structures Built' to default

Then make a new trig and post this in it:

JASS:
function Trig_build_Actions takes nothing returns nothing
    if (GetSpellAbilityId() == cgm_OSTDBUILD) then
        // DISABLE ADVANCED
        call SetPlayerTechMaxAllowed( Player(0), 'ovln', 0)
        call SetPlayerTechMaxAllowed( Player(0), 'owtw', 0)
        call SetPlayerTechMaxAllowed( Player(0), 'obea', 0)
        call SetPlayerTechMaxAllowed( Player(0), 'oalt', 0)
        call SetPlayerTechMaxAllowed( Player(0), 'otto', 0)
        call SetPlayerTechMaxAllowed( Player(0), 'osld', 0)
        // ENABLE STANDARD
        call SetPlayerTechMaxAllowed( Player(0), 'ogre', -1)
        call SetPlayerTechMaxAllowed( Player(0), 'otrb', -1)
        call SetPlayerTechMaxAllowed( Player(0), 'obar', -1)
        call SetPlayerTechMaxAllowed( Player(0), 'ofor', -1)
    else
        // DISABLE STANDARD
        call SetPlayerTechMaxAllowed( Player(0), 'ogre', 0)
        call SetPlayerTechMaxAllowed( Player(0), 'otrb', 0)
        call SetPlayerTechMaxAllowed( Player(0), 'obar', 0)
        call SetPlayerTechMaxAllowed( Player(0), 'ofor', 0)
        // ENABLE ADVANCED
        call SetPlayerTechMaxAllowed( Player(0), 'ovln', -1)
        call SetPlayerTechMaxAllowed( Player(0), 'owtw', -1)
        call SetPlayerTechMaxAllowed( Player(0), 'obea', -1)
        call SetPlayerTechMaxAllowed( Player(0), 'oalt', -1)
        call SetPlayerTechMaxAllowed( Player(0), 'otto', -1)
        call SetPlayerTechMaxAllowed( Player(0), 'osld', -1)
    endif
    
    call IssueImmediateOrderById( GetTriggerUnit(), BUILD_ORDER )
endfunction

function Trig_build_Conditions takes nothing returns boolean
    if (GetSpellAbilityId() == cgm_OSTDBUILD) or (GetSpellAbilityId() == cgm_OADVBUILD) then
        return true
    endif

    return false
endfunction

function InitTrig_build takes nothing returns nothing
    set gg_trg_build = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_build, EVENT_PLAYER_UNIT_SPELL_CAST )
    call TriggerAddCondition( gg_trg_build, Condition( function Trig_build_Conditions ) )
    call TriggerAddAction( gg_trg_build, function Trig_build_Actions )
endfunction

Obviously that's just for testing, but you can see how it performs.


deathismyfriend said:
Player variable do need to be nulled.

When googling that, I came across a lot of mixed answers on that. I'll take your word for it though and edit my post so this isn't relayed lol.
 
^Thanks, I already tried something similar with triggers, see jhere.

I think I'm going with a different approach, it's so simple it's silly. You actually suggested it already.

I've made an edited Channel ability for each building, which I put into a spellbook which is the Advanced Structure button.
I put the advanced buildings at the bottom of the Peasant's structures list, and set their icons to 3, 2.
Then I made a filler building for each empty spot in the basic build menu, with a blank icon, an empty tooltip, and itself as a tech requirement (put it between the basic and advanced buildings in the list).

The only downside is that you're stuck with the targeting image until you actually give the build order, and all queued orders are cancelled once an avanced build order is executed. Also, you have to manually put the gold/lumber cost in the tooltip with [G] and [L], unless there's a way to put icons in tooltips.
But in terms of functionality, it's definitely the best solution. Least amount of bugs, least complex.
 

Attachments

  • advanced build test 3.w3x
    19.8 KB · Views: 58
Last edited:
Status
Not open for further replies.
Top