• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

Resource System v1.3

A system I made a year ago, the last thing before I went more or less inactive in Wc3.
Originally made for a contest on inwarcraft.de, achieving one of the two first places.

Features:
  • Creation and handling of custom resources
  • Events for resource-related actions (e.g.: Buying an Item, casting a Spell, mining a resource)
  • Function to check if a building is finished

Requires:
  • JassHelper by Vexorian
  • TimerUtils by Vexorian
  • SimError by Vexorian
  • GroupUtils by Rising_Dusk
  • GetUnitCollisionSize by me. Code is included in the map, since I made this system for the Resource-System

JASS:
//Resource System
//Made by Verethragna (aka D1000)
//v1.3
//For more informations read the readme

//! zinc
    library ResourceSystem requires GroupUtils, TimerUtils, GetUnitCollisionSize, SimError {
        private constant integer MAX_RESOURCES = 30; //Maximal number of resources, can't exceed 511
    
        private constant integer MINE_ABILITY = 'MINE'; // The id of the "Mine"-ability
        private constant string MINE_ORDER = "channel"; // The order of the "Mine"-Ability
        private constant integer RETURN_ABILITY = 'RETU'; // The id of the "Return"-ability
        private constant string RETURN_ORDER = "absorb"; // The order of the "Return"-Ability
        
        private constant boolean USE_EVENTS = false; //Set this to true, if you want to use events in your map. Setting it to false will make the system a bit faster.
        
        private constant real STORAGE_RANGE = 150, //How close the worker has to be to the storage to return the resources
                              STORAGE_SEARCH_RANGE = 6144, //Search range to return the resources
                              SOURCE_SEARCH_RANGE = 3072, //Search range to find new sources
                                            
                              MAX_BUILD_RANGE = 100; //I don´t know if it is posible to change it, but if you do, change this value too.
        
        private constant string NOT_ENOUGH_TEXT = "Not enough ", //Text displayed when trying to buy something with insufficient resources 
        
                              MULTIBOARD_NAME = "Resources"; //Title of the multiboard
        private constant real MULTIBOARD_NAME_WIDTH = 0.05, // Width of the multiboard column for the name of the resources
                              MULTIBOARD_VALUE_WIDTH = 0.015, // Width of the multiboard column for the amount of the resources
                
                              BUILDING_REFUND_FACTOR = 0.75, //Percentage of the spent resources, that you'll get back when you cancel a building
                              ITEM_SELL_FACTOR = 0.75; //Percentage of the spent resources, that you'll get back when you sell an item
        //===================================================================\\
        
        public constant integer FARM_TYPE_INSTANT = 0,
                                FARM_TYPE_WALK = 1,
                                               
                                BUY_TYPE_SOLD = 1,
                                BUY_TYPE_BUILDING = 2,
                                BUY_TYPE_TRAINED = 3,
                                BUY_TYPE_ABILITY = 4;
                                                 
        public type ResourceEventCast extends function();
        public type ResourceEventItemSale extends function();
        public type ResourceEventUnitSale extends function();
        public type ResourceEventTrain extends function();
        public type ResourceEventBuildOrder extends function();
        
        public type ResourceEventMine extends function(Resource, unit, widget);
        public type ResourceEventReturn extends function(Resource, unit, integer, unit);
                                                 
        private trigger trigOnDeath;
            
        public struct Resource{            
            private static thistype number, tempResource;
            private static boolexpr addWorkerPick, addStoragePick, searchSourcePickUnit, searchSourcePickItem, searchSourcePickDestructable, searchStoragePick, allBuildings;
            private static rect wholeMap;
            private static integer tempType;
            private static real tempDistance, tempX, tempY;
            private static trigger inRange, trainCancelled, constructionCancelled, orderGiven;
            private static multiboard properties[12];
            private static widget tempWidget;
            private static unit tempUnit;
            
            private string name,
                           icon,
                           colour = "";
            private integer amount[0x10];
            private boolean onKill;
            private sound hitSound;
            
            private static constant integer AMOUNT_CARRIED = 0,
                                            GAIN_PER_HIT = 0x10,
                                            DAMAGE = 0x20,
                                            FARM_TYPE = 0x30,
                                            SPEED = 0x40,
                                            ANIMATION = -1,
                                            COSTS = -2,
                                            BUY_TYPE = -3,
                                            CURRENTLY_BUILDING = -4,
                                            FINISHED = -5,
                                            
                                            EVENT_CAST = -6,
                                            EVENT_ITEM = -7,
                                            EVENT_UNIT = -8,
                                            EVENT_TRAIN = -9,
                                            EVENT_BUILD = -10,
                                            EVENT_MINE = -11,
                                            EVENT_RETURN = -12,
                                            
                                            SOURCE = 0,
                                            MINER = 2,
                                            MINER_STRUCT = 3,
                                            STORAGE = 4,
                                                                
                                            ORDER_SMART = 851971;
                                            //ORDER_RALLIED = 851970; -> GetOrderTarget does not work in some cases
                                                                
            private static hashtable data = InitHashtable();
            
            static method create(string name, string icon, boolean onKill) -> thistype {
                integer i;
                
                number = thistype.allocate();
                static if (DEBUG_MODE)
                    if(integer(number) > MAX_RESOURCES)
                        BJDebugMsg("|cffFF0000ResourceSystem ERROR:|r Number of resources exceeded 'MAX_RESOURCES'");
                number.name = name;
                number.icon = icon;
                number.onKill = onKill;
                
                return number;
            }
            
            method getName() -> string {
                return name;
            }
            
            method getIcon() -> string {
                return icon;
            }
            
            method addSource(integer sourceId) {
                SaveInteger(data, SOURCE, sourceId, this);
            }
            
            static method getResourceBySource(integer sourceId) -> thistype {
                return LoadInteger(data, SOURCE, sourceId);
            }
            
            method addStorage(integer id) {
                group g;
                SaveBoolean(data, STORAGE*MAX_RESOURCES+this, id, true);
                
                if (LoadBoolean(data, STORAGE, id))
                    return;
                    
                SaveBoolean(data, STORAGE, id, true);
                tempType = id;
                GroupEnumUnitsInRect(ENUM_GROUP, wholeMap, addStoragePick);
                GroupClear(ENUM_GROUP);
            }
            
            method isStorage(integer id) -> boolean {
                return LoadBoolean(data, STORAGE*MAX_RESOURCES+this, id);
            }
            
            method addMiner(integer unitTypeId, string anim ,real speed, real damagePerHit, integer gain, integer amountCarried, integer farmType) {
                group g;
                integer i;
                for(0 <= i < bj_MAX_PLAYERS) {
                    SaveInteger(data, (i + AMOUNT_CARRIED)*MAX_RESOURCES+this, unitTypeId, amountCarried);
                    SaveInteger(data, (i + GAIN_PER_HIT)*MAX_RESOURCES+this, unitTypeId, gain);
                    SaveReal(data, (i + DAMAGE)*MAX_RESOURCES+this, unitTypeId, damagePerHit);
                    SaveInteger(data, (i + FARM_TYPE)*MAX_RESOURCES+this, unitTypeId, farmType);
                    SaveReal(data, (i + SPEED)*MAX_RESOURCES+this, unitTypeId, speed);
                    SaveStr(data, ANIMATION*MAX_RESOURCES+this, unitTypeId, anim);
                }
                
                if (LoadBoolean(data, MINER, unitTypeId))
                    return;
                    
                SaveBoolean(data, MINER, unitTypeId, true);
                tempType = unitTypeId;
                GroupEnumUnitsInRect(ENUM_GROUP, wholeMap, addWorkerPick);
                GroupClear(ENUM_GROUP);
            }
            
            method addProperty(integer whichPlayer, integer amount) {
                this.amount[whichPlayer] += amount;
                updateMultiboard(whichPlayer);
            }
            
            method setProperty(integer whichPlayer, integer amount) {
                this.amount[whichPlayer] = amount;
                updateMultiboard(whichPlayer);
            }
            
            method getProperty(integer whichPlayer) -> integer{
                return this.amount[whichPlayer];
            }
            
            static method showMultiboard(integer whichPlayer, boolean flag) {
                if (GetLocalPlayer() == Player(whichPlayer))
                    MultiboardDisplay(properties[whichPlayer], flag);
            }
            
            static method minimizeMultiboard(integer whichPlayer, boolean flag) {
                MultiboardMinimize(properties[whichPlayer], flag);
            }
            
            //A textmacro creating the set & get functions for integer values (AmountCarried, GainPerHit & FarmType)
            //! textmacro setDataInteger takes NAME, VAR
                method set$NAME$(player p, integer value, integer unitTypeId) {
                    SaveInteger(data, (GetPlayerId(p) + $VAR$)*MAX_RESOURCES+this, unitTypeId, value);
                }
            
                method set$NAME$ById(integer p, integer value, integer unitTypeId) {
                    SaveInteger(data, (p + $VAR$)*MAX_RESOURCES+this, unitTypeId, value);
                }
                
                method get$NAME$(player p, integer unitTypeId) -> integer {
                    return LoadInteger(data, (GetPlayerId(p) + $VAR$)*MAX_RESOURCES+this, unitTypeId);
                }
            
                method get$NAME$ById(integer p, integer unitTypeId) -> integer {
                    return LoadInteger(data, (p + $VAR$)*MAX_RESOURCES+this, unitTypeId);
                }
            //! endtextmacro
            
            //A textmacro creating the set & get functions for real values (Damage & Speed)
            //! textmacro setDataReal takes NAME, VAR
                method set$NAME$(player p, real value, integer unitTypeId) {
                    SaveReal(data, (GetPlayerId(p) + $VAR$)*MAX_RESOURCES+this, unitTypeId, value);
                }
            
                method set$NAME$ById(integer p, real value, integer unitTypeId) {
                    SaveReal(data, (p + $VAR$)*MAX_RESOURCES+this, unitTypeId, value);
                }
                
                method get$NAME$(player p, integer unitTypeId) -> real {
                    return LoadReal(data, (GetPlayerId(p) + $VAR$)*MAX_RESOURCES+this, unitTypeId);
                }
            
                method get$NAME$ById(integer p, integer unitTypeId) -> real {
                    return LoadReal(data, (p + $VAR$)*MAX_RESOURCES+this, unitTypeId);
                }
            //! endtextmacro
            
            //! runtextmacro setDataInteger("AmountCarried", "AMOUNT_CARRIED")
            //! runtextmacro setDataInteger("GainPerHit", "GAIN_PER_HIT")
            //! runtextmacro setDataReal("Damage", "DAMAGE")
            //! runtextmacro setDataReal("Speed", "SPEED")
            //! runtextmacro setDataInteger("FarmType", "FARM_TYPE")
            
            method setCosts(integer typeId, integer costs, integer buyType) {
                SaveInteger(data, COSTS*MAX_RESOURCES+this, typeId, costs);
                SaveInteger(data, BUY_TYPE, typeId, buyType);
            }
            
            method getCosts(integer typeId) -> integer {
                return LoadInteger(data,COSTS*MAX_RESOURCES+this,typeId);
            }
            
            method setAnimation(integer unitTypeId, string anim) {
                SaveStr(data, ANIMATION*MAX_RESOURCES+this, unitTypeId, anim);
            }
            
            method getAnimation(integer unitTypeId) -> string {
                return LoadStr(data, ANIMATION*MAX_RESOURCES+this, unitTypeId);
            }
            
            method setColour(string colour) {
                this.colour= "|cff" + colour;
            }
            
            method getColour() -> string {
                return colour;
            }
            
            static method isBuildingFinished(unit building) -> boolean {
                return LoadBoolean(data, FINISHED, GetHandleId(building));
            }
            
            method floatingValue(integer value, real x, real y) {
                texttag t = CreateTextTag();
                SetTextTagPos(t, x, y, 20);
                SetTextTagPermanent(t, false);
                SetTextTagText(t, colour + "+" + I2S(value)+ "|r", 0.023);
                SetTextTagVelocity(t, 0, 0.035);
                SetTextTagLifespan(t, 1.5);
                SetTextTagFadepoint(t, 0.8);
                t = null;
            }
            
            //A textmacro creating the functions to set static events
            //! textmacro setEvent takes NAME, VAR
                static method set$NAME$Event(integer id, ResourceEvent$NAME$ callback){
                    SaveInteger(data, EVENT_$VAR$, id, callback);
                }
            //! endtextmacro
            
            //! runtextmacro setEvent("Cast", "CAST")
            //! runtextmacro setEvent("ItemSale", "ITEM")
            //! runtextmacro setEvent("UnitSale", "UNIT")
            //! runtextmacro setEvent("Train", "TRAIN")
            //! runtextmacro setEvent("BuildOrder", "BUILD")
            
            //A textmacro creating the functions to set non-static events
            //! textmacro setEvent2 takes NAME, VAR
                method set$NAME$Event(ResourceEvent$NAME$ callback){
                    SaveInteger(data, EVENT_$VAR$, this, callback);
                }
            //! endtextmacro
            
            //! runtextmacro setEvent2("Mine", "MINE")
            //! runtextmacro setEvent2("Return", "RETURN")

//---------------------------------------------------------------------------------------------------------------------------------------------\\
//----------------------------------------------------Everything is private below this line----------------------------------------------------\\
//---------------------------------------------------------------------------------------------------------------------------------------------\\
            
            private static method getDistanceBetweenPointsXYSquare(real x1, real y1, real x2, real y2) -> real {
                return (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2);
            }
            
            private method searchSource(miner worker) {                
                tempWidget = null;
                tempDistance = SOURCE_SEARCH_RANGE*SOURCE_SEARCH_RANGE;
                tempResource = this;
                tempX = GetUnitX(worker.worker); tempY = GetUnitY(worker.worker);
                
                GroupEnumUnitsInRect(ENUM_GROUP, wholeMap, searchSourcePickUnit);
                GroupClear(ENUM_GROUP);                
                EnumItemsInRect(wholeMap, searchSourcePickItem, null);
                EnumDestructablesInRect(wholeMap, searchSourcePickDestructable, null);
                
                
                IssueTargetOrder(worker.worker, MINE_ORDER, tempWidget);
            }
            
            //A textmacro creating the source-searching functions for destructables, items and units
            //! textmacro sourceFunc takes NAME, TYPE
            private static method searchSourceFunc$NAME$() -> boolean {
                $TYPE$ w = GetFilter$NAME$();
                real r = getDistanceBetweenPointsXYSquare(tempX, tempY, GetWidgetX(w), GetWidgetY(w));
                
                if(LoadInteger(data, SOURCE, Get$NAME$TypeId(w)) == tempResource && tempDistance >= r && GetWidgetLife(w) > 0.405) {
                    tempWidget = w;
                    tempDistance = r;
                }
                
                w = null;
                return false;
            }
            //! endtextmacro
            
            //! runtextmacro sourceFunc("Destructable", "destructable")
            //! runtextmacro sourceFunc("Item", "item")
            //! runtextmacro sourceFunc("Unit", "unit")
            
            private method searchStorage(miner worker) {
                integer farmType = LoadInteger(data, (worker.owner + FARM_TYPE)*MAX_RESOURCES+this, worker.unitType);
                group g;
                
                if (farmType == FARM_TYPE_INSTANT) {
                    addProperty(worker.owner, worker.currentlyCarryingAmount);
                    floatingValue(worker.currentlyCarryingAmount, GetUnitX(worker.worker), GetUnitY(worker.worker));
                    worker.currentlyCarryingAmount = 0;
                    
                    searchSource(worker);
                    
                } else if(farmType == FARM_TYPE_WALK) {
                    tempX = GetUnitX(worker.worker);  tempY = GetUnitX(worker.worker);
                    tempDistance = STORAGE_SEARCH_RANGE * STORAGE_SEARCH_RANGE;
                    tempResource = this;
                    tempUnit = null;
                    
                    GroupEnumUnitsOfPlayer(ENUM_GROUP, Player(worker.owner), searchStoragePick);
                    GroupClear(ENUM_GROUP);
                    
                    worker.currentStorage = tempUnit;
                    IssueTargetOrder(worker.worker, "smart", worker.currentStorage);
                }
            }
            
            private static method searchStorageFunc() -> boolean {
                unit u = GetFilterUnit();
                real r = getDistanceBetweenPointsXYSquare(tempX, tempY, GetWidgetX(u), GetWidgetY(u));
                
                if (tempResource.isStorage(GetUnitTypeId(u)) && !IsUnitType(u,UNIT_TYPE_DEAD) && isBuildingFinished(u) && tempDistance > r) {
                    tempUnit = u;
                    tempDistance = r;
                }
                
                u = null;
                return false;
            }
            
            //A textmacro to fill an integer array with the costs of a widget, used in multiple methods
            //! textmacro setCosts
                    for (0 < i < number+1) {
                        costs[i] = Resource(i).getCosts(boughtType);
                        if (costs[i] > Resource(i).getProperty(GetPlayerId(buyer))) {
                            SimError(buyer, NOT_ENOUGH_TEXT+Resource(i).name);
                            enough = false;
                            break;
                        }
                    }
            //! endtextmacro
            
            //A textmacro to get the pay for a widget from a player, used in multiple methods
            //! textmacro demandCosts
                    for (0 < i < number+1) {
                        Resource(i).addProperty(GetPlayerId(buyer), -costs[i]); 
                    }
            //! endtextmacro
            
            //A textmacro to refund the costs of a widget to a player, used in multiple methods
            //! textmacro refundCosts takes FUNC, ID, FACTOR
                for (0 < i < number+1) {
                        Resource(i).addProperty($FUNC$(buyer), R2I(Resource(i).getCosts($ID$)$FACTOR$));
                }
            //! endtextmacro
            
            private static method stopConstruction() {
                    timer t = GetExpiredTimer();
                    tempUnit u = GetTimerData(t);
                    DisableTrigger(constructionCancelled);
                    DisableTrigger(orderGiven);
                    IssueImmediateOrder(u.u, "stop");
                    EnableTrigger(orderGiven);
                    EnableTrigger(constructionCancelled);
                    ReleaseTimer(t);
                    u.destroy();
            }
            
            private static method stopTraining() {
                    timer t = GetExpiredTimer();
                    tempUnit u = GetTimerData(t);
                    DisableTrigger(trainCancelled);
                    IssueImmediateOrderById(u.u,851976);
                    EnableTrigger(trainCancelled);
                    ReleaseTimer(t);
                    u.destroy();
            }
            
            private static method onConstructionCancelled() -> boolean {
                integer i, boughtType = GetUnitTypeId(GetCancelledStructure()), buyer = GetPlayerId(GetOwningPlayer(GetFilterUnit()));
                
                for (0 < i < number+1) {
                    Resource(i).addProperty(buyer, R2I(Resource(i).getCosts(boughtType) * BUILDING_REFUND_FACTOR)); 
                }
                
                return false;
            }
            
            private static method onConstructionStart() -> boolean {
                    unit u, b = GetTriggerUnit();
                    real x = GetUnitX(b), y = GetUnitY(b);
                    integer id = GetUnitTypeId(b);
                    orderTarget o;
                    b = null;
                    
                    GroupEnumUnitsInRange(ENUM_GROUP, x, y, MAX_BUILD_RANGE, null); 
                    
                    u = FirstOfGroup(ENUM_GROUP);
                    while (u != null) {
                        o = LoadInteger(data, CURRENTLY_BUILDING, GetHandleId(u));
                        if (o.x == x && o.y == y && o.order == id) {
                            o.destroy();
                            SaveInteger(data, CURRENTLY_BUILDING, GetHandleId(u), 0);
                            u = null;
                            break;
                        }
                        GroupRemoveUnit(ENUM_GROUP,u);
                        u = FirstOfGroup(ENUM_GROUP);
                    }
                    
                    return false;
            }
            
            private static method onConstructionFinished() -> boolean {
                SaveBoolean(data, FINISHED, GetHandleId(GetTriggerUnit()), true);
                return false;
            }
            
            private static method onTrainCancelled() -> boolean {
                    integer i, boughtType = GetTrainedUnitType(), buyer = GetPlayerId(GetOwningPlayer(GetFilterUnit()));
                
                    if (boughtType == 0)
                        boughtType = GetResearched();
                
                    //! runtextmacro refundCosts("", "boughtType", "")
                
                    return false;
            }
            
            private static method onUnitCast() -> boolean {
                integer boughtType = GetSpellAbilityId(),
                        buyType = LoadInteger(data, BUY_TYPE, boughtType),
                        i,
                        costs[];
                boolean enough = true;
                player buyer;
                
                 if (buyType == BUY_TYPE_ABILITY) {
                    buyer = GetTriggerPlayer();
                    //! runtextmacro setCosts()
                    if (enough)
                        //! runtextmacro demandCosts()
                    else 
                         IssueImmediateOrder(GetTriggerUnit(), "stop");
                }
                
                static if(USE_EVENTS)
                    if(enough){
                        i = LoadInteger(data, EVENT_CAST, boughtType);
                        if(i != 0)
                            ResourceEventCast(i).evaluate();
                    }
                
                return false;
            }
            
            private static method onUnitBought() -> boolean {
                integer boughtType = GetUnitTypeId(GetSoldUnit()),
                             buyType = LoadInteger(data, BUY_TYPE, boughtType),
                             i,
                             costs[];
                boolean enough = true;
                player buyer;
                
                 if (buyType == BUY_TYPE_SOLD) {
                    buyer = GetOwningPlayer(GetBuyingUnit());
                    //! runtextmacro setCosts()
                    if (enough){
                        //! runtextmacro demandCosts()
                        
                        static if(USE_EVENTS){
                            i = LoadInteger(data, EVENT_UNIT, boughtType);
                            if(i != 0)
                                ResourceEventUnitSale(i).evaluate();
                        }
                    } else {
                         RemoveUnit(GetSoldUnit());
                         //?????????????????????????????????????
                    }
                }
                
                return false;
            }
            
            private static method onItemBought() -> boolean {
                integer boughtType = GetItemTypeId(GetSoldItem()),
                             buyType = LoadInteger(data, BUY_TYPE, boughtType),
                             i,
                             costs[];
                boolean enough = true;
                player buyer;
                
                 if (buyType == BUY_TYPE_SOLD) {
                    buyer = GetOwningPlayer(GetBuyingUnit());
                    //! runtextmacro setCosts()
                    if (enough){
                        //! runtextmacro demandCosts()
                        
                        static if(USE_EVENTS){
                            i = LoadInteger(data, EVENT_ITEM, boughtType);
                            if(i != 0)
                                ResourceEventItemSale(i).evaluate();
                        }
                    } else {
                         RemoveItem(GetSoldItem());
                         //?????????????????????????????????????
                    }
                }
                
                return false;
            }
            
            private static method onItemSold() -> boolean {
                integer boughtType = GetItemTypeId(GetSoldItem()),
                             buyType = LoadInteger(data, BUY_TYPE, boughtType),
                             i,
                             buyer = GetPlayerId(GetTriggerPlayer());
                
                 if (buyType == BUY_TYPE_SOLD) {
                    //! runtextmacro refundCosts("", "boughtType", "*ITEM_SELL_FACTOR")
                }
                
                return false;
            }
            
            private static method onOrder() -> boolean {
                miner worker;
                thistype this;
                unit ordered = GetTriggerUnit();
                integer targetId,
                             boughtType = GetIssuedOrderId(),
                             buyType = LoadInteger(data, BUY_TYPE, boughtType),
                             i,
                             costs[];
                player buyer = GetOwningPlayer(ordered);
                boolean enough = true;
                timer t;
                orderTarget o = LoadInteger(data, CURRENTLY_BUILDING, GetHandleId(ordered));
                
                if (o > 0) {
                    //! runtextmacro refundCosts("GetPlayerId","o.order", "")
                
                    o.destroy();
                }
                
                if (buyType == BUY_TYPE_BUILDING) {
                    //! runtextmacro setCosts()
                    if (enough) {
                        //! runtextmacro demandCosts()
                        o = orderTarget.create();
                        o.x = GetOrderPointX();
                        o.y = GetOrderPointY();
                        o.order = boughtType;
                        SaveInteger(data, CURRENTLY_BUILDING, GetHandleId(ordered), o);
                        
                        static if(USE_EVENTS){
                            i = LoadInteger(data, EVENT_BUILD, boughtType);
                            if(i != 0)
                                ResourceEventCast(i).evaluate();
                        }
                    } else {
                        t = NewTimer();
                        SetTimerData(t, tempUnit.create(ordered));
                        TimerStart(t, 0, false, function Resource.stopConstruction);
                        SaveInteger(data, CURRENTLY_BUILDING, GetHandleId(ordered), 0);
                    }
                    
                } else {
                    SaveInteger(data, CURRENTLY_BUILDING, GetHandleId(ordered), 0);
                    if (buyType == BUY_TYPE_TRAINED) {
                        //! runtextmacro setCosts()
                        if (enough){
                            //! runtextmacro demandCosts()
                        
                            static if(USE_EVENTS){
                                i = LoadInteger(data, EVENT_TRAIN, boughtType);
                                if(i != 0)
                                    ResourceEventCast(i).evaluate();
                            }
                        } else {
                            t = NewTimer();
                            SetTimerData(t, tempUnit.create(ordered));
                            TimerStart(t, 0, false, function Resource.stopTraining);
                        }
                    }
                }
                
                
                buyer = null;
                
                if(LoadBoolean(data, MINER, GetUnitTypeId(ordered))) {
                    worker = LoadInteger(data, MINER_STRUCT, GetHandleId(ordered));
                    ordered = null;
                
                    if (worker.isMining) {
                        worker.isMining = false;
                        PauseTimer(worker.tim);
                    }
                
                    if (boughtType != ORDER_SMART) {
                        worker.currentStorage = null;
                        return false;
                    }
                
                    targetId = GetUnitTypeId(GetOrderTargetUnit());
                    if (targetId == 0) {
                        targetId = GetDestructableTypeId(GetOrderTargetDestructable());
                        if (targetId == 0) {
                            targetId = GetItemTypeId(GetOrderTargetItem());
                            if (targetId == 0)  {
                                worker.currentStorage = null;
                                return false;
                            }
                        }
                    }
                    
                    this = LoadInteger(data, SOURCE, targetId);
                
                    if(this > 0) {
                        if(LoadInteger(data, (worker.owner + AMOUNT_CARRIED)*MAX_RESOURCES+this, worker.unitType) > 0)
                            IssueTargetOrder(worker.worker, MINE_ORDER, GetOrderTarget());
                        
                        worker.currentStorage = null;
                        return false;
                    }
                
                    this = worker.currentlyCarrying;
                
                    if (LoadBoolean(data, STORAGE*MAX_RESOURCES+this, targetId) || true)
                        worker.currentStorage = GetOrderTargetUnit();
                } else
                    ordered = null;
                    
                return false;
            }
            
            private static method mine() {
                miner worker = GetTimerData(GetExpiredTimer());
                thistype this = worker.currentlyCarrying;
                
                integer gain = LoadInteger(data, (worker.owner + GAIN_PER_HIT)*MAX_RESOURCES+this, worker.unitType), 
                             max = LoadInteger(data, (worker.owner + AMOUNT_CARRIED)*MAX_RESOURCES+this, worker.unitType),
                             farmType = LoadInteger(data, (worker.owner + FARM_TYPE)*MAX_RESOURCES+this, worker.unitType);
                real damage = LoadReal(data, (worker.owner + DAMAGE)*MAX_RESOURCES+this, worker.unitType),
                        life = GetWidgetLife(worker.currentTarget);
                
                
                if (life <= 0.405) {
                    searchSource(worker);
                    return;
                }
                
                if (GetUnitAbilityLevel(worker.worker, RETURN_ABILITY) <= 0 && farmType == FARM_TYPE_WALK)
                    UnitAddAbility(worker.worker, RETURN_ABILITY);
                    
                SetWidgetLife(worker.currentTarget, life - damage);
                
                if(!onKill){
                    worker.currentlyCarryingAmount += gain;
                    
                    static if(USE_EVENTS){
                        farmType = LoadInteger(data, EVENT_MINE, this);
                        if(farmType != 0)
                            ResourceEventMine(farmType).evaluate(this, worker.worker, worker.currentTarget);
                    }
                }
                    
                if (GetWidgetLife(worker.currentTarget) <= 0.405) {
                    if(onKill){
                        worker.currentlyCarryingAmount += gain;
                    
                        static if(USE_EVENTS){
                            farmType = LoadInteger(data, EVENT_MINE, this);
                            if(farmType != 0)
                                ResourceEventMine(farmType).evaluate(this, worker.worker, worker.currentTarget);
                        }
                    }
                    
                    if(worker.currentlyCarryingAmount >= max) {
                        worker.currentlyCarryingAmount = max;
                        searchStorage(worker);
                    } else
                        searchSource(worker);
                        
                } else if(worker.currentlyCarryingAmount >= max) {
                    worker.currentlyCarryingAmount = max;
                    searchStorage(worker);
                } else {
                    SetUnitAnimation(worker.worker , LoadStr(data, ANIMATION*MAX_RESOURCES+worker.currentlyCarrying, worker.unitType));
                    
                    TimerStart(worker.tim, LoadReal(data, (worker.owner + SPEED)*MAX_RESOURCES+this, worker.unitType), false, function Resource.mine);
                }
            }
            
            private static method onSpell() -> boolean {
                miner worker = LoadInteger(data, MINER_STRUCT, GetHandleId(GetTriggerUnit()));
                thistype this;
                integer targetId,
                             amount;
                
                if(GetSpellAbilityId() == RETURN_ABILITY) {
                    worker.currentlyCarrying.searchStorage(worker);
                } else if (GetSpellAbilityId() == MINE_ABILITY) {
                    targetId = GetUnitTypeId(GetSpellTargetUnit());
                    worker.currentTarget = GetSpellTargetUnit();
                
                    if (targetId == 0) {
                        targetId = GetDestructableTypeId(GetSpellTargetDestructable());
                        worker.currentTarget = GetSpellTargetDestructable();
                        if (targetId == 0) {
                            targetId = GetItemTypeId(GetSpellTargetItem());
                            worker.currentTarget = GetSpellTargetItem();
                            if (targetId == 0) 
                                worker.currentStorage = null;
                                worker.currentTarget = null;
                                return false;
                        }
                    }
                
                    this = LoadInteger(data, SOURCE, targetId);
                
                    amount = LoadInteger(data, (worker.owner + AMOUNT_CARRIED)*MAX_RESOURCES+this, worker.unitType);
                    if (amount <= 0)
                        return false;
                
                    if (worker.currentlyCarrying != this) {
                        worker.currentlyCarrying = this;
                        worker.currentlyCarryingAmount = 0;
                        UnitRemoveAbility(worker.worker, RETURN_ABILITY);
                    }
                
                    if (amount <= worker.currentlyCarryingAmount) {
                        worker.currentlyCarryingAmount = amount;
                        IssueImmediateOrder(worker.worker, RETURN_ORDER);
                    } else {
                        IssueImmediateOrder(worker.worker, "stop");
                        SetUnitAnimation(worker.worker , LoadStr(data, ANIMATION*MAX_RESOURCES+worker.currentlyCarrying, worker.unitType));
                    
                        worker.isMining = true;
                        TimerStart(worker.tim, LoadReal(data, (worker.owner + SPEED)*MAX_RESOURCES+this, worker.unitType), false, function Resource.mine);
                    }
                }
                return false;
            }
            
            private static method workerInRange() -> boolean {
                miner worker = LoadInteger(data, MINER_STRUCT, GetHandleId(GetTriggerUnit()));
                static if(USE_EVENTS) integer i;
                
                if (worker.currentStorage != null && worker.currentlyCarryingAmount > 0 && IsUnitInRange(worker.worker, worker.currentStorage, STORAGE_RANGE+0.1)) {
                    UnitRemoveAbility(worker.worker, RETURN_ABILITY);
                    worker.currentlyCarrying.addProperty(worker.owner, worker.currentlyCarryingAmount);
                    worker.currentlyCarrying.floatingValue(worker.currentlyCarryingAmount, GetUnitX(worker.worker), GetUnitY(worker.worker));
                    
                    static if(USE_EVENTS){
                        i = LoadInteger(data, EVENT_RETURN, worker.currentlyCarrying);
                        if(i != 0)
                            ResourceEventReturn(i).evaluate(worker.currentlyCarrying, worker.worker, worker.currentlyCarryingAmount, worker.currentStorage);
                    }
                    
                    worker.currentlyCarryingAmount = 0;
                    
                    worker.currentlyCarrying.searchSource(worker);
                }
                return false;
            }
            
            private static method addWorkerFunc() -> boolean {
                if ((tempType == GetUnitTypeId(GetFilterUnit())) && (GetUnitAbilityLevel(GetFilterUnit(), MINE_ABILITY) <= 0))
                    SaveInteger(data, MINER_STRUCT, GetHandleId(GetFilterUnit()), miner.create(GetFilterUnit()));
                return false;
            }
            
            private static method addStorageFunc() -> boolean {
                if (tempType == GetUnitTypeId(GetFilterUnit()))
                    TriggerRegisterUnitInRange(inRange, GetFilterUnit(), STORAGE_RANGE + GetUnitCollisionSize(GetFilterUnit()), null);
                    
                return false;
            }
        
            private static method onUnitEntersMap() -> boolean {
                integer id = GetUnitTypeId(GetFilterUnit());
                
                if(LoadBoolean(data, MINER, id))
                    SaveInteger(data, MINER_STRUCT, GetHandleId(GetFilterUnit()), miner.create(GetFilterUnit()));
                    
                if(LoadBoolean(data, STORAGE, id))
                    TriggerRegisterUnitInRange(inRange, GetFilterUnit(), STORAGE_RANGE + GetUnitCollisionSize(GetFilterUnit()), null);
                    
                return false;
            }
            
            private static method pickFinishedBuildings() -> boolean {
                unit u = GetFilterUnit();
                if (IsUnitType(u, UNIT_TYPE_STRUCTURE))
                    SaveBoolean(data, FINISHED, GetHandleId(u), true);
                u = null;
                return false;
            }
            
            private static method onDeath() -> boolean{
                unit u = GetTriggerUnit();
                integer s = LoadInteger(data, CURRENTLY_BUILDING, GetHandleId(u)),
                            buyer,
                            i;
                if (s > 0) {
                    buyer = GetPlayerId(GetOwningPlayer(u));
                    //! runtextmacro refundCosts("","orderTarget(s).order", "")
                    orderTarget(s).destroy();
                }
                
                s = LoadInteger(data, MINER_STRUCT, GetHandleId(u));
                if (s > 0)
                    miner(s).destroy();
                    
                u = null;
                return false;
            }
            
            private static method onRemove(unit u) {
                integer s = LoadInteger(data, CURRENTLY_BUILDING, GetHandleId(u)),
                            buyer,
                            i;
                if (s > 0) {
                    buyer = GetPlayerId(GetOwningPlayer(u));
                    //! runtextmacro refundCosts("","orderTarget(s).order", "")
                    orderTarget(s).destroy();
                }
                
                s = LoadInteger(data, MINER_STRUCT, GetHandleId(u));
                if (s > 0)
                    miner(s).destroy();
            }
        
            private static method isMiner() -> boolean {
                return LoadBoolean(data, MINER, GetUnitTypeId(GetFilterUnit()));
            }
        
            private static method onInit(){
                trigger t = CreateTrigger(),
                            t2 = CreateTrigger(),
                            sellItem = CreateTrigger(),
                            buyItem = CreateTrigger(),
                            sellUnit = CreateTrigger(),
                            cast  = CreateTrigger(),
                            finish = CreateTrigger();
                region r = CreateRegion();
                integer i;
                
                wholeMap = GetWorldBounds();
                
                RegionAddRect(r, wholeMap);
                addWorkerPick = Condition(function Resource.onUnitEntersMap);
                TriggerRegisterEnterRegion(t, r, addWorkerPick);
                
                addWorkerPick = Condition(function Resource.isMiner);
                inRange = CreateTrigger();
                TriggerAddCondition(inRange, addWorkerPick);
                TriggerAddCondition(inRange, Condition(function Resource.workerInRange));
                
                trainCancelled = CreateTrigger();
                constructionCancelled = CreateTrigger();
                orderGiven = CreateTrigger();
                t = CreateTrigger();
                
                for(0 <= i < bj_MAX_PLAYERS){
                    TriggerRegisterPlayerUnitEvent(orderGiven, Player(i), EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, null);
                    TriggerRegisterPlayerUnitEvent(orderGiven, Player(i), EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, null);
                    TriggerRegisterPlayerUnitEvent(orderGiven, Player(i), EVENT_PLAYER_UNIT_ISSUED_ORDER, null);
                    
                    TriggerRegisterPlayerUnitEvent(t2, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, addWorkerPick);
                    
                    TriggerRegisterPlayerUnitEvent(trainCancelled, Player(i), EVENT_PLAYER_UNIT_RESEARCH_CANCEL, null);
                    TriggerRegisterPlayerUnitEvent(trainCancelled, Player(i), EVENT_PLAYER_UNIT_TRAIN_CANCEL, null);
                    
                    TriggerRegisterPlayerUnitEvent(constructionCancelled, Player(i),EVENT_PLAYER_UNIT_CONSTRUCT_CANCEL, null);
                    
                    TriggerRegisterPlayerUnitEvent(finish, Player(i),EVENT_PLAYER_UNIT_CONSTRUCT_FINISH, null);
                    
                    TriggerRegisterPlayerUnitEvent(t, Player(i),EVENT_PLAYER_UNIT_CONSTRUCT_START, null);
                    
                    TriggerRegisterPlayerUnitEvent(buyItem, Player(i), EVENT_PLAYER_UNIT_SELL_ITEM, null);
                    
                    TriggerRegisterPlayerUnitEvent(sellItem, Player(i),EVENT_PLAYER_UNIT_PAWN_ITEM, null);
                    
                    TriggerRegisterPlayerUnitEvent(sellUnit, Player(i),EVENT_PLAYER_UNIT_SELL, null);
                    
                    TriggerRegisterPlayerUnitEvent(cast, Player(i),EVENT_PLAYER_UNIT_SPELL_CAST, null);
                }
                
                TriggerAddCondition(trainCancelled, Condition(function Resource.onTrainCancelled));
                TriggerAddCondition(constructionCancelled, Condition(function Resource.onConstructionCancelled));
                TriggerAddCondition(orderGiven, Condition(function Resource.onOrder));
                TriggerAddCondition(t2, Condition(function Resource.onSpell));
                TriggerAddCondition(t ,Condition(function Resource.onConstructionStart));
                TriggerAddCondition(finish, Condition(function Resource.onConstructionFinished));
                TriggerAddCondition(buyItem, Condition(function Resource.onItemBought));
                TriggerAddCondition(sellItem, Condition(function Resource.onItemSold));
                TriggerAddCondition(sellUnit, Condition(function Resource.onUnitBought));
                TriggerAddCondition(cast, Condition(function Resource.onUnitCast));
                
                trigOnDeath = CreateTrigger();
                TriggerAddCondition(trigOnDeath, Condition(function Resource.onDeath));
                
                addWorkerPick = Condition(function Resource.addWorkerFunc);
                addStoragePick = Condition(function Resource.addStorageFunc);
                
                searchSourcePickUnit = Condition(function Resource.searchSourceFuncUnit);
                searchSourcePickDestructable = Condition(function Resource.searchSourceFuncDestructable);
                searchSourcePickItem = Condition(function Resource.searchSourceFuncItem);
                searchStoragePick = Condition(function Resource.searchStorageFunc);
                
                allBuildings = Condition(function Resource.pickFinishedBuildings);
                
                TimerStart(CreateTimer(), 0, false, function Resource.createMultiboards);
                t = null;
                t2 = null;
                sellItem = null;
                buyItem = null;
                sellUnit = null;
                
                static if (DEBUG_MODE)
                    if(MAX_RESOURCES > 511 || MAX_RESOURCES <= 0)
                        BJDebugMsg("|cffFF0000ResourceSystem ERROR:|r 'MAX_RESOURCES' must be set to a positive value lower than 511");
            }
            
            private static method createMultiboards() {
                integer i, j, k = number;
                thistype this;
                multiboarditem mi;
                
                GroupEnumUnitsInRect(ENUM_GROUP, wholeMap, allBuildings);
                
                ReleaseTimer(GetExpiredTimer());
                
                for(0 <= i < 12) {
                    properties[i] = CreateMultiboard();
                    MultiboardSetTitleText(properties[i], MULTIBOARD_NAME);
                    MultiboardSetColumnCount(properties[i], 2);
                    MultiboardSetRowCount(properties[i], k);
                    
                    if(GetLocalPlayer() == Player(i))
                        MultiboardDisplay(properties[i], true);
                        
                    for(0 <= j < k) {
                        this = j+1;
                    
                        mi = MultiboardGetItem(properties[i], j, 0);
                        MultiboardSetItemIcon(mi, icon);
                        MultiboardSetItemValue(mi, colour + name + "|r");
                        MultiboardSetItemWidth(mi, MULTIBOARD_NAME_WIDTH);
                        MultiboardReleaseItem(mi);
                        
                        mi = MultiboardGetItem(properties[i], j, 1);
                        MultiboardSetItemStyle(mi, true, false);
                        MultiboardSetItemValue(mi, colour + I2S(amount[i]) + "|r");
                        MultiboardSetItemWidth(mi, MULTIBOARD_VALUE_WIDTH);
                        MultiboardReleaseItem(mi);
                    }
                }
                
                mi = null;
            }
            
            private static method updateMultiboard(integer whichPlayer) {
                integer i, j, k = number;
                thistype this;
                multiboarditem mi;
                
                MultiboardSetRowCount(properties[whichPlayer], k);
                        
                for(0 <= j < k) {
                    this = j+1;
                    
                    mi = MultiboardGetItem(properties[whichPlayer], j, 0);
                    MultiboardSetItemIcon(mi, icon);
                    MultiboardSetItemValue(mi, colour + name + "|r");
                    MultiboardSetItemWidth(mi, MULTIBOARD_NAME_WIDTH);
                    MultiboardReleaseItem(mi);
                        
                    mi = MultiboardGetItem(properties[whichPlayer], j, 1);
                    MultiboardSetItemStyle(mi, true, false);
                    MultiboardSetItemValue(mi, colour + I2S(amount[whichPlayer]) + "|r");
                    MultiboardSetItemWidth(mi, MULTIBOARD_VALUE_WIDTH);
                    MultiboardReleaseItem(mi);
                }
                
                mi = null;
            }
            
            private method destroy() {}
        }
        
        private struct miner {
            unit worker,
                    currentStorage;
            
            Resource currentlyCarrying;
            integer currentlyCarryingAmount,
                         unitType,
                         owner;
            
            widget currentTarget;
            real currentTargetX, currentTargetY;
            
            timer tim;
            boolean isMining = false;
            
            static method create(unit u)  -> thistype{
                thistype this = thistype.allocate();
                worker = u;
                unitType = GetUnitTypeId(u);
                owner = GetPlayerId(GetOwningPlayer(u));
                
                tim = NewTimer();
                SetTimerData(tim, this);
                
                UnitAddAbility(u, MINE_ABILITY);
                
                if (!IsUnitType(u, UNIT_TYPE_HERO))
                    TriggerRegisterUnitEvent(trigOnDeath, u, EVENT_UNIT_DEATH);
                
                return this;
            }
            
            private method onDestroy() {ReleaseTimer(tim);}
        }
        
        private struct tempUnit {
            unit u;
            
            static method create(unit u) -> tempUnit {
                thistype this = thistype.allocate();
                this.u = u;
                return this;
            }
        }
        
        private struct orderTarget {
            real x, y;
            integer order;
        }
    }
//! endzinc

hook RemoveUnit Resource.onRemove
JASS:
//GetUnitCollisionSize
//by Verethragna (aka D1000)
//v1.1

//Use "GetUnitCollsionSize(unit)" or "GetUnitTypeCollsionSize(unittype)" to get the approximate collsion size of a unit

//Based on Vexorians "GetUnitCollsionSize", but made in ZinC & saves values in a hashtable (if enabled)

//! zinc
    library GetUnitCollisionSize requires optional xebasic {
        //Only needed if xebasic is not used
            private constant real MAX_COLLSION_SIZE = 197;
        //Number of iterations used, the more, the accurate
            private constant integer ITERATIONS = 20;
        //If you want that collsion-values are saved in hashtables, set this to true. Else it will be calculated every time again.
            private constant boolean USE_HASHTABLE = true;
        //The position of eventual temporal units, if you use "GetUnitTypeCollisionSize"
            private constant real UNIT_POS_X = 0;
            private constant real UNIT_POS_Y = 0;
        
//==================================================================================================\\
//==================================================================================================\\
        
        private hashtable savedValues; 
        
        //A textmacro containing the algorithm for getting the collision size, since it is used in two different functions
        //! textmacro unitCollisionSize
            static if (LIBRARY_xebasic) 
                higher = XE_MAX_COLLISION_SIZE;
            else
                higher = MAX_COLLSION_SIZE;
            lower = 0;
            
            for(0 <= i < ITERATIONS) {
                middle = (lower+higher)/2;
                if (IsUnitInRangeXY(u, x+middle, y, 0))
                    lower = middle;
                else
                    higher = middle;
            }
            
            static if (USE_HASHTABLE)
                SaveReal(savedValues, 0, uType, middle);
        //! endtextmacro
        
        public function GetUnitCollisionSize(unit u) -> real {
            real lower, higher, middle, i, x, y;
            
            static if (USE_HASHTABLE) {
                integer uType = GetUnitTypeId(u);
                lower = LoadReal(savedValues, 0, uType);
                if(lower > 0)
                    return lower;
            }
            
            x = GetUnitX(u); y = GetUnitY(u);
            //! runtextmacro unitCollisionSize()
            
            return middle;
        }
        
        public function GetUnitTypeCollisionSize(integer uType) -> real {
            real lower, higher, middle, i, x = UNIT_POS_X, y = UNIT_POS_Y;
            unit u;
            static if (USE_HASHTABLE) {
                lower = LoadReal(savedValues, 0, uType);
                if(lower > 0)
                    return lower;
            }
                            
            u = CreateUnit(Player(15), uType, 0, 0, 0);
            ShowUnit(u, false);
            
            //! runtextmacro unitCollisionSize()
            
            RemoveUnit(u);
            u = null;
            
            return lower;
        }
        
        private function onInit() {
            static if (USE_HASHTABLE)
                savedValues = InitHashtable();
        }
    }
//! endzinc
JASS:
Resource System
Version 1.3
Made by Verethragna (aka D1000)

//==================================================\\
Requirements:
    Libraries:
        GetUnitCollisionSize made by Verethragna (aka D1000)
        GroupUtils made by Rising_Dusk
        TimerUtils made by Vexorian
        SimError made by Vexorian

    Abilities:
        Mine
        Return
//==================================================\\
Credits to:
    Vexorian: TimerUtils, SimError, JassHelper
    Rising_Dusk: GroupUtils
    Cedi: Got an idea how to get the constructing unit in the event EVENT_PLAYER_UNIT_CONSTRUCT_START
//==================================================\\
Import Instructions:
    1) Copy the ResourceSystem-Folder & the required libraries to your map
    2) Copy the two required abilities to your map (required abilities & libraries listed above)
    3) Adjust the constants in the "ResourceSystem"-Library
    4) Use the methods listed in "ResourceSystem API Resource Creation" to create and adjust new resources
    5) Give credits to me & the people who made the required libraries
    6) Enjoy
//==================================================\\
JASS:
Resource System
Version 1.3
Made by Verethragna (aka D1000)

//==================================================\\
//Methods to create, adjust and manipulate resources.
//==================================================\\
____________________________________________
static method create(string name, string icon, boolean onKill) -> thistype
//Creates a new Resource.
//Usage: 
local Resource yourResource = Resource.create("name","icon", true/false)
//Parameters:
name -> Name of the Resource
icon -> Multiboard-Icon of the Resource
onKill -> Defines if you get this resource each hit on a source or only if you kill it.
____________________________________________
method addSource(integer sourceId)
//Adds a source to the Resource
//Usage:
call yourResource.addSource('type')
//Parameter:
sourceId -> type-id of the source. Can be an item a unit or a destructable, but it must be selectable ingame.
____________________________________________
static method getResourceBySource(integer sourceId) -> thistype
//Returns the resource that can be extracted from the given source.
//Usage:
local Resource yourResource = getResourceBySource('type')
//Parameter:
sourceId -> type-id of the source
____________________________________________
method addStorage(integer id)
//Adds a Storage-building to the Resource. Only needed if your workers have the farm type "FARM_TYPE_WALK"
//Usage:
call yourResource.addStorage('type')
//Parameter:
id -> type-id of the storage. Should be a building.
____________________________________________
method isStorage(integer id) -> boolean
//Returns true if the given building is a storage of your resource
//Usage:
local boolean b = yourResource.isStorage('type')
//Paramter:
id -> The id of the building you want to check.
____________________________________________
method addMiner(integer unitTypeId, string anim ,real speed, real damagePerHit, integer gain, integer amountCarried, integer farmType)
//Adds a miner to the resource
//Usage:
call yourResource.addMiner('type', "animation", 0.0, 0.0, 0, 0, FARM_TYPE_INSTANT/WALK)
//Parameters:
unitTypeId -> type-id of the worker
anim -> animation that is played when the unit mines this resource
speed -> mining-speed
damagePerHit -> damage done to the source per hit
gain -> resource-gain per hit/kill
amountCarried -> how much of the resource the worker can mine until he brings it back to a storage. Should always be higher then "gain", even if the farm type is "FARM_TYPE_WALK"
farmType -> Either "FARM_TYPE_INSTANT" or "FARM_TYPE_WALK". Defines if the worker has to bring the mined resources back to a storage
____________________________________________
method setCosts(integer typeId, integer costs, integer buyType)
//Sets the costs of a Unit/Building/Item/Research
//Usage:
call yourResource.setCosts('type', 0, BUY_TYPE_SOLD/_BUILDING/_TRAINED/_ABILITY)
//Parameters:
typeId -> type-id of the object
costs -> how much the object should cost
buyType -> how the object is bought. If you add costs to this object for different resources, the buyType is set in the last call.
          (For example: call yourResource1.setCosts('type', 10, 0); call yourResource2.setCosts('type', 30, BUY_TYPE_SOLD); -> The buyType is BUY_TYPE_SOLD)
____________________________________________
method getCosts(integer typeId) -> integer
//Gets the costs of an object
//Usage:
local integer i = yourResource.getCosts(typeId)
//Parameter:
typeId -> the type-id of the unit/building/item/research
____________________________________________
method getName() -> string
//Returns the name of your resource
//Usage:
local string s = yourResource.getName()
____________________________________________
method getIcon() -> string
//Returns the icon of your resource
//Usage:
local string s = yourResource.getIcon()
____________________________________________
method setAmountCarried(player p, integer value, integer typeId)
//Changes the amount of you resources that can be carried by the given unit and player
//Usage:
call yourResource.setAmountCarried(Player(0), 0, 'type')
//Paramters:
p -> The player who is affected by the change
value -> The new value
typeId -> The affected unitType
____________________________________________
method setAmountCarriedById(integer p, integer value, integer typeId)
//Changes the amount of you resources that can be carried by the given unit and player
//Usage:
call yourResource.setAmountCarried(0, 0, 'type')
//Paramters:
p -> The id of the player who is affected by the change
value -> The new value
typeId -> The affected unitType
____________________________________________
method getAmountCarried(player p,  integer typeId)
//Gets the amount of you resources that can be carried by the given unit and player
//Usage:
local integer i = yourResource.getAmountCarried(Player(0), 'type')
//Paramters:
p -> The player
typeId -> The unitType
____________________________________________
method getAmountCarriedById(integer p,  integer typeId)
//Gets the amount of you resources that can be carried by the given unit and player
//Usage:
local integer i = yourResource.getAmountCarried(0, 'type')
//Paramters:
p -> The id of the player
typeId -> The unitType
____________________________________________
method setGainPerHit(player p, integer value, integer typeId)
//Changes the gain per hit of the given unit and player
//Usage:
call yourResource.setGainPerHit(Player(0), 0, 'type')
//Paramters:
p -> The player who is affected by the change
value -> The new value
typeId -> The affected unitType
____________________________________________
method setGainPerHitById(integer p, integer value, integer typeId)
//Changes the gain per hit of the given unit and player
//Usage:
call yourResource.setGainPerHit(0, 0, 'type')
//Paramters:
p -> The id of the player who is affected by the change
value -> The new value
typeId -> The affected unitType
____________________________________________
method getGainPerHit(player p,  integer typeId)
//Gets the gain per hit of the given unit and player
//Usage:
local integer i = yourResource.getGainPerHit(Player(0), 'type')
//Paramters:
p -> The player
typeId -> The unitType
____________________________________________
method getGainPerHitById(integer p,  integer typeId)
//Gets the gain per hit of the given unit and player
//Usage:
local integer i = yourResource.getGainPerHit(0, 'type')
//Paramters:
p -> The id of the player
typeId -> The unitType
____________________________________________
method setDamage(player p, real value, integer typeId)
//Changes the damage per hit of the given unit and player
//Usage:
call yourResource.setDamage(Player(0), 0.0, 'type')
//Paramters:
p -> The player who is affected by the change
value -> The new value
typeId -> The affected unitType
____________________________________________
method setDamageById(integer p, real value, integer typeId)
//Changes the damage per hit of the given unit and player
//Usage:
call yourResource.setDamage(0, 0.0, 'type')
//Paramters:
p -> The id of the player who is affected by the change
value -> The new value
typeId -> The affected unitType
____________________________________________
method getDamage(player p,  integer typeId) -> real
//Gets the damage per hit of the given unit and player
//Usage:
local integer i = yourResource.getDamage(Player(0), 'type')
//Paramters:
p -> The player
typeId -> The unitType
____________________________________________
method getDamageById(integer p,  integer typeId) -> real
//Gets the damage per hit of the given unit and player
//Usage:
local integer i = yourResource.getDamage(0, 'type')
//Paramters:
p -> The id of the player
typeId -> The unitType
____________________________________________
method setSpeed(player p, real value, integer typeId)
//Changes the speed of the given unit and player
//Usage:
call yourResource.setSpeed(Player(0), 0.0, 'type')
//Paramters:
p -> The player who is affected by the change
value -> The new value
typeId -> The affected unitType
____________________________________________
method setSpeedById(integer p, real value, integer typeId)
//Changes the speed of the given unit and player
//Usage:
call yourResource.setSpeed(0, 0.0, 'type')
//Paramters:
p -> The id of the player who is affected by the change
value -> The new value
typeId -> The affected unitType
____________________________________________
method getSpeed(player p,  integer typeId) -> real
//Gets the speed of the given unit and player
//Usage:
local integer i = yourResource.getSpeed(Player(0), 'type')
//Paramters:
p -> The player
typeId -> The unitType
____________________________________________
method getSpeedById(integer p,  integer typeId) -> real
//Gets the speed of the given unit and player
//Usage:
local integer i = yourResource.getSpeed(0, 'type')
//Paramters:
p -> The id of the player
typeId -> The unitType
___________________________________________
method setFarmType(player p, integer value, integer typeId)
//Changes the FarmType of the given unit and player
//Usage:
call yourResource.setFarmType(Player(0), 0, 'type')
//Paramters:
p -> The player who is affected by the change
value -> The new value
typeId -> The affected unitType
____________________________________________
method setFarmTypeById(integer p, integer value, integer typeId)
//Changes the FarmType of the given unit and player
//Usage:
call yourResource.setFarmType(0, 0, 'type')
//Paramters:
p -> The id of the player who is affected by the change
value -> The new value
typeId -> The affected unitType
____________________________________________
method getFarmType(player p,  integer typeId)
//Gets the FarmType of the given unit and player
//Usage:
local integer i = yourResource.getFarmType(Player(0), 'type')
//Paramters:
p -> The player
typeId -> The unitType
____________________________________________
method getFarmTypeById(integer p,  integer typeId)
//Gets the FarmType of the given unit and player
//Usage:
local integer i = yourResource.getFarmType(0, 'type')
//Paramters:
p -> The id of the player
typeId -> The unitType
____________________________________________
method setColour(string colour)
//Sets the colour of the Resource in the multiboard and in the text-tags
//Usage:
call yourResource.setColour("000000")
//Parameter:
colour -> The colour as a hexadezimal number in a string. For example: "FF0000" is red, "000000" is black.
____________________________________________
method getColour() -> string
//Returns the colour of the Resource in the multiboard and in the text-tags in the format |cffXXXXXX
//Usage:
local string s = yourResource.getColour()
_____________________________________________
method setAnimation(integer unitTypeId, string anim)
//Sets the animation that is played by the given unitType when it mines this resource
//Usage:
call yourResource.setAnimation('type', "animation")
//Parameters:
unitTypeId -> The id of the unitType
anim -> The animation that should be played
_____________________________________________
method getAnimation(integer unitTypeId)
//Returns the animation that is played by the given unitType when it mines this resource
//Usage:
set string s = yourResource.setAnimation('type')
//Parameters:
unitTypeId -> The id of the unitType
_____________________________________________
method addProperty(integer whichPlayer, integer amount)
//Adds the given value to the amount of your Resource a players has. Use negative values to decrease it
//Usage:
call yourResource.addProperty(0, 0)
//Parameters
whichPlayer -> The id of the player
amount -> The amount which will be added to the chosen players properties
____________________________________________
method setProperty(integer whichPlayer, integer amount)
//Sets the given value to the amount of your Resource a players has. Use negative values to decrease it
//Usage:
call yourResource.setProperty(0, 0)
//Parameters
whichPlayer -> The id of the player
amount -> The properties of the chosen player will be set to this value
____________________________________________
method getProperty(integer whichPlayer) -> integer
//Returns the amount of your Resource the given player has.
//Usage:
local integer i = yourResource.getProperty(0)
//Parameters
whichPlayer -> The id of the player
_____________________________________________
JASS:
Resource System
Version 1.3
Made by Verethragna (aka D1000)

//==================================================\\
//Methods used for various things, not necessarily concerning resources.
//==================================================\\
___________________________________________
static method showMultiboard(integer whichPlayer, boolean flag)
//Shows/Hides the multiboard with the Resources of the given player to the given player
//Usage:
call Resource.showMultiboard(0, true/false)
//Parameters:
whichPlayer -> The id of the player
flag -> if true -> shows the multiboard, false -> hides the multiboard
_____________________________________________
static method minimizeMultiboard(integer whichPlayer, boolean flag)
//Minimizes/Maximizes the multiboard with the Resources of the given player
//Usage:
call Resource.minimizeMultiboard(0, true/false)
//Parameters:
whichPlayer -> The id of the player
flag -> if true -> minimizes the multiboard, false -> maximizes the multiboard
____________________________________________
static method isBuildingFinished(unit building) -> boolean
//Returns if the given building is finished, also works for building which are not used by the system
//Usage:
set boolean b = Resource.isBuildingFinished(yourBuilding)
//Parameters
building -> Your building
____________________________________________
method floatingValue(integer value, real x, real y)
//Creates a floating number in the resources colour at the given point
//Usage:
call yourResource.floatingValue(0,0.0,0.0)
//Parameters
value -> shown value
x -> x-coordinate of the value
y -> y-coordinate of the value
____________________________________________
JASS:
Resource System
Version 1.3
Made by Verethragna (aka D1000)

//==================================================\\
//Methods for setting events. Events can be used instead of triggers (-> less triggers) and only react, when the player has enough resources
//                                                                                                     (or there aren't any resource-costs anyway)
//Don't forget to set 'USE_EVENTS' to 'true', if you want to use events.
//You can use 0 as function name, if you want to reset the event
//==================================================\\
___________________________________________
static method setCastEvent(integer id, ResourceEventCast callback)
//Sets the function, which is to be called, when the given ability is casted
//Usage:
call Resource.setCastEvent('type', functionName)
//Parameters:
id -> type-id of the ability which should trigger the event
callback -> Function which should be called
//Type:
public type ResourceEventCast extends function();
___________________________________________
static method setItemSaleEvent(integer id, ResourceEventItemSale callback)
//Sets the function, which is to be called, when the given item is bought by a unit
//Usage:
call Resource.setItemSaleEvent('type', functionName)
//Parameters:
id -> type-id of the item which should trigger the event
callback -> Function which should be called
//Type:
public type ResourceEventItemSale extends function();
___________________________________________
static method setUnitSaleEvent(integer id, ResourceEventUnitSale callback)
//Sets the function, which is to be called, when the given unit is brought (not trained!)
//Usage:
call Resource.setUnitSaleEvent('type', functionName)
//Parameters:
id -> Id of the unit which should trigger the event
callback -> Function which should be called
//Type:
public type ResourceEventUnitSale extends function();
___________________________________________
static method setTrainEvent(integer id, ResourceEventTrain callback)
//Sets the function, which is to be called, when the given unit/upgrade is trained/researched
//Usage:
call Resource.setTrainEvent('type', functionName)
//Parameters:
id -> Id of the unit or upgrade which should trigger the event
callback -> Function which should be called
//Type:
public type ResourceEventTrain extends function();
___________________________________________
static method setBuildOrderEvent(integer id, ResourceEventBuildOrder callback)
//Sets the function, which is to be called, when a unit is ordered to construct the given building
//Usage:
call Resource.setBuildOrderEvent('type', functionName)
//Parameters:
id -> Id of the building which should trigger the event
callback -> Function which should be called
//Type:
public type ResourceEventBuildOrder extends function();
___________________________________________
method setMineEvent(ResourceEventMine callback)
//Sets the function, which is to be called, when the given resource is mined
//Usage:
call yourResource.setMineEvent(functionName)
//Parameters:
callback -> Function which should be called
//Type:
public type ResourceEventMine extends function(Resource, unit, widget);
//Type-Parameters:
Resource -> The resource this reacts to
unit -> The mining unit
widget -> The unit, item or destructable which is mined
___________________________________________
method setReturnEvent(ResourceEventReturn callback)
//Sets the function, which is to be called, when the given resource is brought to a storage
//Usage:
call yourResource.setReturnEvent(functionName)
//Parameters:
callback -> Function which should be called
//Type:
public type ResourceEventReturn extends function(Resource, unit, integer, unit);
//Type-Parameters:
Resource -> The resource this reacts to
unit -> The unit, which brings the resource
integer -> Amount of the resource, which is brought
unit -> The storage-unit
___________________________________________
JASS:
|____________________________________________________________________________________________________
|"Bugs which are known to me, but I don't know how to fix:"|
|____________________________________________________________________________________________________
|• No Rally-Point-Support
|• Nearest storage/resource is determined via beeline, thus there are problems if the way is blocked 
|   (Could solve this with a path-finding-algorithm, but it would probably need to much computing)
|• Trying to buy an item/merc without having enough of a custom resource reduces the item/merc stock by 1

|____________________________________________________________________________________________________
JASS:
_____________________________________________
|'v1.3 - December 22th, 2011'|
|'Bugs fixed:'|
|---
|'New features:'|
|• Text displayed when trying to buy something with insufficient resources is now changeable
|• Added "getColour"-method
|• Added events (see "ResourceSystem API Events" for details)
|• Added comments to the examples
|• "floatingValue"-method is now public accessible (see "ResourceSystem API Misc" for details)
|'Other changes:'|
|• Fixed mistakes in the Readme
|• The system now requires only one hashtable, instead of one plus one per resource
|• Restructured Readme in multiple files
|• Changed "Known Bugs" to "Known Problems"
_____________________________________________
|'v1.2 - July 20th, 2011'|
|'Bugs fixed:'|
|---
|'New features:'|
|• "GetUnitCollisionSize" now has functions for units and unittypes
|• Commented the textmacros
|• Added import instructions to the readme
|'Other changes:'|
|• Improved "GetCollisionSize" and renamed it to "GetUnitCollisionSize"
|• Changed the respective function-calls in the resource-system
|• Now using GroupUtil´s "ENUM_GROUP" instead of creating a new group in some cases
|• Renamed the Readme, Changelog & Known Bugs, to avoid name-conflicts with other systems
_____________________________________________
|'v1.1 - June 25th, 2011'|
First release on "http://www.hiveworkshop.com" (english)
|'Bugs fixed:'|
|• Fixed an issue involving "smart"-orders targetting items
|'New features:'|
|• Added "Changelog"
|• Added "Known Bugs"
|'Other changes:'|
|• Made the methods "minimizeMultiboard" and "showMultiboard" static
|• Changed my name in the description and everywhere else to the more up-to-date "Verethragna"
|• Tried to add rally-point-support, failed
_____________________________________________
|'v1.0 - July 6th, 2010'|
First release, only on "http://warcraft.ingame.de/forum/showthread.php?t=203327" (german)
|´'Bugs fixed:'|
|• Its not possible anymore to return resources to unfinished buildings
|'New features:'|
|• Resources can now be used to buy things like items or units...
|• ...and respective methods were added
|• added static method "isBuildingFinished"
|'Other changes:'|
|• Now using TriggerAddCondition instead of TriggerAddAction
_____________________________________________
|'v0 - Somewhen between February 13th and 27th, 2010'|
Unfinished version, submitted to the "Trigger-it-Out"-Contest on "http://warcraft.ingame.de/forum/showthread.php?t=198065" (german)
|'Bugs fixed:'|
|---
|'New features:'|
|• Resource System
|• Readme
|• GetCollisionSize System
|'Other changes:'|
|---
_____________________________________________

Keywords:
Resource System, Resource, System, Verethragna, D1000, Custom Resource, ZinC
Contents

Resource System (Map)

Reviews
10 Nov 2011 Bribe: Vexorian's GetUnitCollisionSize does not use game cache... it uses Table. Your re-write of it is still bad and GetUnitTypeCollisionSize does not need the textmacro it just needs to call the other function. Don't abuse textmaros...

Moderator

M

Moderator

10 Nov 2011
Bribe: Vexorian's GetUnitCollisionSize does not use game cache... it uses Table. Your re-write of it is still bad and GetUnitTypeCollisionSize does not need the textmacro it just needs to call the other function. Don't abuse textmaros.

I have no idea why you make this so complex. The code is so enormous in length that it makes it impossible to determine what code is superflous and what actually needs to be there.

I will start with some things I can make sense out of:

Textmacros... so many of them... makes the code impossible to read actually. A lot of very similar functions that look to me from what I can tell could be structured very differently without the need to make the system so complex.

RegisterPlayerUnitEvent will help a lot.

There are systems and snippets in general that could make your life easier, but I am going to require a PhD in your system to figure out how to give this thing a proper review.

Right now I'm going to say "it works" and I can't spot any critical room for error without that PhD, so I recommend adjusting that Collision library and meanwhile, your resource is now approved.
 
OMG

JASS:
            private static thistype number, tempResource;
            private static boolexpr addWorkerPick, addStoragePick, searchSourcePickUnit, searchSourcePickItem, searchSourcePickDestructable, searchStoragePick, allBuildings;
            private static rect wholeMap;
            private static integer tempType;
            private static real tempDistance, tempX, tempY;
            private static trigger inRange, trainCancelled, constructionCancelled, orderGiven;
            private static multiboard properties[12];
            private static widget tempWidget;
            private static unit tempUnit;
            
            private string name,
                                    icon,
                                    colour = "";
            private hashtable data = InitHashtable();
            private integer amount[0x10];
            private boolean onKill;
            
            private static constant integer AMOUNT_CARRIED = 0,
                                                                GAIN_PER_HIT = 0x10,
                                                                DAMAGE = 0x20,
                                                                FARM_TYPE = 0x30,
                                                                SPEED = 0x40,
                                                                ANIMATION = -1,
                                                                COSTS = -2,
                                                                BUY_TYPE = -3,
                                                                CURRENTLY_BUILDING = -4,
                                                                FINISHED = -5,
                                                                
                                                                SOURCE = 0,
                                                                MINER = 2,
                                                                MINER_STRUCT = 3,
                                                                STORAGE = 4,
                                                                
                                                                ORDER_SMART = 851971;

You abused the Same-Line-Same-Type feature :p
Please don't that >.< xD
 
Level 4
Joined
Jun 22, 2008
Messages
49
I should stop using a different font in my editor, everything I format looks just stupid for everyone else xD


OMG

You abused the Same-Line-Same-Type feature :p
Please don't that >.< xD
I thought that's what it's for

Hey, you finished the system. I was realy sad that such a perfect system was unfinished and unusable although it had such a nice code. Well done ;)
I uploaded the finished version about a year ago on InWc, but no one seemed to care about it (at least nobody wrote a comment for a whole year...)
 
Last edited:
Level 10
Joined
May 19, 2008
Messages
176
Sadly that the people doesn't see how much effort and time D1000 used for this system and don't even rate such a welldone and nice system. Not even one of the mods looked at it...

Its also sad that the people would be much more responsive if it was done in GUI. But we can't anticipate more from a community full of GUI'ler which don't even try to understand the beauty and very good performance of a system done in a improved language and by a good coder.

Anyway. You showed me this a year ago and its still the best and only ressource system on this site.

+RP and 5/5
 
Level 4
Joined
Jun 22, 2008
Messages
49
Bribe said:
GetCollisionSize is a really bad library, try using the actual one
by Vexorian it doesn't use CreateUnit which makes it 1000x better.
I won't use the original one, since it doesn't use hashtables, but I'll rewrite mine.

Bribe said:
If you use GroupUtils take advantage of the ENUM_GROUP
Okay, I'll change that too

Bribe said:
You really limit the map that uses your system by also making it
require a multiboard. What if the user wants the multiboard for their
own purposes?
The multiboard can be hidden

Bribe said:
The code is enormous, even moreso when you drop down the
textmacros to make it happen.
What do you mean?

Bribe said:
The textmacros also make it utterly unreadable...
I Think they make it easier to read...

This thing really deserves more attention.
Thanks^^
 
Level 4
Joined
Jun 22, 2008
Messages
49
Well, they DO make the code shorter, so I don't want to remove them
But I commented them in the new version, I uploaded a few hours ago

/€: About the textmacros in the CollisionSize system: well, it's one function-call less this way^^
 
Last edited:
You know, this system can be replaced entirely with this:

JASS:
//! zinc

public struct Resource[] {
    private static integer stack = 0;
    private integer amount;
    method add(integer i) {
        this.amount += i;
    }
    method subtract(integer i) {
        this.amount -= i;
    }
    method get()->integer {
        return this.amount;
    }
    method set(integer i) {
        this.amount = i;
    }
    static method create()->thistype {
        stack++;
        return stack;
    }
}

//! endzinc

This way, ANYONE wanting to add an extra resource to his map could use this and do anything.
 
Level 4
Joined
Jun 22, 2008
Messages
49
Soo your snippets allows you to
  • assign workers to the resource, which automatically search for assigned sources and bring them to an assigned storage after an assigned time
  • buy items, units, upgrades and buildings with this resources
  • show the resources in sepparate multiboard for each player
and a few more details?

(sorry for the mistakes, i'm writing this on a mobile)

/€: did you even take a look at the API or the Testmap?
 
did you even take a look at the API or the Testmap?

Yes I did, and that API is what limited the user.

This is a solution:

You can take code and a player unit event from the user, then convert the code to a boolexpr and store it in a Table.
Then you'd store the event in another Table using the same index you used for the boolexpr and you'd use an integer to track the number of boolexprs you added for a certain instance.
And then, you'd just have to call RegisterPlayerUnitEvent(playerunitevent, function onEvent)

onEvent would be a function inside your library. You'd loop through all the Resources (start at 1 and exit the loop when the instance is greater than stack).

You should nest a loop in it that would loop from index 0 to "number of boolexprs" for a certain instance. Then, you'd check if the event loaded from the Table with this index is equal to the Triggering trigger's event, and if that's true, call ForceEnumPlayersCounted(TEMP_FORCE, boolexpr, 1) to evaluate the boolexpr. (Or you can call FireCondition(boolexpr) with this http://www.hiveworkshop.com/forums/submissions-414/snippet-needs-work-firecode-208389/)
Exit the loop if your index becomes equal to the number of boolexprs.

Note: TEMP_FORCE is a global force set to CreateForce(). (I know setting it to bj_FORCE_PLAYER[0] saves a handle, but that would totally ruin the force itself killing all other systems that use it.

That would be efficient and it would have the useful features.

As for the multiboard, a lot of users aren't going to need one.
 
Level 10
Joined
May 19, 2008
Messages
176
As for the multiboard, a lot of users aren't going to need one.

LAL, seriously? But you're right, why should anyone want a multiboard that shows him how much of what ressources he has? No one needs that ...

And btw. most events this system uses are not existing as a hardcoded event. So you can't register them for the units.

Also the system is really easy to use at the moment, but if D1000 would change the code to fit it your suggeston, no GUI user would ever be able to use it, because they had to write most of the code by themselves and not only copy and paste a few lines.

Next if you want a system that isn't limited by an API then you should write it yourself. This system is made for people that don't have the skill or the time to do it by themselves.

Last I think you coded you're system and you're really proud of it, gratz. I don't have a problem with that, but suggesting that you're way is the best and telling the author to rewrite all the code of his well working system only to find you're approval is what I don't like.
 
Last I think you coded you're system and you're really proud of it, gratz. I don't have a problem with that, but suggesting that you're way is the best and telling the author to rewrite all the code of his well working system only to find you're approval is what I don't like.

I was suggesting a rewrite because most of the code is really messy.
It's a good system at the moment, but why would GUI users use a vJass system?
Most of the GUIers I know only like using GUI systems and have problems implementing Jass scripts because a lot of them don't know how to create a trigger with custom text and there are even more who don't know where the map header is.

The majority of the people who could benefit from using this are the vJassers.
And vJass has a lot of standards.

GUI users are more comfortable using GUI systems like Bribe's UnitIndexer which he wrote in GUI-Jass (A combination of the 2)

Plus, any GUIer who uses this system would be expected to know the least bit about Jass, so modifying anything isn't a problem. And who said anything about Breaking backwards compatibilty? You can keep the current API and add to it, then rewrite the old functions so that they'd just call the new ones.
 
Level 4
Joined
Jun 22, 2008
Messages
49
To summarize your comments:
First you wrote:
You know, this system can be replaced entirely with this:
This statement is nothing but wrong. Your snippet isn't able to do even remotely the same. If you think it does, you didn't look at the testmap at all.

Later you wrote:
most of the code is really messy.
Well, if you think so, please explain what you mean. Otherwise it's just a plain insult.

The other comment between these two, was at least a bit constructive.
[link]
But I'm not sure I get what you're writing about. Either you're talking about the whole system, or about the events. If it's the former, I have no idea what you mean, in the case of the latter it's nonsense. Firstly, not all of the events can be registered with TriggerRegisterPlayerUnitEvent, since some of them (Hit, Return) don't exist without my system. Secondly the whole purpose of the other events is, that you don't have to use TriggerRegisterPlayerUnitEvent & to trigger your function only, if you have enough resources (if there were any resource-costs assigned to the given unit/ability/item/upgrade/building), since, for example, EVENT_PLAYER_UNIT_SPELL_CAST triggers, even when you don't have enough resources to cast your spell. (The system can't stop the cast completely, but just order the casting unit stop it instantly. Effects from the object editor are stopped this way, triggers are not.)
Furthermore the events are nothing but a small extra.


Btw, @Bribe:
Where? In my init function? It will just
1) Add an additional function-call per trigger
2) Use a separate loop for all of the triggers, instead of using one big loop
Maybe it reduces the amount of TriggerRegisterPlayerUnitEvent in the whole map, but just for my system alone, it doesn't make sense, no event is used twice.
 
This statement is nothing but wrong. Your snippet isn't able to do even remotely the same. If you think it does, you didn't look at the testmap at all.

Ofcourse it's not going to cover everything. What do you expect me to do? Rewrite the entire thing? I just gave you the basis of what it should look like to be more versatile.
You can add all those little functions like getIcon into it.

Well, if you think so, please explain what you mean. Otherwise it's just a plain insult.

Well, I'm not trying to insult you here. It's not your fault, it's Zincs fault.
The least you could do is add more comments at the core of the code and put more linebreaks so that it's easier to read.
And if you remove it from the hidden tag, it will look a lot better (The font is ugly inside hidden tags)

And to think I'd invest my time to insult you, is an insult to me :(

Where? In my init function? It will just
1) Add an additional function-call per trigger
2) Use a separate loop for all of the triggers, instead of using one big loop
Maybe it reduces the amount of TriggerRegisterPlayerUnitEvent in the whole map, but just for my system alone, it doesn't make sense, no event is used twice.

You know, TriggerRegisterAnyUnitEventBJ is one of the very few BJs we're ok with using :/
It does nothing for efficiency, but it decreases code size, which is a plus.
If you use RegisterPlayerUnitEvent, you wouldn't need those TriggerAddCondition lines and you wouldn't need those triggers.
 
Level 4
Joined
Jun 22, 2008
Messages
49
Ofcourse it's not going to cover everything. What do you expect me to do? Rewrite the entire thing? I just gave you the basis of what it should look like to be more versatile.
You can add all those little functions like getIcon into it.
You mean these little functions, like those which allow you to let units actually mine these new resources? Or these to spend them on items/units/buildings/upgrades/abilities? You do know, that this is not possible without a system like this, don't you? Otherwise you'd have to replace gold & lumber, instead of adding additional resources.

The least you could do is add more comments at the core of the code and put more linebreaks so that it's easier to read.
To be true, I'm to lazy to do that^^" I'm not really into mapping in wc3 anymore, if I want to program something, I mainly use, Java, C or the ScII-editor.
But I did comment the examples in the newest testmap.

And if you remove it from the hidden tag, it will look a lot better (The font is ugly inside hidden tags)
Is there a way to do this without the jass-tags being opened by default?


And to think I'd invest my time to insult you, is an insult to me :(
Well...it seemed that way to me...

You know, TriggerRegisterAnyUnitEventBJ is one of the very few BJs we're ok with using :/
But I'm not okay with using this^^ Why should I use 10 loops, if one is enough?


I still have no idea what you were talking about here: [link] (as mentioned in my previous post)
 
To sum up everything, I'm just asking for more readable and modular code :c

You mean these little functions, like those which allow you to let units actually mine these new resources? Or these to spend them on items/units/buildings/upgrades/abilities? You do know, that this is not possible without a system like this, don't you? Otherwise you'd have to replace gold & lumber, instead of adding additional resources.

Well, I'm sorry, but Zinc is a really language and I thought that the struct you store Resources in ended somewhere at the top when it turns out that it extended much longer :x

And thanks for giving me another reason to update RegisterPlayerUnitEvent ^.^
I'll update it so that the efficiency bonus is present in your case :D

I'll add this:

function RegisterPlayerUnitEventEx takes playerunitevent p, code c, player p returns nothing
 
Level 7
Joined
Apr 11, 2011
Messages
173
You mean these little functions, like those which allow you to let units actually mine these new resources? Or these to spend them on items/units/buildings/upgrades/abilities? You do know, that this is not possible without a system like this, don't you? Otherwise you'd have to replace gold & lumber, instead of adding additional resources.

To be true, I'm to lazy to do that^^" I'm not really into mapping in wc3 anymore, if I want to program something, I mainly use, Java, C or the ScII-editor.
But I did comment the examples in the newest testmap.

Is there a way to do this without the jass-tags being opened by default?


Well...it seemed that way to me...

But I'm not okay with using this^^ Why should I use 10 loops, if one is enough?


I still have no idea what you were talking about here: [link] (as mentioned in my previous post)

What isn't possible with what system? Why would you need to replace gold and lumber? As a gui'er and a somewhat jass'er myself, I find this just another system even though it is great, and you put a lot of effort and time into it. Just no use to pretty much 85% of Wc3 players that mapmake.

You should be told that Wc3 is just as good as Sc2 since it can do anything too even if it might lag a little but, not going to argue however so don't reply to this sentence.

Thank you for contributing D1000 but, it isn't that special of a system and for the only one who is trying to help you, your being rude to him.

Oh, and ummm Cedi not many GUI users will use this and the ones that will, will be able to deal with the updates to better code made.
 
Ok, here is what you need to do.


Split the resource up into different libraries. One thing for resources, one thing for workers, etc, etc... as it is now, it's just a mass of spaghetti code >.<. Furthermore, the lack of comments makes this near impossible to review in a timely manner anyways. Comments are important, so use them >: O.


Next, interface functions and interfaces add extra pointless overhead. Why not just use Events? >.>


Stuff like this should be easily customizable..
JASS:
            private static method updateMultiboard(integer whichPlayer) {
                integer i, j, k = number;
                thistype this;
                multiboarditem mi;
                
                MultiboardSetRowCount(properties[whichPlayer], k);
                        
                for(0 <= j < k) {
                    this = j+1;
                    
                    mi = MultiboardGetItem(properties[whichPlayer], j, 0);
                    MultiboardSetItemIcon(mi, icon);
                    MultiboardSetItemValue(mi, colour + name + "|r");
                    MultiboardSetItemWidth(mi, MULTIBOARD_NAME_WIDTH);
                    MultiboardReleaseItem(mi);
                        
                    mi = MultiboardGetItem(properties[whichPlayer], j, 1);
                    MultiboardSetItemStyle(mi, true, false);
                    MultiboardSetItemValue(mi, colour + I2S(amount[whichPlayer]) + "|r");
                    MultiboardSetItemWidth(mi, MULTIBOARD_VALUE_WIDTH);
                    MultiboardReleaseItem(mi);
                }
                
                mi = null;
            }

What if you don't want to use a multiboard?


Coupling things like this makes your system harder to modify for specific map needs, harder to use/understand, and harder to review.


The community knows how to fix these issues, but inlining something like a path finding algorithm is a really bad idea >.>. It also doesn't require too much power, A* works just fine.

JASS:
|• No Rally-Point-Support
|• Nearest storage/resource is determined via beeline, thus there are problems if the way is blocked 
|   (Could solve this with a path-finding-algorithm, but it would probably need to much computing)
|• Trying to buy an item/merc without having enough of a custom resource reduces the item/merc stock by 1


I see many things just glancing at the code that can be improved, but it's such a mess right now that I don't even want to look through it. It reminds me of how jasshelper was initially written >.<.


I also obviously really don't like this -> private static hashtable data = InitHashtable();



Now I'm only especially hard on this resource because large scale resources like these are generally very clunky and are not tightly written at all. The fact that the resource is large is generally a very bad sign : P.



I will review this resource once it is in a reviewable state =)
 
Last edited:
Yes! Splitting this up into many libraries is a wonderful idea!
You can make the Multiboard an extra plugin that you'd implement with optional textmacros

What you could do is make it so that the Resource struct is the main library and everything else (Mining/Buying/etc..) would be split up into libraries of their own.

This way, a user could decide exactly what he wants and the system would be perfect for anyone! :D
 
Level 5
Joined
Apr 16, 2009
Messages
23
okay this was great when i tested it but i keep getting this problem

10xd4lf.png


and as an unexperienced vjasser i don't know how to fix it so would be gratefull for help :) ps. sry for bad english

if u cannot see the pic it stands.

Line 2718 expected "type", "struct", "interface", "function", "keyword" or "scope"
 
Level 4
Joined
Jun 22, 2008
Messages
49
Since I'm not really actively mapping anymore, I don't know if I can help you. The error doesn't appear when I save the system, could you send me the map (via pm), so I can take a look at it?
 
Top