//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