GunChleoc has proposed merging lp:~widelands-dev/widelands/dynamic_tribe_loading into lp:widelands.
Commit message: Dynamic loading of the tribes' map objects and of custom scenario tribe objects: - Simplified tribes/init.lua: The tribe directory is now walked automatically, without the need of manually getting the load order right. Missing prerequisite map objects are parsed during postload. - If scripting/tribes/init.lua exists in a map, run it to load custom tribe objects when starting a new scenario. So far, only buildings are supported via a new function Tribes::add_custom_building{table} in lua_root. Requested reviews: Widelands Developers (widelands-dev) Related bugs: Bug #1705950 in widelands: "empire mission 4" https://bugs.launchpad.net/widelands/+bug/1705950 For more details, see: https://code.launchpad.net/~widelands-dev/widelands/dynamic_tribe_loading/+merge/329198 Reworked the tribe loading to make it more moddable. I'd like this branch to have priority, because it affects hessenfarmer's work on the Empire 4 scenario. An adjusted version of the new scenario for testing is available in https://code.launchpad.net/~widelands-dev/widelands/dynamic_tribe_loading_datadir -- Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/dynamic_tribe_loading into lp:widelands.
=== modified file 'data/tribes/init.lua' --- data/tribes/init.lua 2017-02-12 09:10:57 +0000 +++ data/tribes/init.lua 2017-08-17 11:49:32 +0000 @@ -11,12 +11,33 @@ -- -- Basic load order (first wares, then immovables etc.) is important, -- because checks will be made in C++. --- Also, enhanced/upgraded units need to come before their basic units. -- tribes = wl.Tribes() include "scripting/mapobjects.lua" +-- Load all init.lua files in the given table of directory names +function load_directories(directories) + -- Helper function to check for file name endings + function string.ends(haystack, needle) + return needle == '' or string.sub(haystack, -string.len(needle)) == needle + end + + while #directories > 0 do + local filepath = directories[1] + table.remove(directories, 1) + if path.is_directory(filepath) then + for idx, listed_path in ipairs(path.list_directory(filepath)) do + if path.is_directory(listed_path) then + table.insert(directories, listed_path) + elseif string.ends(listed_path , "init.lua") then + include(listed_path) + end + end + end + end +end + print("┏━ Running Lua for tribes:") print_loading_message("┗━ took", function() @@ -25,9 +46,7 @@ -- =================================== print_loading_message("┃ Ships", function() - include "tribes/ships/atlanteans/init.lua" - include "tribes/ships/barbarians/init.lua" - include "tribes/ships/empire/init.lua" + load_directories({"tribes/ships"}) end) -- =================================== @@ -35,91 +54,7 @@ -- =================================== print_loading_message("┃ Wares", function() - include "tribes/wares/armor/init.lua" - include "tribes/wares/armor_chain/init.lua" - include "tribes/wares/armor_gilded/init.lua" - include "tribes/wares/armor_helmet/init.lua" - include "tribes/wares/ax/init.lua" - include "tribes/wares/ax_battle/init.lua" - include "tribes/wares/ax_broad/init.lua" - include "tribes/wares/ax_bronze/init.lua" - include "tribes/wares/ax_sharp/init.lua" - include "tribes/wares/ax_warriors/init.lua" - include "tribes/wares/basket/init.lua" - include "tribes/wares/beer/init.lua" - include "tribes/wares/beer_strong/init.lua" - include "tribes/wares/blackroot/init.lua" - include "tribes/wares/blackroot_flour/init.lua" - include "tribes/wares/blackwood/init.lua" - include "tribes/wares/bread_atlanteans/init.lua" - include "tribes/wares/bread_barbarians/init.lua" - include "tribes/wares/bread_empire/init.lua" - include "tribes/wares/bread_paddle/init.lua" - include "tribes/wares/buckets/init.lua" - include "tribes/wares/cloth/init.lua" - include "tribes/wares/coal/init.lua" - include "tribes/wares/corn/init.lua" - include "tribes/wares/cornmeal/init.lua" - include "tribes/wares/diamond/init.lua" - include "tribes/wares/felling_ax/init.lua" - include "tribes/wares/fire_tongs/init.lua" - include "tribes/wares/fish/init.lua" - include "tribes/wares/fishing_net/init.lua" - include "tribes/wares/fishing_rod/init.lua" - include "tribes/wares/flour/init.lua" - include "tribes/wares/gold/init.lua" - include "tribes/wares/gold_ore/init.lua" - include "tribes/wares/gold_thread/init.lua" - include "tribes/wares/granite/init.lua" - include "tribes/wares/grape/init.lua" - include "tribes/wares/grout/init.lua" - include "tribes/wares/hammer/init.lua" - include "tribes/wares/helmet/init.lua" - include "tribes/wares/helmet_mask/init.lua" - include "tribes/wares/helmet_warhelm/init.lua" - include "tribes/wares/hook_pole/init.lua" - include "tribes/wares/hunting_bow/init.lua" - include "tribes/wares/hunting_spear/init.lua" - include "tribes/wares/iron/init.lua" - include "tribes/wares/iron_ore/init.lua" - include "tribes/wares/kitchen_tools/init.lua" - include "tribes/wares/log/init.lua" - include "tribes/wares/marble/init.lua" - include "tribes/wares/marble_column/init.lua" - include "tribes/wares/meal/init.lua" - include "tribes/wares/meat/init.lua" - include "tribes/wares/milking_tongs/init.lua" - include "tribes/wares/pick/init.lua" - include "tribes/wares/planks/init.lua" - include "tribes/wares/quartz/init.lua" - include "tribes/wares/ration/init.lua" - include "tribes/wares/saw/init.lua" - include "tribes/wares/scythe/init.lua" - include "tribes/wares/shield_advanced/init.lua" - include "tribes/wares/shield_steel/init.lua" - include "tribes/wares/shovel/init.lua" - include "tribes/wares/smoked_fish/init.lua" - include "tribes/wares/smoked_meat/init.lua" - include "tribes/wares/snack/init.lua" - include "tribes/wares/spear/init.lua" - include "tribes/wares/spear_advanced/init.lua" - include "tribes/wares/spear_heavy/init.lua" - include "tribes/wares/spear_war/init.lua" - include "tribes/wares/spear_wooden/init.lua" - include "tribes/wares/spidercloth/init.lua" - include "tribes/wares/spider_silk/init.lua" - include "tribes/wares/tabard/init.lua" - include "tribes/wares/tabard_golden/init.lua" - include "tribes/wares/thatch_reed/init.lua" - include "tribes/wares/trident_double/init.lua" - include "tribes/wares/trident_heavy_double/init.lua" - include "tribes/wares/trident_light/init.lua" - include "tribes/wares/trident_long/init.lua" - include "tribes/wares/trident_steel/init.lua" - include "tribes/wares/water/init.lua" - include "tribes/wares/wheat/init.lua" - include "tribes/wares/wine/init.lua" - include "tribes/wares/wool/init.lua" + load_directories({"tribes/wares"}) end) -- =================================== @@ -127,44 +62,7 @@ -- =================================== print_loading_message("┃ Immovables", function() - include "tribes/immovables/ashes/init.lua" - include "tribes/immovables/blackrootfield_harvested/init.lua" - include "tribes/immovables/blackrootfield_medium/init.lua" - include "tribes/immovables/blackrootfield_ripe/init.lua" - include "tribes/immovables/blackrootfield_small/init.lua" - include "tribes/immovables/blackrootfield_tiny/init.lua" - include "tribes/immovables/cornfield_harvested/init.lua" - include "tribes/immovables/cornfield_medium/init.lua" - include "tribes/immovables/cornfield_ripe/init.lua" - include "tribes/immovables/cornfield_small/init.lua" - include "tribes/immovables/cornfield_tiny/init.lua" - include "tribes/immovables/destroyed_building/init.lua" - include "tribes/immovables/field_harvested/init.lua" - include "tribes/immovables/field_medium/init.lua" - include "tribes/immovables/field_ripe/init.lua" - include "tribes/immovables/field_small/init.lua" - include "tribes/immovables/field_tiny/init.lua" - include "tribes/immovables/grapevine_medium/init.lua" - include "tribes/immovables/grapevine_ripe/init.lua" - include "tribes/immovables/grapevine_small/init.lua" - include "tribes/immovables/grapevine_tiny/init.lua" - include "tribes/immovables/reed_medium/init.lua" - include "tribes/immovables/reed_ripe/init.lua" - include "tribes/immovables/reed_small/init.lua" - include "tribes/immovables/reed_tiny/init.lua" - include "tribes/immovables/resi_coal1/init.lua" - include "tribes/immovables/resi_coal2/init.lua" - include "tribes/immovables/resi_gold1/init.lua" - include "tribes/immovables/resi_gold2/init.lua" - include "tribes/immovables/resi_iron1/init.lua" - include "tribes/immovables/resi_iron2/init.lua" - include "tribes/immovables/resi_none/init.lua" - include "tribes/immovables/resi_stones1/init.lua" - include "tribes/immovables/resi_stones2/init.lua" - include "tribes/immovables/resi_water1/init.lua" - include "tribes/immovables/shipconstruction_atlanteans/init.lua" - include "tribes/immovables/shipconstruction_barbarians/init.lua" - include "tribes/immovables/shipconstruction_empire/init.lua" + load_directories({"tribes/immovables"}) end) -- =================================== @@ -172,103 +70,7 @@ -- =================================== print_loading_message("┃ Workers", function() - include "tribes/workers/atlanteans/carrier/init.lua" - include "tribes/workers/atlanteans/armorsmith/init.lua" - include "tribes/workers/atlanteans/baker/init.lua" - include "tribes/workers/atlanteans/blackroot_farmer/init.lua" - include "tribes/workers/atlanteans/builder/init.lua" - include "tribes/workers/atlanteans/charcoal_burner/init.lua" - include "tribes/workers/atlanteans/farmer/init.lua" - include "tribes/workers/atlanteans/fishbreeder/init.lua" - include "tribes/workers/atlanteans/fisher/init.lua" - include "tribes/workers/atlanteans/forester/init.lua" - include "tribes/workers/atlanteans/geologist/init.lua" - include "tribes/workers/atlanteans/horse/init.lua" - include "tribes/workers/atlanteans/horsebreeder/init.lua" - include "tribes/workers/atlanteans/hunter/init.lua" - include "tribes/workers/atlanteans/miller/init.lua" - include "tribes/workers/atlanteans/miner/init.lua" - include "tribes/workers/atlanteans/recruit/init.lua" - include "tribes/workers/atlanteans/sawyer/init.lua" - include "tribes/workers/atlanteans/scout/init.lua" - include "tribes/workers/atlanteans/shipwright/init.lua" - include "tribes/workers/atlanteans/smelter/init.lua" - include "tribes/workers/atlanteans/smoker/init.lua" - include "tribes/workers/atlanteans/soldier/init.lua" - include "tribes/workers/atlanteans/spiderbreeder/init.lua" - include "tribes/workers/atlanteans/stonecutter/init.lua" - include "tribes/workers/atlanteans/toolsmith/init.lua" - include "tribes/workers/atlanteans/trainer/init.lua" - include "tribes/workers/atlanteans/weaponsmith/init.lua" - include "tribes/workers/atlanteans/weaver/init.lua" - include "tribes/workers/atlanteans/woodcutter/init.lua" - - include "tribes/workers/barbarians/carrier/init.lua" - include "tribes/workers/barbarians/baker/init.lua" - include "tribes/workers/barbarians/blacksmith_master/init.lua" - include "tribes/workers/barbarians/blacksmith/init.lua" - include "tribes/workers/barbarians/brewer_master/init.lua" - include "tribes/workers/barbarians/brewer/init.lua" - include "tribes/workers/barbarians/builder/init.lua" - include "tribes/workers/barbarians/cattlebreeder/init.lua" - include "tribes/workers/barbarians/charcoal_burner/init.lua" - include "tribes/workers/barbarians/farmer/init.lua" - include "tribes/workers/barbarians/fisher/init.lua" - include "tribes/workers/barbarians/gamekeeper/init.lua" - include "tribes/workers/barbarians/gardener/init.lua" - include "tribes/workers/barbarians/geologist/init.lua" - include "tribes/workers/barbarians/helmsmith/init.lua" - include "tribes/workers/barbarians/hunter/init.lua" - include "tribes/workers/barbarians/innkeeper/init.lua" - include "tribes/workers/barbarians/lime_burner/init.lua" - include "tribes/workers/barbarians/lumberjack/init.lua" - include "tribes/workers/barbarians/miner_master/init.lua" - include "tribes/workers/barbarians/miner_chief/init.lua" - include "tribes/workers/barbarians/miner/init.lua" - include "tribes/workers/barbarians/ox/init.lua" - include "tribes/workers/barbarians/ranger/init.lua" - include "tribes/workers/barbarians/recruit/init.lua" - include "tribes/workers/barbarians/scout/init.lua" - include "tribes/workers/barbarians/shipwright/init.lua" - include "tribes/workers/barbarians/smelter/init.lua" - include "tribes/workers/barbarians/soldier/init.lua" - include "tribes/workers/barbarians/stonemason/init.lua" - include "tribes/workers/barbarians/trainer/init.lua" - include "tribes/workers/barbarians/weaver/init.lua" - - include "tribes/workers/empire/carrier/init.lua" - include "tribes/workers/empire/armorsmith/init.lua" - include "tribes/workers/empire/baker/init.lua" - include "tribes/workers/empire/brewer/init.lua" - include "tribes/workers/empire/builder/init.lua" - include "tribes/workers/empire/carpenter/init.lua" - include "tribes/workers/empire/charcoal_burner/init.lua" - include "tribes/workers/empire/donkey/init.lua" - include "tribes/workers/empire/donkeybreeder/init.lua" - include "tribes/workers/empire/farmer/init.lua" - include "tribes/workers/empire/fisher/init.lua" - include "tribes/workers/empire/forester/init.lua" - include "tribes/workers/empire/geologist/init.lua" - include "tribes/workers/empire/hunter/init.lua" - include "tribes/workers/empire/innkeeper/init.lua" - include "tribes/workers/empire/lumberjack/init.lua" - include "tribes/workers/empire/miller/init.lua" - include "tribes/workers/empire/miner_master/init.lua" - include "tribes/workers/empire/miner/init.lua" - include "tribes/workers/empire/pigbreeder/init.lua" - include "tribes/workers/empire/recruit/init.lua" - include "tribes/workers/empire/scout/init.lua" - include "tribes/workers/empire/shepherd/init.lua" - include "tribes/workers/empire/shipwright/init.lua" - include "tribes/workers/empire/smelter/init.lua" - include "tribes/workers/empire/soldier/init.lua" - include "tribes/workers/empire/stonemason/init.lua" - include "tribes/workers/empire/toolsmith/init.lua" - include "tribes/workers/empire/trainer/init.lua" - include "tribes/workers/empire/vinefarmer/init.lua" - include "tribes/workers/empire/vintner/init.lua" - include "tribes/workers/empire/weaponsmith/init.lua" - include "tribes/workers/empire/weaver/init.lua" + load_directories({"tribes/workers"}) end) -- =================================== @@ -276,17 +78,7 @@ -- =================================== print_loading_message("┃ Warehouses", function() - include "tribes/buildings/warehouses/atlanteans/headquarters/init.lua" - include "tribes/buildings/warehouses/atlanteans/port/init.lua" - include "tribes/buildings/warehouses/atlanteans/warehouse/init.lua" - include "tribes/buildings/warehouses/barbarians/headquarters/init.lua" - include "tribes/buildings/warehouses/barbarians/headquarters_interim/init.lua" - include "tribes/buildings/warehouses/barbarians/port/init.lua" - include "tribes/buildings/warehouses/barbarians/warehouse/init.lua" - include "tribes/buildings/warehouses/empire/headquarters/init.lua" - include "tribes/buildings/warehouses/empire/headquarters_shipwreck/init.lua" - include "tribes/buildings/warehouses/empire/port/init.lua" - include "tribes/buildings/warehouses/empire/warehouse/init.lua" + load_directories({"tribes/buildings/warehouses"}) end) -- =================================== @@ -294,125 +86,7 @@ -- =================================== print_loading_message("┃ Productionsites", function() - -- Atlanteans small - include "tribes/buildings/productionsites/atlanteans/quarry/init.lua" - include "tribes/buildings/productionsites/atlanteans/woodcutters_house/init.lua" - include "tribes/buildings/productionsites/atlanteans/foresters_house/init.lua" - include "tribes/buildings/productionsites/atlanteans/fishers_house/init.lua" - include "tribes/buildings/productionsites/atlanteans/fishbreeders_house/init.lua" - include "tribes/buildings/productionsites/atlanteans/hunters_house/init.lua" - include "tribes/buildings/productionsites/atlanteans/well/init.lua" - include "tribes/buildings/productionsites/atlanteans/gold_spinning_mill/init.lua" - include "tribes/buildings/productionsites/atlanteans/scouts_house/init.lua" - -- Atlanteans medium - include "tribes/buildings/productionsites/atlanteans/sawmill/init.lua" - include "tribes/buildings/productionsites/atlanteans/smokery/init.lua" - include "tribes/buildings/productionsites/atlanteans/mill/init.lua" - include "tribes/buildings/productionsites/atlanteans/bakery/init.lua" - include "tribes/buildings/productionsites/atlanteans/charcoal_kiln/init.lua" - include "tribes/buildings/productionsites/atlanteans/smelting_works/init.lua" - include "tribes/buildings/productionsites/atlanteans/shipyard/init.lua" - include "tribes/buildings/productionsites/atlanteans/toolsmithy/init.lua" - include "tribes/buildings/productionsites/atlanteans/weaponsmithy/init.lua" - include "tribes/buildings/productionsites/atlanteans/armorsmithy/init.lua" - include "tribes/buildings/productionsites/atlanteans/barracks/init.lua" - - -- Atlanteans big - include "tribes/buildings/productionsites/atlanteans/horsefarm/init.lua" - include "tribes/buildings/productionsites/atlanteans/farm/init.lua" - include "tribes/buildings/productionsites/atlanteans/blackroot_farm/init.lua" - include "tribes/buildings/productionsites/atlanteans/spiderfarm/init.lua" - include "tribes/buildings/productionsites/atlanteans/weaving_mill/init.lua" - - -- Atlanteans mines - include "tribes/buildings/productionsites/atlanteans/crystalmine/init.lua" - include "tribes/buildings/productionsites/atlanteans/coalmine/init.lua" - include "tribes/buildings/productionsites/atlanteans/ironmine/init.lua" - include "tribes/buildings/productionsites/atlanteans/goldmine/init.lua" - -- Barbarians small - include "tribes/buildings/productionsites/barbarians/quarry/init.lua" - include "tribes/buildings/productionsites/barbarians/lumberjacks_hut/init.lua" - include "tribes/buildings/productionsites/barbarians/rangers_hut/init.lua" - include "tribes/buildings/productionsites/barbarians/fishers_hut/init.lua" - include "tribes/buildings/productionsites/barbarians/hunters_hut/init.lua" - include "tribes/buildings/productionsites/barbarians/gamekeepers_hut/init.lua" - include "tribes/buildings/productionsites/barbarians/well/init.lua" - include "tribes/buildings/productionsites/barbarians/scouts_hut/init.lua" - -- Barbarians medium - include "tribes/buildings/productionsites/barbarians/wood_hardener/init.lua" - include "tribes/buildings/productionsites/barbarians/lime_kiln/init.lua" - include "tribes/buildings/productionsites/barbarians/reed_yard/init.lua" - include "tribes/buildings/productionsites/barbarians/bakery/init.lua" - include "tribes/buildings/productionsites/barbarians/brewery/init.lua" - include "tribes/buildings/productionsites/barbarians/micro_brewery/init.lua" - include "tribes/buildings/productionsites/barbarians/big_inn/init.lua" - include "tribes/buildings/productionsites/barbarians/inn/init.lua" - include "tribes/buildings/productionsites/barbarians/tavern/init.lua" - include "tribes/buildings/productionsites/barbarians/charcoal_kiln/init.lua" - include "tribes/buildings/productionsites/barbarians/smelting_works/init.lua" - include "tribes/buildings/productionsites/barbarians/shipyard/init.lua" - include "tribes/buildings/productionsites/barbarians/warmill/init.lua" - include "tribes/buildings/productionsites/barbarians/ax_workshop/init.lua" - include "tribes/buildings/productionsites/barbarians/metal_workshop/init.lua" - include "tribes/buildings/productionsites/barbarians/barracks/init.lua" - - -- Barbarians big - include "tribes/buildings/productionsites/barbarians/cattlefarm/init.lua" - include "tribes/buildings/productionsites/barbarians/farm/init.lua" - include "tribes/buildings/productionsites/barbarians/weaving_mill/init.lua" - include "tribes/buildings/productionsites/barbarians/helmsmithy/init.lua" - -- Barbarians mines - include "tribes/buildings/productionsites/barbarians/granitemine/init.lua" - include "tribes/buildings/productionsites/barbarians/coalmine_deeper/init.lua" - include "tribes/buildings/productionsites/barbarians/coalmine_deep/init.lua" - include "tribes/buildings/productionsites/barbarians/coalmine/init.lua" - include "tribes/buildings/productionsites/barbarians/ironmine_deeper/init.lua" - include "tribes/buildings/productionsites/barbarians/ironmine_deep/init.lua" - include "tribes/buildings/productionsites/barbarians/ironmine/init.lua" - include "tribes/buildings/productionsites/barbarians/goldmine_deeper/init.lua" - include "tribes/buildings/productionsites/barbarians/goldmine_deep/init.lua" - include "tribes/buildings/productionsites/barbarians/goldmine/init.lua" - -- Empire small - include "tribes/buildings/productionsites/empire/quarry/init.lua" - include "tribes/buildings/productionsites/empire/lumberjacks_house/init.lua" - include "tribes/buildings/productionsites/empire/foresters_house/init.lua" - include "tribes/buildings/productionsites/empire/fishers_house/init.lua" - include "tribes/buildings/productionsites/empire/hunters_house/init.lua" - include "tribes/buildings/productionsites/empire/well/init.lua" - include "tribes/buildings/productionsites/empire/scouts_house/init.lua" - -- Empire medium - include "tribes/buildings/productionsites/empire/stonemasons_house/init.lua" - include "tribes/buildings/productionsites/empire/sawmill/init.lua" - include "tribes/buildings/productionsites/empire/mill/init.lua" - include "tribes/buildings/productionsites/empire/bakery/init.lua" - include "tribes/buildings/productionsites/empire/brewery/init.lua" - include "tribes/buildings/productionsites/empire/vineyard/init.lua" - include "tribes/buildings/productionsites/empire/winery/init.lua" - include "tribes/buildings/productionsites/empire/inn/init.lua" - include "tribes/buildings/productionsites/empire/tavern/init.lua" - include "tribes/buildings/productionsites/empire/charcoal_kiln/init.lua" - include "tribes/buildings/productionsites/empire/smelting_works/init.lua" - include "tribes/buildings/productionsites/empire/shipyard/init.lua" - include "tribes/buildings/productionsites/empire/toolsmithy/init.lua" - include "tribes/buildings/productionsites/empire/armorsmithy/init.lua" - -- Empire big - include "tribes/buildings/productionsites/empire/donkeyfarm/init.lua" - include "tribes/buildings/productionsites/empire/farm/init.lua" - include "tribes/buildings/productionsites/empire/piggery/init.lua" - include "tribes/buildings/productionsites/empire/sheepfarm/init.lua" - include "tribes/buildings/productionsites/empire/weaving_mill/init.lua" - include "tribes/buildings/productionsites/empire/weaponsmithy/init.lua" - include "tribes/buildings/productionsites/empire/barracks/init.lua" - - -- Empire mines - include "tribes/buildings/productionsites/empire/coalmine_deep/init.lua" - include "tribes/buildings/productionsites/empire/coalmine/init.lua" - include "tribes/buildings/productionsites/empire/ironmine_deep/init.lua" - include "tribes/buildings/productionsites/empire/ironmine/init.lua" - include "tribes/buildings/productionsites/empire/marblemine_deep/init.lua" - include "tribes/buildings/productionsites/empire/marblemine/init.lua" - include "tribes/buildings/productionsites/empire/goldmine_deep/init.lua" - include "tribes/buildings/productionsites/empire/goldmine/init.lua" + load_directories({"tribes/buildings/productionsites"}) end) -- =================================== @@ -420,13 +94,7 @@ -- =================================== print_loading_message("┃ Trainingsites", function() - include "tribes/buildings/trainingsites/atlanteans/dungeon/init.lua" - include "tribes/buildings/trainingsites/atlanteans/labyrinth/init.lua" - include "tribes/buildings/trainingsites/barbarians/battlearena/init.lua" - include "tribes/buildings/trainingsites/barbarians/trainingcamp/init.lua" - include "tribes/buildings/trainingsites/empire/colosseum/init.lua" - include "tribes/buildings/trainingsites/empire/arena/init.lua" - include "tribes/buildings/trainingsites/empire/trainingcamp/init.lua" + load_directories({"tribes/buildings/trainingsites"}) end) -- =================================== @@ -434,26 +102,7 @@ -- =================================== print_loading_message("┃ Militarysites", function() - include "tribes/buildings/militarysites/atlanteans/guardhouse/init.lua" - include "tribes/buildings/militarysites/atlanteans/guardhall/init.lua" - include "tribes/buildings/militarysites/atlanteans/tower_small/init.lua" - include "tribes/buildings/militarysites/atlanteans/tower_high/init.lua" - include "tribes/buildings/militarysites/atlanteans/tower/init.lua" - include "tribes/buildings/militarysites/atlanteans/castle/init.lua" - - include "tribes/buildings/militarysites/barbarians/sentry/init.lua" - include "tribes/buildings/militarysites/barbarians/barrier/init.lua" - include "tribes/buildings/militarysites/barbarians/tower/init.lua" - include "tribes/buildings/militarysites/barbarians/citadel/init.lua" - include "tribes/buildings/militarysites/barbarians/fortress/init.lua" - - include "tribes/buildings/militarysites/empire/sentry/init.lua" - include "tribes/buildings/militarysites/empire/blockhouse/init.lua" - include "tribes/buildings/militarysites/empire/barrier/init.lua" - include "tribes/buildings/militarysites/empire/outpost/init.lua" - include "tribes/buildings/militarysites/empire/tower/init.lua" - include "tribes/buildings/militarysites/empire/castle/init.lua" - include "tribes/buildings/militarysites/empire/fortress/init.lua" + load_directories({"tribes/buildings/militarysites"}) end) -- =================================== @@ -461,8 +110,7 @@ -- =================================== print_loading_message("┃ Partially finished buildings", function() - include "tribes/buildings/partially_finished/constructionsite/init.lua" - include "tribes/buildings/partially_finished/dismantlesite/init.lua" + load_directories({"tribes/buildings/partially_finished"}) end) -- =================================== === modified file 'src/game_io/game_loader.cc' --- src/game_io/game_loader.cc 2017-07-02 21:00:58 +0000 +++ src/game_io/game_loader.cc 2017-08-17 11:49:32 +0000 @@ -84,6 +84,15 @@ M.read(fs_, game_); log("Game: Reading Map Data took %ums\n", timer.ms_since_last_query()); + // This has to be loaded after the map packet so that the map's filesystem will exist. + // The custom tribe scripts are saved when the map scripting packet is saved, but we need + // to load them as early as possible here. + if (fs_.file_exists("map/scripting/tribes/init.lua")) { + log("Game: Reading Scenario Tribes ... "); + game_.lua().run_script("map:scripting/tribes/init.lua"); + log("Game: Reading Scenario Tribes took %ums\n", timer.ms_since_last_query()); + } + log("Game: Reading Player Info ... "); { GamePlayerInfoPacket p; === modified file 'src/io/filewrite.cc' --- src/io/filewrite.cc 2017-01-25 18:55:59 +0000 +++ src/io/filewrite.cc 2017-08-17 11:49:32 +0000 @@ -36,12 +36,12 @@ filepos_ = 0; } -void FileWrite::write(FileSystem& fs, char const* const filename) { +void FileWrite::write(FileSystem& fs, const std::string& filename) { fs.write(filename, data_, length_); clear(); } -void FileWrite::write_append(RealFSImpl& fs, char const* const filename) { +void FileWrite::write_append(RealFSImpl& fs, const std::string& filename) { fs.write(filename, data_, length_, true); clear(); } === modified file 'src/io/filewrite.h' --- src/io/filewrite.h 2017-01-25 18:55:59 +0000 +++ src/io/filewrite.h 2017-08-17 11:49:32 +0000 @@ -76,11 +76,11 @@ /// Write the file out to disk. If successful, this clears the buffers. /// Otherwise, an exception is thrown but the buffer remains intact (don't /// worry, it will be cleared by the destructor). - void write(FileSystem& fs, char const* const filename); + void write(FileSystem& fs, const std::string& filename); /// Same as above, just that the data is appended to the file /// NOTE RealFSImpl is used by purpose - zip filesystems do not support appending - void write_append(RealFSImpl& fs, char const* const filename); + void write_append(RealFSImpl& fs, const std::string& filename); /// Get the position that will be written to in the next write operation that /// does not specify a position. === modified file 'src/logic/game.cc' --- src/logic/game.cc 2017-08-16 13:23:15 +0000 +++ src/logic/game.cc 2017-08-17 11:49:32 +0000 @@ -205,6 +205,12 @@ loader_ui.step(_("Loading tribes")); tribes(); + // If the scenario has custrom tribe entites, load them. + const std::string custom_tribe_script = mapname + "/scripting/tribes/init.lua"; + if (g_fs->file_exists(custom_tribe_script)) { + lua().run_script(custom_tribe_script); + } + // We have to create the players here. loader_ui.step(_("Creating players")); PlayerNumber const nr_players = map().get_nrplayers(); === modified file 'src/logic/map_objects/tribes/building.cc' --- src/logic/map_objects/tribes/building.cc 2017-08-16 04:31:56 +0000 +++ src/logic/map_objects/tribes/building.cc 2017-08-17 11:49:32 +0000 @@ -57,9 +57,9 @@ BuildingDescr::BuildingDescr(const std::string& init_descname, const MapObjectType init_type, const LuaTable& table, - const EditorGameBase& egbase) + EditorGameBase* egbase) : MapObjectDescr(init_type, table.get_string("name"), init_descname, table), - egbase_(egbase), + egbase_(*egbase), buildable_(false), size_(BaseImmovable::SMALL), mine_(false), @@ -112,22 +112,11 @@ if (enh == name()) { throw wexception("enhancement to same type"); } - DescriptionIndex const en_i = egbase_.tribes().building_index(enh); - if (egbase_.tribes().building_exists(en_i)) { - enhancement_ = en_i; - - // Merge the enhancements workarea info into this building's - // workarea info. - const BuildingDescr* tmp_enhancement = egbase_.tribes().get_building_descr(en_i); - for (auto area : tmp_enhancement->workarea_info_) { - std::set<std::string>& strs = workarea_info_[area.first]; - for (std::string str : area.second) - strs.insert(str); - } + if (egbase_.tribes().building_exists(enh)) { + set_enhances_to(enh); } else { - throw wexception( - "\"%s\" has not been defined as a building type (wrong declaration order?)", - enh.c_str()); + // The advanced building wasn't loaded yet, so we'll try this again in postload. + egbase->mutable_tribes()->add_mapobject_enhancement({MapObjectType::BUILDING, name(), enh}); } } @@ -164,6 +153,18 @@ } } +void BuildingDescr::set_enhances_to(const std::string& name) { + enhancement_ = egbase_.tribes().safe_building_index(name); + + // Merge the enhancements workarea info into this building's + // workarea info. + for (auto area : egbase_.tribes().get_building_descr(enhancement_)->workarea_info_) { + std::set<std::string>& strs = workarea_info_[area.first]; + for (std::string str : area.second) + strs.insert(str); + } +} + Building& BuildingDescr::create(EditorGameBase& egbase, Player& owner, Coords const pos, === modified file 'src/logic/map_objects/tribes/building.h' --- src/logic/map_objects/tribes/building.h 2017-08-09 17:55:34 +0000 +++ src/logic/map_objects/tribes/building.h 2017-08-17 11:49:32 +0000 @@ -66,7 +66,7 @@ BuildingDescr(const std::string& init_descname, MapObjectType type, const LuaTable& t, - const EditorGameBase& egbase); + EditorGameBase* egbase); ~BuildingDescr() override { } @@ -80,6 +80,9 @@ return enhanced_building_; } + /// Define a building that this building can be enhanced to. 'name' is the descr().name() of the other building. + void set_enhances_to(const std::string& name); + /** * The build cost for direct construction */ === modified file 'src/logic/map_objects/tribes/carrier.cc' --- src/logic/map_objects/tribes/carrier.cc 2017-04-22 06:45:52 +0000 +++ src/logic/map_objects/tribes/carrier.cc 2017-08-17 11:49:32 +0000 @@ -568,7 +568,7 @@ CarrierDescr::CarrierDescr(const std::string& init_descname, const LuaTable& table, - const EditorGameBase& egbase) + EditorGameBase* egbase) : WorkerDescr(init_descname, MapObjectType::CARRIER, table, egbase), ware_hotspot_(Vector2i(0, 15)) { if (table.has_key("ware_hotspot")) { === modified file 'src/logic/map_objects/tribes/carrier.h' --- src/logic/map_objects/tribes/carrier.h 2017-06-25 19:12:30 +0000 +++ src/logic/map_objects/tribes/carrier.h 2017-08-17 11:49:32 +0000 @@ -29,7 +29,7 @@ public: CarrierDescr(const std::string& init_descname, const LuaTable& table, - const EditorGameBase& egbase); + EditorGameBase* egbase); ~CarrierDescr() override { } === modified file 'src/logic/map_objects/tribes/constructionsite.cc' --- src/logic/map_objects/tribes/constructionsite.cc 2017-06-23 16:16:28 +0000 +++ src/logic/map_objects/tribes/constructionsite.cc 2017-08-17 11:49:32 +0000 @@ -45,7 +45,7 @@ */ ConstructionSiteDescr::ConstructionSiteDescr(const std::string& init_descname, const LuaTable& table, - const EditorGameBase& egbase) + EditorGameBase* egbase) : BuildingDescr(init_descname, MapObjectType::CONSTRUCTIONSITE, table, egbase) { add_attribute(MapObject::CONSTRUCTIONSITE); } === modified file 'src/logic/map_objects/tribes/constructionsite.h' --- src/logic/map_objects/tribes/constructionsite.h 2017-06-24 08:47:46 +0000 +++ src/logic/map_objects/tribes/constructionsite.h 2017-08-17 11:49:32 +0000 @@ -65,7 +65,7 @@ public: ConstructionSiteDescr(const std::string& init_descname, const LuaTable& t, - const EditorGameBase& egbase); + EditorGameBase* egbase); ~ConstructionSiteDescr() override { } === modified file 'src/logic/map_objects/tribes/dismantlesite.cc' --- src/logic/map_objects/tribes/dismantlesite.cc 2017-04-23 12:11:19 +0000 +++ src/logic/map_objects/tribes/dismantlesite.cc 2017-08-17 11:49:32 +0000 @@ -45,7 +45,7 @@ DismantleSiteDescr::DismantleSiteDescr(const std::string& init_descname, const LuaTable& table, - const EditorGameBase& egbase) + EditorGameBase* egbase) : BuildingDescr(init_descname, MapObjectType::DISMANTLESITE, table, egbase) { add_attribute(MapObject::Attribute::CONSTRUCTIONSITE); // Yep, this is correct. } === modified file 'src/logic/map_objects/tribes/dismantlesite.h' --- src/logic/map_objects/tribes/dismantlesite.h 2017-06-25 19:12:30 +0000 +++ src/logic/map_objects/tribes/dismantlesite.h 2017-08-17 11:49:32 +0000 @@ -47,7 +47,7 @@ public: DismantleSiteDescr(const std::string& init_descname, const LuaTable& t, - const EditorGameBase& egbase); + EditorGameBase* egbase); ~DismantleSiteDescr() override { } === modified file 'src/logic/map_objects/tribes/militarysite.cc' --- src/logic/map_objects/tribes/militarysite.cc 2017-08-16 13:23:15 +0000 +++ src/logic/map_objects/tribes/militarysite.cc 2017-08-17 11:49:32 +0000 @@ -296,7 +296,7 @@ */ MilitarySiteDescr::MilitarySiteDescr(const std::string& init_descname, const LuaTable& table, - const EditorGameBase& egbase) + EditorGameBase* egbase) : BuildingDescr(init_descname, MapObjectType::MILITARYSITE, table, egbase), conquer_radius_(0), num_soldiers_(0), === modified file 'src/logic/map_objects/tribes/militarysite.h' --- src/logic/map_objects/tribes/militarysite.h 2017-08-16 13:23:15 +0000 +++ src/logic/map_objects/tribes/militarysite.h 2017-08-17 11:49:32 +0000 @@ -45,7 +45,7 @@ public: MilitarySiteDescr(const std::string& init_descname, const LuaTable& t, - const EditorGameBase& egbase); + EditorGameBase* egbase); ~MilitarySiteDescr() override { } === modified file 'src/logic/map_objects/tribes/productionsite.cc' --- src/logic/map_objects/tribes/productionsite.cc 2017-08-16 04:31:56 +0000 +++ src/logic/map_objects/tribes/productionsite.cc 2017-08-17 11:49:32 +0000 @@ -63,7 +63,7 @@ const std::string& msgctxt, MapObjectType init_type, const LuaTable& table, - const EditorGameBase& egbase) + EditorGameBase* egbase) : BuildingDescr(init_descname, init_type, table, egbase), out_of_resource_productivity_threshold_(100) { i18n::Textdomain td("tribes"); @@ -86,15 +86,15 @@ if (table.has_key("outputs")) { for (const std::string& output : table.get_table("outputs")->array_entries<std::string>()) { try { - DescriptionIndex idx = egbase.tribes().ware_index(output); - if (egbase.tribes().ware_exists(idx)) { + DescriptionIndex idx = egbase->tribes().ware_index(output); + if (egbase->tribes().ware_exists(idx)) { if (output_ware_types_.count(idx)) { throw wexception("this ware type has already been declared as an output"); } output_ware_types_.insert(idx); } else { - idx = egbase.tribes().worker_index(output); - if (egbase.tribes().worker_exists(idx)) { + idx = egbase->tribes().worker_index(output); + if (egbase->tribes().worker_exists(idx)) { if (output_worker_types_.count(idx)) { throw wexception("this worker type has already been declared as an output"); } @@ -119,8 +119,8 @@ if (amount < 1 || 255 < amount) { throw wexception("amount is out of range 1 .. 255"); } - DescriptionIndex idx = egbase.tribes().ware_index(ware_name); - if (egbase.tribes().ware_exists(idx)) { + DescriptionIndex idx = egbase->tribes().ware_index(ware_name); + if (egbase->tribes().ware_exists(idx)) { for (const auto& temp_inputs : input_wares()) { if (temp_inputs.first == idx) { throw wexception("duplicated"); @@ -128,8 +128,8 @@ } input_wares_.push_back(WareAmount(idx, amount)); } else { - idx = egbase.tribes().worker_index(ware_name); - if (egbase.tribes().worker_exists(idx)) { + idx = egbase->tribes().worker_index(ware_name); + if (egbase->tribes().worker_exists(idx)) { for (const auto& temp_inputs : input_workers()) { if (temp_inputs.first == idx) { throw wexception("duplicated"); @@ -153,8 +153,8 @@ if (amount < 1 || 255 < amount) { throw wexception("count is out of range 1 .. 255"); } - DescriptionIndex const woi = egbase.tribes().worker_index(worker_name); - if (egbase.tribes().worker_exists(woi)) { + DescriptionIndex const woi = egbase->tribes().worker_index(worker_name); + if (egbase->tribes().worker_exists(woi)) { for (const auto& wp : working_positions()) { if (wp.first == woi) { throw wexception("duplicated"); @@ -187,7 +187,7 @@ program_descname = pgettext_expr(msgctxt.c_str(), program_descname_unlocalized.c_str()); } programs_[program_name] = std::unique_ptr<ProductionProgram>(new ProductionProgram( - program_name, program_descname, program_table->get_table("actions"), egbase, this)); + program_name, program_descname, program_table->get_table("actions"), *egbase, this)); } catch (const std::exception& e) { throw wexception("program %s: %s", program_name.c_str(), e.what()); } @@ -197,7 +197,7 @@ ProductionSiteDescr::ProductionSiteDescr(const std::string& init_descname, const std::string& msgctxt, const LuaTable& table, - const EditorGameBase& egbase) + EditorGameBase* egbase) : ProductionSiteDescr(init_descname, msgctxt, MapObjectType::PRODUCTIONSITE, table, egbase) { } === modified file 'src/logic/map_objects/tribes/productionsite.h' --- src/logic/map_objects/tribes/productionsite.h 2017-06-24 08:47:46 +0000 +++ src/logic/map_objects/tribes/productionsite.h 2017-08-17 11:49:32 +0000 @@ -63,11 +63,11 @@ const std::string& msgctxt, MapObjectType type, const LuaTable& t, - const EditorGameBase& egbase); + EditorGameBase* egbase); ProductionSiteDescr(const std::string& init_descname, const std::string& msgctxt, const LuaTable& t, - const EditorGameBase& egbase); + EditorGameBase* egbase); Building& create_object() const override; === modified file 'src/logic/map_objects/tribes/soldier.cc' --- src/logic/map_objects/tribes/soldier.cc 2017-07-05 19:21:57 +0000 +++ src/logic/map_objects/tribes/soldier.cc 2017-08-17 11:49:32 +0000 @@ -63,7 +63,7 @@ SoldierDescr::SoldierDescr(const std::string& init_descname, const LuaTable& table, - const EditorGameBase& egbase) + EditorGameBase* egbase) : WorkerDescr(init_descname, MapObjectType::SOLDIER, table, egbase), health_(table.get_table("health")), attack_(table.get_table("attack")), === modified file 'src/logic/map_objects/tribes/soldier.h' --- src/logic/map_objects/tribes/soldier.h 2017-06-24 08:47:46 +0000 +++ src/logic/map_objects/tribes/soldier.h 2017-08-17 11:49:32 +0000 @@ -41,7 +41,7 @@ public: friend class Economy; - SoldierDescr(const std::string& init_descname, const LuaTable& t, const EditorGameBase& egbase); + SoldierDescr(const std::string& init_descname, const LuaTable& t, EditorGameBase* egbase); ~SoldierDescr() override { } === modified file 'src/logic/map_objects/tribes/trainingsite.cc' --- src/logic/map_objects/tribes/trainingsite.cc 2017-06-24 20:22:19 +0000 +++ src/logic/map_objects/tribes/trainingsite.cc 2017-08-17 11:49:32 +0000 @@ -47,7 +47,7 @@ */ TrainingSiteDescr::TrainingSiteDescr(const std::string& init_descname, const LuaTable& table, - const EditorGameBase& egbase) + EditorGameBase* egbase) : ProductionSiteDescr(init_descname, "", MapObjectType::TRAININGSITE, table, egbase), num_soldiers_(table.get_int("soldier_capacity")), max_stall_(table.get_int("trainer_patience")), === modified file 'src/logic/map_objects/tribes/trainingsite.h' --- src/logic/map_objects/tribes/trainingsite.h 2017-06-25 19:12:30 +0000 +++ src/logic/map_objects/tribes/trainingsite.h 2017-08-17 11:49:32 +0000 @@ -35,7 +35,7 @@ public: TrainingSiteDescr(const std::string& init_descname, const LuaTable& table, - const EditorGameBase& egbase); + EditorGameBase* egbase); ~TrainingSiteDescr() override { } === modified file 'src/logic/map_objects/tribes/tribe_descr.cc' --- src/logic/map_objects/tribes/tribe_descr.cc 2017-07-19 20:40:32 +0000 +++ src/logic/map_objects/tribes/tribe_descr.cc 2017-08-17 11:49:32 +0000 @@ -165,67 +165,10 @@ for (const std::string& buildingname : table.get_table("buildings")->array_entries<std::string>()) { - try { - DescriptionIndex index = tribes_.safe_building_index(buildingname); - if (has_building(index)) { - throw GameDataError("Duplicate definition of building '%s'", buildingname.c_str()); - } - buildings_.push_back(index); - - // Register trainigsites - if (get_building_descr(index)->type() == MapObjectType::TRAININGSITE) { - trainingsites_.push_back(index); - } - - // Register construction materials - for (const auto& build_cost : get_building_descr(index)->buildcost()) { - if (!is_construction_material(build_cost.first)) { - construction_materials_.insert(build_cost.first); - } - } - for (const auto& enhancement_cost : get_building_descr(index)->enhancement_cost()) { - if (!is_construction_material(enhancement_cost.first)) { - construction_materials_.insert(enhancement_cost.first); - } - } - } catch (const WException& e) { - throw GameDataError("Failed adding building '%s': %s", buildingname.c_str(), e.what()); - } - } - - // Set default trainingsites proportions for AI. Make sure that we get a sum of ca. 100 - float trainingsites_without_percent = 0.f; - int used_percent = 0; - for (const DescriptionIndex& index : trainingsites_) { - const BuildingDescr& descr = *tribes_.get_building_descr(index); - if (descr.hints().trainingsites_max_percent() == 0) { - ++trainingsites_without_percent; - } else { - used_percent += descr.hints().trainingsites_max_percent(); - } - } - if (trainingsites_without_percent > 0.f && used_percent > 100) { - throw GameDataError( - "Predefined training sites proportions add up to > 100%%: %d", used_percent); - } else if (trainingsites_without_percent > 0) { - const int percent_to_use = std::ceil((100 - used_percent) / trainingsites_without_percent); - if (percent_to_use < 1) { - throw GameDataError("Training sites without predefined proportions add up to < 1%% and " - "will never be built: %d", - used_percent); - } - for (const DescriptionIndex& index : trainingsites_) { - BuildingDescr* descr = tribes_.get_mutable_building_descr(index); - if (descr->hints().trainingsites_max_percent() == 0) { - descr->set_hints_trainingsites_max_percent(percent_to_use); - used_percent += percent_to_use; - } - } - } - if (used_percent < 100) { - throw GameDataError( - "Final training sites proportions add up to < 100%%: %d", used_percent); - } + add_building(buildingname); + } + + update_trainingsites_proportions(); // Special types builder_ = add_special_worker(table.get_string("builder")); @@ -503,6 +446,99 @@ } } +void TribeDescr::add_building(const std::string& buildingname) { + try { + DescriptionIndex index = tribes_.safe_building_index(buildingname); + if (has_building(index)) { + throw GameDataError("Duplicate definition of building '%s'", buildingname.c_str()); + } + buildings_.push_back(index); + + // Register trainigsites + if (get_building_descr(index)->type() == MapObjectType::TRAININGSITE) { + trainingsites_.push_back(index); + } + + // Register construction materials + for (const auto& build_cost : get_building_descr(index)->buildcost()) { + if (!is_construction_material(build_cost.first)) { + construction_materials_.insert(build_cost.first); + } + } + for (const auto& enhancement_cost : get_building_descr(index)->enhancement_cost()) { + if (!is_construction_material(enhancement_cost.first)) { + construction_materials_.insert(enhancement_cost.first); + } + } + } catch (const WException& e) { + throw GameDataError("Failed adding building '%s': %s", buildingname.c_str(), e.what()); + } +} + +void TribeDescr::update_trainingsites_proportions(const std::string& new_site) { + // Set default trainingsites proportions for AI. Make sure that we get a sum of ca. 100 + float trainingsites_without_percent = 0.f; + int used_percent = 0; + if (new_site.empty()) { + for (const DescriptionIndex& index : trainingsites_) { + const BuildingDescr& descr = *tribes_.get_building_descr(index); + if (descr.hints().trainingsites_max_percent() == 0) { + ++trainingsites_without_percent; + } else { + used_percent += descr.hints().trainingsites_max_percent(); + } + } + if (trainingsites_without_percent > 0.f && used_percent > 100) { + throw GameDataError( + "Predefined training sites proportions add up to > 100%%: %d", used_percent); + } else if (trainingsites_without_percent > 0) { + const int percent_to_use = std::ceil((100 - used_percent) / trainingsites_without_percent); + if (percent_to_use < 1) { + throw GameDataError("Training sites without predefined proportions add up to < 1%% and " + "will never be built: %d", + used_percent); + } + for (const DescriptionIndex& index : trainingsites_) { + BuildingDescr* descr = tribes_.get_mutable_building_descr(index); + if (descr->hints().trainingsites_max_percent() == 0) { + descr->set_hints_trainingsites_max_percent(percent_to_use); + used_percent += percent_to_use; + } + } + } + } else { + // Adjust percentages to include a custom scenario training site. + assert(!trainingsites().empty()); + + // Make sure that the new site gets a fair share. + BuildingDescr* newsite_descr = tribes_.get_mutable_building_descr(tribes_.safe_building_index(new_site)); + if (newsite_descr->hints().trainingsites_max_percent() == 0) { + newsite_descr->set_hints_trainingsites_max_percent(100 / trainingsites().size()); + } + + // See what we have. + int total_percent = 0; + for (const DescriptionIndex& index : trainingsites_) { + const BuildingDescr* descr = tribes_.get_building_descr(index); + total_percent += descr->hints().trainingsites_max_percent(); + } + + // Subtract the surplus. + const int percent_to_subtract = std::ceil((total_percent - 100) / trainingsites().size()); + for (const DescriptionIndex& index : trainingsites_) { + BuildingDescr* descr = tribes_.get_mutable_building_descr(index); + if (descr->name() != newsite_descr->name()) { + descr->set_hints_trainingsites_max_percent(descr->hints().trainingsites_max_percent() - percent_to_subtract); + } + used_percent += descr->hints().trainingsites_max_percent(); + } + } + if (used_percent < 100) { + throw GameDataError( + "Final training sites proportions add up to < 100%%: %d", used_percent); + } +} + /** * Helper functions */ === modified file 'src/logic/map_objects/tribes/tribe_descr.h' --- src/logic/map_objects/tribes/tribe_descr.h 2017-07-19 20:40:32 +0000 +++ src/logic/map_objects/tribes/tribe_descr.h 2017-08-17 11:49:32 +0000 @@ -157,6 +157,9 @@ return ship_names_; } + void add_building(const std::string& buildingname); + void update_trainingsites_proportions(const std::string& new_site = ""); + private: // Helper function for adding a special worker type (carriers etc.) DescriptionIndex add_special_worker(const std::string& workername); === modified file 'src/logic/map_objects/tribes/tribes.cc' --- src/logic/map_objects/tribes/tribes.cc 2017-03-23 07:36:36 +0000 +++ src/logic/map_objects/tribes/tribes.cc 2017-08-17 11:49:32 +0000 @@ -78,28 +78,28 @@ tribes_(new DescriptionMaintainer<TribeDescr>()) { } -void Tribes::add_constructionsite_type(const LuaTable& table, const EditorGameBase& egbase) { +void Tribes::add_constructionsite_type(const LuaTable& table, EditorGameBase* egbase) { i18n::Textdomain td("tribes"); buildings_->add(new ConstructionSiteDescr( pgettext_expr(table.get_string("msgctxt").c_str(), table.get_string("descname").c_str()), table, egbase)); } -void Tribes::add_dismantlesite_type(const LuaTable& table, const EditorGameBase& egbase) { +void Tribes::add_dismantlesite_type(const LuaTable& table, EditorGameBase* egbase) { i18n::Textdomain td("tribes"); buildings_->add(new DismantleSiteDescr( pgettext_expr(table.get_string("msgctxt").c_str(), table.get_string("descname").c_str()), table, egbase)); } -void Tribes::add_militarysite_type(const LuaTable& table, const EditorGameBase& egbase) { +void Tribes::add_militarysite_type(const LuaTable& table, EditorGameBase* egbase) { i18n::Textdomain td("tribes"); buildings_->add(new MilitarySiteDescr( pgettext_expr(table.get_string("msgctxt").c_str(), table.get_string("descname").c_str()), table, egbase)); } -void Tribes::add_productionsite_type(const LuaTable& table, const EditorGameBase& egbase) { +void Tribes::add_productionsite_type(const LuaTable& table, EditorGameBase* egbase) { i18n::Textdomain td("tribes"); const std::string msgctxt = table.get_string("msgctxt"); buildings_->add( @@ -107,14 +107,14 @@ msgctxt, table, egbase)); } -void Tribes::add_trainingsite_type(const LuaTable& table, const EditorGameBase& egbase) { +void Tribes::add_trainingsite_type(const LuaTable& table, EditorGameBase* egbase) { i18n::Textdomain td("tribes"); buildings_->add(new TrainingSiteDescr( pgettext_expr(table.get_string("msgctxt").c_str(), table.get_string("descname").c_str()), table, egbase)); } -void Tribes::add_warehouse_type(const LuaTable& table, const EditorGameBase& egbase) { +void Tribes::add_warehouse_type(const LuaTable& table, EditorGameBase* egbase) { i18n::Textdomain td("tribes"); buildings_->add(new WarehouseDescr( pgettext_expr(table.get_string("msgctxt").c_str(), table.get_string("descname").c_str()), @@ -140,21 +140,21 @@ table)); } -void Tribes::add_carrier_type(const LuaTable& table, const EditorGameBase& egbase) { +void Tribes::add_carrier_type(const LuaTable& table, EditorGameBase* egbase) { i18n::Textdomain td("tribes"); workers_->add(new CarrierDescr( pgettext_expr(table.get_string("msgctxt").c_str(), table.get_string("descname").c_str()), table, egbase)); } -void Tribes::add_soldier_type(const LuaTable& table, const EditorGameBase& egbase) { +void Tribes::add_soldier_type(const LuaTable& table, EditorGameBase* egbase) { i18n::Textdomain td("tribes"); workers_->add(new SoldierDescr( pgettext_expr(table.get_string("msgctxt").c_str(), table.get_string("descname").c_str()), table, egbase)); } -void Tribes::add_worker_type(const LuaTable& table, const EditorGameBase& egbase) { +void Tribes::add_worker_type(const LuaTable& table, EditorGameBase* egbase) { i18n::Textdomain td("tribes"); workers_->add(new WorkerDescr( pgettext_expr(table.get_string("msgctxt").c_str(), table.get_string("descname").c_str()), @@ -170,6 +170,20 @@ } } +void Tribes::add_custom_building(const LuaTable& table) { + const std::string tribename = table.get_string("tribename"); + if (Widelands::tribe_exists(tribename)) { + TribeDescr* descr = tribes_->get_mutable(tribe_index(tribename)); + const std::string buildingname = table.get_string("buildingname"); + descr->add_building(buildingname); + if (descr->get_building_descr(descr->building_index(buildingname))->type() == MapObjectType::TRAININGSITE) { + descr->update_trainingsites_proportions(buildingname); + } + } else { + throw GameDataError("The tribe '%s'' has no preload file.", tribename.c_str()); + } +} + size_t Tribes::nrbuildings() const { return buildings_->size(); } @@ -189,6 +203,9 @@ bool Tribes::ware_exists(const DescriptionIndex& index) const { return wares_->get_mutable(index) != nullptr; } +bool Tribes::worker_exists(const std::string& workername) const { + return workers_->exists(workername) != nullptr; +} bool Tribes::worker_exists(const DescriptionIndex& index) const { return workers_->get_mutable(index) != nullptr; } @@ -329,7 +346,61 @@ } } +void Tribes::add_worker_buildcost(const WorkerBuildcost& buildcost) { + postload_workers_buildcost_.push_back(buildcost); +} + +void Tribes::add_mapobject_enhancement(const MapObjectEnhancement& becomes) { + postload_mapobject_enhancements_.push_back(becomes); +} + void Tribes::postload() { + // Some workers have other workers as build cost, so we postload those in order to perform the necessary checks. + for (const WorkerBuildcost& buildcost : postload_workers_buildcost_) { + if (!worker_exists(buildcost.worker)) { + throw GameDataError( + "Trying to add a worker buildcost to non-existing worker '%s'", buildcost.worker.c_str()); + } + if (!worker_exists(buildcost.needed_worker)) { + throw GameDataError( + "The worker '%s' to be added as builcost to the worker '%s' does not exist", buildcost.needed_worker.c_str(), buildcost.worker.c_str()); + } + workers_->get_mutable(safe_worker_index(buildcost.worker))->add_worker_to_buildcost(buildcost.needed_worker, buildcost.quantity); + } + postload_workers_buildcost_.clear(); + + // Likewise, more experienced workers and advanced buildings might not have been available yet when a lower-level worker or building was loaded. + for (const MapObjectEnhancement& enhancement : postload_mapobject_enhancements_) { + switch (enhancement.type) { + case MapObjectType::BUILDING: + if (!building_exists(enhancement.name)) { + throw GameDataError( + "Trying to add a building enhancement to non-existing building '%s'", enhancement.name.c_str()); + } + if (!building_exists(enhancement.enhanced_name)) { + throw GameDataError( + "The building '%s' to be added as enhancement to the building '%s' does not exist", enhancement.enhanced_name.c_str(), enhancement.name.c_str()); + } + buildings_->get_mutable(safe_building_index(enhancement.name))->set_enhances_to(enhancement.enhanced_name); + break; + case MapObjectType::WORKER: + if (!worker_exists(enhancement.name)) { + throw GameDataError( + "Trying to add a worker enhancement to non-existing worker '%s'", enhancement.name.c_str()); + } + if (!worker_exists(enhancement.enhanced_name)) { + throw GameDataError( + "The worker '%s' to be added as enhancement to the worker '%s' does not exist", enhancement.enhanced_name.c_str(), enhancement.name.c_str()); + } + workers_->get_mutable(safe_worker_index(enhancement.name))->set_becomes(enhancement.enhanced_name); + break; + default: + NEVER_HERE(); + } + } + postload_mapobject_enhancements_.clear(); + + // Add building info to wares here, since wares were loaded first. for (DescriptionIndex i = 0; i < buildings_->size(); ++i) { BuildingDescr& building_descr = *buildings_->get_mutable(i); === modified file 'src/logic/map_objects/tribes/tribes.h' --- src/logic/map_objects/tribes/tribes.h 2017-03-23 07:36:36 +0000 +++ src/logic/map_objects/tribes/tribes.h 2017-08-17 11:49:32 +0000 @@ -65,22 +65,22 @@ } /// Adds this building type to the tribe description. - void add_constructionsite_type(const LuaTable& table, const EditorGameBase& egbase); - - /// Adds this building type to the tribe description. - void add_dismantlesite_type(const LuaTable& table, const EditorGameBase& egbase); - - /// Adds this building type to the tribe description. - void add_militarysite_type(const LuaTable& table, const EditorGameBase& egbase); - - /// Adds this building type to the tribe description. - void add_productionsite_type(const LuaTable& table, const EditorGameBase& egbase); - - /// Adds this building type to the tribe description. - void add_trainingsite_type(const LuaTable& table, const EditorGameBase& egbase); - - /// Adds this building type to the tribe description. - void add_warehouse_type(const LuaTable& table, const EditorGameBase& egbase); + void add_constructionsite_type(const LuaTable& table, EditorGameBase* egbase); + + /// Adds this building type to the tribe description. + void add_dismantlesite_type(const LuaTable& table, EditorGameBase* egbase); + + /// Adds this building type to the tribe description. + void add_militarysite_type(const LuaTable& table, EditorGameBase* egbase); + + /// Adds this building type to the tribe description. + void add_productionsite_type(const LuaTable& table, EditorGameBase* egbase); + + /// Adds this building type to the tribe description. + void add_trainingsite_type(const LuaTable& table, EditorGameBase* egbase); + + /// Adds this building type to the tribe description. + void add_warehouse_type(const LuaTable& table, EditorGameBase* egbase); /// Adds this immovable type to the tribe description. void add_immovable_type(const LuaTable& table); @@ -92,23 +92,26 @@ void add_ware_type(const LuaTable& table); /// Adds this worker type to the tribe description. - void add_carrier_type(const LuaTable& table, const EditorGameBase& egbase); - - /// Adds this worker type to the tribe description. - void add_soldier_type(const LuaTable& table, const EditorGameBase& egbase); - - /// Adds this worker type to the tribe description. - void add_worker_type(const LuaTable& table, const EditorGameBase& egbase); + void add_carrier_type(const LuaTable& table, EditorGameBase* egbase); + + /// Adds this worker type to the tribe description. + void add_soldier_type(const LuaTable& table, EditorGameBase* egbase); + + /// Adds this worker type to the tribe description. + void add_worker_type(const LuaTable& table, EditorGameBase* egbase); /// Adds a specific tribe's configuration. void add_tribe(const LuaTable& table, const EditorGameBase& egbase); + void add_custom_building(const LuaTable& table); + size_t nrbuildings() const; size_t nrtribes() const; size_t nrwares() const; size_t nrworkers() const; bool ware_exists(const DescriptionIndex& index) const; + bool worker_exists(const std::string& workername) const; bool worker_exists(const DescriptionIndex& index) const; bool building_exists(const std::string& buildingname) const; bool building_exists(const DescriptionIndex& index) const; @@ -148,6 +151,22 @@ /// Complete the Description objects' information with data from other Description objects. void postload(); + /// Some workers have other workers as part of their buildcost. If the other worker hasn't been loaded yet, it will need to be added during postload. + struct WorkerBuildcost { + const std::string worker; + const std::string needed_worker; + const Quantity quantity; + }; + void add_worker_buildcost(const WorkerBuildcost& buildcost); + + /// Enhanced buildings/workers might not have been loaded yet when a more basic type is being loaded, so we will need to add some of them during postload. + struct MapObjectEnhancement { + const MapObjectType type; // Worker or building + const std::string name; + const std::string enhanced_name; + }; + void add_mapobject_enhancement(const MapObjectEnhancement& becomes); + private: std::unique_ptr<DescriptionMaintainer<BuildingDescr>> buildings_; std::unique_ptr<DescriptionMaintainer<ImmovableDescr>> immovables_; @@ -156,6 +175,11 @@ std::unique_ptr<DescriptionMaintainer<WorkerDescr>> workers_; std::unique_ptr<DescriptionMaintainer<TribeDescr>> tribes_; + /// Worker buildcost records to be added in postload. This container will be empty after postload. + std::vector<WorkerBuildcost> postload_workers_buildcost_; + /// Worker/Building enhancement records to be added in postload. This container will be empty after postload. + std::vector<MapObjectEnhancement> postload_mapobject_enhancements_; + DISALLOW_COPY_AND_ASSIGN(Tribes); }; === modified file 'src/logic/map_objects/tribes/warehouse.cc' --- src/logic/map_objects/tribes/warehouse.cc 2017-07-01 15:36:36 +0000 +++ src/logic/map_objects/tribes/warehouse.cc 2017-08-17 11:49:32 +0000 @@ -293,7 +293,7 @@ */ WarehouseDescr::WarehouseDescr(const std::string& init_descname, const LuaTable& table, - const EditorGameBase& egbase) + EditorGameBase* egbase) : BuildingDescr(init_descname, MapObjectType::WAREHOUSE, table, egbase), conquers_(0), heal_per_second_(0) { === modified file 'src/logic/map_objects/tribes/warehouse.h' --- src/logic/map_objects/tribes/warehouse.h 2017-06-25 19:12:30 +0000 +++ src/logic/map_objects/tribes/warehouse.h 2017-08-17 11:49:32 +0000 @@ -50,7 +50,7 @@ public: WarehouseDescr(const std::string& init_descname, const LuaTable& t, - const EditorGameBase& egbase); + EditorGameBase* egbase); ~WarehouseDescr() override { } === modified file 'src/logic/map_objects/tribes/worker_descr.cc' --- src/logic/map_objects/tribes/worker_descr.cc 2017-04-21 17:03:26 +0000 +++ src/logic/map_objects/tribes/worker_descr.cc 2017-08-17 11:49:32 +0000 @@ -38,12 +38,12 @@ WorkerDescr::WorkerDescr(const std::string& init_descname, MapObjectType init_type, const LuaTable& table, - const EditorGameBase& egbase) + EditorGameBase* egbase) : BobDescr(init_descname, init_type, MapObjectDescr::OwnerType::kTribe, table), buildable_(false), needed_experience_(INVALID_INDEX), becomes_(INVALID_INDEX), - egbase_(egbase) { + egbase_(*egbase) { if (icon_filename().empty()) { throw GameDataError("Worker %s has no menu icon", table.get_string("name").c_str()); } @@ -60,17 +60,18 @@ throw GameDataError( "a buildcost item of this ware type has already been defined: %s", key.c_str()); } - if (!tribes.ware_exists(tribes.ware_index(key)) && - !tribes.worker_exists(tribes.worker_index(key))) { - throw GameDataError("\"%s\" has not been defined as a ware/worker type (wrong " - "declaration order?)", - key.c_str()); - } - int32_t value = items_table->get_int(key); - uint8_t const count = value; - if (count != value) + int32_t temp_quantity = items_table->get_int(key); + const uint8_t quantity = temp_quantity; + if (quantity != temp_quantity) { throw GameDataError("count is out of range 1 .. 255"); - buildcost_.insert(std::pair<std::string, uint8_t>(key, count)); + } + if (tribes.ware_exists(tribes.ware_index(key)) || + tribes.worker_exists(tribes.worker_index(key))) { + buildcost_.insert(std::make_pair(key, quantity)); + } else { + // The buildcost's worker wasn't loaded yet, so we'll try this again in postload. + egbase->mutable_tribes()->add_worker_buildcost({name(), key, quantity}); + } } catch (const WException& e) { throw GameDataError("[buildcost] \"%s\": %s", key.c_str(), e.what()); } @@ -91,8 +92,14 @@ // Read what the worker can become and the needed experience if (table.has_key("becomes")) { - becomes_ = egbase_.tribes().safe_worker_index(table.get_string("becomes")); needed_experience_ = table.get_int("experience"); + const std::string becomes = table.get_string("becomes"); + if (egbase_.tribes().worker_exists(becomes)) { + set_becomes(becomes); + } else { + // The expert worker wasn't loaded yet, so we'll try this again in postload. + egbase->mutable_tribes()->add_mapobject_enhancement({MapObjectType::WORKER, name(), becomes}); + } } // Read programs @@ -122,13 +129,22 @@ WorkerDescr::WorkerDescr(const std::string& init_descname, const LuaTable& table, - const EditorGameBase& egbase) + EditorGameBase* egbase) : WorkerDescr(init_descname, MapObjectType::WORKER, table, egbase) { } WorkerDescr::~WorkerDescr() { } +void WorkerDescr::add_worker_to_buildcost(const std::string& name, uint8_t quantity) { + buildcost_.insert(std::make_pair(name, quantity)); +} + +void WorkerDescr::set_becomes(const std::string& name) { + assert(needed_experience_ > 0 && needed_experience_ != INVALID_INDEX); + becomes_ = egbase_.tribes().safe_worker_index(name); +} + /** * Get a program from the workers description. */ === modified file 'src/logic/map_objects/tribes/worker_descr.h' --- src/logic/map_objects/tribes/worker_descr.h 2017-02-20 14:15:07 +0000 +++ src/logic/map_objects/tribes/worker_descr.h 2017-08-17 11:49:32 +0000 @@ -43,13 +43,13 @@ friend struct WorkerProgram; public: - using Buildcost = std::map<std::string, Quantity>; + using Buildcost = std::map<std::string, uint8_t>; WorkerDescr(const std::string& init_descname, MapObjectType type, const LuaTable& table, - const EditorGameBase& egbase); - WorkerDescr(const std::string& init_descname, const LuaTable& t, const EditorGameBase& egbase); + EditorGameBase* egbase); + WorkerDescr(const std::string& init_descname, const LuaTable& t, EditorGameBase* egbase); ~WorkerDescr() override; Bob& create_object() const override; @@ -62,6 +62,14 @@ return buildcost_; } + /// Some workers need to be added to the buildcost in postload, because the other worker might not have been loaded into the engine yet. + /// 'name' is the descr().name() of the other worker, 'quantity' is the amount of that woker type needed. + void add_worker_to_buildcost(const std::string& name, uint8_t quantity); + + /// Sets the descr().name() of the worker that this worker will enhance to, given enough experience. + /// 'needed_experience_' needs to be set first. + void set_becomes(const std::string& name); + /// How much of the worker type that an economy should store in warehouses. /// The special value std::numeric_limits<uint32_t>::max() means that the /// the target quantity of this ware type will never be checked and should === modified file 'src/map_io/map_scripting_packet.cc' --- src/map_io/map_scripting_packet.cc 2017-01-25 18:55:59 +0000 +++ src/map_io/map_scripting_packet.cc 2017-08-17 11:49:32 +0000 @@ -67,18 +67,29 @@ } void MapScriptingPacket::write(FileSystem& fs, EditorGameBase& egbase, MapObjectSaver& mos) { - fs.ensure_directory_exists("scripting"); - + + // Write all .lua files that exist in the given 'path' in 'map_fs' to the 'target_fs'. + auto write_dir = [] (FileSystem& target_fs, FileSystem* map_fs, const std::string& path) { + if (map_fs) { + target_fs.ensure_directory_exists(path); + for (const std::string& script : + filter(map_fs->list_directory(path), + [](const std::string& fn) { return boost::ends_with(fn, ".lua"); })) { + size_t length; + void* input_data = map_fs->load(script, length); + target_fs.write(script, input_data, length); + free(input_data); + } + } + }; + + // Write any scenario scripting files in the map's basic scripting dir FileSystem* map_fs = egbase.map().filesystem(); - if (map_fs) { - for (const std::string& script : - filter(map_fs->list_directory("scripting"), - [](const std::string& fn) { return boost::ends_with(fn, ".lua"); })) { - size_t length; - void* input_data = map_fs->load(script, length); - fs.write(script, input_data, length); - free(input_data); - } + write_dir(fs, map_fs, "scripting"); + + // Write any custom scenario tribe entities + if (map_fs->file_exists("scripting/tribes/init.lua")) { + write_dir(fs, map_fs, "scripting/tribes"); } // Dump the global environment if this is a game and not in the editor === modified file 'src/scripting/lua_path.cc' --- src/scripting/lua_path.cc 2017-06-24 08:47:46 +0000 +++ src/scripting/lua_path.cc 2017-08-17 11:49:32 +0000 @@ -143,7 +143,7 @@ :returns: An :class:`array` of file paths in lexicographical order. */ static int L_list_files(lua_State* L) { - std::string filename_template = luaL_checkstring(L, 1); + const std::string filename_template = luaL_checkstring(L, 1); NumberGlob glob(filename_template); std::string filename; @@ -161,9 +161,48 @@ return 1; } +/* RST +.. function:: list_directory(filename) + + Returns all file names contained in the given directory. + + :type filename: class:`string` + :arg filename: The directory to read. + + :returns: An :class:`array` of file names. +*/ +static int L_list_directory(lua_State* L) { + lua_newtable(L); + int idx = 1; + for (const std::string& filename : g_fs->list_directory(luaL_checkstring(L, 1))) { + lua_pushint32(L, idx++); + lua_pushstring(L, filename); + lua_settable(L, -3); + } + return 1; +} + + +/* RST +.. function:: is_directory(filename) + + Checks whether the given filename points to a directory. + + :type filename: class:`string` + :arg filename: The filename to check. + + :returns: ``true`` if the given path is a directory. +*/ +static int L_is_directory(lua_State* L) { + lua_pushboolean(L, g_fs->is_directory(luaL_checkstring(L, -1))); + return 1; +} + const static struct luaL_Reg path[] = {{"basename", &L_basename}, {"dirname", &L_dirname}, {"list_files", &L_list_files}, + {"list_directory", &L_list_directory}, + {"is_directory", &L_is_directory}, {nullptr, nullptr}}; void luaopen_path(lua_State* L) { === modified file 'src/scripting/lua_root.cc' --- src/scripting/lua_root.cc 2017-01-25 18:55:59 +0000 +++ src/scripting/lua_root.cc 2017-08-17 11:49:32 +0000 @@ -560,6 +560,7 @@ METHOD(LuaTribes, new_soldier_type), METHOD(LuaTribes, new_worker_type), METHOD(LuaTribes, new_tribe), + METHOD(LuaTribes, add_custom_building), {0, 0}, }; const PropertyType<LuaTribes> LuaTribes::Properties[] = { @@ -606,8 +607,8 @@ try { LuaTable table(L); // Will pop the table eventually. - EditorGameBase& egbase = get_egbase(L); - egbase.mutable_tribes()->add_constructionsite_type(table, egbase); + EditorGameBase* egbase = &get_egbase(L); + egbase->mutable_tribes()->add_constructionsite_type(table, egbase); } catch (std::exception& e) { report_error(L, "%s", e.what()); } @@ -630,8 +631,8 @@ try { LuaTable table(L); // Will pop the table eventually. - EditorGameBase& egbase = get_egbase(L); - egbase.mutable_tribes()->add_dismantlesite_type(table, egbase); + EditorGameBase* egbase = &get_egbase(L); + egbase->mutable_tribes()->add_dismantlesite_type(table, egbase); } catch (std::exception& e) { report_error(L, "%s", e.what()); } @@ -654,8 +655,8 @@ try { LuaTable table(L); // Will pop the table eventually. - EditorGameBase& egbase = get_egbase(L); - egbase.mutable_tribes()->add_militarysite_type(table, egbase); + EditorGameBase* egbase = &get_egbase(L); + egbase->mutable_tribes()->add_militarysite_type(table, egbase); } catch (std::exception& e) { report_error(L, "%s", e.what()); } @@ -678,8 +679,8 @@ try { LuaTable table(L); // Will pop the table eventually. - EditorGameBase& egbase = get_egbase(L); - egbase.mutable_tribes()->add_productionsite_type(table, egbase); + EditorGameBase* egbase = &get_egbase(L); + egbase->mutable_tribes()->add_productionsite_type(table, egbase); } catch (std::exception& e) { report_error(L, "%s", e.what()); } @@ -702,8 +703,8 @@ try { LuaTable table(L); // Will pop the table eventually. - EditorGameBase& egbase = get_egbase(L); - egbase.mutable_tribes()->add_trainingsite_type(table, egbase); + EditorGameBase* egbase = &get_egbase(L); + egbase->mutable_tribes()->add_trainingsite_type(table, egbase); } catch (std::exception& e) { report_error(L, "%s", e.what()); } @@ -726,8 +727,8 @@ try { LuaTable table(L); // Will pop the table eventually. - EditorGameBase& egbase = get_egbase(L); - egbase.mutable_tribes()->add_warehouse_type(table, egbase); + EditorGameBase* egbase = &get_egbase(L); + egbase->mutable_tribes()->add_warehouse_type(table, egbase); } catch (std::exception& e) { report_error(L, "%s", e.what()); } @@ -818,8 +819,8 @@ try { LuaTable table(L); // Will pop the table eventually. - EditorGameBase& egbase = get_egbase(L); - egbase.mutable_tribes()->add_carrier_type(table, egbase); + EditorGameBase* egbase = &get_egbase(L); + egbase->mutable_tribes()->add_carrier_type(table, egbase); } catch (std::exception& e) { report_error(L, "%s", e.what()); } @@ -841,8 +842,8 @@ try { LuaTable table(L); // Will pop the table eventually. - EditorGameBase& egbase = get_egbase(L); - egbase.mutable_tribes()->add_soldier_type(table, egbase); + EditorGameBase* egbase = &get_egbase(L); + egbase->mutable_tribes()->add_soldier_type(table, egbase); } catch (std::exception& e) { report_error(L, "%s", e.what()); } @@ -864,8 +865,8 @@ try { LuaTable table(L); // Will pop the table eventually. - EditorGameBase& egbase = get_egbase(L); - egbase.mutable_tribes()->add_worker_type(table, egbase); + EditorGameBase* egbase = &get_egbase(L); + egbase->mutable_tribes()->add_worker_type(table, egbase); } catch (std::exception& e) { report_error(L, "%s", e.what()); } @@ -896,6 +897,38 @@ return 0; } + +/* RST + .. method:: add_custom_building{table} + + Adds a custom building to a tribe, e.g. for use in a scenario. + The building must already be known to the tribes. + The table has the following entries: + + **tribename** + *Mandatory*. The name of the tribe that this building will be added to. + + **buildingname** + *Mandatory*. The name of the building to be added to the tribe. + + :returns: :const:`0` +*/ +int LuaTribes::add_custom_building(lua_State* L) { + if (lua_gettop(L) != 2) { + report_error(L, "Takes only one argument."); + } + + try { + LuaTable table(L); // Will pop the table eventually. + EditorGameBase& egbase = get_egbase(L); + egbase.mutable_tribes()->add_custom_building(table); + } catch (std::exception& e) { + report_error(L, "%s", e.what()); + } + return 0; +} + + /* ========================================================== C METHODS === modified file 'src/scripting/lua_root.h' --- src/scripting/lua_root.h 2017-06-24 08:47:46 +0000 +++ src/scripting/lua_root.h 2017-08-17 11:49:32 +0000 @@ -172,6 +172,7 @@ int new_soldier_type(lua_State* L); int new_worker_type(lua_State* L); int new_tribe(lua_State* L); + int add_custom_building(lua_State* L); /* * C methods
_______________________________________________ Mailing list: https://launchpad.net/~widelands-dev Post to : widelands-dev@lists.launchpad.net Unsubscribe : https://launchpad.net/~widelands-dev More help : https://help.launchpad.net/ListHelp