1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  3. The 15th Mini-Mapping Contest came to an end. The Secrets of Warcraft 3 are soon to be revealed! Come and vote in the public poll for your favorite maps.
    Dismiss Notice
  4. The 12th incarnation of the Music Contest is LIVE! The theme is Synthwave. Knight Rider needs a song to listen to on his journey. You should definitely have some fun with this theme!
    Dismiss Notice
  5. Join other hivers in a friendly concept-art contest. The contestants have to create a genie coming out of its container. We wish you the best of luck!
    Dismiss Notice
  6. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

Custom Resources 1.31

Submitted by ZiBitheWand3r3r
This bundle is marked as approved. It works and satisfies the submission rules.
Custom Resource System v1.30


You are no longer limited to gold and lumber. This system expand resources to unlimited number.
All custom resources are based on human lumber harvesting.
I made this system as a reply to UmbraUnda request.

Requirements:
GUI Unit Event v2.4.0.0
Order Ids
Player Resource Monitoring v1.01

Installation:
  • Copy&Paste unit "DummyTreeChecker" from demo-map. It should have abilities: 'Ahrl' 'Avul' 'Aloc'
  • Copy&Paste destructable "DummyTree" from demo-map.
  • Copy&Paste whole category "CustomResources" from demo-map.
  • Configuration is done in "CustomResourcesConfig" trigger.
  1. Prepare 1 harvest ability for *each* custom resource (for normal lumber-also). Base it on 'Ahrl' "Harvest (Ghouls Lumber)". You must use diffrent harvest ability for each custom resource.
    1. Prepare workers (max99 for each resource), returning-structures (max99 for each resource) and destructables based on tree (max99 for each resource). All registered workers should have one harvest ability from this system set it in ObjectEditor. If worker is able to harvest more then 1 resource, then this ability you set in OE will be default (after training/creating). All registered Buildings must have ability "Return Lumber" 'Arlm' or "Return Gold and Lumber" 'Argl'
    2. You can use the same Worker(s) in many custom resources.
    3. You can use the same Building(s) in many custom resources.
    4. You cannot use the same destructable in more then one custom resource.
  2. "ResourceWOOD" is obligatory trigger. It must exist. Define there all trees that will be harvest-able for lumber.
  3. Next triggers (4 in demo map) are examples. Every trigger represents one custom resource. Follow instruction inside triggers.
  4. Example1: To have only 1 custom resource: Set ability/workers/structures/destructables in "ResourceWOOD" trigger,rename "ResourceMUSHROOMS", set "Resource_Number=2" and set ability/workers/structures/destructables.Delete triggers "ResourceSTONES", "ResourceICEROCKS", "ResourcePLANTS".
  5. Example2: To have 5 custom resources:Edit existing triggers "ResourceWood/Mushrooms/Stones/Icerocks/Plants".Copy&paste "ResourcePLANTS" trigger. Rename it. Set "Resource_Number=5" and set ability/workers/structures/destructables inside this new trigger. Done.

Available functions:
AdjustPlayerCustomResource(player <p>, integer <resourceNumber>, integer <value>) returns nothing

use negative <value> to subtract or positive to add resource
GetPlayerCustomResource(player <p>, integer <resourceNumber>) returns integer

<resourceNumber> is an integer used in configuration ResourceSOMENAME triggers in first line (Set Resource_Number = X)

"Custom resource has been changed EVENT"
In GUI trigger use as event "Resource_Event" becomes equal to X (where X is Resource_Number defined in triggers "ResourceSOMENAME").
Variables available inside trigger: "Resource_Player" - owner whos resource nr X has been changed, value is: "Resource_Value" and "Resource_Number" - resource's number (defined in triggers "ResourceSOMENAME")

Tip: in game, to change workers ability to harvest diffrent resource-type: right-click on resource (left-click and order "harvest" targeted on new destructable-type is blocked).

Additionaly library "CustomResource" offers boolean variable that returns true for building under construction. Use like this:
if udg_isBuildingUnderConstruction[Custom Value of Unit] then




When worker brings resources to building he is ordered to "harvest", This order is issued excatly at the same time when player's lumber has changed. This is our event "worker brings resources". There is dummy tree in case if there are not resources left nearby (and then worker wont be ordered, dummy tree prevents that). I am manipulating and following worker orders)
There is one small problem that I cannot fix:
imagine that worker is harvesting, he is full and start walking to nearby valid delivery-structure (he is redirected by the system). If that structure get destroyed then worker will be internally redirected to closest player's structure with ability (Arlm) or (Argl). Resource-type will counted fine, only returning structure may be not valid

Addon library CustomResourcesCost
Allows to set prices for: training / building / upgrades / researches
This library does not support custom prices for item sell / unit sell.

Installation and configuration
Copy 2 triggers: "CustomResourcesCostConfig" and "CustomResourcesCost"
All configuration is done inside trigger "CustomResourcesCostConfig".
If you're going to use not more then 10 custom resources - use custom script. In this demo-map there are 4 custom resources (don't count normal WOOD): Mushrooms/Stones/Icerocks/Plants. They have numbers: Mushrooms-2/Stones-3/Icerocks-4/Plants-5.
For 4 resources to configure cost use function CR_SaveCost4, (for 6 resources use CR_SaveCost6, etc.)
So unit cost is set using 2 lines:
Set Resource_CostUnit = Peasant
Custom Script: call CR_SaveCost4(1, 0, 0, 1)
First CR_SaveCost4 parameter is Mushrooms price, then Stones price, Icerocks price and Plants price.
So Peasant costs: 1 Mushroom(number2), 0 Stones(number3), 0 Icerocks(number4) and 1 Plant(number5).
(there is alternative configuration trigger for users who needs more then 10 custom resources)

LIBRARY CustomResources configuration GUI trigger
  • CustomResourcesConfig
    • Events
      • Game - UnitIndexEvent becomes Equal to -1.00
    • Conditions
    • Actions
      • -------- Common configuration for whole CustomResources system --------
      • -------- ---------------------------- --------
      • -------- used to check if destructable is a tree, please copy from demo map, unit should have 3 abilities: 'Ahrl' 'Avul' 'Aloc' --------
      • Set Resource_Dummy = DummyTreeChecker
      • -------- ---------------------------- --------
      • -------- please copy this tree from demo map --------
      • Set Resource_DummyTree = DummyTree (CustomResourceSystem)
      • -------- ---------------------------- --------
      • -------- if "true" game will print warnings such as "returning building not found" & "unit cannot harvest this destructable" & "not enough resources" --------
      • Set Resource_PrintWarnings = True
      • -------- ---------------------------- --------
      • -------- if "true" game will print developing-mode messages ( for checking the system) --------
      • Set Resource_PrintMsgDeveloping = False
      • -------- ---------------------------- --------
      • -------- End of configuration --------
      • -------- ---------------------------- --------
      • -------- Please, don't touch following lines --------
      • Custom script: set g_resourceUnitTreeChecker = CreateUnit(Player(15), udg_Resource_Dummy, 0.00, 0.00, 0.00)
      • Custom script: call ShowUnit(g_resourceUnitTreeChecker, false)
      • -------- ---------------------------- --------
      • -------- Don't enable the following 4 lines as they exist simply to make copying the system easier --------
      • Set Resource_Event = 0.00
      • Set Resource_Player = Player 1 (Red)
      • Set Resource_Value = 0
      • Set isBuildingUnderConstruction[0] = False


LIBRARY CustomResources
Code (vJASS):



// CustomResources library requires:
    // 1. Player Resources Monitoring ver 1.01
    // https://www.hiveworkshop.com/threads/player-resource-monitoring-v1-01.276158/
    // 2. GUI Unit Event by Bribe, version 2.4.0.0
    // https://www.hiveworkshop.com/threads/gui-unit-event-v2-4-0-0.201641/
    // 3. OrderList
    // https://www.thehelper.net/threads/order-ids.148097/

// Installation:
    // Copy&Paste unit "DummyTreeChecker" from demo-map. It should have abilities: 'Ahrl' 'Avul' 'Aloc'
    // Copy&Paste destructable "DummyTree" from demo-map.
    // Copy&Paste whole category "CustomResources" from demo-map.
    // Configuration is done in "CustomResourcesConfig" trigger. There is nothing to configure here.

    // Prepare 1 harvest ability for *each* custom resource (for normal lumber-also). Base it on 'Ahrl' "Harvest (Ghouls Lumber)"
    // You must use diffrent harvest ability for each custom resource.
    // Prepare workers (max99 for each resource), returning-structures (max99 for each resource) and
    // destructables based on tree (max99 for each resource).

    // All registered workers should have one harvest ability from this system set it in ObjectEditor.
    // If worker is able to harvest more then 1 resource, then this ability you set in OE will be default (after training/creating).
    // All registered Buildings must have ability "Return Lumber" 'Arlm' or "Return Gold and Lumber" 'Argl'

    // You can use the same Worker(s) in many custom resources.
    // You can use the same Building(s) in many custom resources.
    // You -->cannot<-- use the same destructable in more then one custom resource.

    // "ResourceWOOD" is obligatory trigger. It must exist. Define there all trees that will be harvest-able for lumber.
    // Next triggers (4 in demo map) are examples. Edit them freely, add new triggers (as many as you want).
    // Every trigger represents one resource. Follow instruction inside triggers.
    // Example1: To have only 1 custom resource:
    // Set ability/workers/structures/destructables in "ResourceWOOD" trigger
    // Rename "ResourceMUSHROOMS", use "Resource_Number=2" and set ability/workers/structures/destructables.
    // delete triggers "ResourceSTONES", "ResourceICEROCKS", "ResourcePlants".

    // Example2: To have 5 custom resources:
    // Edit existing triggers "ResourceWood/Mushrooms/Stones/Icerocks/Plants".
    // Copy/paste "ResourcePLANTS" trigger. Rename it. Set "Resource_Number=5" and
    // set ability/workers/structures/destructables inside this new trigger. Done.

// Available functions:
    // AdjustPlayerCustomResource(player <p>, integer <resourceNumber>, integer <value>) returns nothing
        // use negative <value> to subtract or positive to add resource
    // GetPlayerCustomResource(player <p>, integer <resourceNumber>) returns integer
        // <resourceNumber> is an integer used in configuration ResourceSOMENAME triggers in first line (Set Resource_Number = X)

    // "Custom resource has been changed EVENT"
    // use as event "Resource_Event" becomes equal to X (where X is Resource_Number defined in triggers "ResourceSOMENAME")
    // variables available inside trigger: "Resource_Player" - owner whos resource nr X has been changed, value is: "Resource_Value" and
    // "Resource_Number" - resource's number (defined in triggers "ResourceSOMENAME")

// Tip: in game, to change workers ability to harvest diffrent resource-type:
// right-click on resource (left-click and order "harvest" targeted on new destructable-type is blocked)

// Additionaly library "CustomResource" offers boolean variable that returns true for building under construction. Use like this:
    // if udg_isBuildingUnderConstruction[Custom Value of Unit] then ...
   
library CustomResources initializer Init uses ORDER //v1.31 25-12-2017
globals
    public hashtable               g_hashCR
    private integer array           g_harvestAbility
    public integer                 g_customResourcesCount=0
    unit                    g_resourceUnitTreeChecker=null
    private real array              g_timeStampLastLumberRecived    // [player id]
    private integer array           g_lastLumberRecivedValue        // [player id]
    private timer                   g_stopWorkerTimer = CreateTimer()
    private timer                   g_gameTimer = CreateTimer()    
    private group                   g_workerToStopGroup = CreateGroup()
    private group                   g_workerRedirectGroup = CreateGroup()
    private timer                   g_workerRedirectTimer = CreateTimer()
    private real array            g_X // [GetUnitUserData(u)]
    private real array            g_Y // [GetUnitUserData(u)]
    private integer array           g_workerHarvestAbi              // [GetUnitUserData(u)]
    private unit                    g_closestStructure = null
    private unit                    g_returningWorker = null
    private real                    g_tempDistance2 = 0.00
    private group array             g_deliveryStructureGroup
    private destructable array      g_connectedDummyTree            // [structure's UnitUserData]
    public trigger                 g_trg_WorkerBringsResources = CreateTrigger()
    private trigger                 g_trg_ResumeharvestingRedirect = CreateTrigger()
    private trigger                 g_trg_DummyTreeDies = CreateTrigger()
    //hashtable keys:
    private constant integer    KEY_100 = 100 // total workers quantity, connected with harvest ability (abilityId as a parentKey)
    private constant integer    KEY_200 = 200 // total destructables quantity, connected with harvest ability (abilityId as a parentKey)
    private constant integer    KEY_300 = 300 // total structures quantity, connected with harvest ability (abilityId as a parentKey)
    private constant integer    KEY_400 = 400 // for saving abilityId able to harvest given destructable (destructableId as parentKey)
    private constant integer    KEY_401 = 401 // for saving boolean, as a sign that destructable is registered (destructableId as parentKey)
    private constant integer    KEY_410 = 410 // for saving boolean, as a sign that worker is registered (workerId as parentKey)
    private constant integer    KEY_420 = 420 // for saving integer udg_Resource_Number set up at ResourceXXXXX triggers (abilityId as a parentKey)
    private constant integer    KEY_430 = 430 // for saving boolean, as a sign that structure is registered (structureId as parentKey)
    private constant integer    KEY_500 = 500 // for saving each players custom resource value (abilityId as a parentKey)
    //used in "CustomResourcesCost" addon:
    public constant integer    KEY_600 = 600 // for saving prices, used as 600 + resourceNumber, (objectId as parentKey)
                                                                        // remember resource2 cost at 602 key, resource3 at 603 key, .. etc
    public constant integer    KEY_601 = 601 // for saving boolean, as a sign that object is registered (objectId as parentKey)
       

endglobals
//=============================================================

function MsgD takes string s returns nothing //for developing mode messages
    if udg_Resource_PrintMsgDeveloping then
        call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 10.00, s)
    endif
endfunction
function Msg takes player p, string s returns nothing //warning messages
    if udg_Resource_PrintWarnings then
        call DisplayTextToPlayer(p, 0.00, 0.00, s)
    endif
endfunction

private function OnConstruct_Start_Cond takes nothing returns boolean
    set udg_isBuildingUnderConstruction[GetUnitUserData(GetConstructingStructure())]=true
    return false
endfunction

private function OnConstruct_Finish_Cond takes nothing returns boolean
    set udg_isBuildingUnderConstruction[GetUnitUserData(GetConstructedStructure())]=false
    return false
endfunction

private function OnBuilding_Enters_Cond takes nothing returns boolean
    if IsUnitType(udg_UDexUnits[udg_UDex], UNIT_TYPE_STRUCTURE) then
        set udg_isBuildingUnderConstruction[udg_UDex]=false
    endif
    return false
endfunction
//=============================================================

private function InitSettingGUIVariables takes nothing returns nothing // run at init
    local integer x=1
    loop
        exitwhen x>99
        set udg_Resource_Worker[x]=0
        set udg_Resource_Destructable[x]=0
        set udg_Resource_Structure[x]=0
        set x=x+1
    endloop
endfunction

//---------------------------------------------------------------------------------------
private function InitResource_SaveWorkers takes nothing returns nothing
    local integer x=1
    loop
        exitwhen udg_Resource_Worker[x]==0 or x>99
        call SaveInteger(g_hashCR, udg_Resource_Ability, KEY_100+x, udg_Resource_Worker[x]) // remember 1st worker "id" at 101 key
        call SaveBoolean(g_hashCR, udg_Resource_Worker[x], KEY_410, true) // save "true" as sign that worker is registered in this system
        set x=x+1
    endloop
    // abilityId as a parentKey , KEY_420 -- abilityId Number (integer)
    call SaveInteger(g_hashCR, udg_Resource_Ability, KEY_420, udg_Resource_Number)
           
    //save total workers quantity at "100 key" for this ability
    call SaveInteger(g_hashCR, udg_Resource_Ability, KEY_100, x-1)
    //reset:
    loop
        exitwhen x==0
        set udg_Resource_Worker[x]=0
        set x=x-1
    endloop
endfunction
//--------------------------------------------------------------------------------
private function InitResource_SaveDestructables takes nothing returns nothing
    local integer x=1
    loop
        exitwhen udg_Resource_Destructable[x]==0 or x>99
        call SaveInteger(g_hashCR, udg_Resource_Ability, KEY_200+x, udg_Resource_Destructable[x]) // remember 1st destruct at 201 key
        call SaveInteger(g_hashCR, udg_Resource_Destructable[x], KEY_400, udg_Resource_Ability) // remember abiID to harvest this destr
        call SaveBoolean(g_hashCR, udg_Resource_Destructable[x], KEY_401, true) // for IsDestructableRegistered
        set x=x+1
    endloop
    //save total destructables quantity at "200 key" for this ability
    call SaveInteger(g_hashCR, udg_Resource_Ability, KEY_200, x-1)
    //reset:
    loop
        exitwhen x==0
        set udg_Resource_Destructable[x]=0
        set x=x-1
    endloop
endfunction
//--------------------------------------------------------------------------------
private function InitResource_SaveStructures takes nothing returns nothing
    local integer x=1
    loop
        exitwhen udg_Resource_Structure[x]==0 or x>99
        call SaveInteger(g_hashCR, udg_Resource_Ability, KEY_300+x, udg_Resource_Structure[x]) // remember 1st structure at 301 key
        call SaveBoolean(g_hashCR, udg_Resource_Structure[x], KEY_430, true) // saves boolean for registered structures
        set x=x+1
    endloop
    //save total structures quantity at "300 key" for this ability
    call SaveInteger(g_hashCR, udg_Resource_Ability, KEY_300, x-1)
    //reset:
    loop
        exitwhen x==0
        set udg_Resource_Structure[x]=0
        set x=x-1
    endloop
endfunction
//--------------------------------------------------------------------------------
function InitResource takes nothing returns nothing
    set g_harvestAbility[udg_Resource_Number] = udg_Resource_Ability
    if udg_Resource_Number > g_customResourcesCount then
        set g_customResourcesCount = udg_Resource_Number
    endif
    set g_deliveryStructureGroup[udg_Resource_Number] = CreateGroup()
    call InitResource_SaveWorkers()
    call InitResource_SaveDestructables()
    call InitResource_SaveStructures()
endfunction
//=============================================================

//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------    

/*    
HASHTABLE KEYS

    KEY_100 -- workers quantity (abilityId as a parentKey)
    101--199 - workers Id's (abilityId as a parentKey)
   
    KEY_200 -- destructables quantity (abilityId as a parentKey)
    201--299 - destructables Id's (abilityId as a parentKey)
   
    KEY_300 -- structures quantity (abilityId as a parentKey)
    301--399 - structures Id's (abilityId as a parentKey)
   
    KEY_400 -- destructableId as parentKey --> remembers abilityId to harvest this destructabe
    call SaveInteger(g_hashCR, destructableId, KEY_400, abiId) --> set abiId = LoadInteger(g_hashCR, destructableId, KEY_400)
   
    KEY_401 -- destructableId as parentKey --> remembers boolean true as sign that destructabe is registered in system
    call SaveBoolean(g_hashCR, destructableId, KEY_401, true) --> if LoadBoolean(g_hashCR, destructableId, KEY_401) then ...
   
    KEY_410 -- workerId as parentKey -- > saves boolean true for registered workers in this system
    call SaveBoolean(g_hashCR, workerId, KEY_410, true) -->  if LoadBoolean(g_hashCR, workerId, KEY_410) then ...

    abilityId as a parentKey
    KEY_420 -- abilityId Number - integer: udg_Resource_Number (set up at ResourceXXXXX triggers by a user)

    KEY_430 -- structureId as parentKey -- > saves boolean true for registered structures in this system
    call SaveBoolean(g_hashCR, structureId, KEY_430, true) --> if LoadBoolean(g_hashCR, structureId, KEY_430) then ...
   
    abilityId as a parentKey
    KEY_500+playerId => 500 ..to.. 511 - each players resource value
   
    //for Addon "CustomResourcesCost"
    KEY_601 -- units/structure/research/upgradeBuilding/items ID as parentKey -- > saves boolean true for registered objects for prices purposes
    call SaveBoolean(g_hashCR, objectId, KEY_601, true) --> if LoadBoolean(g_hashCR, objectId, KEY_601) then ...
    KEY_600+resourceNumber => 602 .. and up for prices -- object ID as parent key

*/

//-------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------
function IsTree takes destructable d returns boolean
    return (IssueTargetOrderById(g_resourceUnitTreeChecker, ORDER_harvest, d)) and (IssueImmediateOrderById(g_resourceUnitTreeChecker, ORDER_stop))
endfunction
//------------------------------------------------------------------------------------

//------------------------------------------------------------------------------------
// is harvest allowed by ability worker currently has:
//------------------------------------------------------------------------------------
private function IsHarvestAllowed takes unit u, integer destructableId returns boolean
    local integer x=1
    local integer abi = g_workerHarvestAbi[GetUnitUserData(u)] // ability unit has to harvest
    local integer count = LoadInteger(g_hashCR, abi, KEY_200) // gives total number of destructables quantity at 200 key
    loop
        exitwhen x>count
        if LoadInteger(g_hashCR, abi, KEY_200+x) == destructableId then
            return true
        endif
        set x=x+1
    endloop    
    return false
endfunction
//----------------------------------------------------------------------------------
// can worker change ability to new one
//----------------------------------------------------------------------------------
private function IsAbilityAllowedForWorker takes integer abi, integer workerId returns boolean
    local integer x=1
    local integer count = LoadInteger(g_hashCR, abi, KEY_100) // total number of workers at 100 key
    loop
        exitwhen x>count
        if LoadInteger(g_hashCR, abi, KEY_100+x) == workerId then
            return true
        endif
        set x=x+1
    endloop
    return false
endfunction
//----------------------------------------------------------------------------------
private function ChangeAbilityToHarvestDestructable takes unit u, integer destructableId returns boolean
    local integer abi = LoadInteger(g_hashCR, destructableId, KEY_400) //-- > gives abiId able to harvest given destructable
    local integer x=1
   
    if abi>0 and IsAbilityAllowedForWorker(abi, GetUnitTypeId(u)) then //can unit have this abi?
       
        call AddUnitAnimationProperties(u, "Lumber", false)
        set g_workerHarvestAbi[GetUnitUserData(u)] = abi
       
        loop //remove ALL other harvest abils
            exitwhen x>g_customResourcesCount
            call UnitRemoveAbility(u, g_harvestAbility[x])
            set x=x+1
        endloop
        call UnitAddAbility(u, abi)
        return true
    endif
    return false
endfunction
//----------------------------------------------------------------------------------
//----------------------------------------------------------------------------------
private function IsWorkerRegistered takes integer workerId returns boolean
    return LoadBoolean(g_hashCR, workerId, KEY_410)
endfunction
//----------------------------------------------------------------------------------
private function IsDestructableRegistered takes integer destructableId returns boolean
    return LoadBoolean(g_hashCR, destructableId, KEY_401)
endfunction
//----------------------------------------------------------------------------------
private function IsStructureRegistered takes integer structureId returns boolean
    return LoadBoolean(g_hashCR, structureId, KEY_430)
endfunction
//----------------------------------------------------------------------------------
//----------------------------------------------------------------------------------
// returns udg_Resource_Number set up at ResourceXXXXX triggers by user    
private function GetHarvestAbilityNumber takes integer workerCurentAbi returns integer
    return LoadInteger(g_hashCR, workerCurentAbi, KEY_420)  
endfunction

//-----------------------------------------------------------------------------------------------------------
//------------------------------DELIVERY STRUCTURES---------------------------------------------------
//-----------------------------------------------------------------------------------------------------------

private function RemoveDeliveryStructureFromAllGroups takes unit structure returns nothing
    local integer c=1
    loop //loop over all groups
        exitwhen c>g_customResourcesCount
        call GroupRemoveUnit(g_deliveryStructureGroup[c], structure)
        set c=c+1
    endloop
endfunction
//----------------------------------------------------------------------------------
private function AddDeliveryStructureToGroups takes unit structure returns nothing
    local integer structureId = GetUnitTypeId(structure)
    local integer abi
    local integer count
    local integer x
    local integer c=1
   
    loop //loop over all harvest abiliies
        exitwhen c>g_customResourcesCount
        //inside loop begin
        set abi = g_harvestAbility[c]
        set count = LoadInteger(g_hashCR, abi, KEY_300) //  total number of structures for this abi (at 300 key)
        set x=1
        loop
            exitwhen x>count
            if LoadInteger(g_hashCR, abi, KEY_300+x) == structureId then
                call GroupAddUnit(g_deliveryStructureGroup[c], structure)
            endif
            set x=x+1
        endloop
        //inside loop end
        set c=c+1
    endloop
endfunction
//----------------------------------------------------------------------------------
private function IsDeliveryStructureValid takes unit u, unit structure returns boolean
    local integer abi = g_workerHarvestAbi[GetUnitUserData(u)] // current worker's ability to harvest
    local integer nr = GetHarvestAbilityNumber(abi)
    return IsUnitInGroup(structure, g_deliveryStructureGroup[nr])
endfunction

//----------------------------------------------------------------------------------
//----------------------------------------------------------------------------------
//----------------------------------------------------------------------------------
//this not for a user:
private function AdjustPlayerCustomResource_ByAbility takes integer playerId, integer abi, integer value returns nothing
    local integer resourceNr = GetHarvestAbilityNumber(abi)
    local integer currentValue = LoadInteger(g_hashCR, abi, KEY_500+playerId)
    local integer newValue = currentValue+value
    if newValue<0 then //resource level cannot be negative
        set newValue=0
    endif
    call SaveInteger(g_hashCR, abi, KEY_500+playerId, newValue)
    // here FIRE EVENT (changed 22-12-2017)
    set udg_Resource_Player = Player(playerId)
    set udg_Resource_Value = value
    set udg_Resource_Number = resourceNr
   
    set udg_Resource_Event = 0.00
    set udg_Resource_Event = I2R(resourceNr)
    set udg_Resource_Event = 0.00
endfunction
//----------------------------------------------------------------------------------
//----------------------------------------------------------------------------------
//for user: (use negative "value" to subtract or positive to add resource)
function AdjustPlayerCustomResource takes player p, integer resourceNumber, integer value returns nothing
    call AdjustPlayerCustomResource_ByAbility(GetPlayerId(p), g_harvestAbility[resourceNumber], value)
endfunction
//----------------------------------------------------------------------------------
//for user:
function GetPlayerCustomResource takes player p, integer resourceNumber returns integer
    local integer abi = g_harvestAbility[resourceNumber]
    return LoadInteger(g_hashCR, abi, KEY_500+GetPlayerId(p))
endfunction


//===========================================================================
//===========================================================================
//-------------------------------TRIGGERS RELATED FUNCTIONS--------------------------------------------------------------
//===========================================================================
//===========================================================================

private function Trig_LumberChanged takes nothing returns nothing // "udg_PRM_EVENT", EQUAL, 2.00
    local integer i = GetPlayerId(udg_PRM_Player)
    if udg_PRM_Change > 0 then    
    //it fires as 1st, after this - "order harvest"  (or other) runs
    //remember time-stamp for this player and value:
    set g_timeStampLastLumberRecived[i] = TimerGetElapsed(g_gameTimer)
    set g_lastLumberRecivedValue[i] = udg_PRM_Change
    call MsgD("(PRM) time stamp: " + R2S(TimerGetElapsed(g_gameTimer)) + ", " + GetPlayerName(udg_PRM_Player) + ", value: " + I2S(udg_PRM_Change))
    endif
endfunction
//===========================================================================
// this is an EVENT "worker brings resources"
// we got unit "u", g_lastLumberRecivedValue[playerId], and g_workerHarvestAbi[GetunitUserData(u)] - integer abiId
private function Trig_WorkerBringsResources_Cond takes nothing returns boolean
    //if peasant recive ANY order like: harvest dummyTree, harvest normal resources ,
    //or player queued any order (stop, attack, move, etc) then:
    if TimerGetElapsed(g_gameTimer) == g_timeStampLastLumberRecived[GetPlayerId(GetOwningPlayer(GetOrderedUnit()))] then
        return IsWorkerRegistered(GetUnitTypeId(GetOrderedUnit()))
        // ordered unit is 'last lumber supplier'
    endif
    return false
endfunction
//------------------------------------------------------------------------------------
private function Trig_WorkerBringsResources_Act takes nothing returns nothing
    local unit u = GetOrderedUnit()
    local player pla = GetOwningPlayer(u)
    local integer i = GetPlayerId(pla)
    local integer abi = g_workerHarvestAbi[GetUnitUserData(u)]
    local integer resourceNr = GetHarvestAbilityNumber(abi) // new <---
    if not (abi == g_harvestAbility[1]) then // it is *custom resource* - subtract from normal wood!
        set udg_PRM_FireEvent = false
        call SetPlayerState(pla, PLAYER_STATE_RESOURCE_LUMBER, GetPlayerState(pla, PLAYER_STATE_RESOURCE_LUMBER)-g_lastLumberRecivedValue[i])
        set udg_PRM_FireEvent = true
        call AdjustPlayerCustomResource_ByAbility(i, abi, g_lastLumberRecivedValue[i]) // writes value in hash (we're not writing "normal lumber")
        call MsgD(GetUnitName(u) + " brings resource nr(" + I2S(resourceNr) +"), value: " + I2S(g_lastLumberRecivedValue[i]) + ", time stamp: " + R2S(TimerGetElapsed(g_gameTimer)))
    endif //FIRE EVENT - MOVED 22-12-2017
    set u=null
    set pla=null
endfunction
//===========================================================================
private function StopWorker_Enum takes nothing returns nothing
    call IssueImmediateOrderById(GetEnumUnit(), ORDER_stop)
    call GroupRemoveUnit(g_workerToStopGroup, GetEnumUnit())
endfunction
private function StopWorker takes nothing returns nothing
    call DisableTrigger(g_trg_WorkerBringsResources) // protect agains double fire "worker brings resource" event
    call ForGroup(g_workerToStopGroup, function StopWorker_Enum)
    call EnableTrigger(g_trg_WorkerBringsResources)
endfunction
//--------------------------------------------------------------------------------
private function Trig_HarvestSmartOffset_Cond takes nothing returns boolean
    local integer ord=GetIssuedOrderId() // ORDER_OFFSET when building right-cick on destructable and trained worker start walking
    if ord==ORDER_harvest or ord==ORDER_smart or ord==ORDER_OFFSET then // 851970
        return GetOrderTargetDestructable() != null and IsWorkerRegistered(GetUnitTypeId(GetOrderedUnit()))
    endif
    return false
endfunction
//--------------------------------------------------------------------------------
private function Trig_HarvestSmartOffset_Act takes nothing returns nothing
    local unit u = GetOrderedUnit()
    local integer destructableId = GetDestructableTypeId(GetOrderTargetDestructable())
    local integer ord = GetIssuedOrderId()
    if ord==ORDER_harvest and (not IsHarvestAllowed(u, destructableId)) then
        // does current ability allow to harvest this destr? if no, then stop worker
        call GroupAddUnit(g_workerToStopGroup, u)
        call TimerStart(g_stopWorkerTimer, 0.00, false, function StopWorker)
    endif
    //  851970 --> when worker trained with rally-point set up on tree
    // 851970 or "smart" order --> need to change ability harvest for diffrent destructables
    if (ord==ORDER_smart or ord==ORDER_OFFSET) and IsTree(GetOrderTargetDestructable()) then
        if (not IsHarvestAllowed(u, destructableId)) then
            if not ChangeAbilityToHarvestDestructable(u, destructableId) then
                call Msg(GetOwningPlayer(u), GetUnitName(u) + " |cffff0000cannot|r harvest " + GetDestructableName(GetOrderTargetDestructable()))
                call GroupAddUnit(g_workerToStopGroup, u)
                call TimerStart(g_stopWorkerTimer, 0.00, false, function StopWorker)
            endif
        endif
    endif
    set u=null
endfunction
//---------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------
private function Trig_PointOrderHarvestSmartOffset_Cond takes nothing returns boolean
    local integer ord=GetIssuedOrderId() // ORDER_OFFSET = 851970 when building right-cick on destructable and trained worker start walking
    return (ord==ORDER_harvest or ord==ORDER_smart or ord==ORDER_OFFSET) and IsWorkerRegistered(GetUnitTypeId(GetOrderedUnit()))
endfunction
//---------------------------------------------------------------------------------------------------------------
private function RedirectWorker_Enum takes nothing returns nothing    
    call IssuePointOrder(GetEnumUnit(), "smart", g_X[GetUnitUserData(GetEnumUnit())], g_Y[GetUnitUserData(GetEnumUnit())]) //attack-move
    call GroupRemoveUnit(g_workerRedirectGroup, GetEnumUnit())
endfunction
private function RedirectWorker takes nothing returns nothing
    call ForGroup(g_workerRedirectGroup, function RedirectWorker_Enum)
endfunction
//------------------------------------
private function Trig_PointOrderHarvestSmartOffset_Act takes nothing returns nothing
    local unit u=GetOrderedUnit()
    if not IsVisibleToPlayer(GetOrderPointX(), GetOrderPointY(), GetOwningPlayer(u)) then
        set g_X[GetUnitUserData(u)]=GetOrderPointX()
        set g_Y[GetUnitUserData(u)]=GetOrderPointY()
        call GroupAddUnit(g_workerRedirectGroup, u)
        call TimerStart(g_workerRedirectTimer, 0.00, false, function RedirectWorker)
    endif
    set u=null
endfunction

//===========================================================================
private function FindClosestStructure_Enum takes nothing returns nothing
    local unit u = GetEnumUnit()
    local real dx = GetUnitX(g_returningWorker)-GetUnitX(u)
    local real dy = GetUnitY(g_returningWorker)-GetUnitY(u)
    local real dist2 = SquareRoot(dx * dx + dy * dy)
    if GetOwningPlayer(u)==GetOwningPlayer(g_returningWorker) and (not udg_isBuildingUnderConstruction[GetUnitUserData(u)]) then
        if dist2 < g_tempDistance2 then
            set g_tempDistance2 = dist2
            set g_closestStructure = u
        endif
    endif
    set u=null
endfunction
//-----------------------------------------------------------------------------
private function FindClosestStructure takes unit worker returns nothing
    local integer id = GetUnitUserData(worker)
    local integer nr = GetHarvestAbilityNumber(g_workerHarvestAbi[id])
    set g_tempDistance2 = 999999.00
    set g_closestStructure = null
    set g_returningWorker = worker
    call ForGroup(g_deliveryStructureGroup[nr], function FindClosestStructure_Enum)
endfunction
//-----------------------------------------------------------------------------
private function Trig_ResumeharvestingRedirect_Cond takes nothing returns boolean
    return GetIssuedOrderId()==ORDER_resumeharvesting and IsWorkerRegistered(GetUnitTypeId(GetOrderedUnit()))
endfunction
//-----------------------------------------------------------------------------
// when button "return resources" clicked  OR  auto come-back with resources
private function Trig_ResumeharvestingRedirect_Act takes nothing returns nothing
    local unit u = GetOrderedUnit()
    local integer id = GetUnitUserData(u)
    call FindClosestStructure(u) // it writes global "g_closestStructure" as a valid building to deliver resources
    if g_closestStructure != null then
        call DisableTrigger(g_trg_ResumeharvestingRedirect) // this trigger
        if IssueTargetOrderById(u, ORDER_resumeharvesting, g_closestStructure) then//and udg_Resource_PrintMsgDeveloping then
            call MsgD("Redirecting " + GetUnitName(u) + " to " + GetUnitName(g_closestStructure))
        endif
        call EnableTrigger(g_trg_ResumeharvestingRedirect)
    else //no valid structure..
        call Msg(GetOwningPlayer(u), GetUnitName(u) + ": |cffffff00 No proper building found to return. |r")
        call GroupAddUnit(g_workerToStopGroup, u)
        call TimerStart(g_stopWorkerTimer, 0.00, false, function StopWorker)
    endif
    set u=null
endfunction
//===========================================================================
private function Trig_ReturnSmartRestrictions_Cond takes nothing returns boolean
    if GetIssuedOrderId() == ORDER_smart then
        if GetOrderTargetUnit() != null and IsWorkerRegistered(GetUnitTypeId(GetOrderedUnit())) then
            return GetOwningPlayer(GetOrderedUnit()) == GetOwningPlayer(GetOrderTargetUnit())
        endif
    endif
    return false
endfunction
//-------------------------------------------------------------------------------------
//if player give order "smart" on Worker and if building is not valid to take resources then convert smart-->move order
private function Trig_ReturnSmartRestrictions_Act takes nothing returns nothing
    local unit u = GetOrderedUnit()
    local unit target = GetOrderTargetUnit()
    //check if worker is returning resources (it may also be order repair/finish construction)
    call DisableTrigger(g_trg_ResumeharvestingRedirect)
    if IssueTargetOrderById(u, ORDER_resumeharvesting, target) then //he has resources
        if (not IsDeliveryStructureValid(u, target)) then
            call IssueTargetOrderById(u, ORDER_move, target)
        endif
    endif
    call EnableTrigger(g_trg_ResumeharvestingRedirect)
    set u=null
    set target=null
endfunction
//===========================================================================
private function Trig_WorkerStructureCreated_Cond takes nothing returns boolean
    local integer unitId = GetUnitTypeId(udg_UDexUnits[udg_UDex])
    return IsStructureRegistered(unitId) or IsWorkerRegistered(unitId)
endfunction
//------------------------------------------------------------------------------------
private function Trig_WorkerStructureCreated_Act takes nothing returns nothing
    local integer x=1
    local integer unitId = GetUnitTypeId(udg_UDexUnits[udg_UDex])
    if IsStructureRegistered(unitId) then //BUILDING CREATED:
        //find x/y for DummyTree : item (Wirt's Leg) version
        set bj_lastCreatedItem = CreateItem('wtlg', GetUnitX(udg_UDexUnits[udg_UDex]), GetUnitY(udg_UDexUnits[udg_UDex]))
        set g_connectedDummyTree[udg_UDex] = CreateDestructable(udg_Resource_DummyTree, GetItemX(bj_lastCreatedItem), GetItemY(bj_lastCreatedItem), 0.00, 1, 0)
        call RemoveItem(bj_lastCreatedItem)        
        call TriggerRegisterDeathEvent(g_trg_DummyTreeDies, g_connectedDummyTree[udg_UDex]) //add event to DummyTreeDies
        call AddDeliveryStructureToGroups(udg_UDexUnits[udg_UDex]) //add to delivery group    
    elseif IsWorkerRegistered(unitId) then //WORKER CREATED:
        //------------------------------------------------------------------------------
        // all registered workers should have one harvest ability from this system (set in OE)
        //-----------------------------------------------------------------------------
        loop
            exitwhen x>g_customResourcesCount // loop over all harvest abilities and set variable
            if GetUnitAbilityLevel(udg_UDexUnits[udg_UDex], g_harvestAbility[x])>0 then
                set g_workerHarvestAbi[udg_UDex] = g_harvestAbility[x]
                return
            endif
            set x=x+1
        endloop
    endif
endfunction
//===========================================================================
private function Trig_StructureUpgraded_Act takes nothing returns nothing // runs when structure *finishes* upgrade
    call RemoveDeliveryStructureFromAllGroups(udg_UDexUnits[udg_UDex]) //remove from groups    
    if IsStructureRegistered(GetUnitTypeId(udg_UDexUnits[udg_UDex])) then // is upgraded building registered?
        call AddDeliveryStructureToGroups(udg_UDexUnits[udg_UDex]) // add this new upgraded building    
        //if connected dummy TREE is dead/none (means previous structure was not registered)
        if g_connectedDummyTree[udg_UDex]==null or GetDestructableLife(g_connectedDummyTree[udg_UDex]) <= 0 then
            //find x/y for DummyTree : item version
            set bj_lastCreatedItem = CreateItem('wtlg', GetUnitX(udg_UDexUnits[udg_UDex]), GetUnitY(udg_UDexUnits[udg_UDex]))
            set g_connectedDummyTree[udg_UDex] = CreateDestructable(udg_Resource_DummyTree, GetItemX(bj_lastCreatedItem), GetItemY(bj_lastCreatedItem), 0.00, 1, 0)
            call RemoveItem(bj_lastCreatedItem)            
            call TriggerRegisterDeathEvent(g_trg_DummyTreeDies, g_connectedDummyTree[udg_UDex]) //add event to DummyTreeDies
        endif    
    else // New (upgraded) structure is not registered --> remove connected dummy TREE
        call RemoveDestructable(g_connectedDummyTree[udg_UDex])
    endif
endfunction
//===========================================================================
private function Trig_RegisteredStructureDies_Cond takes nothing returns boolean
    if IsStructureRegistered(GetUnitTypeId(GetDyingUnit())) then
        call RemoveDeliveryStructureFromAllGroups(GetDyingUnit()) //remove from all groups
        call RemoveDestructable(g_connectedDummyTree[GetUnitUserData(GetDyingUnit())]) //remove connected dummy TREE
    endif
    return false
endfunction
//===========================================================================
private function Trig_DummyTreeDies_Actions takes nothing returns nothing
    call DestructableRestoreLife(GetDyingDestructable(), GetDestructableMaxLife(GetDyingDestructable()), false) //resurect
endfunction
//===========================================================================



//===========================================================================
//===========================================================================
private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    local integer i=0
    loop
        set g_timeStampLastLumberRecived[i] = 0.00
        set i=i+1
        exitwhen i==bj_MAX_PLAYERS //12
    endloop
    set udg_Resource_Event = 0.00

    set g_hashCR = InitHashtable()
    call TimerStart(g_gameTimer, 36000.00, false, null)
    call InitSettingGUIVariables()
    //moved to the trigger "CustomResourcesConfig"
    //set udg_UnitIndexerEnabled = false
    //set g_resourceUnitTreeChecker = CreateUnit(Player(15), udg_Resource_Dummy, 0.00, 0.00, 0.00)
    //set udg_UnitIndexerEnabled = true
    //call ShowUnit(g_resourceUnitTreeChecker, false)

    //--------for udg_isBuildingUnderConstruction check  [GetUnitUserData(u)] ------------
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_CONSTRUCT_START)
    call TriggerAddCondition(t, Condition(function OnConstruct_Start_Cond))
    set t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_CONSTRUCT_FINISH)
    call TriggerAddCondition(t, Condition(function OnConstruct_Finish_Cond))
    set t = CreateTrigger()
    call TriggerRegisterVariableEvent(t, "udg_UnitIndexEvent", EQUAL, 1.00)
    call TriggerAddCondition(t, Condition(function OnBuilding_Enters_Cond))
    //----for lumber changed trigger -----------------------------------------------------
    set t = CreateTrigger()
    call TriggerRegisterVariableEvent(t, "udg_PRM_EVENT", EQUAL, 2.00)
    call TriggerAddAction(t, function Trig_LumberChanged)
    //-----------for worker-brings-resources event --------------------------------------
    call TriggerRegisterAnyUnitEventBJ(g_trg_WorkerBringsResources, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER)
    call TriggerRegisterAnyUnitEventBJ(g_trg_WorkerBringsResources, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER)
    call TriggerRegisterAnyUnitEventBJ(g_trg_WorkerBringsResources, EVENT_PLAYER_UNIT_ISSUED_ORDER)
    call TriggerAddCondition(g_trg_WorkerBringsResources, Condition(function Trig_WorkerBringsResources_Cond))
    call TriggerAddAction(g_trg_WorkerBringsResources, function Trig_WorkerBringsResources_Act)
    // ----------harvest destructable, orders: harvest/smart/offset ---------------------
    set t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER)
    call TriggerAddCondition(t, Condition(function Trig_HarvestSmartOffset_Cond))
    call TriggerAddAction(t, function Trig_HarvestSmartOffset_Act)
    // point order on destructable - if point not visible redirect order to "smart" to prevent auto-harvest on not-valid destructables
    set t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER)
    call TriggerAddCondition(t, Condition(function Trig_PointOrderHarvestSmartOffset_Cond))
    call TriggerAddAction(t, function Trig_PointOrderHarvestSmartOffset_Act)
   
    //------ for order resumeharvesting  -> redirect -------------------------------------
    call TriggerRegisterAnyUnitEventBJ(g_trg_ResumeharvestingRedirect, EVENT_PLAYER_UNIT_ISSUED_ORDER) // when button "return resources" clicked
    call TriggerRegisterAnyUnitEventBJ(g_trg_ResumeharvestingRedirect, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER) // auto come-back    
    call TriggerAddCondition(g_trg_ResumeharvestingRedirect, Condition(function Trig_ResumeharvestingRedirect_Cond))
    call TriggerAddAction(g_trg_ResumeharvestingRedirect, function Trig_ResumeharvestingRedirect_Act)
    // if player give order "smart" on Worker and if building is not valid to take resources then convert smart-->move order
    set t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER)
    call TriggerAddCondition(t, Condition( function Trig_ReturnSmartRestrictions_Cond))
    call TriggerAddAction(t, function Trig_ReturnSmartRestrictions_Act)
    // worker / building created -----------------------------------------------------------
    set t = CreateTrigger()
    call TriggerRegisterVariableEvent(t, "udg_UnitIndexEvent", EQUAL, 1.00)
    call TriggerAddCondition(t, Condition( function Trig_WorkerStructureCreated_Cond))
    call TriggerAddAction(t, function Trig_WorkerStructureCreated_Act)
    // building transformed (upgraded) ---------------------------------------------------
    set t = CreateTrigger()
    call TriggerRegisterVariableEvent(t, "udg_UnitTypeEvent", EQUAL, 1.00)
    call TriggerAddAction(t, function Trig_StructureUpgraded_Act)
    // building dies ------------------------------------------------------------------------
    set t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
    call TriggerAddCondition(t, Condition( function Trig_RegisteredStructureDies_Cond))
    // dummy tree dies -------------------------------------------------------------------
    set g_trg_DummyTreeDies = CreateTrigger()
    call TriggerAddAction(g_trg_DummyTreeDies, function Trig_DummyTreeDies_Actions)
   
    call MsgD("Init in library CS finished succefully")
   
    set t=null
endfunction
endlibrary
 


obligatory config triger for normal lumber harvesting:
  • ResourceWOOD
    • Events
      • Game - UnitIndexEvent becomes Equal to -1.00
    • Conditions
    • Actions
      • -------- THIS TRIGGER IS RESTRICTED TO "NORMAL WARCRAFT3 LUMBER HARVESTING" --------
      • -------- ---------------------------- --------
      • -------- start from "1", in next triggers use 2, 3, 4, 5 etc, no gaps please --------
      • Set Resource_Number = 1
      • Set Resource_Name[Resource_Number] = Lumber
      • -------- ---------------------------- --------
      • -------- do NOT use once declared harvest ability in other custom resources --------
      • Set Resource_Ability = Harvest_1 (trees)
      • -------- ---------------------------- --------
      • -------- start from "1", no gaps please --------
      • -------- you can use the same Worker in many custom resources --------
      • Set Resource_Worker[1] = Peasant
      • Set Resource_Worker[2] = Blood Mage
      • -------- ---------------------------- --------
      • -------- start from "1", no gaps please --------
      • -------- do NOT use once declared destructable in other custom resources --------
      • Set Resource_Destructable[1] = Summer Tree Wall
      • Set Resource_Destructable[2] = Cityscape Winter Tree Wall
      • Set Resource_Destructable[3] = Cityscape Snowy Tree Wall
      • -------- ---------------------------- --------
      • -------- start from "1", no gaps please --------
      • -------- you can use the same Building in many custom resources --------
      • Set Resource_Structure[1] = Town Hall
      • Set Resource_Structure[2] = Keep
      • Set Resource_Structure[3] = Castle
      • -------- ---------------------------- --------
      • -------- ---------------------------- --------
      • Custom script: call InitResource()

example custom resource configuration trigger:
  • ResourceMUSHROOMS
    • Events
      • Game - UnitIndexEvent becomes Equal to -1.00
    • Conditions
    • Actions
      • Set Resource_Number = 2
      • Set Resource_Name[Resource_Number] = Mushrooms
      • -------- ---------------------------- --------
      • -------- do NOT use once declared harvest ability in other custom resources --------
      • Set Resource_Ability = Harvest_2 (mushrums)
      • -------- ---------------------------- --------
      • -------- start from "1", no gaps please --------
      • Set Resource_Worker[1] = Peasant
      • Set Resource_Worker[2] = Priest
      • Set Resource_Worker[3] = Blood Mage
      • -------- ---------------------------- --------
      • -------- start from "1", no gaps please --------
      • Set Resource_Destructable[1] = Blue Mushrooms
      • Set Resource_Destructable[2] = Green Mushrooms
      • -------- ---------------------------- --------
      • -------- start from "1", no gaps please --------
      • Set Resource_Structure[1] = Keep
      • -------- ---------------------------- --------
      • -------- ---------------------------- --------
      • Custom script: call InitResource()
example "Custom resource has changed" event
  • UpdateLeaderboard
    • Events
      • Game - Resource_Event becomes Equal to 2.00
      • Game - Resource_Event becomes Equal to 3.00
      • Game - Resource_Event becomes Equal to 4.00
      • Game - Resource_Event becomes Equal to 5.00
    • Conditions
    • Actions
      • -------- there are 3 variables available to read --------
      • -------- "Resource_Player" - owner who recieved custom resource --------
      • -------- "Resource_Value" - how many resources have been delivered --------
      • -------- "Resource_Number" - delivered resource's number (defined in triggers "ResourceNAME"). It is usefull if you're using multiple events - like in this example. --------
      • -------- --- --------
      • Custom script: set udg_tempInt = GetPlayerCustomResource(Player(0), 2)
      • Leaderboard - Change the value for Player 6 (Orange) in (Last created leaderboard) to tempInt
      • Custom script: set udg_tempInt = GetPlayerCustomResource(Player(0), 3)
      • Leaderboard - Change the value for Player 1 (Red) in (Last created leaderboard) to tempInt
      • Custom script: set udg_tempInt = GetPlayerCustomResource(Player(0), 4)
      • Leaderboard - Change the value for Player 2 (Blue) in (Last created leaderboard) to tempInt
      • Custom script: set udg_tempInt = GetPlayerCustomResource(Player(0), 5)
      • Leaderboard - Change the value for Player 5 (Yellow) in (Last created leaderboard) to tempInt
      • -------- --- --------
      • -------- --- --------
      • Game - Display to (All players) the text: ((DemoTrigger) Player + ((Name of Resource_Player) + ( gets + ((String(Resource_Value)) + ( + (Resource_Name[Resource_Number] + <Empty String>))))))

configuration trigger for addon library CustomResourcesCost
  • CustomResourcesCostConfig
    • Events
      • Game - UnitIndexEvent becomes Equal to -1.00
    • Conditions
    • Actions
      • -------- UPGRADE --------
      • Set Resource_CostUnit = Keep
      • Custom script: call CR_SaveCost4(0, 0, 4, 0)
      • -------- ---------------------------- --------
      • -------- RESEARCH --------
      • Set Resource_CostResearch = Iron Forged Swords
      • Custom script: call CR_SaveCost4(0, 7, 1, 0)
      • -------- ---------------------------- --------
      • -------- UNIT TRAIN --------
      • Set Resource_CostUnit = Peasant
      • Custom script: call CR_SaveCost4(1, 0, 0, 1)
      • -------- ---------------------------- --------
      • Set Resource_CostUnit = Dragonhawk Rider
      • Custom script: call CR_SaveCost4(3, 0, 0, 2)
      • -------- ---------------------------- --------
      • -------- BUILD STRUCTURE --------
      • Set Resource_CostUnit = Farm
      • Custom script: call CR_SaveCost4(5, 3, 2, -5)
      • -------- ---------------------------- --------
      • -------- BUILD STRUCTURE (USING ITEM TINY FARM/etc) --------
      • Set Resource_CostItem = Tiny Farm
      • Custom script: call CR_SaveCost4(5, 3, 2, -5)
      • -------- ---------------------------- --------


addon library CustomResourcesCost
Code (vJASS):


// This is an addon to "CustomResources" library.
// Allows to set prices for: training / building / upgrades / researches
// This library does not support custom prices for item sell / unit sell.
//
// Installation and configuration
// Copy 2 triggers: "CustomResourcesCostConfig" and "CustomResourcesCost"
// All configuration is done inside trigger "CustomResourcesCostConfig".
// If you're going to use not more then 10 custom resources - use custom script
// in this demo-map there are 4 custom resources (don't count normal WOOD): Mushrooms/Stones/Icerocks/Plants
// they have numbers: Mushrooms-2/Stones-3/Icerocks-4/Plants-5
// for 4 resources to configure cost use function CR_SaveCost4, (for 6 resources use CR_SaveCost6, etc.)
// So unit cost is set using 2 lines:
// Set Resource_CostUnit = Peasant
// Custom Script: call CR_SaveCost4(1, 0, 0, 1)
// First CR_SaveCost4 parameter is Mushrooms price, then Stones price, Icerocks price and Plants price.
// So Peasant costs: 1 Mushroom(number2), 0 Stones(number3), 0 Icerocks(number4) and 1 Plant(number5).
//
// (there is alternative configuration trigger for users who needs more then 10 custom resources)
//
//=========================================================
library CustomResourcesCosts initializer Init uses CustomResources
//------------------------------------------------------------------------------------------------
globals
    private group                       g_groupCancel = CreateGroup()
    private timer                        g_timerCancel = CreateTimer()
    private integer array           g_upgradeTarget
    trigger                     g_triggerMultiOrderCancel = CreateTrigger()
endglobals
//------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------
    // this Addon uses 2 hashtable keys
    // KEY_600 => for saving prices, used as 600 + resourceNumber, (objectId as parentKey)
                        // remember resource2 cost at 602 key, resource3 at 603 key, .. etc
    // KEY_601 => for saving boolean, as a sign that object is registered (objectId as parentKey)
//===============================================================================
//experimental version of saving cost 25-12-2017
function CR_SaveCost10 takes integer v2, integer v3, integer v4, integer v5, integer v6, integer v7, integer v8, integer v9, integer v10, integer v11 returns nothing
    local integer x=2 // we do not start from "1" as 1 is normal-wood
    local integer parentKey // check if it's object or research or item?
    local integer array cost
    set cost[2]=v2
    set cost[3]=v3
    set cost[4]=v4
    set cost[5]=v5
    set cost[6]=v6
    set cost[7]=v7
    set cost[8]=v8
    set cost[9]=v9
    set cost[10]=v10
    set cost[11]=v11
   
    if udg_Resource_CostUnit>0 then
        set parentKey = udg_Resource_CostUnit
    elseif udg_Resource_CostResearch>0 then
        set parentKey = udg_Resource_CostResearch
    elseif udg_Resource_CostItem>0 then
        set parentKey = udg_Resource_CostItem        
    endif
    call SaveBoolean(CustomResources_g_hashCR, parentKey, CustomResources_KEY_601, true) // save "true" as sign that object is registered
    loop
        exitwhen x>CustomResources_g_customResourcesCount
        call SaveInteger(CustomResources_g_hashCR, parentKey, CustomResources_KEY_600+x, cost[x])
        // remember resource2 cost at 602 key, resource3 at 603 key, .. etc
        set x=x+1
    endloop    
    //reset integers for next call:
    set udg_Resource_CostUnit = 0
    set udg_Resource_CostResearch = 0    
    set udg_Resource_CostItem = 0    
endfunction
//-------------------------------------------------------------------------------------------------------------------------
function CR_SaveCost9 takes integer v2, integer v3, integer v4, integer v5, integer v6, integer v7, integer v8, integer v9, integer v10 returns nothing
    call CR_SaveCost10(v2,v3,v4,v5,v6,v7,v8,v9,v10,0)
endfunction
function CR_SaveCost8 takes integer v2, integer v3, integer v4, integer v5, integer v6, integer v7, integer v8, integer v9 returns nothing
    call CR_SaveCost10(v2,v3,v4,v5,v6,v7,v8,v9,0,0)
endfunction
function CR_SaveCost7 takes integer v2, integer v3, integer v4, integer v5, integer v6, integer v7, integer v8 returns nothing
    call CR_SaveCost10(v2,v3,v4,v5,v6,v7,v8,0,0,0)
endfunction
function CR_SaveCost6 takes integer v2, integer v3, integer v4, integer v5, integer v6, integer v7 returns nothing
    call CR_SaveCost10(v2,v3,v4,v5,v6,v7,0,0,0,0)
endfunction
function CR_SaveCost5 takes integer v2, integer v3, integer v4, integer v5, integer v6 returns nothing
    call CR_SaveCost10(v2,v3,v4,v5,v6,0,0,0,0,0)
endfunction
function CR_SaveCost4 takes integer v2, integer v3, integer v4, integer v5 returns nothing
    call CR_SaveCost10(v2,v3,v4,v5,0,0,0,0,0,0)
endfunction
function CR_SaveCost3 takes integer v2, integer v3, integer v4 returns nothing
    call CR_SaveCost10(v2,v3,v4,0,0,0,0,0,0,0)
endfunction
function CR_SaveCost2 takes integer v2, integer v3 returns nothing
    call CR_SaveCost10(v2,v3,0,0,0,0,0,0,0,0)
endfunction
function CR_SaveCost1 takes integer v2 returns nothing
    call CR_SaveCost10(v2,0,0,0,0,0,0,0,0,0)
endfunction


//-------------------------------------------------------------------------------------------------------------------------
function CustomResource_SaveCosts takes nothing returns nothing
    local integer x=2 // we do not start from "1" as 1 is normal-wood
    local integer parentKey
    //is it object or research or item?
    if udg_Resource_CostUnit>0 then
        set parentKey = udg_Resource_CostUnit
    elseif udg_Resource_CostResearch>0 then
        set parentKey = udg_Resource_CostResearch
    elseif udg_Resource_CostItem>0 then
        set parentKey = udg_Resource_CostItem        
    endif
   
    call SaveBoolean(CustomResources_g_hashCR, parentKey, CustomResources_KEY_601, true) // save "true" as sign that object is registered
    loop
        exitwhen x>CustomResources_g_customResourcesCount
        call SaveInteger(CustomResources_g_hashCR, parentKey, CustomResources_KEY_600+x, udg_Resource_Cost[x])
        // remember resource2 cost at 602 key, resource3 at 603 key, .. etc
        //reset for next object:
        set udg_Resource_Cost[x] = 0
        set x=x+1
    endloop    
    //reset integers:
    set udg_Resource_CostUnit = 0
    set udg_Resource_CostResearch = 0    
    set udg_Resource_CostItem = 0    
endfunction
//------------------------------------------------------------------------------------------------
private function IsCustomCostDefined takes integer objectId returns boolean // object: unit/structure/upgrade-building/research
    return LoadBoolean(CustomResources_g_hashCR, objectId, CustomResources_KEY_601)
endfunction
//------------------------------------------------------------------------------------------------
private function GetObjectCost takes integer objectId, integer resourceNumber returns integer
    // resourceNumber = defined in ResourceMUSHROOM and so on triggers, as udg_Resource_Number
    // will not return value for resourceNumber=1 as it is normal war3 wood!
    return LoadInteger(CustomResources_g_hashCR, objectId, CustomResources_KEY_600+resourceNumber)
endfunction


//=========================================================
//------------------------TRIGGERS  RELATED---------------------------------------------------
//=========================================================

//------------------------------------------------------------------------------
private function CancelOrder_Enum takes nothing returns nothing
    local unit u = GetEnumUnit()
    call GroupRemoveUnit(g_groupCancel, u)
    call IssueImmediateOrderById(u, 851976) //upgrades , researches , trains
    call IssueImmediateOrderById(u, ORDER_stop) //for builders
    set u=null
endfunction

private function CancelOrder takes nothing returns nothing
    call DisableTrigger(g_triggerMultiOrderCancel)
    call DisableTrigger(CustomResources_g_trg_WorkerBringsResources) // protect agains double fire "worker brings resource" event
    call ForGroup(g_groupCancel, function CancelOrder_Enum)
    call EnableTrigger(CustomResources_g_trg_WorkerBringsResources)
    call EnableTrigger(g_triggerMultiOrderCancel)        
endfunction

private function UnitCancelOrder takes unit u returns nothing
    call GroupAddUnit(g_groupCancel, u)
    call TimerStart(g_timerCancel, 0.00, false, function CancelOrder)
endfunction
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
private function PlayerHasMoneyForObject takes player pla, integer objectId returns boolean
    local integer x=2
    local integer cost=0
    local string s = " "
    local boolean hasMoney = true
    loop
        exitwhen x>CustomResources_g_customResourcesCount
        set cost = GetObjectCost(objectId, x)
        if cost>0 then
            if cost <= GetPlayerCustomResource(pla, x) then
                set s = s + ("|cff00ff00"+udg_Resource_Name[x] + "|r(" + I2S(cost) + ") ") // green color
            else // not enough resource nr x
                set hasMoney=false
                set s = s + ("|cffff0000"+udg_Resource_Name[x] + "|r(" + I2S(cost) + ") ") // red color
            endif
        endif
        set x=x+1
    endloop
   
    if (not hasMoney) then//and udg_Resource_PrintWarnings then
        call Msg(pla, "Not enough resources: " + s)
    endif
    return hasMoney
endfunction
//-------------------------------------------------------------------------------------------
private function PlayerPayPriceForObject takes player pla, integer objectId returns nothing
    local integer x=2
    local integer cost=0
    loop
        exitwhen x>CustomResources_g_customResourcesCount
        set cost = GetObjectCost(objectId, x)
        //if cost>0 then
            call AdjustPlayerCustomResource(pla, x, - cost)
        //endif
        set x=x+1
    endloop
    call MsgD("CustomCost "+GetPlayerName(pla) + " just payed price for " + GetObjectName(objectId))
endfunction
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
private function Trig_MultiOrder_Cond takes nothing returns boolean
    return IsCustomCostDefined(GetIssuedOrderId())
endfunction
//------------------------------------------------------------------------------
private function Trig_MultiOrder_Act takes nothing returns nothing
    local integer h = GetHandleId(GetTriggerEventId())
    local unit u = GetOrderedUnit()
    local player pla = GetOwningPlayer(u)
    local integer ord = GetIssuedOrderId()
    local integer id = GetUnitUserData(u)
   
    if h==38 then // instant: TRAIN  UNIT or RESEARCH or UPGRADE
            if not PlayerHasMoneyForObject(pla, ord) then //no $$$ ?
                call UnitCancelOrder(u)
            else //has $?, pay price:
                set g_upgradeTarget[id] = ord //if it is UPGRADE save target-building (ord) as variable under number id
                call PlayerPayPriceForObject(pla, ord)
            endif
   
    elseif h==39 and (UnitId2String(ord) != null) then // point-order: BUILD  STRUCTURE
            if not PlayerHasMoneyForObject(pla, ord) then //no $$$ ?
                call UnitCancelOrder(u)
                call MsgD("CustomCost "+GetUnitName(u) + " canceled build structure - no money" )
            endif // paying is executed under event EVENT_PLAYER_UNIT_CONSTRUCT_START
    endif
   
    set u=null
    set pla=null
endfunction

//----------------------------------------------------------------------------------
//---------------------------trigger Begins Construction -------------------------
//----------------------------------------------------------------------------------
private function Trig_BeginsConstr_Cond takes nothing returns boolean                
    return IsCustomCostDefined(GetUnitTypeId(GetConstructingStructure()))
endfunction
//------------------------------------------------------------------
private function Trig_BeginsConstr_Act takes nothing returns nothing
    local unit u = GetConstructingStructure()
    local player pla = GetOwningPlayer(u)
    local integer objectId = GetUnitTypeId(u)
    if not PlayerHasMoneyForObject(pla, objectId) then //no $$$ ?
        call UnitCancelOrder(u)  
    else //has $?, pay price:
        call PlayerPayPriceForObject(pla, objectId)
    endif
    set u=null
    set pla=null
endfunction

//---------------------------------------------------------------------------------------------
// --------------------------trigger CANCEL--------------------------------------------------
//---------------------------------------------------------------------------------------------
private function GiveMoneyBackOnCancel takes player pla, integer objectId returns nothing
    local integer x=2
    local integer cost=0
    loop
        exitwhen x>CustomResources_g_customResourcesCount
        set cost = GetObjectCost(objectId, x)
        call AdjustPlayerCustomResource(pla, x, cost)
        set x=x+1
    endloop
endfunction
//---------------------------------------------------------------------------------------------
private function Trig_MultiOrderCancel_Actions takes nothing returns nothing
    local integer h = GetHandleId(GetTriggerEventId())    
    local unit u = GetTriggerUnit() // the same for all 4 events
    local player pla = GetOwningPlayer(u) // the same for all 4 events
    local integer objectId
    //---------------------------------------------------------------------------
    if h==33 then // EVENT_PLAYER_UNIT_TRAIN_CANCEL
        set objectId = GetTrainedUnitType()
        if IsCustomCostDefined(objectId) then
            call GiveMoneyBackOnCancel(pla, objectId)
            call MsgD("CustomCost "+GetUnitName(u) + " canceled TRAIN" )
        endif        
    //---------------------------------------------------------------------------
    elseif h==27 then // EVENT_PLAYER_UNIT_CONSTRUCT_CANCEL
        set objectId = GetUnitTypeId(u)
        if IsCustomCostDefined(objectId) then
            call GiveMoneyBackOnCancel(pla, objectId)
            call MsgD("CustomCost "+GetUnitName(u) + " canceled BUILD" )
        endif
    //---------------------------------------------------------------------------
    elseif h==36 then // EVENT_PLAYER_UNIT_RESEARCH_CANCEL
        set objectId = GetResearched()
        if IsCustomCostDefined(objectId) then
            call GiveMoneyBackOnCancel(pla, objectId)
            call MsgD("CustomCost "+GetPlayerName(pla) + " canceled RESEARCH" )
        endif
    //---------------------------------------------------------------------------
    elseif h==30 then // EVENT_PLAYER_UNIT_UPGRADE_CANCEL
        set objectId = g_upgradeTarget[GetUnitUserData(u)] // upgrade TARGET building
                                                                        // (gives Keep - if Keep cancels upgrade and revert itself to TownHall)
        if IsCustomCostDefined(objectId) then
            call GiveMoneyBackOnCancel(pla, objectId)
            call MsgD("CustomCost "+GetPlayerName(pla) + " canceled UPGRADE BUILDING" )
        endif
    //---------------------------------------------------------------------------    
    endif
    set u=null
    set pla=null
endfunction
//---------------------------------------------------------------------------------------------
// items based on "Build Tiny Farm" etc:
private function Trig_ItemUsed takes nothing returns boolean
    local integer itemId=GetItemTypeId(GetManipulatedItem())
    local unit u=GetManipulatingUnit()
    local boolean backup=udg_Resource_PrintWarnings
    set udg_Resource_PrintWarnings=false
    if IsCustomCostDefined(itemId) and (not PlayerHasMoneyForObject(GetOwningPlayer(u), itemId)) then //refund lost item:
        set bj_lastCreatedItem = CreateItem(itemId, GetUnitX(u), GetUnitY(u))
        call UnitAddItem(u, bj_lastCreatedItem)
    endif
    set udg_Resource_PrintWarnings=backup
    set u=null
    return false
endfunction

//=========================================================
//=========================================================
private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_ISSUED_ORDER )
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER )
    call TriggerAddCondition( t, Condition(function Trig_MultiOrder_Cond))
    call TriggerAddAction( t, function Trig_MultiOrder_Act )
   
    set t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_CONSTRUCT_START )
    call TriggerAddCondition( t, Condition( function Trig_BeginsConstr_Cond ) )
    call TriggerAddAction( t, function Trig_BeginsConstr_Act )

    call TriggerRegisterAnyUnitEventBJ( g_triggerMultiOrderCancel, EVENT_PLAYER_UNIT_TRAIN_CANCEL )
    call TriggerRegisterAnyUnitEventBJ( g_triggerMultiOrderCancel, EVENT_PLAYER_UNIT_CONSTRUCT_CANCEL )
    call TriggerRegisterAnyUnitEventBJ( g_triggerMultiOrderCancel, EVENT_PLAYER_UNIT_RESEARCH_CANCEL )
    call TriggerRegisterAnyUnitEventBJ( g_triggerMultiOrderCancel, EVENT_PLAYER_UNIT_UPGRADE_CANCEL )
    call TriggerAddAction( g_triggerMultiOrderCancel, function Trig_MultiOrderCancel_Actions )

    set t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_USE_ITEM)
    call TriggerAddCondition(t, Condition(function Trig_ItemUsed))
    call MsgD("Init in library Cost finished succefully")
    set t=null
endfunction

endlibrary
 


update 01-06-2017
added Event "Custom resource was delivered", small code improvements
update 18-08-2017
fixed bug when resource was not visible it allows to harvest by not valid harvester
update 25-12-2017
fixing code to work properly with normal warcraft lumber, following DSG suggestions, merge CustomResources library with CustomResourcesCost library
Previews
Contents

Custom Resources 1.31 (Map)

Reviews
Dr Super Good
A useful custom resource system that seems pretty robust. Also easy to setup and configure for GUI users with limited JASS knowledge. Improvement Suggestions: Add features to the test map to demonstrate and show off the system more. Place floating...
  1. UmbraUnda

    UmbraUnda

    Joined:
    Mar 31, 2016
    Messages:
    605
    Resources:
    1
    Maps:
    1
    Resources:
    1
    Next level custom harvesting right here!
     
  2. A]mun

    A]mun

    Joined:
    Dec 4, 2007
    Messages:
    741
    Resources:
    0
    Resources:
    0
    Pretty interesting, is there any performance hit noticeable?
     
  3. ZiBitheWand3r3r

    ZiBitheWand3r3r

    Joined:
    Nov 21, 2012
    Messages:
    896
    Resources:
    15
    Maps:
    7
    Spells:
    8
    Resources:
    15
    not at all, if you look at variables there is hashtable, few unit group (for 5 custom resource = 5 groups), not much ;) no periodic loops
     
  4. Reventhous

    Reventhous

    Joined:
    May 2, 2015
    Messages:
    104
    Resources:
    1
    Spells:
    1
    Resources:
    1
    Cool idea! Here's my suggestion.

    -this is vJass, so instead
    call ExecuteFunc("func_name")
    , just directly
    call func_name

    -this system is about creating custom resources, instead using hashtable, try
    struct

    -put
    uses
    after
    library SystemName
    and list down the required system
    -better use Table instead hashtable.
    -I believe there is IsDestructibleATree snippet out there, use it
    -use TimerUtils when working with timers
    -try use GetClosestWidget snippet
    -
    Code (vJASS):
    private function InitSettingGUIVariables takes nothing returns nothing // run at init
        local integer x=1
        loop
            exitwhen x>99
            set udg_Resource_Worker[x]=0
            set udg_Resource_Destructable[x]=0
            set udg_Resource_Structure[x]=0
            set x=x+1
        endloop
    endfunction

    what is 99?
     
  5. ZiBitheWand3r3r

    ZiBitheWand3r3r

    Joined:
    Nov 21, 2012
    Messages:
    896
    Resources:
    15
    Maps:
    7
    Spells:
    8
    Resources:
    15
    I do not want to ask user to place library above configuration triggers. thats why ExecuteFunc has been used
    I do not understand structs fully to use it in public resource, sorry
    I'll do that for library ORDER
    It's true, but since I am tracking/manipulating orders (like harvest, resumeharvesting and so on) it may interfere if end-user uses worker id in this system which is registered in external IsDestructibleATree library. Thats why I prefer to stay with unique unit for checking is destructable a tree.
    This system already requires 3 other system. Heh, are you serious to suggest to use TimerUtils instead of two (2) global timers? One timer runs as a game timer, I just read its elapsed time, no more. Second is used as 0sec duration timer to cancel some ordrers ;)
    as above. And it is 20 lines of code now, GetClosestWidget is a little more. Plus I am searching for closest unit using
    ForGroup
    with condition
    isBuildingUnderConstruction

    anyway thanks for input

    edit
    99 is max number of destructables, workers and structures for each custom resource
     
  6. Reventhous

    Reventhous

    Joined:
    May 2, 2015
    Messages:
    104
    Resources:
    1
    Spells:
    1
    Resources:
    1
    You are using
    library
    dude, doesn't need to do that. Believe me, use
    call func_name


    Explain to me why you use global timer instead local for the 0sec.

    There's two option,
    1. Learn struct and your work will become easier
    2. Stay with hashtable and make the 99 configurable

    I can't find any of the global variable being used outside the library so,
    remove the prefix (
    g_
    ) and add
    private
     
  7. ZiBitheWand3r3r

    ZiBitheWand3r3r

    Joined:
    Nov 21, 2012
    Messages:
    896
    Resources:
    15
    Maps:
    7
    Spells:
    8
    Resources:
    15
    Good news! Bribe updated his famous Unit Event (which is requirement here) with new event that this system needed. I made also small updated with
    call
    instead of
    ExecuteFunc
    (Reventhous, I really forgot that system was packed as library :D), variables are private (with one exception)
     
  8. UmbraUnda

    UmbraUnda

    Joined:
    Mar 31, 2016
    Messages:
    605
    Resources:
    1
    Maps:
    1
    Resources:
    1
    Nice - what does that affect in game play? or does it just run smoother now?

    Also, will you be able to add unit cost and multiboard w/ icons later on?
     
  9. ZiBitheWand3r3r

    ZiBitheWand3r3r

    Joined:
    Nov 21, 2012
    Messages:
    896
    Resources:
    15
    Maps:
    7
    Spells:
    8
    Resources:
    15
    call vs ExecuteFunc is faster, but it won't affect game play as function InitResources() is called only once for each resource at map init.
    I can make an addon for this library for training/build costs.
    Displaying custom resources is not really connected with system's core and can be made as leaderboard (like in demo map) or multiboard or diffrent ways. In your Medieval Realms you're already using MB for displaying upgrading structure's parameters, and knowing that only 1 MB can be displayed for player it may be a problem. Leaderboard however can be displayed parallel with MB, I'm just not sure if leaderboard allows to display icons?
     
  10. UmbraUnda

    UmbraUnda

    Joined:
    Mar 31, 2016
    Messages:
    605
    Resources:
    1
    Maps:
    1
    Resources:
    1
    Awesome, you're the best mate.

    Oh, I see. Aw, really? Only one MB per player? Hmm, perhaps there's a way to add resource icons + amounts to the MB that's already there?
    Otherwise, I'll be satisfied if I can add icons to the leaderboard - i'll tinker around and see if I find something in the GUI for that.

    EDIT: Didn't find anything icon related to display on leaderboard. However I found 'Leaderboard - Show/Hide' line that works with a variable. Can variables be icons?
     
    Last edited: May 30, 2017
  11. UmbraUnda

    UmbraUnda

    Joined:
    Mar 31, 2016
    Messages:
    605
    Resources:
    1
    Maps:
    1
    Resources:
    1
    What's new in v1.20?
     
  12. ZiBitheWand3r3r

    ZiBitheWand3r3r

    Joined:
    Nov 21, 2012
    Messages:
    896
    Resources:
    15
    Maps:
    7
    Spells:
    8
    Resources:
    15
    "Custom resource was delivered EVENT"
    look at 'food1' trigger as point of reference,
    use as event "Resource_Event" becomes equal to X, where X is Resource_Number defined in triggers "ResourceNAME"
    variables available inside trigger: "Resource_Player" - owner who recieved resource nr X, value is: "Resource_Value"

    mostly this, also added Resource_Name (it is for warning messages for addon system ResourceCost)
    for your needs: new event allows to update gold instead of displaying one custom resource on leaderboard
     
  13. UmbraUnda

    UmbraUnda

    Joined:
    Mar 31, 2016
    Messages:
    605
    Resources:
    1
    Maps:
    1
    Resources:
    1
    Not sure if I comprehend everything... I'll try to implement and see what happens.

    EDIT: Ok, I get the first part.
    What new event to allow gold update? and where is it?
    Could you be more descriptive or post screenshots or use trigger/jass tags?
     
    Last edited: Jun 2, 2017
  14. ZiBitheWand3r3r

    ZiBitheWand3r3r

    Joined:
    Nov 21, 2012
    Messages:
    896
    Resources:
    15
    Maps:
    7
    Spells:
    8
    Resources:
    15
    upon an Event read this value (for food)
    GetPlayerCustomResource(player p, integer resourceNumber) returns integer
    and Set player's gold to read value

    event allows you to not use periodic to update gold, do it just when event fires
    ps you may want to try an addon Custom ResourcesCost
     
  15. UmbraUnda

    UmbraUnda

    Joined:
    Mar 31, 2016
    Messages:
    605
    Resources:
    1
    Maps:
    1
    Resources:
    1
    What? So after the first line in trigger Food1, I add that line as custom script? Can you jump on Hive chat?
    EDIT: I got errors - idk what you mean by "upon an Event, read this value" and idk how to "Set player's gold to read value"
     
  16. Reventhous

    Reventhous

    Joined:
    May 2, 2015
    Messages:
    104
    Resources:
    1
    Spells:
    1
    Resources:
    1
    :) . The variable looks better now.
    About the function, can you remove the
    trig_
    because its not really neccesary to have them.
    And can you make their name shorter.


    Took me a while to find this line
    set udg_Resource_Event = 5.00

    because doesn't exist. :D
    I'm not sure about the "X" thing, better leave it to 5.00 or something constant.
     
  17. ZiBitheWand3r3r

    ZiBitheWand3r3r

    Joined:
    Nov 21, 2012
    Messages:
    896
    Resources:
    15
    Maps:
    7
    Spells:
    8
    Resources:
    15
    hehe you right ;)
    this does inside function
    Trig_WorkerBringsResources_Act

    Code (vJASS):

    set udg_Resource_Event = 0.00
    set udg_Resource_Event = I2R(resourceNr)
    set udg_Resource_Event = 0.00
    we do not know how many resources user will add, so it may fire with diffrent numbers, its not constant. Event==5 means resource number5 was delivered, other numbers are for other resources
     
  18. Reventhous

    Reventhous

    Joined:
    May 2, 2015
    Messages:
    104
    Resources:
    1
    Spells:
    1
    Resources:
    1
    I'm saying "not sure" because that method will requires user to have multiple event or trigger which is doesn't look... good, and I believe that method will be a problem for you to add another event.

    So, my idea is change the event to 1.00 (constant), then add two integer variable named "Resource_LastCreatedType" and "Resource_EventId".
    After that, make the
    InitResource
    function returns integer. In the end of the line in that function, add this
    Code (vJASS):
    set udg_Resource_LastCreatedType = theId
    return udg_Resource_LastCreatedType

    Now, the user will have the ability to do this.
    • Custom script: set udg_myCustomResource = InitResource()

    or this
    • Custom script: call InitResource()
    • Set myCustomResource = Resource_LastCreatedType


    In the
    Trig_WorkerBringsResources_Act
    add
    set udg_Resource_EventId = currentId
    before event execution.

    Finally,
    • ResourceDelivered
      • Events
        • Game - Resource_Event becomes Equal to 1.00
      • Conditions
        • Resource_EventId Equal to myCustomResource
      • Actions
        • Do anything


    Done. :)
     
    Last edited: Jun 5, 2017
  19. ZiBitheWand3r3r

    ZiBitheWand3r3r

    Joined:
    Nov 21, 2012
    Messages:
    896
    Resources:
    15
    Maps:
    7
    Spells:
    8
    Resources:
    15
    to be honest I don't see advantages from adding 2 more variables,
    not at all, it already works 100% fine!
    Let me describe it:
    Trig_WorkerBringsResources_Act
    fires event
    Code (vJASS):
    set udg_Resource_Event = 0.00
    set udg_Resource_Event = I2R(resourceNr)
    set udg_Resource_Event = 0.00

    local integer resourceNr = GetHarvestAbilityNumber(abi)
    and GetHarvestAbilityNumber load integer from hashtable. this integer is resource_number defined by a user in configuration trigger(s) at map init:
    • Set Resource_Number = 2
    • Set Resource_Name[Resource_Number] = Mushrooms
    • Set Resource_Ability = Harvest_2 (mushrums)

    InitResource is called once per resource only at map init.

    when player right click on diffrent resource-type the ability on ordered worker is switched to match new destructable-type (if worker is allowed to harvest this of course). When worker brings harvested resources I'm reading his abilityId and fire event connected to ability have been read. (By the way the key to make it all works is event "worker brings lumber" which I developed here) I guess it is easier to understand if view in World Editor not only pure code ;)