1-- RST 2-- infrastructure.lua 3-- ------------------ 4-- 5-- This script contains function that ease setting up an initial infrastructure 6-- for maps. This includes placing buildings and roads. 7 8-- RST 9-- .. function:: connected_road(roadtype, plr, sflag, descr[, create_carriers = true]) 10-- 11-- This is a convenience function to create roads and waterways. It is basically a frontend 12-- to :meth:`wl.game.Player.place_road`. The road is forced, that is other 13-- stuff in the way is removed. A sample usage: 14-- 15-- .. code-block:: lua 16-- 17-- local game = wl.Game() 18-- connected_road("normal", game.players[1], game.map:get_field(20,20).immovable, "r,r|br,r|r,r") 19-- 20-- This would create a road starting from the Flag standing at field(20,20) 21-- which must exist and goes from there 2 steps right (east), places a new 22-- flag. Then it goes one step bottom-right and one step right, places the 23-- next flag and then goes two steps right again and places the last flag. 24-- 25-- :arg roadtype: 'normal', 'busy', or 'waterway' 26-- :type roadtype: :class:`string` 27-- :arg plr: The player for which the road is created 28-- :type plr: :class:`wl.game.Player` 29-- :arg sflag: The starting flag for this road. 30-- :type sflag: :class:`wl.map.Flag` 31-- :arg descr: The description of this road. This are comma separated 32-- directions as those accepted by :meth:`wl.game.Player.place_road`. A 33-- :const:`|` defines a flag. 34-- :arg create_carriers: If this is :const:`true` carriers are created for 35-- the roads. Otherwise no carriers will be created. 36-- :type create_carriers: :class:`boolean` 37 38function connected_road(roadtype, p, start, cmd, g_create_carriers) 39 create_carriers = true 40 if g_create_carriers ~= nil then 41 create_carriers = g_create_carriers 42 end 43 44 if cmd:sub(-1) ~= "|" then 45 cmd = cmd .. "|" 46 end 47 48 moves = {} 49 for m in cmd:gmatch("%a+[,|]") do 50 moves[#moves+1] = m:sub(1,-2) 51 if(m:sub(-1) == '|') then 52 moves[#moves+1] = true -- Force the road 53 r = p:place_road(roadtype, start, table.unpack(moves)) 54 start = r.end_flag 55 if create_carriers then 56 if roadtype == "normal" then r:set_workers(p.tribe.carrier, 1) 57 elseif roadtype == "busy" then r:set_workers({[p.tribe.carrier] = 1, [p.tribe.carrier2] = 1}) 58 else r:set_workers(p.tribe.ferry, 1) end 59 end 60 moves = {} 61 end 62 end 63end 64 65-- RST 66-- .. function:: prefilled_buildings(plr, b1_descr[, b2_descr, ...]) 67-- 68-- Create pre-filled buildings. Each description is a arrays which contain 69-- building type, build field and pre-fill information. A sample usage: 70-- 71-- .. code-block:: lua 72-- 73-- prefilled_buildings(wl.Game().players[1], 74-- {"sentry", 57, 9}, -- Sentry completely full with soldiers 75-- {"sentry", 57, 9, soldier={[{0,0,0,0}]=1}}, -- Sentry with one soldier 76-- {"bakery", 55, 20, inputs = {wheat=6, water=6}}, -- bakery with wares and workers 77-- {"well", 52, 30}, -- a well with workers 78-- ) 79-- 80-- :arg plr: The player for which the building is created 81-- :type plr: :class:`wl.game.Player` 82-- :arg b1_descr: An array of tables. Each table must contain at 83-- least the name of the building, and the x and y positions of the field 84-- where the building should be created. Optional entries are: 85-- 86-- wares 87-- A table of (name,count) as expected by 88-- :meth:`wl.map.Warehouse.set_wares`. This is valid for 89-- :class:`wl.map.Warehouse` and must not be used otherwise. 90-- inputs 91-- A table of (name,count) as expected by 92-- :meth:`wl.map.ProductionSite.set_inputs`. Inputs are wares or workers 93-- which are consumed by the building. This is valid for 94-- :class:`wl.map.ProductionSite` and must not be used otherwise. 95-- soldiers 96-- A table of (soldier_descr,count) as expected by 97-- :meth:`wl.map.HasSoldiers.set_soldiers`. If this is nil, the site 98-- will be filled with {0,0,0,0} soldiers. 99-- workers 100-- A table of (workers_name,count) as expected by 101-- :meth:`wl.map.Warehouse.set_workers`. Note that ProductionSites 102-- are filled with workers by default. 103-- :type b1_descr: :class:`array` 104-- 105-- :returns: A table of created buildings 106 107function prefilled_buildings(p, ...) 108 local b_table = {} 109 for idx,bdescr in ipairs({...}) do 110 local b = p:place_building(bdescr[1], wl.Game().map:get_field(bdescr[2],bdescr[3]), false, true) 111 -- Fill with workers 112 if b.valid_workers then b:set_workers(b.valid_workers) end 113 if bdescr.workers then b:set_workers(bdescr.workers) end 114 -- Fill with soldiers 115 if b.max_soldiers and b.set_soldiers then 116 if bdescr.soldiers then 117 b:set_soldiers(bdescr.soldiers) 118 else 119 b:set_soldiers({0,0,0,0}, b.max_soldiers) 120 end 121 elseif bdescr.soldiers then -- Must be a warehouse 122 b:set_soldiers(bdescr.soldiers) 123 end 124 -- Fill with wares if this is requested 125 if bdescr.wares then b:set_wares(bdescr.wares) end 126 if bdescr.inputs then b:set_inputs(bdescr.inputs) end 127 128 table.insert(b_table, b) 129 end 130 return b_table 131end 132 133-- RST 134-- .. function:: place_building_in_region(plr, building, region[, opts]) 135-- 136-- Tries to place a building randomly in the given region. It places houses 137-- using :func:`prefilled_buildings`, therefore uses the same options. 138-- If it fails, an error is thrown. This is a most useful function when 139-- defining starting conditions (initializations) in normal games. 140-- 141-- :arg plr: The player for which the building is created 142-- :type plr: :class:`wl.game.Player` 143-- :arg building: The name of the building to create. 144-- :type building: :class:`string` 145-- :arg region: The fields which are tested for suitability. 146-- :type region: :class:`array` 147-- :arg opts: A table with prefill information (wares, soldiers, workers, 148-- see :func:`prefilled_buildings`) 149-- :type opts: :class:`table` 150-- 151-- :returns: The building created 152 153function place_building_in_region(plr, building, fields, gargs) 154 set_textdomain("widelands") 155 local idx 156 local f 157 local args = gargs or {} 158 159 while #fields > 0 do 160 local idx = math.random(#fields) 161 local f = fields[idx] 162 163 if plr:get_suitability(building, f) then 164 args[1] = building 165 args[2] = f.x 166 args[3] = f.y 167 return prefilled_buildings(plr, args)[1] 168 end 169 table.remove(fields, idx) 170 end 171 plr:send_message( 172 -- TRANSLATORS: Short for "Not enough space" 173 _"No Space", 174 p(_([[Some of your starting buildings didn’t have enough room and weren’t built. You are at a disadvantage with this; consider restarting this map with a fair starting condition.]])), 175 {popup=true, heading=_"Not enough space"} 176 ) 177end 178 179 180-- RST 181-- .. function:: is_building(immovable) 182-- 183-- Checks whether an immovable is a finished building, i.e. not 184-- a construction site. 185-- 186-- :arg immovable: The immovable to test 187-- 188-- :returns: true if the immovable is a building 189 190function is_building(immovable) 191 return immovable.descr.type_name == "productionsite" or 192 immovable.descr.type_name == "warehouse" or 193 immovable.descr.type_name == "militarysite" or 194 immovable.descr.type_name == "trainingsite" 195end 196 197-- RST 198-- .. function:: add_wares_to_warehouse(player, warehouse, waretable) 199-- 200-- Adds(subtracts) wares to the first warehouse of specified type 201-- 202-- :arg player: the player to add wares to 203-- :arg warehouse: The type of warehouse (string) to add wares to 204-- :arg waretable: a table of pairs {ware = value} 205 206function add_wares_to_warehouse(player, warehouse, waretable) 207 local hq = player:get_buildings(warehouse)[1] 208 for ware,warecount in pairs(waretable) do 209 local oldwarecount = hq:get_wares(ware) or 0 210 if warecount < 0 and -warecount > oldwarecount then 211 warecount = -oldwarecount 212 end 213 hq:set_wares(ware, oldwarecount+warecount) 214 end 215end 216 217-- RST 218-- .. function:: check_trees_rocks_poor_hamlet(player, sf, warehouse, waretable_rocks, waretable_trees) 219-- 220-- Checks for rocks or trees in the region of the player starting field and adds wares if no immovables found 221-- 222-- :arg player: the player to check 223-- :arg sf: starting field of the player 224-- :arg warehouse: The type of warehouse (string) to add wares to 225-- :arg waretable_rocks: a table of pairs {ware = value} to add if no rocks 226-- :arg waretable_trees: a table of pairs {ware = value} to add if no trees 227 228function check_trees_rocks_poor_hamlet(player, sf, warehouse, waretable_rocks, waretable_trees) 229 -- NOTE: pessimistically, this could be a single rock and a single tree 230 local has_rocks = false 231 local has_trees = false 232 for k,f in pairs(sf:region(10)) do 233 if f.immovable then 234 if not has_rocks and f.immovable:has_attribute('rocks') then 235 has_rocks = true 236 elseif not has_trees and f.immovable:has_attribute('tree') then 237 has_trees = true 238 end 239 if has_trees and has_rocks then 240 break 241 end 242 end 243 end 244 if not has_rocks then 245 add_wares_to_warehouse(player, warehouse, waretable_rocks) 246 player:send_message(_"No rocks nearby", _"There are no rocks near to your starting position. Therefore, you receive extra resources for bootstrapping your economy.") 247 end 248 -- adding exactly one forester 249 if not has_trees then 250 add_wares_to_warehouse(player, warehouse, waretable_trees) 251 player:send_message(_"No trees nearby", _"There are no trees near to your starting position. Therefore, you receive extra resources for bootstrapping your economy.") 252 end 253end 254