- Joined
- Nov 2, 2004
- Messages
- 1,921
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
Build set switcher
Keep track of orders with object target
Keep track of orders with point target
Keep track of orders with no target
I'll update this post as I work on it, any feedback/suggestions would be very appreciated
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
Last edited: