________________________________________________________________________________________
Farm Land Generator 1.5.3
This spell Produces Farm Land around a finished building of your choice. The Yield produced
is customizable depending on the terrain under the Farm Land. The production value is stored
and the purpose is to use it in another trigger to have more flexible farm output - be it for
income, food or anything else really.
Features: Support multiple farm-lands and farm-tiles.
Customizeable color gradient, and display toggle.
Calculation of expected yield before production
Test-map containing income examples as well as customization of yield.
Link: http://www.hiveworkshop.com/forums/spells-569/farm-land-1-5-a-265277/
________________________________________________________________________________________
Appendix
1. Requirments
2. Implementation
3. API
4. Known Issues
5. Credits
6. Change Log
________________________________________________________________________________________
1. Requirments
1) UnitIndexer(*) http://www.hiveworkshop.com/forums/spells-569/gui-unit-indexer-1-2-0-2-a-197329/?prev=search%3DUNIT%2520INDEXER%26d%3Dlist%26r%3D20
2) PathingLibrary http://www.hiveworkshop.com/forums/spells-569/pathing-type-v1-2-a-263230/
3) Table http://www.hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
4) WorldBounds http://www.hiveworkshop.com/forums/graveyard-418/snippet-worldbounds-180494/
5) Native Declaration (See Trigger in the Required Category)
________________________________________________________________________________________
2. Implementation
a) Make sure your map contains all the required resources (See folder Required).
*Note that The used UnitIndexer is optional in the sense that you can use any
availible unit indexer based on unit-custom value.
b) Copy the trigger: FarmLandLib into your map.
c) Copy FarmMain or make a similar trigger using those functions.
d) Change the configuration to suit your needs.
e) Change FarmMain or what ever trigger you are using to match your need. When in doubt
take a look at the API and the provided exmples.
________________________________________________________________________________________
3. Application Programming Interface
struct FarmLand
1. static method create takes nothing returns FarmLand
2. method addGrid takes real x, real y returns nothing
3. method setTerrainYield takes integer terrain, real yield, integer farmTile returns nothing
4. method addFarmType takes integer unitTypeId returns nothing
5. method setupRadius takes nothing returns nothing
6. method addColor takes string colorcode returns nothing
7. method getTerrainYield takes real x, real y returns real
8. method isGridFarmable takes real x, real y returns boolean
Functions
1. function IsUnitFarm takes unit u returns boolean
2. function GetUnitFarmLand takes unit u returns FarmLand
3. function AddFarm takes FarmLand fl, unit u returns nothing
4. function RemoveFarm takes FarmLand fl, unit u returns nothing
5. function UpdateRadiusAtUnitLoc takes unit u returns nothing
6. function DisplayUnitYield takes FarmLand fl, unit u, real yield returns nothing
7. function CalcExpectedYieldXY takes FarmLand fl, real x, real y, integer unitTypeId returns real
8. function GetFarmGroup takes FarmLand fl returns group
9. function GetUnitYield takes unit u returns real
10. function GetLastChangedFarm takes nothing returns unit
________________________________________________________________________________________
4. Known Issues
a) Some times the update of farmland isnt 100% correct.
________________________________________________________________________________________
5. Credits
a) Dalvengyr for making Pathing Library
b) Bribe for making GUI-UnitIndexer and Table library used.
c) Dr Super Good, PurgeandFire and others for teaching me.
d) rulerofiron99 for giving me suggestions.
e) BPower for Native Declaration
f) edo494 and Moderators for giving feedback
g) SAUS for suggestions on how to solve the unit pathing check.
________________________________________________________________________________________
6. Change Log
v. 1.5
- Rewrote it.
- Uses WorldBounds
- Now supports multiple types of FarmLand (Testmap-contains 3 races)
- Support farm-tile variation, so you can have rough farm tile on low yield and lush farm tile on high yield.
- Now Uses Table http
- Yield Display can now exceed 100%
- New functionality: Can change unit yield without any events and also reset it to the state it should be in.
- Can toggle a display variable on and off.
- Added Example Triggers to show how to use the custom yield change.
- improved income example delta somewhat in function OnYieldChange
v. 1.5 .1
- fixed texttag bug
- fixed income bug
- fixed issue with the order of the function calls
- fixed documentation error
- removed customizeation of farm yield outside of event
- Grid and Color struct now uses Table instead of an array
v. 1.5 .2
- Units no longer block added farmland.
- Two new functions: AddFarm and RemoveFarm
- Made RefreshAreaAroundUnit privet.
- Minor bug-fixes
v. 1.5 .3
- Made the methods getTerrainYield and isGridFarmable 'public'
- Changed getTerrainYield paremeter to only take real x, y and made the previous integer i into a private global
________________________________________________________________________________________
library FarmLand initializer Init uses PathingLib, WorldBounds, Table
globals
private hashtable udg_hash = InitHashtable()
private group search = CreateGroup()
private group g1 = CreateGroup()
private group g2 = CreateGroup()
private real array sum
private real array yield
private integer i
boolean fland_show = true
real fland_event
unit fland_unit
real fland_yield_old
real fland_yield_new
endglobals
private struct Color
Table s
integer count = 0
real max = 0.
static method create takes nothing returns Color
local thistype this = .allocate()
set this.s = Table.create()
return this
endmethod
method getColor takes real yield returns string
set i = 0
if not (.count == 0) then
if yield <= .max then
loop
exitwhen i == .count - 1
if yield <= (.max / R2I(.count - 2))*R2I(i) then
return .s.string[i]
endif
set i = i +1
endloop
endif
return .s.string[.count - 1]
endif
return ""
endmethod
endstruct
private struct Grid
Table x
Table y
integer n = 0
static method create takes nothing returns Grid
local thistype this = .allocate()
set this.x = Table.create()
set this.y = Table.create()
return this
endmethod
endstruct
function IsUnitFarm takes unit u returns boolean
return LoadBoolean(udg_hash, GetUnitTypeId(u), 0)
endfunction
function GetUnitFarmLand takes unit u returns FarmLand
return LoadInteger(udg_hash, GetUnitTypeId(u), 1)
endfunction
private function IsUnitGridOwner takes unit u, real x, real y returns boolean
return u == LoadUnitHandle(udg_hash, R2I(x), R2I(y))
endfunction
private function ClearFarmLand takes real x, real y returns nothing
call SetTerrainType(x,y, LoadInteger(udg_hash, R2I(x), R2I(y)), -1, 1, 1)
call RemoveSavedInteger(udg_hash, R2I(x),R2I(y))
call RemoveSavedHandle(udg_hash, R2I(x), R2I(y))
endfunction
private function SpawnFarmLand takes FarmLand fl, unit u, real x, real y returns nothing
local integer terrain = fl.t2[GetTerrainType(x, y)]
call SaveInteger(udg_hash, R2I(x), R2I(y), GetTerrainType(x,y))
call SaveUnitHandle(udg_hash, R2I(x), R2I(y), u)
call SetTerrainType(x, y, terrain, -1, 1, 1)
endfunction
struct FarmLand
Grid grid
Color color
Table t1
Table t2
group g = CreateGroup()
static real radius = 0.
static method create takes nothing returns FarmLand
local thistype this = allocate()
set this.color = Color.create()
set this.grid = Grid.create()
set this.t1 = Table.create()
set this.t2 = Table.create()
return this
endmethod
method addGrid takes real x, real y returns nothing
set .grid.x.real[.grid.n] = x
set .grid.y.real[.grid.n] = y
set .grid.n = .grid.n + 1
endmethod
method setTerrainYield takes integer terrain, real yield, integer farmTile returns nothing
set .t1.real[terrain] = yield
set .t2[terrain] = farmTile
if yield > color.max then
set color.max = yield
endif
endmethod
method getTerrainYield takes real x, real y returns real
return .t1.real[GetTerrainType(x, y)]
endmethod
method addFarmType takes integer unitTypeId returns nothing
call SaveBoolean(udg_hash, unitTypeId, 0, true)
call SaveInteger(udg_hash, unitTypeId, 1, this)
endmethod
method setupRadius takes nothing returns nothing
local real d
set i = 0
loop
exitwhen i == .grid.n
set d = SquareRoot(.grid.x.real[i]*.grid.x.real[i] + .grid.y.real[i]*.grid.y.real[i])
if d > FarmLand.radius then
set FarmLand.radius = d + 246.
endif
set i = i + 1
endloop
endmethod
method addColor takes string colorcode returns nothing
set color.s.string[color.count] = colorcode
set color.count = color.count + 1
endmethod
method isGridFarmable takes real x, real y returns boolean
return (IsTerrainWalkable(x, y) or i == 0) and .getTerrainYield(x, y) > 0.
endmethod
endstruct
private function RefreshAreaAroundUnit takes FarmLand fl, unit u, unit enum returns nothing
local integer uid = GetUnitUserData(u)
local real change = 0.
local real array x
local real array y
set x[uid] = GetUnitX(u)
set y[uid] = GetUnitY(u)
call GroupEnumUnitsInRange(g1, x[uid], y[uid], FarmLand.radius, null)
loop
set enum = FirstOfGroup(g1)
exitwhen enum == null
call GroupRemoveUnit(g1, enum)
if not IsUnitType(enum, UNIT_TYPE_STRUCTURE) then
call GroupAddUnit(g2, enum)
set i = GetUnitUserData(enum)
set x[i] = GetUnitX(enum)
set y[i] = GetUnitY(enum)
call SetUnitX(enum, x[uid])
call SetUnitY(enum, y[uid])
endif
endloop
set i = 0
loop
exitwhen i == fl.grid.n
set x[0] = x[uid] + fl.grid.x.real[i]
set y[0] = y[uid] + fl.grid.y.real[i]
if IsUnitGridOwner(u, x[0], y[0]) and (not IsTerrainWalkable(x[0], y[0]) or not IsUnitInGroup(u, fl.g)) then
call ClearFarmLand(x[0], y[0])
set change = change - fl.getTerrainYield(x[0], y[0])
elseif IsUnitInGroup(u, fl.g) and fl.isGridFarmable(x[0], y[0]) then
set change = change + fl.getTerrainYield(x[0], y[0])
call SpawnFarmLand(fl, u, x[0], y[0])
endif
set i = i + 1
endloop
loop
set enum = FirstOfGroup(g2)
exitwhen enum == null
call GroupRemoveUnit(g2, enum)
set i = GetUnitUserData(enum)
call SetUnitX(enum, x[i])
call SetUnitY(enum, y[i])
endloop
if change != 0. then
set sum[uid] = sum[uid] + change
set fland_yield_old = yield[uid]
set yield[uid] = sum[uid]/fl.grid.n
set fland_yield_new = yield[uid]
set fland_unit = u
set fland_event = 0
set fland_event = 1
endif
endfunction
function UpdateRadiusAtUnitLoc takes unit u returns nothing
call GroupEnumUnitsInRange(search, GetUnitX(u), GetUnitY(u), FarmLand.radius, null)
loop
set u = FirstOfGroup(search)
exitwhen u == null
call GroupRemoveUnit(search, u)
if IsUnitFarm(u) and UnitAlive(u) then
call RefreshAreaAroundUnit(GetUnitFarmLand(u), u, null)
endif
endloop
endfunction
function AddFarm takes FarmLand fl, unit u returns nothing
call GroupAddUnit(fl.g, u)
call RefreshAreaAroundUnit(fl, u, null)
endfunction
function RemoveFarm takes FarmLand fl, unit u returns nothing
call GroupRemoveUnit(fl.g, u)
call RefreshAreaAroundUnit(fl, u, null)
endfunction
function DisplayUnitYield takes FarmLand fl, unit u, real yield returns nothing
local texttag tt = CreateTextTag()
if GetLocalPlayer() == GetOwningPlayer(u) then
call SetTextTagPermanent(tt, false)
call SetTextTagPosUnit(tt, u, 50)
call SetTextTagVelocity(tt, 30*0.071/128*Cos(90*bj_DEGTORAD), 30*0.071/128*Sin(90*bj_DEGTORAD))
call SetTextTagFadepoint(tt, 3.5)
call SetTextTagLifespan(tt, 4)
call SetTextTagText(tt, fl.color.getColor(yield) + I2S(R2I(yield*100)) + "%|r", 12*0.023/10)
endif
endfunction
function CalcExpectedYieldXY takes FarmLand fl, real x0, real y0, integer unitTypeId returns real
local real x
local real y
local real tempSum = 0
set i = 0
loop
exitwhen i == fl.grid.n
set x = x0 + fl.grid.x.real[i]
set y = y0 + fl.grid.y.real[i]
if fl.isGridFarmable(x, y) then
set tempSum = tempSum + fl.getTerrainYield(x,y)
endif
set i = i + 1
endloop
return tempSum/i
endfunction
function GetFarmGroup takes FarmLand fl returns group
return fl.g
endfunction
function GetLastChangedFarm takes nothing returns unit
return fland_unit
endfunction
function GetUnitYield takes unit u returns real
return yield[GetUnitUserData(u)]
endfunction
private function AddPreplacedFarms takes nothing returns nothing
local timer t = GetExpiredTimer()
local unit u
local FarmLand fl
call GroupEnumUnitsInRect(search, WorldBounds.world, null)
loop
set u = FirstOfGroup(search)
exitwhen u == null
call GroupRemoveUnit(search, u)
if IsUnitFarm(u) and UnitAlive(u) then
set fl = GetUnitFarmLand(u)
call GroupAddUnit(fl.g, u)
call RefreshAreaAroundUnit(fl, u, null)
endif
endloop
call DestroyTimer(t)
set t = null
endfunction
private function Init takes nothing returns nothing
local timer t = CreateTimer()
call TimerStart(t, 0.1, false, function AddPreplacedFarms)
set t = null
endfunction
endlibrary
scope Test initializer Init
globals
FarmLand farmland_hu
FarmLand farmland_orc
FarmLand farmland_elf
endglobals
private function ConfigurationHuman takes nothing returns nothing
set farmland_hu = FarmLand.create()
call farmland_hu.addGrid(0,0)
call farmland_hu.addGrid(128,0)
call farmland_hu.addGrid(-128,0)
call farmland_hu.addGrid(0,-128)
call farmland_hu.addGrid(0,128)
call farmland_hu.addGrid(128,128)
call farmland_hu.addGrid(-128,128)
call farmland_hu.addGrid(128,-128)
call farmland_hu.addGrid(-128,-128)
call farmland_hu.addGrid(256,0)
call farmland_hu.addGrid(-256,0)
call farmland_hu.addGrid(0,256)
call farmland_hu.addGrid(0,-256)
call farmland_hu.setTerrainYield('Agrs', 1.0, 'Vcrp') // Base Tile, yield, Farm Tile Produced
call farmland_hu.setTerrainYield('Adrg', 0.75, 'Vcrp')
call farmland_hu.setTerrainYield('Adrd', 0.40, 'Vcrp')
call farmland_hu.addFarmType('hhou')
call farmland_hu.setupRadius()
call farmland_hu.addColor("|c00ff0000") // Lowest Yield Gradient
call farmland_hu.addColor("|c00bf3f00")
call farmland_hu.addColor("|c007f7f00")
call farmland_hu.addColor("|c003fbf00")
call farmland_hu.addColor("|c0000ff00") // Highest Yield Gradient
call farmland_hu.addColor("|c00540081") // This Color will display when above the maxium yield
endfunction
private function ConfigurationOrc takes nothing returns nothing
set farmland_orc = FarmLand.create()
call farmland_orc.addGrid(0,0)
call farmland_orc.addGrid(128,0)
call farmland_orc.addGrid(-128,0)
call farmland_orc.addGrid(0,-128)
call farmland_orc.addGrid(0,128)
call farmland_orc.addGrid(128,128)
call farmland_orc.addGrid(-128,128)
call farmland_orc.addGrid(128,-128)
call farmland_orc.addGrid(-128,-128)
call farmland_orc.setTerrainYield('Agrs', 1.00, 'Ofst')
call farmland_orc.setTerrainYield('Adrg', 0.75, 'Ofst')
call farmland_orc.setTerrainYield('Adrd', 0.40, 'Ofst')
call farmland_orc.addFarmType('otrb')
call farmland_orc.setupRadius()
call farmland_orc.addColor("|c00ff0000")
call farmland_orc.addColor("|c00bf3f00")
call farmland_orc.addColor("|c007f7f00")
call farmland_orc.addColor("|c003fbf00")
call farmland_orc.addColor("|c0000ff00")
call farmland_orc.addColor("|c00540081")
endfunction
private function ConfigurationElf takes nothing returns nothing
set farmland_elf = FarmLand.create()
call farmland_elf.addGrid(0,0)
call farmland_elf.addGrid(128,0)
call farmland_elf.addGrid(-128,0)
call farmland_elf.addGrid(0,-128)
call farmland_elf.addGrid(0,128)
call farmland_elf.addGrid(128,128)
call farmland_elf.addGrid(-128,128)
call farmland_elf.addGrid(128,-128)
call farmland_elf.addGrid(-128,-128)
call farmland_elf.setTerrainYield('Agrs', 1.20, 'Clvg')
call farmland_elf.setTerrainYield('Adrg', 1.00, 'Cgrs')
call farmland_elf.setTerrainYield('Adrd', 0.24, 'Cvin')
call farmland_elf.addFarmType('emow')
call farmland_elf.setupRadius()
endfunction
private function ConfigurationUndead takes nothing returns nothing
endfunction
// Note: If you only have one element of FarmLand, you can replace GetUnitFarmLand(u) with that variable.
private function OnFinishConstruction takes nothing returns boolean
local unit u = GetFilterUnit()
if IsUnitFarm(u) then
call AddFarm(GetUnitFarmLand(u), u)
endif
set u = null
return false
endfunction
private function OnStartConstruction takes nothing returns boolean
local unit u = GetFilterUnit()
if not IsUnitFarm(u) then
call UpdateRadiusAtUnitLoc(u)
endif
// Optional Example Usage - Detecting when a farm "should" be canceled.
if IsUnitFarm(u) then
if CalcExpectedYieldXY(GetUnitFarmLand(u), GetUnitX(u), GetUnitY(u), GetUnitTypeId(u)) <= 0.25 then
call BJDebugMsg("Yield is below 25%! - CANCEL IT!")
endif
endif
set u = null
return false
endfunction
private function OnBuildingDeath takes nothing returns boolean
local unit u = GetTriggerUnit()
if IsUnitType(u, UNIT_TYPE_STRUCTURE) then
if IsUnitFarm(u) then
call RemoveFarm(GetUnitFarmLand(u), u)
endif
call UpdateRadiusAtUnitLoc(u)
endif
set u = null
return false
endfunction
private function OnYieldChange takes nothing returns boolean
local integer pid = GetPlayerId(GetOwningPlayer(fland_unit))
local integer uid = GetUnitUserData(fland_unit)
// Example of how to change income variable
set incPlayerTotIncome[pid] = incPlayerTotIncome[pid] - R2I(fland_yield_old*MAX_FARM_INCOME) + R2I(fland_yield_new*MAX_FARM_INCOME)
set incFarmIncome[uid] = R2I(MAX_FARM_INCOME*fland_yield_new)
if UnitAlive(fland_unit) and fland_show then
call DisplayUnitYield(GetUnitFarmLand(fland_unit), fland_unit, GetUnitYield(fland_unit))
endif
return false
endfunction
private function Welcome takes nothing returns nothing
call BJDebugMsg("|cffffcc00Farmland Generator 1.5.3 - By Pinzu|r")
call BJDebugMsg("Build Farms to gain more income.You can destroy your farms by pressing 'esc'. \nHuman Farm Commands: 'display'")
endfunction
private function Init takes nothing returns nothing
local trigger t0 = CreateTrigger()
local trigger t1 = CreateTrigger()
local trigger t2 = CreateTrigger()
local integer i = 0
local player p
loop
set p = Player(i)
call TriggerRegisterPlayerUnitEvent(t1, p, EVENT_PLAYER_UNIT_CONSTRUCT_FINISH, function OnFinishConstruction)
call TriggerRegisterPlayerUnitEvent(t1, p, EVENT_PLAYER_UNIT_DEATH, null)
call TriggerRegisterPlayerUnitEvent(t1, p, EVENT_PLAYER_UNIT_CONSTRUCT_START, function OnStartConstruction)
set i = i + 1
exitwhen i == 12
endloop
call TriggerRegisterTimerEvent(t0, 0.1, false)
call TriggerRegisterVariableEvent(t2, "fland_event", EQUAL, 1)
call TriggerAddAction(t0, function Welcome)
call TriggerAddCondition(t1, Condition(function OnBuildingDeath))
call TriggerAddCondition(t2, Condition(function OnYieldChange))
call ConfigurationHuman()
call ConfigurationOrc()
call ConfigurationElf()
endfunction
endscope