Farm Land Generator 1.5.3

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
JASS:
________________________________________________________________________________________

                            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
    
________________________________________________________________________________________
JASS:
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
JASS:
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

Keywords:
Farm Land Crops Pinzu Yield Resource Income
Contents

Farm Land System 1.5.2 (Map)

Reviews
21:04, 16th Jun 2015 BPower: Lot of things to discuss
Level 15
Joined
Nov 30, 2007
Messages
1,202
Lacks documentation/comments

Added documentation, didn't comment much but instead i cleaned the layout of the code, it should be a lot more clear for you, the moderator to read through it, as well as for the user to change it to suits his needs.

Coded it from scratch again. So I changed name and re-released it as "Farm Land Generator 1.0."

*edit
Update: Version 1.1

Change-log:
Added a flexible configuration for colors
Updated to the latest version of Pathing Library
Removed most local variables from Main
Changed Parameters in UpdateArea from real x, y to unit u
Fixed a bug where yield wouldn't display at 0%
Replaed SEARCH_RADIUS (constant) with a internal variable not to be touched by the user.
Added Setup Functions for TerrainYield, FarmGrid and FarmType. Not sure if they count as "leaks".

It's finished from my end. Feedback would be greatly appreciated.

Could add on Init to pick all preplaced farms and spawn farm land around it. But can't be arsed right now...

---

Is this still on somebodies (mod) to-do list? ^^
 
Last edited:
Level 15
Joined
Nov 30, 2007
Messages
1,202
It was like the farms in Age of Empires.

Ah there you could build farm land without farm buildings on them. This is more like BFME series. ;P

Anyway, having a worker work the land or the farms grow over time could be a interesting feature but I'm not sure if if its such a good idea given I want to keep it light for my map. However, removing the farm entirely and replacing it with "ground" only could be useful, but how would you kill it?

Anyway I have noticed a two issues in the current version:
1) Units can block farm land.
2) a lot of imported models are in that map for no good reason!
 
Hm, sounds interesting.
But isn't this more a system than a spell?

This used config code should not be in main code, as it's not relevant for system itself?
It's probably for demo?

Texttags don't need to be nulled. You can here read more about reference leaks: http://www.hiveworkshop.com/forums/trigger-gui-editor-tutorials-279/memory-leaks-263410/#post2661133

if (Expression == true)
->
if (Expression)

if (Expression == false)
->
if not(Expression)

When a farm gets destroyed, shouldn't be nearby farms potentialy be influenced in terms of getting new land for example?
Haven't tested the submissin yet but that comes in mind.

Haven't analazyed it yet, but idk if you cover all check cases correctly since a tile has multiple x/y values.
 
Level 15
Joined
Nov 30, 2007
Messages
1,202
- Gonna look into the that. Does GetFilterUnit() cause leak?
- I also just realized updateArea could actually take in the radius, for example the GetWorldBounds() could be merged with it.
- Why should the configuration be outside if you want different types of farm areas or what? But you are right, it can be moved but then almost everything should be inside the struct (gx, gy and color), because the whole point of moving it would be to have more farm grid options and as its currently intended I don't see any big value in that.

When a farm gets destroyed, shouldn't be nearby farms potentialy be influenced in terms of getting new land for example?
Haven't tested the submissin yet but that comes in mind.

Haven't analazyed it yet, but idk if you cover all check cases correctly since a tile has multiple x/y values.

When a farm dies the farm tiles produced are first removed then this: call farm.updateArea(u) which updates the farmland of nearby farms. If a nearby farm can grow into that area it will.

Don't know exactly what you mean with the last part. But I've gotten it as correctly as I can, don't know what to do about the tile displacement effecting when a building is supposed to destroy farm land and not. You will see what I mean when you test it.

I want to point out that there is some redundancy in the algorithm as it checks all possible grids all the time, instead of those who are not already taken as well as not only checking the tiles which a building was placed on instead of the whole area. But i figured the effect is negligible.

Version 1.3.5
- shortend the function AddPreplacedFarms
- Changed .updateArea to include two options either search a radius or a rect.
- Changed some true false expressions.
- texttag no longer nulled. ;p

I think I want to add the following but I'm not sure
- Display estimated yield on construction (optional)
- Option to change a specifc farms yield to any percentage no matter the tiles its placed on.
- Cancel farms built on less then X yield automatically. (optional)
- Allow yields above 1.0 and below 0.0 and still keep the color-gradient.
 
Last edited:
Level 15
Joined
Nov 30, 2007
Messages
1,202
I can't even start this thing. It gives me errors and dies.

Thats strange. Gonna take a look, downloading. Might be some configuration of jassnewgen pack? What happens when I open the map is that I have to save it again just before running it. Don't know if that is normal JNGP behavior or not, but I don't think its a problem. It's something I have to do with all my maps in the editor.
 
Level 15
Joined
Nov 30, 2007
Messages
1,202
I tried just throwing it in the scenario folder and playing it through wc3, the fact that it doesn't run bothers me, it says no memory was found: the same thing happens with my map project as well!? :/

However I can get it to run through the editor if I save it just before. Oh well, I'll begin debugging the trigger I suppose and then my map ;(, if anyone see any obvious flaws in the setup please tell me.

The memory error is so random, sometimes it happens but when I disable triggers under example usage it works, or that was just fluke luck, idk. Maybe it's because I didn't write those inside a scope.

Updated 1.3.5.c
- Changed the example usage triggers a bit, which I hope was causing it. I've managed to run it through wc3 a couple of times.
- Fixed a bug where dying farms would give land to farms under construction.
 
Last edited:
Level 19
Joined
Mar 18, 2012
Messages
1,716
I think configuration should be done from a seperated scope/library ( piece of code ).
Just write the full API in the documentation, so users can look it up.
Add one line, explaining the purpose of the function/method.
Nice demo code in the attached map will help to quickly understand correct usage.

For AddPreplacedFarms I would run a single shot timer, which you then can destroy.
This way you don't have that unused trigger remaining for the whole game time ( cleaner approach )
Also I suggest you pass GetWorldBounds() directly as argument or use library WorldBounds as requirement.

farm_tile --> farmTile

Restructure your code. Methods beeing called should be placed above. Of course it compiles either way, but you would be suprised what the JassHelper does with your code.

if onFinish == true then --> if onFinish then
Why: if true == true vs true

I have more ideas, but at first I want to try out the demo map. ( tomorrow ) :)

Just judging from the idea, it is a very cool system.
 
Level 15
Joined
Nov 30, 2007
Messages
1,202
Updated to version 1.4, although I remade it from scratch again.

New features are:
- Calculating the expected yield of a unit before its completed.
- Moved the configuration and all triggers outside of the library, so the user can more easily add them where ever he wants instead of having duplicated triggers.
- There is now an event-variable to detect when a change happened as well as deltaYield variable to get how much was changed. Use it with any income variable you might have to change the income.
- Display was moved to the change-event trigger.

Also added most changes you suggested.

Will work some more on the documentation but it stands completed. If anyone can fix the mess I did with the hashtable it would be great, cus I "designed" it to work with multiple instances. But when I tested it, they appear to load the first configuration, couldn't be bothered fixing it as there is little use in it.
 
Last edited:
Level 19
Joined
Mar 18, 2012
Messages
1,716
The demo map is not so self-explaining to me.

1. function UnitAlive takes unit u returns boolean --> If you declare a function as UnitAlive, please make it private. I say this because there is a
native with the excact same name. native UnitAlive takes unit id returns boolean.
User may run into code conflict. The native is quite powerful, as it is very fast.
If you want to use it, you have to write it somewhere in your map.

2. call TimerStart(CreateTimer(), 0.1, false, function Farm.addPreplacedFarms) !! However is the timer is not required at all as units are preplaced before onInit is called.

3. library WorldBounds would be useful as requirement.

4. call DestroyTimer(GetExpiredTimer())

I'm writing from the phone, but there is more to do.
I continue next time.
 
Level 15
Joined
Nov 30, 2007
Messages
1,202
The demo map is not so self-explaining to me.
Suppose I could re-add the income trigger as a example usage or is there something else you are referring to perhaps? The problem is that I am not sure what someone else might need it to do, I only know what I need it to do. And I added more then that.

1. function UnitAlive takes unit u returns boolean --> If you declare a function as UnitAlive, please make it private. I say this because there is a
native with the excact same name. native UnitAlive takes unit id returns boolean.
User may run into code conflict. The native is quite powerful, as it is very fast.
If you want to use it, you have to write it somewhere in your map.
Okey, didn't know you could declare natives literally anywhere. In that case the user would see that it does exactly that and remove it? Anyway, changed it.

2. call TimerStart(CreateTimer(), 0.1, false, function Farm.addPreplacedFarms) !! However is the timer is not required at all as units are preplaced before onInit is called.
You are mistaken, i can't simply call the function addPreplacedFarms, wont work.

3. library WorldBounds would be useful as requirement.
Don't really get that library, but I'll look into it.

4. call DestroyTimer(GetExpiredTimer())
I don't have to null that timer?

move the functions below the Farms struct, you are generating countless unnecessary trigger evaluations right now
Okey, didn't know it worked like that.
___________________

Updated to version 1.4.2 Added all your suggestions except WorldBounds as I didn't find a good link/explanation and the timer changes, as I'm not sure if it will leak or not.

Other changes:
- New API:s, some BJ:s, for fun, but also just for the user to remove them when he is ready. :p
- Added a income trigger and a example of how to update the total income on eventYieldChange.
- Changed the calculateExpectedYield to take in x, y, and unitType instead of unit so that it will work for units before they are placed, for example on issued building orders or what have you.
- Some other things as well that I can't quite recall.

Noticed a few things I forgot to do: (Documentation error, wood income for test map, quite annoying without it, better in-game description on start) Will do that tomorrow most likely.
 
Last edited:
Level 23
Joined
Apr 16, 2012
Messages
4,041
just because you call your functions BJ doesnt mean they are BJ, and the extensions to the names are purely useless and confusing, because now some users will think that these function exist even without your script since they are BJ(all BJ functions are defined inside blizzard.j, hence bj)
 
Level 15
Joined
Nov 30, 2007
Messages
1,202
just because you call your functions BJ doesnt mean they are BJ, and the extensions to the names are purely useless and confusing, because now some users will think that these function exist even without your script since they are BJ(all BJ functions are defined inside blizzard.j, hence bj)

Yes, yes. Should I remove the functions entirely as they serve no real purpose or keep them?
 
Level 15
Joined
Nov 30, 2007
Messages
1,202
Updated! (WIP)
v. 1.5
- Rewrote it.
- Uses WorldBounds
- Now supprots 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
- 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 in function OnYieldChange
- README

Known Issues
a) Not sure exactly how to use TableArray, so left a udg_hash inside the library. (minor issue)
b) When 100% yield is created you only get 97-99 income out of 100. (major issue)
c) A big enough group of units can actually block farm land creation. (minor issue)
d) Text-tag display is not working for more then 1 FarmLand instance. (major issue)

Any help on these issues would be greatly appreciated!
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
you are missing links for Native and WorldBounds(I dont even know what native means :D), and for Table you linked to your user profile instead of the resource.

In description, you have refreshAreaAroundUnit, in code you have RefreshAreaAroundUnit(capital R at the beginning).

You mentioned native twice, but there is no native in your map script(I recommend adding the native UnitAlive takes unit u returns boolean at the beginning of the script.

FLAND_COLOR_MAX and FLAND_GRID_MAX should be included in the script itself, not in example script, because if I dont copy paste the example, then it wont compile again.

Again, you try to use FarmLand API before declaring the FarmLand struct, resulting in potential trigger evaulations(RefreshAreaAroundUnit).

I suggest adding the members of FarmLand as either readonly or private, I dont think users need to have access to the t1, t2, color etc naked(also possibly change it).

You should use table for both Color and Grid, because right now you can only have 819 colors and 273 Grid instances, so effectivelly 273 normally functioning FarmLand instances. With table, you are not limited on number of Grids nor Colors.

Your loop ends prematurely in CalcExpectedYieldXY, because you do exitwhen i == fl.grid.n before the body, but inside setupRadius you do exitwhen i == .grid.n after the body.

To be honest, I dont see why texttags shouldnt work(the function looks ok to me), and also I dont see the need for TableArray at all(well hashtable is hashtable, but ok)
 
Level 15
Joined
Nov 30, 2007
Messages
1,202
- Added link to world bound, as for Native, it is already declared in the trigger "Native"? Didn't find any official link to native declarations either, think I picked it up from some help-question. I clearly felt that the users better visit my user profile instead. :D
- ok.
- ok.
- ok.
- How do you mean exactly?
- How many instances do people need? But i'll look into changing it.
- The issue was that the loop in radius was incorrect.
- Wanted to use table, because I never used it before. So I figured instead of messing with my map I'll do it here. :D

I found what was wrong with the text-tag, they have no color setup. fixed that. :D Maybe the colorizatin should be shared across all FarmLand instances?
 
Last edited:
Level 23
Joined
Apr 16, 2012
Messages
4,041
basically, your function tries to access FarmLand API, but that needs to call methods inside FarmLand, and they are clearly declared below your function, so it will generate copy of the function, and potentially even trigger evaluation
 
Level 15
Joined
Nov 30, 2007
Messages
1,202
basically, your function tries to access FarmLand API, but that needs to call methods inside FarmLand, and they are clearly declared below your function, so it will generate copy of the function, and potentially even trigger evaluation

Not that part, changed that. This: "I suggest adding the members of FarmLand as either readonly or private, I dont think users need to have access to the t1, t2, color etc naked(also possibly change it)."

Updated.

v. 1.5.1. and 1.5.2f
- fixed texttag bug
- fixed income bug
- fixed issue with the order of the function calls
- fixed documentation error
- removed customization of farm yield outside of event
- Table and Color struct now uses Table instead of variable array
- Units no longer block added farmland.
- Two new functions: AddFarm and RemoveFarm
- Made RefreshAreaAroundUnit privet.
- Bug fix in the test trigger.

Issues:
a) Not sure exactly how to use TableArray, so left a udg_hash inside the library
b) A big enough group of units can actually block farm land creation. (can be problematic)
c) Strangely enough version 1.5.1. has managed to break the update function around dead farms, no idea why as of yet.
d) Selection of units inside FarmLand area are deselected upon farm creations.

The function RefreshAreaAroundUnit uses the unit index of nearby potential farm blockers to store their temporary xy location. However, it might be a smarter move to utilize a table for this: what do you think?
________________________________________

I think it is finished now, sure i could add some more features but I finally think its at a good-enough quality. Thank you for your help and feedback. Hope you find this useful, albeit its a bit nished...
 
Last edited:
Level 19
Joined
Mar 18, 2012
Messages
1,716
JASS:
    private function Init takes nothing returns nothing 
        local timer t = CreateTimer()
        call TimerStart(t, 0.1, false, function AddPreplacedFarms)
        set t = null
    endfunction
-->
JASS:
    private function Init takes nothing returns nothing 
        call TimerStart(CreateTimer(), 0.1, false, function AddPreplacedFarms)
    endfunction
Also call DestroyTimer(t) --> call DestroyTimer(GetExpiredTimer())

Btw this can run onInit.
JASS:
function main takes nothing returns nothing
    call SetCameraBounds( -1536.0 + GetCameraMargin(CAMERA_MARGIN_LEFT), -2688.0 + GetCameraMargin(CAMERA_MARGIN_BOTTOM), 2432.0 - GetCameraMargin(CAMERA_MARGIN_RIGHT), 1536.0 - GetCameraMargin(CAMERA_MARGIN_TOP), -1536.0 + GetCameraMargin(CAMERA_MARGIN_LEFT), 1536.0 - GetCameraMargin(CAMERA_MARGIN_TOP), 2432.0 - GetCameraMargin(CAMERA_MARGIN_RIGHT), -2688.0 + GetCameraMargin(CAMERA_MARGIN_BOTTOM) )
    call SetDayNightModels( "Environment\\DNC\\DNCAshenvale\\DNCAshenvaleTerrain\\DNCAshenvaleTerrain.mdl", "Environment\\DNC\\DNCAshenvale\\DNCAshenvaleUnit\\DNCAshenvaleUnit.mdl" )
    call NewSoundEnvironment( "Default" )
    call SetAmbientDaySound( "AshenvaleDay" )
    call SetAmbientNightSound( "AshenvaleNight" )
    call SetMapMusic( "Music", true, 0 )
    call InitSounds(  )
    call CreateRegions(  )
    call CreateAllUnits(  )// Here all units are created
    call InitBlizzard(  )

//! initstructs
call ExecuteFunc("PathingLib___onInit")// Here is your library executed
call ExecuteFunc("FarmLand___Init")
call Test___Init()
call Display___Init()
call Income___Init()

//! initdatastructs
    call InitGlobals(  )
    call InitCustomTriggers(  )
    call RunInitializationTriggers(  )

endfunction

I would like to see that you variable naming follows the JPAG established by Bribe.
It's designed to stays a close as possible to the normal wc3 variable/function declaration.
---> fland_unit --> flandUnit

Why fland and not farmLand?

static group g in struct FarmLand should be privte. The two Table instances should
be at least readonly, if not also private.
Encapsulation is very important, so users do not accidently break your code.

unit enum ... yay ... no! My JassHelper highlights "enum",
if it does for you also then please change the variable name.

private integer i [icode=jass] should local integer in every respective function. [icode=jass]local texttag tt should be nulled at the end of function DisplayUnitYield
Texttag velocity, fadepoint, lifespan and size should be part of the global configuarion block and not hardcoded.

You always pass in null for enum in private function RefreshAreaAroundUnit takes FarmLand fl, unit u, unit enum returns nothing.
So basically this argument is useless ( private function ) and should not exist in the first place.

call DisplayUnitYield(farmland_hu, u, GetUnitYield(u)) always displays the same number for me. Is that correct?

Farm and Grid instances are never recycled, but farms can be destroyed!

Set to Need Fix
 
Last edited:
Level 23
Joined
Apr 16, 2012
Messages
4,041
enum is not Jass keyword.

The complete list of Jass keywords:
JASS:
type
extends
function
takes
returns
return
loop
local
if
else
elseif
endif
endloop
exitwhen
endfunction
globals
endglobals
debug
native
constant

Enum is not reserved(yet)
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Thanks for enlightening me edo. My JassHelper highlights "enum" in gold like it does
for half of the JASS keywords (if, then, else, loop, ... ).
I don't excacly know who made the Syntax Highlighter for moyack's. I think it's from Van Damm.
Still it would be nice, if you change the variable name, because I think
this JNPG is used from many people.
 
Level 15
Joined
Nov 30, 2007
Messages
1,202
- Improved the encapsulation, most stuff is put inside the struct. and all the struct variables are privet.
- Changed fland_someVariable into farmLandSomeVariable
- Changed the paremeters and enum variable in a couple of functions, mostly all functions were moved inside the struct again so farmLand fl is no longer required in the parameter.
- constant variables created for texttag setting.
- texttag now nulled
- timer t is no longer a local variabe.
- Added onDestroy methods to Grid, FarmLand and Color

There is not really a problem with running the thing on initialization, only problem is that the configuration must run before the function AddPreplacedFarms, so either I remove the timer trigger and let it actually be a callable function or leave it as it is. Another option is to remove that from the library as it is easy to recreate with the already available API, perhaps just move it to "FarmLand Main"

Have discovered one bug, once that is fixed I'll update it.

call DisplayUnitYield(farmland_hu, u, GetUnitYield(u)) Not sure how you tested that, but it should display the same number if nothing has changed and you entered "display".
 
Top