• 🏆 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!

[vJASS] CG Building Costs + Insta Build

Level 14
Joined
Nov 20, 2005
Messages
1,156
Ever made a TD, and wanted to have a sell tower option, but wanted it to be low maintainance? Wanted to have an easy way to get the cost of the tower? Now you can, with this system.

GetUpgradeGoldCost returns the cost of the last upgrade, GetPreviousGoldCost is that of all previous expenditure APART from the last one, and GetTotalGoldCost is all gold spent. It only works for buildings that have been built at least once in game - not those that are created by triggers.

Does NOT work with lumber costing towers. As in, it might break. If you want me to change it to work with lumber as well, then drop me a PM.

Includes insta-build. If you don't want that, then just set the constant to false.

Anyway, it's easy to use, and requires NO setup - just plug and play! No need for any more laborious working. The only exception is if you are using food in your map for towers. If you are, then add into the IncrementFood function a create unit which creates a dummy unit that provides more food than any tower uses. You can then remove it in the DecrementFood function. Since they take place instantaneously, you can use a global to carry the unit across (or just imbed it straight into the code, if you know JASS; that would be more efficient).

NB: If you are using another gamecache, then you may as well merge them. If it is called gc, then great, simple remove the global for it from this library, and also the init call for it at the bottom. If not, remove the global and init call, and then do a fine and replay for gc with whatever your game cache variable is.

If you feel this is beyond you, then adding 'private' to the line 'gamecache gc', to give 'private gamecache gc', will solve the problem, but won't be quite so memory efficient.

NB2: You may encounter problems with some pathing problems, I'm not sure. You SHOULDN'T with this version, but then again, WC3 can work in strange and wonderful ways.


Updated. Now fully supports lumber and food, includes functions to get them. Fixed possible bugs in the original release, some that could cause critical errors due to Blizzard bugs (I've run into so many of them on this script it is rediculous).

Updated again. Should fix bug with refunding first building cost (unless of course that was brought about by a patch, in which case it won't). Also adds support for getting unit costs.

JASS:
library CGUnitCost initializer Init

globals // This is the only code you should touch. Touch anything else and it's at your own risk.
    private constant boolean INSTA_BUILD = true //if you want it to instabuild and upgrade; note that upgrades will keep the same scaling size, regardless of object editor settings (native bug)
    private constant string GAME_CACHE_STRING = "cgbuildingblargh" // just make sure this doesn't collide with anything
    
    private constant real DISTANCE_CHECK_RANGE = 150. // max distance a worker might be able to move and start building in 0.05 seconds.
    // Also max distance where a worker may get in the way of the building, and thus have to move out of its own way; bigger buildings than a castle may need a higher value
    // Designed to stop unnecessary pausing (which can interrupt UI stuff) while maintaining safety. 100 should be fine.
    
    private gamecache gc // don't touch this.
endglobals

// These can be used to get the values. NB: ONLY use on buildings that have been built in game.

function GetUpgradeGoldCost takes integer id returns integer // returns the cost of the last upgrade, OR the cost of building (if not upgraded)
    return GetStoredInteger(gc, "CGupgradeGold", I2S(id))
endfunction
function GetTotalGoldCost takes integer id returns integer // Cost of last and all previous
    return GetStoredInteger(gc, "CGtotalGold", I2S(id))
endfunction
function GetPreviousGoldCost takes integer id returns integer // Cost of gold/lumber/food spent not counting the last one (bit misleading name)
    return GetStoredInteger(gc, "CGpreviousGold", I2S(id))
endfunction

function GetUpgradeLumberCost takes integer id returns integer // returns the cost of the last upgrade, OR the cost of building (if not upgraded)
    return GetStoredInteger(gc, "CGupgradeLumber", I2S(id))
endfunction
function GetTotalLumberCost takes integer id returns integer // Cost of last and all previous
    return GetStoredInteger(gc, "CGtotalLumber", I2S(id))
endfunction
function GetPreviousLumberCost takes integer id returns integer // Cost of gold/lumber/food spent not counting the last one (bit misleading name)
    return GetStoredInteger(gc, "CGpreviousLumber", I2S(id))
endfunction

function GetUpgradeFoodCost takes integer id returns integer // returns the cost of the last upgrade, OR the cost of building (if not upgraded)
    return GetStoredInteger(gc, "CGupgradeFood", I2S(id))
endfunction
function GetTotalFoodCost takes integer id returns integer // Cost of last and all previous
    return GetFoodUsed(id)
endfunction
function GetPreviousFoodCost takes integer id returns integer // Cost of gold/lumber/food spent not counting the last one (bit misleading name)
    return GetStoredInteger(gc, "CGpreviousFood", I2S(id))
endfunction

// Don't mess with this stuff unless you want to spend hours painstakingly debugging. WC3 is full of bugs.

private function TimerAttach takes timer t, real dur, real val, code func returns nothing
    call TimerStart(t, val, false, null)
    call PauseTimer(t)
    call TimerStart(t, dur, false, func)
endfunction

globals
    private unit array built
    private unit array ordered
    private integer array orderedID
    private real array orderedX
    private real array orderedY
    private integer c = 1
    private integer o = 1
    private trigger trg_order
    private trigger trg_upgrade
    private trigger trg_train
    
    private integer q = 1
    private unit array qUnit
    private integer array qId
    private real array qX
    private real array qY
    
    private group g
    
    private real distanceCheck
    
    private integer loopInt
    private unit array listU
    private boolean array listSelected
endglobals

private function UpgradeEx takes nothing returns nothing
    local unit u = GetTriggerUnit()
    local player p = GetOwningPlayer(u)
    local integer id = GetUnitTypeId(u)
    local integer gold = GetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD)
    local integer lumber = GetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER)
    local integer iG
    local integer iL
    local integer iF
    local integer costPrevious
    local string s = I2S(id)
    local integer idOld

    call DisableTrigger(trg_upgrade)
    
    call IssueImmediateOrderById(u, 851976)

    call SetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD, 99999999)
    call SetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER, 99999999)
    set iG = GetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD) // incase a limit exists
    set iL = GetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER)
    
    set idOld = GetUnitTypeId(u)
    
    call IssueImmediateOrderById(u, id)
    
    set iG = iG - GetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD)
    set iL = iL - GetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER)
    
    call StoreInteger(gc, "CGupgradeGold", s, iG)
    set costPrevious = GetTotalGoldCost(idOld)
    call StoreInteger(gc, "CGpreviousGold", s, costPrevious)
    call StoreInteger(gc, "CGtotalGold", s, iG+costPrevious)
    
    call StoreInteger(gc, "CGupgradeLumber", s, iL)
    set costPrevious = GetTotalLumberCost(idOld)
    call StoreInteger(gc, "CGpreviousLumber", s, costPrevious)
    call StoreInteger(gc, "CGtotalLumber", s, iL+costPrevious)
    
    set iF = GetFoodUsed(id)
    set costPrevious = GetFoodUsed(idOld)
    call StoreInteger(gc, "CGpreviousFood", s, costPrevious)
    call StoreInteger(gc, "CGupgradeFood", s, iF-costPrevious)

    call SetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD, gold)
    call SetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER, lumber)
    
    call EnableTrigger(trg_upgrade)
    
    set u = null
endfunction

private function Upgrade takes nothing returns boolean
    if not(HaveStoredInteger(gc, "CGtotalGold", I2S(GetUnitTypeId(GetTriggerUnit())))) then
        call UpgradeEx()
    endif
    if INSTA_BUILD then
        call UnitSetUpgradeProgress(GetTriggerUnit(), 100)
    endif
    return false
endfunction

private function BuildEx2 takes nothing returns nothing
    local unit u = built[R2I(TimerGetRemaining(GetExpiredTimer())+0.9)]
    local integer i = GetPlayerState(GetOwningPlayer(u), PLAYER_STATE_RESOURCE_FOOD_CAP)
    call UnitSetConstructionProgress(u, 100)
    call SetPlayerState(GetOwningPlayer(u), PLAYER_STATE_RESOURCE_FOOD_CAP, i + GetUnitFoodMade(u)) //did I mention WC3 sucks? Bug with double food sometimes.
    set u = null
    call DestroyTimer(GetExpiredTimer())
endfunction

private function Build takes nothing returns boolean
    //if INSTA_BUILD then // this isn't needed, but I left it as a reminder this code ONLY runs if INSTA_BUILD is set to true.
        set built[c] = GetConstructingStructure()
        call TimerAttach(CreateTimer(), 0.1, c, function BuildEx2)
        if c >= 254 then
            set c = 1
        else
            set c = c + 1
        endif
    //endif
    return false
endfunction

private function OrderEx4 takes nothing returns nothing
    call RemoveUnit(ordered[R2I(TimerGetRemaining(GetExpiredTimer())+0.9)])
    call DestroyTimer(GetExpiredTimer())
endfunction

private function OrderEx3 takes nothing returns nothing
    local integer i = R2I(TimerGetRemaining(GetExpiredTimer())+0.9)
    local player p = GetOwningPlayer(ordered[i])
    
    call DisableTrigger(trg_order)
    
    call PauseUnit(ordered[i], true)
    call IssueImmediateOrder(ordered[i], "stop")
    call PauseUnit(ordered[i], false)
    
    call TimerAttach(GetExpiredTimer(), 0.1, i, function OrderEx4)
    call EnableTrigger(trg_order)
endfunction

private function EnumEx takes nothing returns boolean
    local unit u = GetFilterUnit()
    set listU[loopInt] = u
    set listSelected[loopInt] = IsUnitSelected(u, GetLocalPlayer())
    call ShowUnit(u, false)
    set loopInt = loopInt + 1
    set u = null
    return false
endfunction

private function OrderEx2 takes unit t, integer id, real x, real y returns nothing
    local player p = GetOwningPlayer(t)
    local integer uid = GetUnitTypeId(t)
    
    local unit u
    local string s = I2S(id)
    
    local integer gold = GetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD)
    local integer lumber = GetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER)
    
    local integer cap = GetPlayerState(p, PLAYER_STATE_RESOURCE_FOOD_CAP)
    local integer ceiling = GetPlayerState(p, PLAYER_STATE_FOOD_CAP_CEILING)
    local integer food = GetFoodUsed(id)
    
    local integer iG
    local integer iL
    local integer max
    
    call DisableTrigger(trg_order)
    call SetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD, 99999999)
    call SetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER, 99999999)
    
    call SetPlayerState(p, PLAYER_STATE_RESOURCE_FOOD_CAP, 10000)
    call SetPlayerState(p, PLAYER_STATE_FOOD_CAP_CEILING, 10000)
    
    set iG = GetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD) // incase a limit exists
    set iL = GetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER)
    
    set u = CreateUnit(p, uid, 0., 0., 0.)
    call SetUnitUseFood(u, false)
    
    call SetUnitVertexColor(u, 0, 0, 0, 0)
    call UnitAddAbility(u, 'Aloc')
    
    call IssueBuildOrderById(u, id, x, y)
    
    set iG = iG - GetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD)
    set iL = iL - GetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER)
    
    call SetPlayerState(p, PLAYER_STATE_RESOURCE_FOOD_CAP, cap)
    call SetPlayerState(p, PLAYER_STATE_FOOD_CAP_CEILING, ceiling)
    
    call StoreInteger(gc, "CGupgradeFood", s, food)
    
    call StoreInteger(gc, "CGtotalGold", s, iG)
    call StoreInteger(gc, "CGupgradeGold", s, iG)
    call SetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD, gold - iG)
    
    call StoreInteger(gc, "CGtotalLumber", s, iL)
    call StoreInteger(gc, "CGupgradeLumber", s, iL)
    call SetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER, lumber - iL)

    set ordered[o] = u
    
    call TimerAttach(CreateTimer(), 0.05, o, function OrderEx3)
    if o >= 254 then
        set o = 1
    else
        set o = o + 1
    endif
    
    call PauseUnit(u, true)
    call ShowUnit(u, false)
    call PauseUnit(t, false)
    
    call EnableTrigger(trg_order)
    
    set u = null
endfunction

private function OrderEx takes nothing returns nothing
    local integer i = R2I(TimerGetRemaining(GetExpiredTimer())+0.9)
    
    call OrderEx2(ordered[i],orderedID[i], orderedX[i], orderedY[i])
    call DestroyTimer(GetExpiredTimer())
endfunction

private function DistanceShort takes real x1, real x2, real y1, real y2 returns boolean
    local real dx = x1 - x2
    local real dy = y1 - y2
    return dx*dx + dy * dy < distanceCheck
endfunction

private function Order takes nothing returns boolean
    local integer id = GetIssuedOrderId()
    if IsUnitIdType(id, UNIT_TYPE_STRUCTURE) == true and not(HaveStoredInteger(gc, "CGtotalGold", I2S(id))) then
        set ordered[o] = GetTriggerUnit()
        set orderedID[o] = id
        set orderedX[o] = GetOrderPointX()
        set orderedY[o] = GetOrderPointY()
        
        if DistanceShort(GetOrderPointX(), GetUnitX(GetTriggerUnit()), GetOrderPointY(), GetUnitY(GetTriggerUnit())) then
            call PauseUnit(GetTriggerUnit(), true)
        endif
        
        call TimerAttach(CreateTimer(), 0.01, o, function OrderEx)
        if o >= 254 then
            set o = 1
        else
            set o = o + 1
        endif
    endif
    return false
endfunction

private function TrainEx takes nothing returns nothing
    local unit u = GetTriggerUnit()
    local player p = GetOwningPlayer(u)
    local integer id = GetTrainedUnitType()
    local integer gold = GetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD)
    local integer lumber = GetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER)
    local integer iG
    local integer iL
    local string s = I2S(id)

    call DisableTrigger(trg_train)
    
    call IssueImmediateOrderById(u, 851976)

    call SetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD, 99999999)
    call SetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER, 99999999)
    set iG = GetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD) // incase a limit exists
    set iL = GetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER)
    
    call IssueImmediateOrderById(u, id)
    
    set iG = iG - GetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD)
    set iL = iL - GetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER)
    
    call StoreInteger(gc, "CGupgradeGold", s, iG)
    call StoreInteger(gc, "CGtotalGold", s, iG)
    
    call StoreInteger(gc, "CGupgradeLumber", s, iL)
    call StoreInteger(gc, "CGtotalLumber", s, iL)
    
    call StoreInteger(gc, "CGupgradeFood", s, GetFoodUsed(id))

    call SetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD, gold)
    call SetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER, lumber)
    
    call EnableTrigger(trg_train)
    
    set u = null
endfunction

private function Train takes nothing returns boolean
    if not(HaveStoredInteger(gc, "CGtotalGold", I2S(GetTrainedUnitType()))) then
        call TrainEx()
    endif
    return false
endfunction

private function Init takes nothing returns nothing
    local trigger t1 = CreateTrigger()
    local trigger t2 = CreateTrigger()
    local trigger t3 = CreateTrigger()
    local trigger t4 = CreateTrigger()
    local boolexpr bx1 = Condition(function Upgrade)
    local boolexpr bx2 = Condition(function Build)
    local boolexpr bx3 = Condition(function Order)
    local boolexpr bx4 = Condition(function Train)
    local integer i = 0

    loop
        exitwhen i >= 16
        call TriggerRegisterPlayerUnitEvent(t1, Player(i), EVENT_PLAYER_UNIT_UPGRADE_START, null)
        if INSTA_BUILD then
            call TriggerRegisterPlayerUnitEvent(t2, Player(i), EVENT_PLAYER_UNIT_CONSTRUCT_START, null)
        endif
        call TriggerRegisterPlayerUnitEvent(t3, Player(i), EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, null)
        call TriggerRegisterPlayerUnitEvent(t4, Player(i), EVENT_PLAYER_UNIT_TRAIN_START, null)
        set i = i + 1
    endloop

    call TriggerAddCondition(t1, bx1)
    if INSTA_BUILD then
        call TriggerAddCondition(t2, bx2)
    endif
    call TriggerAddCondition(t3, bx3)
    call TriggerAddCondition(t4, bx4)

    set trg_order = t3
    set trg_upgrade = t1
    set trg_train = t4

    set g = CreateGroup()
    
    set distanceCheck = DISTANCE_CHECK_RANGE * DISTANCE_CHECK_RANGE
    
    call FlushGameCache(InitGameCache(GAME_CACHE_STRING))
    set gc = InitGameCache(GAME_CACHE_STRING)
endfunction

endlibrary

JASS:
library BuildingCost initializer Init

globals // This is the only code you should touch. Touch anything else and it's at your own risk.
    private constant boolean INSTA_BUILD = true //if you want it to instabuild
    private constant string GAME_CACHE_STRING = "cgbuildingblargh" // just make sure this doesn't collide with anything
    
    private constant real DUMMY_X = 0. // doesn't really matter what this, it'll work anywhere, BUT
    private constant real DUMMY_Y = 0. // There will be a flicker of a shadow here every first instance of a building; suggest you put it in some trees.
    private constant real DISTANCE_CHECK_RANGE = 100. // max distance a worker might be able to move and start building in 0.05 seconds.
    // Designed to stop unnecessary pausing (which can interrupt UI stuff) while maintaining safety. 100 should be fine.
    
    private gamecache gc = null // don't touch this.
endglobals

// These can be used to get the values. NB: ONLY use on buildings that have been built in game.

function GetUpgradeGoldCost takes integer id returns integer // returns the cost of the last upgrade, OR the cost of building (if not upgraded)
    return GetStoredInteger(gc, "CGupgradeGold", I2S(id))
endfunction
function GetTotalGoldCost takes integer id returns integer // Cost of last and all previous
    return GetStoredInteger(gc, "CGtotalGold", I2S(id))
endfunction
function GetPreviousGoldCost takes integer id returns integer // Cost of gold/lumber/food spent not counting the last one (bit misleading name)
    return GetStoredInteger(gc, "CGpreviousGold", I2S(id))
endfunction

function GetUpgradeLumberCost takes integer id returns integer // returns the cost of the last upgrade, OR the cost of building (if not upgraded)
    return GetStoredInteger(gc, "CGupgradeLumber", I2S(id))
endfunction
function GetTotalLumberCost takes integer id returns integer // Cost of last and all previous
    return GetStoredInteger(gc, "CGtotalLumber", I2S(id))
endfunction
function GetPreviousLumberCost takes integer id returns integer // Cost of gold/lumber/food spent not counting the last one (bit misleading name)
    return GetStoredInteger(gc, "CGpreviousLumber", I2S(id))
endfunction

function GetUpgradeFoodCost takes integer id returns integer // returns the cost of the last upgrade, OR the cost of building (if not upgraded)
    return GetStoredInteger(gc, "CGupgradeFood", I2S(id))
endfunction
function GetTotalFoodCost takes integer id returns integer // Cost of last and all previous
    return GetFoodUsed(id)
endfunction
function GetPreviousFoodCost takes integer id returns integer // Cost of gold/lumber/food spent not counting the last one (bit misleading name)
    return GetStoredInteger(gc, "CGpreviousFood", I2S(id))
endfunction

// Don't mess with this stuff unless you want to spend hours painstakingly debugging. WC3 is full of bugs.

private function TimerAttach takes timer t, real dur, real val, code func returns nothing
    call TimerStart(t, val, false, null)
    call PauseTimer(t)
    call TimerStart(t, dur, false, func)
endfunction

globals
    private unit array built
    private unit array ordered
    private integer array orderedID
    private real array orderedX
    private real array orderedY
    private integer c = 1
    private integer o = 1
    private trigger trg_order
    private trigger trg_upgrade
    
    private integer q = 1
    private unit array qUnit
    private integer array qId
    private real array qX
    private real array qY
    
    private group g
    
    private real distanceCheck
    
    private integer loopInt
    private unit array listU
    private boolean array listSelected
endglobals

private function UpgradeEx takes nothing returns nothing
    local unit u = GetTriggerUnit()
    local player p = GetOwningPlayer(u)
    local integer id = GetUnitTypeId(u)
    local integer gold = GetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD)
    local integer lumber = GetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER)
    local integer iG
    local integer iL
    local integer iF
    local integer costPrevious
    local string s = I2S(id)
    local integer idOld

    call DisableTrigger(trg_upgrade)
    
    call IssueImmediateOrderById(u, 851976)

    call SetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD, 99999999)
    call SetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER, 99999999)
    set iG = GetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD) // incase a limit exists
    set iL = GetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER)
    
    set idOld = GetUnitTypeId(u)
    
    call IssueImmediateOrderById(u, id)
    
    set iG = iG - GetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD)
    set iL = iL - GetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER)
    
    call StoreInteger(gc, "CGupgradeGold", s, iG)
    set costPrevious = GetTotalGoldCost(idOld)
    call StoreInteger(gc, "CGpreviousGold", s, costPrevious)
    call StoreInteger(gc, "CGtotalGold", s, iG+costPrevious)
    
    call StoreInteger(gc, "CGupgradeLumber", s, iL)
    set costPrevious = GetTotalLumberCost(idOld)
    call StoreInteger(gc, "CGpreviousLumber", s, costPrevious)
    call StoreInteger(gc, "CGtotalLumber", s, iL+costPrevious)
    
    set iF = GetFoodUsed(id)
    set costPrevious = GetFoodUsed(idOld)
    call StoreInteger(gc, "CGpreviousFood", s, costPrevious)
    call StoreInteger(gc, "CGupgradeFood", s, iF-costPrevious)

    call SetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD, gold)
    call SetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER, lumber)
    
    call EnableTrigger(trg_upgrade)
    
    set u = null
endfunction

private function Upgrade takes nothing returns boolean
    if not(HaveStoredInteger(gc, "CGtotalGold", I2S(GetUnitTypeId(GetTriggerUnit())))) then
        call UpgradeEx()
    endif
    if INSTA_BUILD then
        call UnitSetUpgradeProgress(GetTriggerUnit(), 100)
    endif
    return false
endfunction

private function BuildEx2 takes nothing returns nothing
    local unit u = built[R2I(TimerGetRemaining(GetExpiredTimer())+0.9)]
    local integer i = GetPlayerState(GetOwningPlayer(u), PLAYER_STATE_RESOURCE_FOOD_CAP)
    call UnitSetConstructionProgress(u, 100)
    call SetPlayerState(GetOwningPlayer(u), PLAYER_STATE_RESOURCE_FOOD_CAP, i + GetUnitFoodMade(u)) //did I mention WC3 sucks? Bug with double food sometimes.
    set u = null
    call DestroyTimer(GetExpiredTimer())
endfunction

private function Build takes nothing returns boolean
    //if INSTA_BUILD then // this isn't needed, but I left it as a reminder this code ONLY runs if INSTA_BUILD is set to true.
        set built[c] = GetConstructingStructure()
        call TimerAttach(CreateTimer(), 0.1, c, function BuildEx2)
        if c >= 254 then
            set c = 1
        else
            set c = c + 1
        endif
    //endif
    return false
endfunction

private function OrderEx4 takes nothing returns nothing
    call RemoveUnit(ordered[R2I(TimerGetRemaining(GetExpiredTimer())+0.9)])
    call DestroyTimer(GetExpiredTimer())
endfunction

private function OrderEx3 takes nothing returns nothing
    local integer i = R2I(TimerGetRemaining(GetExpiredTimer())+0.9)
    local player p = GetOwningPlayer(ordered[i])
    local integer gold = GetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD)
    local integer lumber = GetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER)
    
    call DisableTrigger(trg_order)
    
    call PauseUnit(ordered[i], true)
    call IssueImmediateOrder(ordered[i], "stop")
    call PauseUnit(ordered[i], false)
    
    call SetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD, gold)
    call SetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER, lumber)
    
    call TimerAttach(GetExpiredTimer(), 0.1, i, function OrderEx4)
    call EnableTrigger(trg_order)
endfunction

private function EnumEx takes nothing returns boolean
    local unit u = GetFilterUnit()
    set listU[loopInt] = u
    set listSelected[loopInt] = IsUnitSelected(u, GetLocalPlayer())
    call ShowUnit(u, false)
    set loopInt = loopInt + 1
    set u = null
    return false
endfunction

private function OrderEx2 takes unit t, integer id, real x, real y returns nothing
    local player p = GetOwningPlayer(t)
    local integer uid = GetUnitTypeId(t)
    
    local unit u
    local string s = I2S(id)
    
    local integer gold = GetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD)
    local integer lumber = GetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER)
    
    local integer cap = GetPlayerState(p, PLAYER_STATE_RESOURCE_FOOD_CAP)
    local integer ceiling = GetPlayerState(p, PLAYER_STATE_FOOD_CAP_CEILING)
    local integer food = GetFoodUsed(id)
    
    local integer iG
    local integer iL
    local integer max
    
    call DisableTrigger(trg_order)
    call SetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD, 99999999)
    call SetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER, 99999999)
    
    call SetPlayerState(p, PLAYER_STATE_RESOURCE_FOOD_CAP, 10000)
    call SetPlayerState(p, PLAYER_STATE_FOOD_CAP_CEILING, 10000)
    
    set iG = GetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD) // incase a limit exists
    set iL = GetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER)
    
    set u = CreateUnit(p, uid, DUMMY_X, DUMMY_Y, 0.)
    call SetUnitVertexColor(u, 0, 0, 0, 0)
    call UnitAddAbility(u, 'Aloc')
    
    call IssueBuildOrderById(u, id, x, y)
    
    set iG = iG - GetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD)
    set iL = iL - GetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER)
    
    call SetPlayerState(p, PLAYER_STATE_RESOURCE_FOOD_CAP, cap)
    call SetPlayerState(p, PLAYER_STATE_FOOD_CAP_CEILING, ceiling)
    
    call StoreInteger(gc, "CGupgradeFood", s, food)
    
    call StoreInteger(gc, "CGtotalGold", s, iG)
    call StoreInteger(gc, "CGupgradeGold", s, iG)
    call SetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD, gold)
    
    call StoreInteger(gc, "CGtotalLumber", s, iL)
    call StoreInteger(gc, "CGupgradeLumber", s, iL)
    call SetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER, lumber)

    set ordered[o] = u
    
    call TimerAttach(CreateTimer(), 0.05, o, function OrderEx3)
    if o >= 254 then
        set o = 1
    else
        set o = o + 1
    endif
    
    call PauseUnit(u, true)
    call PauseUnit(t, false)
    
    call EnableTrigger(trg_order)
    
    set u = null
endfunction

private function OrderEx takes nothing returns nothing
    local integer i = R2I(TimerGetRemaining(GetExpiredTimer())+0.9)
    
    call OrderEx2(ordered[i],orderedID[i], orderedX[i], orderedY[i])
    call DestroyTimer(GetExpiredTimer())
endfunction

private function DistanceShort takes real x1, real x2, real y1, real y2 returns boolean
    local real dx = x1 - x2
    local real dy = y1 - y2
    return dx*dx + dy * dy < distanceCheck
endfunction

private function Order takes nothing returns boolean
    local integer id = GetIssuedOrderId()
    if IsUnitIdType(id, UNIT_TYPE_STRUCTURE) == true and not(HaveStoredInteger(gc, "CGtotalGold", I2S(id))) then
        set ordered[o] = GetTriggerUnit()
        set orderedID[o] = id
        set orderedX[o] = GetOrderPointX()
        set orderedY[o] = GetOrderPointY()
        
        if DistanceShort(GetOrderPointX(), GetUnitX(GetTriggerUnit()), GetOrderPointY(), GetUnitY(GetTriggerUnit())) then
            call PauseUnit(GetTriggerUnit(), true)
        endif
        
        call TimerAttach(CreateTimer(), 0.01, o, function OrderEx)
        if o >= 254 then
            set o = 1
        else
            set o = o + 1
        endif
    endif
    return false
endfunction

private function Init takes nothing returns nothing
    local trigger t1 = CreateTrigger()
    local trigger t2 = CreateTrigger()
    local trigger t3 = CreateTrigger()
    local boolexpr bx1 = Condition(function Upgrade)
    local boolexpr bx2 = Condition(function Build)
    local boolexpr bx3 = Condition(function Order)
    local integer i = 0

    loop
        exitwhen i >= 12
        call TriggerRegisterPlayerUnitEvent(t1, Player(i), EVENT_PLAYER_UNIT_UPGRADE_START, null)
        if INSTA_BUILD then
            call TriggerRegisterPlayerUnitEvent(t2, Player(i), EVENT_PLAYER_UNIT_CONSTRUCT_START, null)
        endif
        call TriggerRegisterPlayerUnitEvent(t3, Player(i), EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, null)
        set i = i + 1
    endloop

    call TriggerAddCondition(t1, bx1)
    if INSTA_BUILD then
        call TriggerAddCondition(t2, bx2)
    endif
    call TriggerAddCondition(t3, bx3)

    set trg_order = t3
    set trg_upgrade = t1

    set t1 = null
    set t2 = null
    set t3 = null

    set g = CreateGroup()
    
    set distanceCheck = DISTANCE_CHECK_RANGE * DISTANCE_CHECK_RANGE
    
    call FlushGameCache(InitGameCache(GAME_CACHE_STRING))
    set gc = InitGameCache(GAME_CACHE_STRING)
endfunction

endlibrary
 
Last edited:
Level 40
Joined
Dec 14, 2005
Messages
10,532
Does NOT work with lumber costing towers. As in, it might break.
I tried it out with a Farm and a Scout Tower, just out of curiosity, and it worked fine, so unless you mean it has a chance to break, then it seems okay. (I see the theory as to why it may cause problems, but in practice it doesn't seem causable, as the order even has a delay before construction if the tower is placed directly on top of the builder)

Includes insta-build. If you don't want that, then just set the constant to false.
I would suggest leaving it as false then allowing them to set it to true - I wouldn't be surprised if more people who use this won't want it than will.

If you feel this is beyond you, then adding 'private' to the line 'gamecache gc', to give 'private gamecache gc', will solve the problem, but won't be quite so memory efficient.
To be honest, I doubt GUI users will be worrying too much about memory efficiency :p

About the code -


Nothing major, just some minor tweaks that you could do if you care...

In Init, you shouldn't need to null t1/t2/t2 (as they are never destroyed anyways) (For the same reason, using boolexprs seems tedious, and it defeats the purpose of doing so (to my knowledge) if you are not destroying the trigger anyways...)

In Order, I'm pretty sure the rule for buginess with IsUnitType only occurs if the function in question is the last term of the boolean, and only if it is used directly in a boolexpr.

As for "CGtotalcost", is there actually a need of storing that seperately from "CGupgradecost"? It is a tiny bit more efficient, I guess, but so marginal (since I doubt they will be reading the gold cost very rapidly, and if they are, they should store it anyways, even assuming that 2 gamecache calls instead of 1 would tip their code over the edge...) that it seems like a waste of time to store.



Just out of curiosity, what is 851976 (as it has no orderstring) - Cancel (It seems so in the code)?

Also, I was wondering, what is the theory between TimerAttach? When the non-looping timer expires, does it revert to its "old" duration?

Anyways, approved.
 
Level 14
Joined
Nov 20, 2005
Messages
1,156
I tried it out with a Farm and a Scout Tower, just out of curiosity, and it worked fine, so unless you mean it has a chance to break, then it seems okay. (I see the theory as to why it may cause problems, but in practice it doesn't seem causable, as the order even has a delay before construction if the tower is placed directly on top of the builder)

If you don't have enough lumber to make another one, then the system will break, I think. I will, however, make it work with lumber at some point, and I think I have an idea on how to avoid needing to increment/decrement food, as well, but blizzard have a tendancy to throw up random suprises.

I would suggest leaving it as false then allowing them to set it to true - I wouldn't be surprised if more people who use this won't want it than will.

Blargh, up to them. I suspect this will mainly be used in TDs, and most TDs should have insta build imo.

To be honest, I doubt GUI users will be worrying too much about memory efficiency :p

True.

Nothing major, just some minor tweaks that you could do if you care...

In Init, you shouldn't need to null t1/t2/t2 (as they are never destroyed anyways) (For the same reason, using boolexprs seems tedious, and it defeats the purpose of doing so (to my knowledge) if you are not destroying the trigger anyways...)

True, nulling isn't necessary, but meh. Boolexprs are, however, faster.

In Order, I'm pretty sure the rule for buginess with IsUnitType only occurs if the function in question is the last term of the boolean, and only if it is used directly in a boolexpr.

Better safe than sorry.

As for "CGtotalcost", is there actually a need of storing that seperately from "CGupgradecost"? It is a tiny bit more efficient, I guess, but so marginal (since I doubt they will be reading the gold cost very rapidly, and if they are, they should store it anyways, even assuming that 2 gamecache calls instead of 1 would tip their code over the edge...) that it seems like a waste of time to store.

The storing takes place once. The reading can take place a hundred times.

Just out of curiosity, what is 851976 (as it has no orderstring) - Cancel (It seems so in the code)?

It's cancel, correct.

Also, I was wondering, what is the theory between TimerAttach? When the non-looping timer expires, does it revert to its "old" duration?

I don't really know, I need to investigate it now, since it seems to be displaying some odd behaviour at low values (different to normal). It seems to be 100% reliable behaviour, but not entirely consistent. In some way, though, it doesn't fully update stuff if started when paused if in the same moment as it was initially started.

I've been meaning to properly document it since I discovered it, but I'm busy atm. Will do eventually.

Anyways, approved.

Thank you.
 
Level 40
Joined
Dec 14, 2005
Messages
10,532
If you don't have enough lumber to make another one, then the system will break, I think. I will, however, make it work with lumber at some point, and I think I have an idea on how to avoid needing to increment/decrement food, as well, but blizzard have a tendancy to throw up random suprises.
Worked fine for me with exactly enough gold/lumber to afford one farm.

True, nulling isn't necessary, but meh. Boolexprs are, however, faster.
Kewl.

The storing takes place once. The reading can take place a hundred times.
I didn't meant storing speed, so much as memory (Though both ways are insignificant) - I was more wondering why you had coded it that way than suggesting a change.
 
Level 14
Joined
Nov 20, 2005
Messages
1,156
Fixed a bug where I forgot to unpause units held in the queue (which would have been used only if two buildings were ordered made for the first time within 0.01 seconds).

Worked fine for me with exactly enough gold/lumber to afford one farm.

Possibly, didn't actually test it. I will at some point make it work fully with lumber costs as well.

I didn't meant storing speed, so much as memory (Though both ways are insignificant) - I was more wondering why you had coded it that way than suggesting a change.

Memory is very cheap, and it's very little memory, while GC calls are relatively expensive.
 
Level 5
Joined
Jul 25, 2008
Messages
155
Is this like an ability then? I dont really understand how I shall add the code, I just need to add it then it all works?
 

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,178
Was there not some way you could get gold and lumber cost of an object by adding the AI natives to the list of trigger natives allowing you to run those natives in normal triggers? I understand this system does more than just get the cost, as it also stores the total cost but techincically if you could use those natives, it could maybe be made faster?

Or is there some problem with using those natives (next to having to import a file).
 
Level 14
Joined
Nov 20, 2005
Messages
1,156
Updated. Apparently the old version had a bug causing it to refund the first construction cost of a building. I'm presuming that is fixed in the most up to date version, which I forgot to upload here, so uploaded now. Also has unit cost support, not just buildings.
 

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,178
Bug Report -
When using instant build mode and upgrading a building, it results in the building not scaling properly. By this I mean, its graphic scale is not adjusted to that which the upgrade should have.

Also you did not fix the refund bug. It is still in effect in the lattest version.
 
Level 14
Joined
Nov 20, 2005
Messages
1,156
Bug Report -
When using instant build mode and upgrading a building, it results in the building not scaling properly. By this I mean, its graphic scale is not adjusted to that which the upgrade should have.

Also you did not fix the refund bug. It is still in effect in the lattest version.

Can't find any evidence for the refund bug, 1.21b or 1.23. Test map?

I found a couple of bugs with funds being taken away twice on first build for large buildings. Upping the constant to 150 fixed it.

As for the graphics scale, I'll note it, but it's a native bug.
 

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,178
I used it in a version of green TD (attached).
Note however that I did make some sort of fix in the map provided, so for testing it you will need to re import your system into the map header over the existing version.
What happens is I have 800 gold, build a 700 gold tower and then have 700 gold which means a gain of 600 gold. I fixed it by moving when the gold is reset.
Also note that I am using the lattest version of WC3 however I may be jusing an older JNGP.

EDIT - Also seems to have problem when you set the game to use the lattest patch data with the upgrade system whereby it takes your gold but does not build the tower the first time you upgrade one.
 

Attachments

  • Green Circle TD Gold v1.1.w3x
    128.4 KB · Views: 99
Last edited:
Top