[jass=vjass]scope ConstructableFarm initializer Setup
// By Masterninja13579
// --version 1.0.1--
//
// Constructable Farm is a farming system in which workers are
// assigned to crop fields. These workers automatically plant,
// water, and harvest crops to generate a steady gold income.
// The farms can be constructed and destroyed like normal structures.
// Happy Farming!
//
// Import Instructions:
// 1. Copy the custom units--Farm, Farm (constructable), and
// Farm (pathing)--into your map along with the Harvest ability.
// 2. Copy the Dummy unit into your map. If your map already contains
// a standard dummy unit with the model "Dummy.mdx" then skip to
// step 4.
// 3. Export the Dummy.mdx model file then import it into your map.
// Make sure that the Dummy unit has that file as it's model
// 4. Copy the ConstructableFarm trigger into your map. Inside the
// trigger, go to the Modifiable Variables section and enter all
// the rawcodes for the custom units and the harvest ability.
// 5. Test it out and enjoy! To allow units to construct a farm let
// them construct the Farm (constructable) unit. To allow units to
// harvest the farm just give them the Harvest ability
//
// Possible Future Additions:
// - Crops tilting as units walk through them
// - Floating text to show if a farm is occupied.
// - Ability to have multiple workers per farm
// - Implementation of Bribe's MissileRecycler
// - Multiple crop types with different properties
//
// Known Bugs:
// - (IN PROGRESS) Occasionally the worker will get stuck and will
// stop farming. Re-order him to harvest the farm if it happens
// to you.
//
// Credits:
// - Vexorian, for Dummy.mdx
//
//=======================================================
//================ Modifiable Variables =================
//=======================================================
globals
// Rawcode of the farm that is buildable by workers
// --Farm (constructable)
private constant integer CONSTRUCTABLE_ID = 'h000'
// Rawcode of the actual farm
// --Farm
private constant integer ACTUAL_ID = 'h001'
// Rawcode of the dummy unit to expand the pathing of farms
// --Farm (pathing)
private constant integer PATHING_ID = 'h002'
// Rawcode of the standard dummy unit
// --Dummy
private constant integer DUMMY_ID = 'dumi'
// Rawcode of the Harvest ability and it's order string.
// Note: units that use the harvest ability must not have any
// other abilities with the same order string.
private constant integer SPELL_ID = 'A000'
private constant string SPELL_ORDER = "channel"
// Width and length of the area crops can grow in
// More accurately it's the x distance (width) and y distance
// (length) that a crop can grow from the farm center
private constant real GROWABLE_WIDTH = 170
private constant real GROWABLE_LENGTH = 170
// Radius to NOT plant crops from the center of the farm
private constant real CENTER_RADIUS = 35
// Gold reward for each crop harvested
private constant integer GOLD_PER_CROP = 12
// These variables are used to enlarge the crops as they grow
// XY_SCALE is the width and length, and Z_SCALE is the height
private constant real XY_SCALE_START = 0.3
private constant real XY_SCALE_END = 1.0
private constant real Z_SCALE_START = 2.0
private constant real Z_SCALE_END = 1.0
// The total time a crop takes to finish growing
private constant real GROW_TIME = 180.0
// The time a crop can grow before needing more water
private constant real THIRST_INTERVAL = 80.0
// The time a crop takes to die if not watered
private constant real DECAY_TIME = 60.0
// The time it takes to plant a crop
private constant real PLANT_DURATION = 6.0
// The time it takes to water a crop
private constant real WATER_DURATION = 3.0
// The time between water effects
private constant real WATER_SFX_INTERVAL = 1.0
// The start time for water effects. The first water effect
// appears when the time reaches WATER_SFX_INTERVAL
private constant real WATER_SFX_TIME_OFFSET = 0.6
// The time it takes to harvest
private constant real HARVEST_DURATION = 4.0
// The starting height of a water effect
private constant real WATER_HEIGHT = 70.0
// The XY speed of a water effect
private constant real WATER_SPEED = 150.0
// The scaling value os a water effect
private constant real WATER_SCALE = 0.3
// The model path of the crop
private constant string CROP_PATH = "Doodads\\LordaeronSummer\\Plants\\Corn\\Corn.mdl"
// The model path of the water effect
private constant string WATER_SFX = "Abilities\\Weapons\\WaterElementalMissile\\WaterElementalMissile.mdl"
// The model path to use when a crop is harvested
private constant string HARVEST_SFX = "UI\\Feedback\\GoldCredit\\GoldCredit.mdl"
// The model path to use when a crop decays
private constant string DECAY_SFX = "Abilities\\Spells\\Orc\\Devour\\DevourEffectArt.mdl"
// The model path to use when the farm is destroyed
private constant string DEATH_SFX = "Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl"
// I recommend that you do not mess with these very much.
// Having 100 farms with 30 crops per farm will slow your system
// down tremendously, and it is more than enough.
private constant integer MAXIMUM_FARMS = 100
private constant integer MAXIMUM_CROPS = 30
endglobals
//=======================================================
//============= End of Modifiable Variables =============
//=======================================================
native UnitAlive takes unit u returns boolean
private keyword Farm
private keyword Water
globals
private constant integer IDLE = 0
private constant integer PLANTING = 1
private constant integer WATERING = 2
private constant integer HARVESTING = 3
private constant real GRID_DISTANCE = 128
private constant real TIMEOUT = 0.03125000
private trigger harvestPoint = CreateTrigger()
private trigger workerOrder = CreateTrigger()
private Farm array farms
private Water array missiles
private integer totalFarms = 0
private integer totalMissiles = 0
private timer t = CreateTimer()
endglobals
//=======================================================
//======================= Structs =======================
//=======================================================
// struct crop
// method destroy takes nothing returns nothing
// method getX takes nothing returns real
// method getY takes nothing returns real
// method isThirsty takes nothing returns boolean
// method quenchThirst takes nothing returns nothing
// method isMature takes nothing returns boolean
// method tick takes nothing returns nothing
// static method create takes real x, real y, Farm parent returns thistype
private struct Crop
private real x
private real y
private unit u
private effect model
private real age
private real thirst
private real health
private Farm parent
method destroy takes nothing returns nothing
call DestroyEffect(.model)
call RemoveUnit(.u)
set .u = null
set .model = null
call .deallocate()
endmethod
private method updateSize takes nothing returns nothing
local real ratio = .age/GROW_TIME
local real xy = ratio*(XY_SCALE_END - XY_SCALE_START) + XY_SCALE_START
local real z = ratio*(Z_SCALE_END - Z_SCALE_START) + Z_SCALE_START
call SetUnitScale(.u, xy, xy, z)
endmethod
method getX takes nothing returns real
return .x
endmethod
method getY takes nothing returns real
return .y
endmethod
method isThirsty takes nothing returns boolean
return .thirst <= 0
endmethod
method quenchThirst takes nothing returns nothing
set .thirst = THIRST_INTERVAL
endmethod
method isMature takes nothing returns boolean
return age >= GROW_TIME and .health >= DECAY_TIME
endmethod
method tick takes nothing returns nothing
if(.thirst <= 0) then
set .health = .health - TIMEOUT
if(.health <= 0) then
call .parent.decayCrop(this)
endif
else
if(.age < GROW_TIME) then
set .age = .age + TIMEOUT
endif
set .thirst = .thirst - TIMEOUT
if(.health < DECAY_TIME) then
set .health = .health + TIMEOUT
endif
call updateSize()
endif
endmethod
static method create takes real x, real y, Farm parent returns thistype
local thistype this = allocate()
set .x = x
set .y = y
set .parent = parent
set .age = 0
set .thirst = THIRST_INTERVAL
set .health = DECAY_TIME
// Create unit
set .u = CreateUnit(Player(15), DUMMY_ID, x, y, GetRandomReal(0, 360))
set .model = AddSpecialEffectTarget(CROP_PATH, .u, "origin")
call SetUnitAnimationByIndex(.u, 90)
return this
endmethod
endstruct
// struct Water
// method destroy takes nothing returns nothing
// method isFinished takes nothing returns boolean
// method tick takes nothing returns nothing
// static method create takes real x, real y, real x2, real y2 returns thistype
private struct Water
private unit u
private effect model
private real x
private real y
private real z
private real dx
private real dy
private real zSlope // The M variable. (y = mx^2 + b)
private real age
private boolean expired
method destroy takes nothing returns nothing
call DestroyEffect(.model)
call RemoveUnit(.u)
set .u = null
set .model = null
call .deallocate()
endmethod
method isFinished takes nothing returns boolean
return expired
endmethod
method tick takes nothing returns nothing
set .age = .age + TIMEOUT
set .x = .x + .dx
set .y = .y + .dy
set .z = zSlope*.age*.age + WATER_HEIGHT
if(.z <= 0) then
set .expired = true
else
call SetUnitX(.u, .x)
call SetUnitY(.u, .y)
call SetUnitFlyHeight(.u, .z, 0)
endif
endmethod
static method create takes real x, real y, real x2, real y2 returns thistype
local thistype this = allocate()
local real angle = Atan2(y2 - y, x2 - x)
local real distance = SquareRoot(Pow(x2 - x, 2) + Pow(y2 - y, 2))
set .x = x
set .y = y
set .z = WATER_HEIGHT
set .dx = Cos(angle)*WATER_SPEED*TIMEOUT
set .dy = Sin(angle)*WATER_SPEED*TIMEOUT
set .zSlope = (-1*WATER_HEIGHT) / Pow(distance/WATER_SPEED, 2)
set .age = 0
set .expired = false
// Create unit
set .u = CreateUnit(Player(15), DUMMY_ID, x, y, angle)
set .model = AddSpecialEffectTarget(WATER_SFX, .u, "origin")
call PauseUnit(.u, true)
call SetUnitFlyHeight(.u, .z, 0)
call SetUnitFacing(.u, Rad2Deg(angle))
call SetUnitScale(.u, WATER_SCALE, WATER_SCALE, WATER_SCALE)
call SetUnitAnimationByIndex(.u, 90)
return this
endmethod
endstruct
// struct FarmWorker
// method destroy takes nothing returns nothing
// method getTaskX takes nothing returns real
// method getTaskY takes nothing returns real
// method getTaskCrop takes nothing returns crop
// method getUnit takes nothing returns unit
// method getState takes nothing returns integer
// method getParentFarm takes nothing returns Farm
// method registerCastStart takes nothing returns nothing
// method registerCastStop takes nothing returns nothing
// method setTask takes integer state, real x, real y, crop target returns nothing
// method setTaskSimple takes integer state, location l returns nothing
// method tick takes nothing returns nothing
// static method isUnitWorker takes unit u returns boolean
// static method create takes unit u, Farm parent returns thistype
private struct FarmWorker
private real taskX
private real taskY
private Crop taskCrop
private unit u
private integer state
private boolean casting
private real time
private Farm parent
private static group workers = CreateGroup()
method destroy takes nothing returns nothing
call GroupRemoveUnit(workers, .u)
if(.casting) then
call .parent.issuePointOrder(.u, "move", GetUnitX(.u), GetUnitY(.u))
endif
set .u = null
call .deallocate()
endmethod
method getTaskX takes nothing returns real
return .taskX
endmethod
method getTaskY takes nothing returns real
return .taskY
endmethod
method getTaskCrop takes nothing returns Crop
return .taskCrop
endmethod
method getUnit takes nothing returns unit
return .u
endmethod
method getState takes nothing returns integer
return .state
endmethod
method getParentFarm takes nothing returns Farm
return .parent
endmethod
method registerCastStart takes nothing returns nothing
set .casting = true
endmethod
method registerCastStop takes nothing returns nothing
set .casting = false
endmethod
method setTask takes integer state, real x, real y, Crop target returns nothing
set .state = state
set .taskX = x
set .taskY = y
set .taskCrop = target
set .casting = false
set .time = 0
call .parent.issuePointOrder(.u, SPELL_ORDER, x, y)
endmethod
method setTaskSimple takes integer state, location l returns nothing
set .state = state
set .taskX = GetLocationX(l)
set .taskY = GetLocationY(l)
set .taskCrop = 0
set .casting = false
set .time = 0
call .parent.issuePointOrder(.u, SPELL_ORDER, .taskX, .taskY)
endmethod
method tick takes nothing returns nothing
if not(UnitAlive(.u)) then
call .parent.removeFarmWorker()
elseif(.casting) then
set .time = .time + TIMEOUT
if(.state == PLANTING and .time >= PLANT_DURATION) then
call .parent.taskIsDone(this)
elseif(.state == WATERING) then
if(ModuloReal(.time + WATER_SFX_TIME_OFFSET, WATER_SFX_INTERVAL) < TIMEOUT) then
call .parent.createWater(GetUnitX(.u), GetUnitY(.u), .taskX, .taskY)
endif
if(taskCrop == 0) then
set .state = IDLE
elseif(.time >= WATER_DURATION) then
call .parent.taskIsDone(this)
endif
elseif(.state == HARVESTING and .time >= HARVEST_DURATION) then
call .parent.taskIsDone(this)
endif
elseif(.state == WATERING and taskCrop == 0) then
set .state = IDLE
elseif(.state == IDLE) then
call .parent.taskIsDone(this)
elseif not(GetUnitCurrentOrder(.u) == OrderId(SPELL_ORDER)) then
call .parent.issuePointOrder(.u, SPELL_ORDER, .taskX, .taskY)
endif
endmethod
static method isUnitWorker takes unit u returns boolean
return IsUnitInGroup(u, workers)
endmethod
static method create takes unit u, Farm parent returns thistype
local thistype this = allocate()
set .u = u
set .state = IDLE
set .casting = false
set .time = 0
set .parent = parent
call GroupAddUnit(workers, u)
return this
endmethod
endstruct
// struct Farm
// method destroy takes nothing returns nothing
// method destroyCrop takes crop c returns nothing
// method createCrop takes real x, real y returns nothing
// method createWater takes real x, real y, real x2, real y2 returns nothing
// method harvestCrop takes crop c returns nothing
// method decayCrop takes crop c returns nothing
// method isFull takes nothing returns boolean
// method getUnit takes nothing returns unit
// method removeFarmWorker takes nothing returns nothing
// method addFarmWorker takes unit u returns nothing
// method getFarmWorker takes nothing returns FarmWorker
// method getRandomPlantLoc takes nothing returns location
// method issuePointOrder takes unit u, string order, real x, real y returns nothing
// method assignNewTask takes FarmWorker w returns nothing
// method taskIsDone takes FarmWorker w returns nothing
// method tick takes nothing returns nothing
// static method create takes real x, real y, player p returns thistype
private struct Farm
private unit u
private FarmWorker worker
private Crop array crops[MAXIMUM_CROPS]
private integer totalCrops
private group pathing
method destroy takes nothing returns nothing
local integer i = 0
local unit p
if not(.worker == 0) then
set p = .worker.getUnit()
call .worker.destroy()
call IssuePointOrder(p, "move", GetUnitX(p), GetUnitY(p))
endif
loop
exitwhen i >= .totalCrops
if(GetRandomInt(0, 3) == 0) then
call DestroyEffect(AddSpecialEffect(DEATH_SFX, .crops.getX(), .crops.getY()))
endif
call .crops.destroy()
set i = i + 1
endloop
loop
set p = FirstOfGroup(.pathing)
exitwhen p == null
call GroupRemoveUnit(.pathing, p)
call RemoveUnit(p)
endloop
call DestroyEffect(AddSpecialEffect(DEATH_SFX, GetUnitX(.u), GetUnitY(.u)))
call RemoveUnit(.u)
set .u = null
set p = null
set .pathing = null
call DestroyGroup(.pathing)
call .deallocate()
endmethod
method destroyCrop takes Crop c returns nothing
local integer i = 0
loop
exitwhen i >= totalCrops
if(crops == c) then
call c.destroy()
set totalCrops = totalCrops - 1
set crops = crops[totalCrops]
else
set i = i + 1
endif
endloop
endmethod
method createCrop takes real x, real y returns nothing
set crops[totalCrops] = Crop.create(x, y, this)
set totalCrops = totalCrops + 1
endmethod
method createWater takes real x, real y, real x2, real y2 returns nothing
set missiles[totalMissiles] = Water.create(x, y, x2, y2)
set totalMissiles = totalMissiles + 1
endmethod
method harvestCrop takes Crop c returns nothing
local player p = GetOwningPlayer(.u)
call SetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD, GetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD) + GOLD_PER_CROP)
call DestroyEffect(AddSpecialEffect(HARVEST_SFX, c.getX(), c.getY()))
call destroyCrop(c)
set p = null
endmethod
method decayCrop takes Crop c returns nothing
call DestroyEffect(AddSpecialEffect(DECAY_SFX, c.getX(), c.getY()))
call destroyCrop(c)
endmethod
method isFull takes nothing returns boolean
return not(.worker == 0)
endmethod
method getUnit takes nothing returns unit
return .u
endmethod
method removeFarmWorker takes nothing returns nothing
if not(.worker == 0) then
call .worker.destroy()
set .worker = 0
endif
endmethod
method addFarmWorker takes unit u returns nothing
call .removeFarmWorker()
set .worker = FarmWorker.create(u, this)
endmethod
method getFarmWorker takes nothing returns FarmWorker
return .worker
endmethod
method getRandomPlantLoc takes nothing returns location
local real x = GetUnitX(.u)
local real y = GetUnitY(.u)
local real x2
local real y2
local real distance
local boolean success = false
local integer iterations = 0
loop
set x2 = GetRandomReal(x - GROWABLE_WIDTH, x + GROWABLE_WIDTH)
set y2 = GetRandomReal(y - GROWABLE_LENGTH, y + GROWABLE_LENGTH)
set distance = SquareRoot(Pow(x - x2, 2) + Pow(y - y2, 2))
if(distance > CENTER_RADIUS) then
set success = true
endif
set iterations = iterations + 1
exitwhen success or iterations > 50
endloop
if(success) then
return Location(x2, y2)
endif
return Location(0, 0)
endmethod
method issuePointOrder takes unit u, string order, real x, real y returns nothing
call DisableTrigger(harvestPoint)
call DisableTrigger(workerOrder)
call IssuePointOrder(u, order, x, y)
call EnableTrigger(harvestPoint)
call EnableTrigger(workerOrder)
endmethod
method assignNewTask takes FarmWorker w returns nothing
local integer i = 0
loop
exitwhen i >= totalCrops
if(crops.isThirsty()) then
call w.setTask(WATERING, crops.getX(), crops.getY(), crops)
return
endif
set i = i + 1
endloop
set i = 0
loop
exitwhen i >= totalCrops
if(crops.isMature()) then
call w.setTask(HARVESTING, crops.getX(), crops.getY(), crops)
return
endif
set i = i + 1
endloop
if(totalCrops >= MAXIMUM_CROPS) then
call w.setTask(IDLE, 0, 0, 0)
else
call w.setTaskSimple(PLANTING, .getRandomPlantLoc())
endif
endmethod
method taskIsDone takes FarmWorker w returns nothing
if(w.getState() == PLANTING) then
call createCrop(w.getTaskX(), w.getTaskY())
elseif(w.getState() == WATERING) then
call w.getTaskCrop().quenchThirst()
elseif(w.getState() == HARVESTING) then
call harvestCrop(w.getTaskCrop())
endif
call assignNewTask(w)
endmethod
method tick takes nothing returns nothing
local integer i = 0
if not(.worker == 0) then
call .worker.tick()
endif
loop
exitwhen i >= .totalCrops
call .crops.tick()
set i = i + 1
endloop
endmethod
static method create takes real x, real y, player p returns thistype
local thistype this = allocate()
local integer i = -1
local integer i2
set .u = CreateUnit(p, ACTUAL_ID, x, y, bj_UNIT_FACING)
set .totalCrops = 0
set .pathing = CreateGroup()
// Create pathing units
loop
exitwhen i >= 2
set i2 = -1
loop
exitwhen i2 >= 2
call GroupAddUnit(.pathing, CreateUnit(p, PATHING_ID, x + i*GRID_DISTANCE, y + i2*GRID_DISTANCE, bj_UNIT_FACING))
set i2 = i2 + 1
endloop
set i = i + 1
endloop
return this
endmethod
endstruct
//=======================================================
//====================== Functions ======================
//=======================================================
private function Periodic takes nothing returns nothing
local integer i = 0
loop
exitwhen i >= totalFarms
if(UnitAlive(farms.getUnit())) then
call farms.tick()
set i = i + 1
else
call farms.destroy()
set totalFarms = totalFarms - 1
set farms = farms[totalFarms]
endif
endloop
set i = 0
loop
exitwhen i >= totalMissiles
if(missiles.isFinished()) then
call missiles.destroy()
set totalMissiles = totalMissiles - 1
set missiles = missiles[totalMissiles]
else
call missiles.tick()
set i = i + 1
endif
endloop
if(totalMissiles == 0 and totalFarms == 0) then
call PauseTimer(t)
endif
endfunction
private function GetFarm takes unit u returns Farm
local integer i = 0
loop
exitwhen i >= totalFarms
if(farms.getUnit() == u) then
return farms
endif
set i = i + 1
endloop
return 0
endfunction
private function AddWorkerToFarm takes unit u, Farm target returns boolean
if(target == 0 or target.isFull()) then
return false
endif
call target.addFarmWorker(u)
return true
endfunction
private function AddWorker takes unit u returns boolean
local real x = GetUnitX(u)
local real y = GetUnitY(u)
local real distance
local real closest = 99999
local Farm candidate = 0
local player p = GetOwningPlayer(u)
local unit f
local integer i = 0
loop
exitwhen i >= totalFarms
set f = farms.getUnit()
if(GetOwningPlayer(f) == p and not(farms.isFull())) then
set distance = SquareRoot(Pow(x - GetUnitX(f), 2) + Pow(y - GetUnitY(f), 2))
if(distance < closest) then
set closest = distance
set candidate = farms
endif
endif
set i = i + 1
endloop
if not(candidate == 0) then
call AddWorkerToFarm(u, candidate)
return true
endif
set p = null
set u = null
return false
endfunction
private function GetWorker takes unit u returns FarmWorker
local integer i = 0
loop
exitwhen i >= totalFarms
if(farms.getFarmWorker().getUnit() == u) then
return farms.getFarmWorker()
endif
set i = i + 1
endloop
return 0
endfunction
private function RemoveWorker takes unit u returns boolean
local integer i = 0
loop
exitwhen i >= totalFarms
if(farms.getFarmWorker().getUnit() == u) then
call farms.removeFarmWorker()
return true
endif
set i = i + 1
endloop
return false
endfunction
private function CreateFarm takes unit u returns nothing
local real x = GetUnitX(u)
local real y = GetUnitY(u)
local player p = GetOwningPlayer(u)
call RemoveUnit(u)
if(totalFarms < MAXIMUM_FARMS) then
set farms[totalFarms] = Farm.create(x, y, p)
set totalFarms = totalFarms + 1
if(totalFarms == 1) then
call TimerStart(t, TIMEOUT, true, function Periodic)
endif
endif
set p = null
endfunction
private function RegisterTaskStart takes unit u returns nothing
local FarmWorker worker = GetWorker(u)
if not(worker == 0) then
call worker.registerCastStart()
endif
endfunction
private function RegisterTaskStop takes unit u returns nothing
local FarmWorker worker = GetWorker(u)
if not(worker == 0) then
call worker.registerCastStop()
endif
endfunction
private function HandleHarvestUnit takes nothing returns nothing
local unit u = GetOrderedUnit()
local unit t = GetOrderTargetUnit()
local player pu = GetOwningPlayer(u)
local player pt = GetOwningPlayer(t)
local FarmWorker worker = GetWorker(u)
call IssuePointOrder(u, "move", GetUnitX(u), GetUnitY(u))
if not(worker == 0) and pu == pt then
call RemoveWorker(u)
endif
if(GetUnitTypeId(t) == ACTUAL_ID and pu == pt) then
if not(AddWorkerToFarm(u, GetFarm(t))) then
call AddWorker(u)
endif
endif
set u = null
endfunction
//=======================================================
//=================== Event Handlers ====================
//=======================================================
private function HarvestPointCondition takes nothing returns nothing
local unit u = GetOrderedUnit()
if(GetIssuedOrderId() == OrderId(SPELL_ORDER)) then
call IssuePointOrder(u, "move", GetUnitX(u), GetUnitY(u))
endif
set u = null
endfunction
private function HarvestUnitCondition takes nothing returns nothing
if(GetIssuedOrderId() == OrderId(SPELL_ORDER)) then
call HandleHarvestUnit()
endif
endfunction
private function CastCondition takes nothing returns nothing
if(GetSpellAbilityId() == SPELL_ID and FarmWorker.isUnitWorker(GetSpellAbilityUnit())) then
call RegisterTaskStart(GetSpellAbilityUnit())
endif
endfunction
private function WorkerOrderCondition takes nothing returns nothing
if(FarmWorker.isUnitWorker(GetOrderedUnit()) and not(GetIssuedOrderId() == OrderId(SPELL_ORDER))) then
call RemoveWorker(GetOrderedUnit())
endif
endfunction
private function CancelCondition takes nothing returns nothing
if(GetSpellAbilityId() == SPELL_ID and FarmWorker.isUnitWorker(GetSpellAbilityUnit())) then
call RegisterTaskStop(GetSpellAbilityUnit())
endif
endfunction
private function ConstructCondition takes nothing returns nothing
if(GetUnitTypeId(GetConstructedStructure()) == CONSTRUCTABLE_ID) then
call CreateFarm(GetConstructedStructure())
endif
endfunction
//=======================================================
//=================== Event Triggers ====================
//=======================================================
// 1. on order to cast harvest on a point (detects when player
// tries to cast harvest incorrectly)
// 2. on order to cast harvest on a unit (detects when player
// tries to cast harvest incorrectly, but if target unit is
// a farm it will try to assign the ordered unit to it)
// 3. on cast of harvest ability (detects when a worker begins
// a task, since all other attempts at casting harvest will
// be stopped)
// 4. on order of worker unit (detects when a farm worker is
// ordered to perform a different task by the player)
// 5. on cancel of harvest ability (detects if a worker is
// interrupted while planting, watering, or growing crops)
// 6. on finish of construction
private function Setup takes nothing returns nothing
local trigger harvestUnit = CreateTrigger()
local trigger cast = CreateTrigger()
local trigger cancel = CreateTrigger()
local trigger construct = CreateTrigger()
local integer i = 0
local player p
loop
exitwhen i > 11
set p = Player(i)
call TriggerRegisterPlayerUnitEvent(harvestPoint, p, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, null)
call TriggerRegisterPlayerUnitEvent(harvestUnit, p, EVENT_PLAYER_UNIT_ISSUED_UNIT_ORDER, null)
call TriggerRegisterPlayerUnitEvent(cast, p, EVENT_PLAYER_UNIT_SPELL_CHANNEL, null)
call TriggerRegisterPlayerUnitEvent(workerOrder, p, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, null)
call TriggerRegisterPlayerUnitEvent(workerOrder, p, EVENT_PLAYER_UNIT_ISSUED_UNIT_ORDER, null)
call TriggerRegisterPlayerUnitEvent(workerOrder, p, EVENT_PLAYER_UNIT_ISSUED_ORDER, null)
call TriggerRegisterPlayerUnitEvent(cancel, p, EVENT_PLAYER_UNIT_SPELL_ENDCAST, null)
call TriggerRegisterPlayerUnitEvent(construct, p, EVENT_PLAYER_UNIT_CONSTRUCT_FINISH, null)
set i = i + 1
endloop
call TriggerAddAction(harvestPoint, function HarvestPointCondition)
call TriggerAddAction(harvestUnit, function HarvestUnitCondition)
call TriggerAddAction(cast, function CastCondition)
call TriggerAddAction(workerOrder, function WorkerOrderCondition)
call TriggerAddAction(cancel, function CancelCondition)
call TriggerAddAction(construct, function ConstructCondition)
set harvestUnit = null
set cast = null
set cancel = null
set construct = null
set p = null
endfunction
endscope[/code]