widelands-dev team mailing list archive
-
widelands-dev team
-
Mailing list archive
-
Message #08654
[Merge] lp:~notabilis27/widelands/casern into lp:widelands
Notabilis has proposed merging lp:~notabilis27/widelands/casern into lp:widelands.
Requested reviews:
Widelands Developers (widelands-dev)
Related bugs:
Bug #1075562 in widelands: "Implement a casern for soldier recruting"
https://bugs.launchpad.net/widelands/+bug/1075562
For more details, see:
https://code.launchpad.net/~notabilis27/widelands/casern/+merge/309763
Implements a worker queue which allows production buildings to consume
workers as input.
Adds:
- Adds worker queue for production buildings.
- The "inputs" in the lua files for the buildings can now contains worker
names and amounts.
- Production programs can consume workers from the queues.
Changes:
- The barracks-building (new in trunk) now requests and stores carriers
required for recruiting.
- Soldiers can no longer be created in warehouses.
- Renamed the c-for-lua function {set,get,valid}_wares() of production
buildings to {set,get,valid}_inputs() since they now be used to set the
to-be-consumed workers.
- Modified the lua function prefilled_buildings() to required the argument
"inputs" instead of "wares" for production sites.
- Affected scripts should be updated (but only some are tested).
--
Your team Widelands Developers is requested to review the proposed merge of lp:~notabilis27/widelands/casern into lp:widelands.
=== modified file 'data/campaigns/bar01.wmf/scripting/secret_village.lua'
--- data/campaigns/bar01.wmf/scripting/secret_village.lua 2015-10-31 12:11:44 +0000
+++ data/campaigns/bar01.wmf/scripting/secret_village.lua 2016-11-01 14:41:18 +0000
@@ -101,13 +101,13 @@
{"barbarians_gamekeepers_hut", 56, 12},
{"barbarians_farm", 56, 16},
{"barbarians_well", 54, 18},
- {"barbarians_bakery", 55, 20, wares = {wheat = 6, water = 6}},
+ {"barbarians_bakery", 55, 20, inputs = {wheat = 6, water = 6}},
{"barbarians_lumberjacks_hut", 56, 21},
{"barbarians_lumberjacks_hut", 55, 22},
{"barbarians_lumberjacks_hut", 54, 24},
{"barbarians_rangers_hut", 57, 24},
{"barbarians_rangers_hut", 55, 25},
- {"barbarians_wood_hardener", 54, 26, wares = {log = 8}},
+ {"barbarians_wood_hardener", 54, 26, inputs = {log = 8}},
-- to make it more realistic
{"barbarians_warehouse", 53, 28,
wares = {
@@ -116,11 +116,11 @@
meat = 30
}
},
- {"barbarians_inn", 55, 28, wares = {barbarians_bread = 4, meat = 4}},
- {"barbarians_tavern", 57, 28, wares = {barbarians_bread = 4, meat = 4}},
+ {"barbarians_inn", 55, 28, inputs = {barbarians_bread = 4, meat = 4}},
+ {"barbarians_tavern", 57, 28, inputs = {barbarians_bread = 4, meat = 4}},
{"barbarians_well", 52, 30},
{"barbarians_farm", 54, 33},
- {"barbarians_bakery", 51, 35, wares = {wheat = 6, water = 6}},
+ {"barbarians_bakery", 51, 35, inputs = {wheat = 6, water = 6}},
{"barbarians_well", 52, 37}
)
=== modified file 'data/campaigns/tutorial03_seafaring.wmf/scripting/helper_functions.lua'
--- data/campaigns/tutorial03_seafaring.wmf/scripting/helper_functions.lua 2016-01-28 05:24:34 +0000
+++ data/campaigns/tutorial03_seafaring.wmf/scripting/helper_functions.lua 2016-11-01 14:41:18 +0000
@@ -23,5 +23,7 @@
-- Fill with wares
if bdescr.wares then b:set_wares(bdescr.wares)
elseif b.valid_wares then b:set_wares(b.valid_wares) end
+ if bdescr.inputs then b:set_inputs(bdescr.inputs)
+ elseif b.valid_inputs then b:set_inputs(b.valid_inputs) end
end
end
=== modified file 'data/campaigns/tutorial03_seafaring.wmf/scripting/starting_conditions.lua'
--- data/campaigns/tutorial03_seafaring.wmf/scripting/starting_conditions.lua 2015-10-31 12:11:44 +0000
+++ data/campaigns/tutorial03_seafaring.wmf/scripting/starting_conditions.lua 2016-11-01 14:41:18 +0000
@@ -102,8 +102,8 @@
{"atlanteans_horsefarm", 40, 55},
{"atlanteans_spiderfarm", 37, 45},
{"atlanteans_weaving_mill", 45, 45},
- {"atlanteans_smelting_works", 35, 56, wares = {coal = 8, iron_ore = 8}}, -- no gold
- {"atlanteans_smelting_works", 35, 59, wares = {coal = 8, iron_ore = 8}},
+ {"atlanteans_smelting_works", 35, 56, inputs = {coal = 8, iron_ore = 8}}, -- no gold
+ {"atlanteans_smelting_works", 35, 59, inputs = {coal = 8, iron_ore = 8}},
{"atlanteans_toolsmithy", 41, 52},
{"atlanteans_weaponsmithy", 37, 54},
{"atlanteans_tower_small", 34, 63},
=== modified file 'data/campaigns/tutorial04_economy.wmf/scripting/helper_functions.lua'
--- data/campaigns/tutorial04_economy.wmf/scripting/helper_functions.lua 2016-01-28 05:24:34 +0000
+++ data/campaigns/tutorial04_economy.wmf/scripting/helper_functions.lua 2016-11-01 14:41:18 +0000
@@ -24,5 +24,7 @@
-- Fill with wares
if bdescr.wares then b:set_wares(bdescr.wares)
elseif b.valid_wares then b:set_wares(b.valid_wares) end
+ if bdescr.inputs then b:set_inputs(bdescr.inputs)
+ elseif b.valid_inputs then b:set_inputs(b.valid_inputs) end
end
end
=== modified file 'data/campaigns/tutorial04_economy.wmf/scripting/starting_conditions.lua'
--- data/campaigns/tutorial04_economy.wmf/scripting/starting_conditions.lua 2015-10-31 12:11:44 +0000
+++ data/campaigns/tutorial04_economy.wmf/scripting/starting_conditions.lua 2016-11-01 14:41:18 +0000
@@ -94,16 +94,16 @@
{"empire_bakery",116,28},
{"empire_bakery",115,32},
{"empire_tavern",tavern_field.x,tavern_field.y}, -- (105,44), will be destroyed
- {"empire_coalmine",118,45, wares = {beer = 6}},
- {"empire_coalmine",119,39, wares = {beer = 6}},
- {"empire_ironmine",107,59, wares = {beer = 6}},
- {"empire_marblemine",98,38, wares = {wine = 6}},
- {"empire_marblemine",102,38, wares = {wine = 6}},
- {"empire_smelting_works",110,38, wares = {}},
- {"empire_smelting_works",111,43, wares = {}},
- {"empire_toolsmithy",104,64, wares = {log = 8}},
- {"empire_weaponsmithy",113,40, wares = {planks = 8}},
- {"empire_armorsmithy",112,37, wares = {cloth = 8}},
+ {"empire_coalmine",118,45, inputs = {beer = 6}},
+ {"empire_coalmine",119,39, inputs = {beer = 6}},
+ {"empire_ironmine",107,59, inputs = {beer = 6}},
+ {"empire_marblemine",98,38, inputs = {wine = 6}},
+ {"empire_marblemine",102,38, inputs = {wine = 6}},
+ {"empire_smelting_works",110,38, inputs = {}},
+ {"empire_smelting_works",111,43, inputs = {}},
+ {"empire_toolsmithy",104,64, inputs = {log = 8}},
+ {"empire_weaponsmithy",113,40, inputs = {planks = 8}},
+ {"empire_armorsmithy",112,37, inputs = {cloth = 8}},
{"empire_farm",105,70},
{"empire_farm",101,71},
{"empire_farm",99,77},
=== modified file 'data/maps/Trident_of_Fire.wmf/scripting/initial_conditions.lua'
--- data/maps/Trident_of_Fire.wmf/scripting/initial_conditions.lua 2016-03-21 19:29:24 +0000
+++ data/maps/Trident_of_Fire.wmf/scripting/initial_conditions.lua 2016-11-01 14:41:18 +0000
@@ -149,7 +149,7 @@
soldiers = { [{0,0,0,0}] = 45 },
},
{ "barbarians_port", f_port.x, f_port.y},
- { "barbarians_shipyard", f_shipyard.x, f_shipyard.y, wares = {
+ { "barbarians_shipyard", f_shipyard.x, f_shipyard.y, inputs = {
blackwood = 10,
cloth = 4,
log = 2,
@@ -219,7 +219,7 @@
},
{ "empire_port", f_port.x, f_port.y},
{ "empire_shipyard", f_shipyard.x, f_shipyard.y,
- wares = {
+ inputs = {
cloth = 4,
log = 2,
planks = 10,
@@ -289,7 +289,7 @@
soldiers = { [{0,0,0,0}] = 45 },
},
{ "atlanteans_port", f_port.x, f_port.y},
- { "atlanteans_shipyard", f_shipyard.x, f_shipyard.y, wares = {
+ { "atlanteans_shipyard", f_shipyard.x, f_shipyard.y, inputs = {
planks = 10,
spidercloth = 4,
log = 2,
=== modified file 'data/scripting/infrastructure.lua'
--- data/scripting/infrastructure.lua 2016-03-01 09:31:36 +0000
+++ data/scripting/infrastructure.lua 2016-11-01 14:41:18 +0000
@@ -65,7 +65,7 @@
-- prefilled_buildings(wl.Game().players[1],
-- {"sentry", 57, 9}, -- Sentry completely full with soldiers
-- {"sentry", 57, 9, soldier={[{0,0,0,0}]=1}}, -- Sentry with one soldier
--- {"bakery", 55, 20, wares = {wheat=6, water=6}}, -- bakery with wares and workers
+-- {"bakery", 55, 20, inputs = {wheat=6, water=6}}, -- bakery with wares and workers
-- {"well", 52, 30}, -- a well with workers
-- )
--
@@ -77,9 +77,13 @@
--
-- wares
-- A table of (name,count) as expected by
--- :meth:`wl.map.ProductionSite.set_wares`. This is valid for
--- :class:`wl.map.ProductionSite` and :class:`wl.map.Warehouse` and
--- ignored otherwise.
+-- :meth:`wl.map.Warehouse.set_wares`. This is valid for
+-- :class:`wl.map.Warehouse` and ignored otherwise.
+-- inputs
+-- A table of (name,count) as expected by
+-- :meth:`wl.map.ProductionSite.set_inputs`. Inputs are wares or workers
+-- which are consumed by the building. This is valid for
+-- :class:`wl.map.ProductionSite` and ignored otherwise.
-- soldiers
-- A table of (soldier_descr,count) as expected by
-- :meth:`wl.map.HasSoldiers.set_soldiers`. If this is nil, the site
@@ -107,6 +111,7 @@
end
-- Fill with wares if this is requested
if bdescr.wares then b:set_wares(bdescr.wares) end
+ if bdescr.inputs then b:set_inputs(bdescr.inputs) end
end
end
=== modified file 'data/tribes/buildings/productionsites/atlanteans/barracks/init.lua'
--- data/tribes/buildings/productionsites/atlanteans/barracks/init.lua 2016-10-05 04:13:48 +0000
+++ data/tribes/buildings/productionsites/atlanteans/barracks/init.lua 2016-11-01 14:41:18 +0000
@@ -34,6 +34,9 @@
},
aihints = {
+ forced_after = 1000,
+ very_weak_ai_limit = 1,
+ weak_ai_limit = 3
},
working_positions = {
@@ -42,7 +45,8 @@
inputs = {
{ name = "tabard", amount = 8 },
- { name = "trident_light", amount = 8 }
+ { name = "trident_light", amount = 8 },
+ { name = "atlanteans_carrier", amount = 8 }
},
outputs = {
"atlanteans_soldier",
@@ -51,12 +55,11 @@
programs = {
work = {
-- TRANSLATORS: Completed/Skipped/Did not start recruiting soldier because ...
- -- TODO(GunChleoc): this should cost us a carrier as well, or maybe a recruit.
descname = _"recruiting soldier",
actions = {
"sleep=15000",
"return=skipped unless economy needs atlanteans_soldier",
- "consume=tabard trident_light",
+ "consume=tabard trident_light atlanteans_carrier",
"animate=working 15000",
"recruit=atlanteans_soldier"
}
=== modified file 'data/tribes/buildings/productionsites/barbarians/barracks/init.lua'
--- data/tribes/buildings/productionsites/barbarians/barracks/init.lua 2016-10-04 18:07:56 +0000
+++ data/tribes/buildings/productionsites/barbarians/barracks/init.lua 2016-11-01 14:41:18 +0000
@@ -33,6 +33,9 @@
},
aihints = {
+ forced_after = 1000,
+ very_weak_ai_limit = 1,
+ weak_ai_limit = 3
},
working_positions = {
@@ -40,7 +43,9 @@
},
inputs = {
- { name = "ax", amount = 8 }
+ { name = "ax", amount = 8 },
+ { name = "beer", amount = 8 },
+ { name = "barbarians_carrier", amount = 8 }
},
outputs = {
"barbarians_soldier",
@@ -50,11 +55,10 @@
work = {
-- TRANSLATORS: Completed/Skipped/Did not start recruiting soldier because ...
descname = _"recruiting soldier",
- -- TODO(GunChleoc): this should cost us a carrier as well, or maybe a recruit.
actions = {
"sleep=15000",
"return=skipped unless economy needs barbarians_soldier",
- "consume=ax",
+ "consume=ax beer barbarians_carrier",
"animate=working 15000",
"recruit=barbarians_soldier"
}
=== modified file 'data/tribes/buildings/productionsites/empire/barracks/init.lua'
--- data/tribes/buildings/productionsites/empire/barracks/init.lua 2016-10-04 18:07:56 +0000
+++ data/tribes/buildings/productionsites/empire/barracks/init.lua 2016-11-01 14:41:18 +0000
@@ -35,6 +35,9 @@
},
aihints = {
+ forced_after = 1000,
+ very_weak_ai_limit = 1,
+ weak_ai_limit = 3
},
working_positions = {
@@ -43,7 +46,8 @@
inputs = {
{ name = "armor_helmet", amount = 8 },
- { name = "spear_wooden", amount = 8 }
+ { name = "spear_wooden", amount = 8 },
+ { name = "empire_carrier", amount = 8 }
},
outputs = {
"empire_soldier",
@@ -52,12 +56,11 @@
programs = {
work = {
-- TRANSLATORS: Completed/Skipped/Did not start recruiting soldier because ...
- -- TODO(GunChleoc): this should cost us a carrier as well, or maybe a recruit.
descname = _"recruiting soldier",
actions = {
"sleep=15000",
"return=skipped unless economy needs empire_soldier",
- "consume=armor_helmet spear_wooden",
+ "consume=armor_helmet spear_wooden empire_carrier",
"animate=working 15000",
"recruit=empire_soldier"
}
=== modified file 'data/tribes/scripting/starting_conditions/atlanteans/fortified_village.lua'
--- data/tribes/scripting/starting_conditions/atlanteans/fortified_village.lua 2016-03-30 07:23:59 +0000
+++ data/tribes/scripting/starting_conditions/atlanteans/fortified_village.lua 2016-11-01 14:41:18 +0000
@@ -75,7 +75,7 @@
})
place_building_in_region(plr, "atlanteans_labyrinth", sf:region(11), {
- wares = {
+ inputs = {
atlanteans_bread = 4,
smoked_fish = 3,
smoked_meat = 3,
@@ -83,21 +83,21 @@
})
place_building_in_region(plr, "atlanteans_dungeon", sf:region(11), {
- wares = {atlanteans_bread = 4, smoked_fish = 3, smoked_meat = 3}
+ inputs = {atlanteans_bread = 4, smoked_fish = 3, smoked_meat = 3}
})
place_building_in_region(plr, "atlanteans_armorsmithy", sf:region(11), {
- wares = { coal=4, gold =4 }
+ inputs = { coal=4, gold =4 }
})
place_building_in_region(plr, "atlanteans_toolsmithy", sf:region(11), {
- wares = { log = 6 }
+ inputs = { log = 6 }
})
place_building_in_region(plr, "atlanteans_weaponsmithy", sf:region(11), {
- wares = { coal = 8, iron = 8 }
+ inputs = { coal = 8, iron = 8 }
})
place_building_in_region(plr, "atlanteans_sawmill", sf:region(11), {
- wares = { log = 1 }
+ inputs = { log = 1 }
})
end
}
=== modified file 'data/tribes/scripting/starting_conditions/atlanteans/trading_outpost.lua'
--- data/tribes/scripting/starting_conditions/atlanteans/trading_outpost.lua 2016-10-23 09:51:50 +0000
+++ data/tribes/scripting/starting_conditions/atlanteans/trading_outpost.lua 2016-11-01 14:41:18 +0000
@@ -84,7 +84,7 @@
})
place_building_in_region(player, "atlanteans_toolsmithy", sf:region(11), {
- wares = {
+ inputs = {
iron = 6,
log = 6,
spidercloth = 4
@@ -92,13 +92,13 @@
})
place_building_in_region(player, "atlanteans_sawmill", sf:region(11), {
- wares = {
+ inputs = {
log = 8
}
})
place_building_in_region(player, "atlanteans_hunters_house", sf:region(11), {
- wares = {}
+ inputs = {}
})
place_building_in_region(player, "atlanteans_tower", sf:region(13), {
=== modified file 'data/tribes/scripting/starting_conditions/barbarians/fortified_village.lua'
--- data/tribes/scripting/starting_conditions/barbarians/fortified_village.lua 2016-03-30 07:23:59 +0000
+++ data/tribes/scripting/starting_conditions/barbarians/fortified_village.lua 2016-11-01 14:41:18 +0000
@@ -68,7 +68,7 @@
})
place_building_in_region(plr, "barbarians_battlearena", sf:region(12), {
- wares = {
+ inputs = {
barbarians_bread = 8,
fish = 6,
meat = 6,
@@ -78,19 +78,19 @@
place_building_in_region(plr, "barbarians_trainingcamp", sf:region(12))
place_building_in_region(plr, "barbarians_helmsmithy", sf:region(12), {
- wares = { iron = 4, gold = 4 }
+ inputs = { iron = 4, gold = 4 }
})
place_building_in_region(plr, "barbarians_metal_workshop", sf:region(12), {
- wares = { iron = 8 },
+ inputs = { iron = 8 },
})
place_building_in_region(plr, "barbarians_ax_workshop", sf:region(12), {
- wares = { coal = 8 },
+ inputs = { coal = 8 },
})
place_building_in_region(plr, "barbarians_wood_hardener", sf:region(12), {
- wares = { log = 1 },
+ inputs = { log = 1 },
})
place_building_in_region(plr, "barbarians_lime_kiln", sf:region(12), {
- wares = { granite = 6, coal = 3 },
+ inputs = { granite = 6, coal = 3 },
})
end
}
=== modified file 'data/tribes/scripting/starting_conditions/barbarians/trading_outpost.lua'
--- data/tribes/scripting/starting_conditions/barbarians/trading_outpost.lua 2016-10-23 09:51:50 +0000
+++ data/tribes/scripting/starting_conditions/barbarians/trading_outpost.lua 2016-11-01 14:41:18 +0000
@@ -76,20 +76,20 @@
})
place_building_in_region(player, "barbarians_metal_workshop", sf:region(11), {
- wares = {
+ inputs = {
iron = 8,
log = 8
}
})
place_building_in_region(player, "barbarians_wood_hardener", sf:region(11), {
- wares = {
+ inputs = {
log = 8
}
})
place_building_in_region(player, "barbarians_hunters_hut", sf:region(11), {
- wares = {}
+ inputs = {}
})
place_building_in_region(player, "barbarians_tower", sf:region(13), {
=== modified file 'data/tribes/scripting/starting_conditions/empire/fortified_village.lua'
--- data/tribes/scripting/starting_conditions/empire/fortified_village.lua 2016-03-30 07:23:59 +0000
+++ data/tribes/scripting/starting_conditions/empire/fortified_village.lua 2016-11-01 14:41:18 +0000
@@ -74,7 +74,7 @@
})
place_building_in_region(plr, "empire_colosseum", sf:region(11), {
- wares = {
+ inputs = {
empire_bread = 8,
fish = 4,
meat = 4,
@@ -82,7 +82,7 @@
})
place_building_in_region(plr, "empire_trainingcamp", sf:region(11), {
- wares = {
+ inputs = {
fish = 2,
meat = 2,
armor_helmet = 2,
@@ -90,7 +90,7 @@
})
place_building_in_region(plr, "empire_armorsmithy", sf:region(11), {
- wares = {
+ inputs = {
gold = 4,
coal = 8,
cloth = 5,
@@ -98,20 +98,20 @@
})
place_building_in_region(plr, "empire_toolsmithy", sf:region(11), {
- wares = {
+ inputs = {
iron = 8,
}
})
place_building_in_region(plr, "empire_weaponsmithy", sf:region(11), {
- wares = {
+ inputs = {
coal = 4,
planks = 8,
}
})
place_building_in_region(plr, "empire_sawmill", sf:region(11), {
- wares = {
+ inputs = {
log = 1,
}
})
=== modified file 'data/tribes/scripting/starting_conditions/empire/trading_outpost.lua'
--- data/tribes/scripting/starting_conditions/empire/trading_outpost.lua 2016-10-23 09:51:50 +0000
+++ data/tribes/scripting/starting_conditions/empire/trading_outpost.lua 2016-11-01 14:41:18 +0000
@@ -82,20 +82,20 @@
})
place_building_in_region(player, "empire_toolsmithy", sf:region(11), {
- wares = {
+ inputs = {
iron = 8,
log = 8
}
})
place_building_in_region(player, "empire_sawmill", sf:region(11), {
- wares = {
+ inputs = {
log = 8
}
})
place_building_in_region(player, "empire_hunters_house", sf:region(11), {
- wares = {}
+ inputs = {}
})
place_building_in_region(player, "empire_tower", sf:region(13), {
=== modified file 'data/tribes/workers/atlanteans/soldier/init.lua'
--- data/tribes/workers/atlanteans/soldier/init.lua 2016-09-27 06:30:47 +0000
+++ data/tribes/workers/atlanteans/soldier/init.lua 2016-11-01 14:41:18 +0000
@@ -69,12 +69,6 @@
icon = dirname .. "menu.png",
vision_range = 2,
- buildcost = {
- atlanteans_carrier = 1,
- tabard = 1,
- trident_light = 1
- },
-
animations = animations,
-- Battle attributes - initial values and per level increase
=== modified file 'data/tribes/workers/barbarians/soldier/init.lua'
--- data/tribes/workers/barbarians/soldier/init.lua 2016-09-27 06:30:47 +0000
+++ data/tribes/workers/barbarians/soldier/init.lua 2016-11-01 14:41:18 +0000
@@ -69,11 +69,6 @@
icon = dirname .. "menu.png",
vision_range = 2,
- buildcost = {
- barbarians_carrier = 1,
- ax = 1
- },
-
animations = animations,
-- Battle attributes - initial values and per level increase
=== modified file 'data/tribes/workers/empire/soldier/init.lua'
--- data/tribes/workers/empire/soldier/init.lua 2016-09-27 06:30:47 +0000
+++ data/tribes/workers/empire/soldier/init.lua 2016-11-01 14:41:18 +0000
@@ -69,12 +69,6 @@
icon = dirname .. "menu.png",
vision_range = 2,
- buildcost = {
- empire_carrier = 1,
- armor_helmet = 1,
- spear_wooden = 1
- },
-
animations = animations,
-- Battle attributes - initial values and per level increase
=== modified file 'src/economy/CMakeLists.txt'
--- src/economy/CMakeLists.txt 2015-11-28 22:29:26 +0000
+++ src/economy/CMakeLists.txt 2016-11-01 14:41:18 +0000
@@ -42,6 +42,8 @@
warehousesupply.h
wares_queue.cc
wares_queue.h
+ workers_queue.cc
+ workers_queue.h
DEPENDS
base_exceptions
base_log
=== added file 'src/economy/workers_queue.cc'
--- src/economy/workers_queue.cc 1970-01-01 00:00:00 +0000
+++ src/economy/workers_queue.cc 2016-11-01 14:41:18 +0000
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2004, 2006-2011 by the Widelands Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "economy/workers_queue.h"
+
+#include "economy/economy.h"
+#include "economy/request.h"
+#include "io/fileread.h"
+#include "io/filewrite.h"
+#include "logic/editor_game_base.h"
+#include "logic/game.h"
+#include "logic/map_objects/tribes/tribe_descr.h"
+#include "logic/player.h"
+#include "map_io/map_object_loader.h"
+#include "map_io/map_object_saver.h"
+
+namespace Widelands {
+
+WorkersQueue::WorkersQueue
+ (PlayerImmovable & init_owner,
+ DescriptionIndex const init_worker,
+ uint8_t const init_max_size)
+ : owner_(init_owner), worker_type_(init_worker), max_capacity_(init_max_size),
+ capacity_(init_max_size), workers_(), request_(nullptr) {
+ // Can happen when loading a game
+ if (worker_type_ != INVALID_INDEX)
+ update_request();
+
+ // TODO(Notabilis): When set_filled() is called here, a later script call to set the worker of the
+ // building will fail. Not sure if this is a bug and/or a bug of this class. I don't really think so.
+}
+
+void WorkersQueue::set_capacity(Quantity capacity) {
+ assert(capacity <= max_capacity_);
+ if (capacity_ != capacity) {
+ capacity_ = capacity;
+ update_request();
+ }
+}
+
+void WorkersQueue::drop(Worker & worker) {
+ Game & game = dynamic_cast<Game&>(owner().egbase());
+
+ std::vector<Worker *>::iterator it =
+ std::find(workers_.begin(), workers_.end(), &worker);
+ if (it == workers_.end()) {
+ return;
+ }
+
+ workers_.erase(it);
+
+ worker.reset_tasks(game);
+ worker.start_task_leavebuilding(game, true);
+
+ update_request();
+}
+
+void WorkersQueue::remove_workers(Quantity amount) {
+// TODO(Notabilis): Check if there are any resources lost when removing workers
+// Especially if there are any worker-memory-objects left or too much is removed
+// I am not sure about how it should be done, so please check it
+
+ assert(get_filled() >= amount);
+
+ Game & game = dynamic_cast<Game&>(owner().egbase());
+
+ // Note: This might be slow (removing from start) but we want to consume
+ // the first worker in the queue first
+ for (Quantity i = 0; i < amount; i++) {
+ // Maybe: Remove from economy (I don't think this is required)
+ // owner_.economy().remove_workers(worker_type_, amount);
+ // Remove worker
+ (*(workers_.begin()))->schedule_destroy(game);
+ // Remove reference from list
+ workers_.erase(workers_.begin());
+ }
+
+ update_request();
+}
+
+Quantity WorkersQueue::get_filled() const {
+ return workers_.size();
+}
+
+void WorkersQueue::set_filled(Quantity amount) {
+
+ if (amount > max_capacity())
+ amount = max_capacity_;
+ const size_t currentAmount = get_filled();
+ if (amount == currentAmount)
+ return;
+
+ // Now adjust them
+ while (get_filled() < amount) {
+ // Create new worker
+ const TribeDescr& tribe = owner().tribe();
+ const WorkerDescr* worker_descr = tribe.get_worker_descr(worker_type_);
+ EditorGameBase& egbase = owner().egbase();
+ // Worker& create(EditorGameBase&, Player&, PlayerImmovable*, Coords) const;
+ Worker& w = worker_descr->create(egbase, owner(), nullptr, owner_.get_positions(egbase).front());
+ if (incorporate_worker(egbase, w) == -1) {
+ NEVER_HERE();
+ }
+ }
+ if (currentAmount > amount) {
+ // Drop workers
+ remove_workers(currentAmount - amount);
+ }
+}
+
+int WorkersQueue::incorporate_worker(EditorGameBase & egbase, Worker & w) {
+ if (w.get_location(egbase) != &(owner_)) {
+ if (get_filled() + 1 > max_capacity_) {
+ return -1;
+ }
+ w.set_location(&(owner_));
+ }
+
+ // Bind the worker into this house, hide him on the map
+ if (upcast(Game, game, &egbase)) {
+ w.start_task_idle(*game, 0, -1);
+ }
+
+ // Not quite sure about next line, the training sites are doing it inside add_worker().
+ // But that method is not available for ware/worker-queues.
+ // But anyway: Add worker to queue
+ workers_.push_back(&w);
+
+ // Make sure the request count is reduced or the request is deleted.
+ update_request();
+ return 0;
+}
+
+void WorkersQueue::remove_from_economy(Economy &) {
+ if (worker_type_ != INVALID_INDEX) {
+ // Setting request_->economy to nullptr will crash the game on load,
+ // but dropping the request and creating a new one in add_to_economy
+ // works fine
+ if (request_) {
+ delete request_;
+ request_ = nullptr;
+ }
+ // Removal of workers from the economy is not required, this is done by the building (or so)
+ }
+}
+
+void WorkersQueue::add_to_economy(Economy &) {
+ if (worker_type_ != INVALID_INDEX) {
+ update_request();
+ }
+}
+
+constexpr uint16_t kCurrentPacketVersion = 2;
+
+void WorkersQueue::write(FileWrite & fw, Game & game, MapObjectSaver & mos) {
+ // Adapted copy from WaresQueue
+ fw.unsigned_16(kCurrentPacketVersion);
+
+ // Owner and callback is not saved, but this should be obvious on load.
+ fw.c_string
+ (owner().tribe().get_worker_descr(worker_type_)->name().c_str());
+ fw.signed_32(max_capacity_);
+ fw.signed_32(capacity_);
+ if (request_) {
+ fw.unsigned_8(1);
+ request_->write(fw, game, mos);
+ } else {
+ fw.unsigned_8(0);
+ }
+ // Store references to the workers
+ fw.unsigned_32(workers_.size());
+ for (Worker * w : workers_) {
+ assert(mos.is_object_known(*w));
+ fw.unsigned_32(mos.get_object_file_index(*w));
+ }
+}
+
+
+void WorkersQueue::read(FileRead & fr, Game & game, MapObjectLoader & mol) {
+ // Adapted copy from WaresQueue
+ uint16_t const packet_version = fr.unsigned_16();
+ try {
+ if (packet_version == kCurrentPacketVersion) {
+ delete request_;
+ worker_type_ = owner().tribe().worker_index(fr.c_string());
+ max_capacity_ = fr.unsigned_32();
+ capacity_ = fr.signed_32();
+ assert(capacity_ <= max_capacity_);
+ if (fr.unsigned_8 ()) {
+ request_ =
+ new Request
+ (owner_,
+ 0,
+ WorkersQueue::request_callback,
+ wwWORKER);
+ request_->read(fr, game, mol);
+ } else {
+ request_ = nullptr;
+ }
+ size_t nr_workers = fr.unsigned_32();
+ assert(nr_workers <= capacity_);
+ assert(workers_.empty());
+ for (size_t i = 0; i < nr_workers; ++i) {
+ workers_.push_back(&mol.get<Worker>(fr.unsigned_32()));
+ }
+ assert(workers_.size() == nr_workers);
+ } else {
+ throw UnhandledVersionError("WorkersQueue", packet_version, kCurrentPacketVersion);
+ }
+ } catch (const GameDataError & e) {
+ throw GameDataError("workersqueue: %s", e.what());
+ }
+}
+
+void WorkersQueue::request_callback
+ (Game & game,
+ Request &,
+ DescriptionIndex const,
+ Worker * const worker,
+ PlayerImmovable & target)
+{
+ WorkersQueue & wq =
+ dynamic_cast<Building&>(target).workersqueue(worker->descr().worker_index());
+
+ assert(worker != nullptr);
+ assert(wq.workers_.size() < wq.max_capacity_);
+ assert(worker->descr().can_act_as(wq.worker_type_));
+
+ assert(worker->get_location(game) == &target);
+
+ // Update
+ wq.incorporate_worker(game, *worker);
+}
+
+void WorkersQueue::update_request() {
+ assert(worker_type_ != INVALID_INDEX);
+
+ if (workers_.size() < capacity_) {
+ if (!request_) {
+ request_ =
+ new Request
+ (owner_,
+ worker_type_,
+ WorkersQueue::request_callback,
+ wwWORKER);
+ // TODO(Notabilis): If it is possible to restrict the request to exactly the worker-type
+ // of this queue, do so. Currently there are sometimes improved workers (e.g. Chief Miner)
+ // coming to enter the building (where Miners are requested).
+ // This happened after I kicked the Chief Miner from its mine.
+ // The Master Miners in the headquarters however show no intentions of entering the building
+ }
+
+ request_->set_count(capacity_ - workers_.size());
+ } else if (workers_.size() >= capacity_) {
+ delete request_;
+ request_ = nullptr;
+
+ while (workers_.size() > capacity_) {
+ drop(**workers_.rbegin());
+ }
+ }
+}
+
+}
=== added file 'src/economy/workers_queue.h'
--- src/economy/workers_queue.h 1970-01-01 00:00:00 +0000
+++ src/economy/workers_queue.h 2016-11-01 14:41:18 +0000
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2004, 2006-2011 by the Widelands Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef WL_ECONOMY_WORKERS_QUEUE_H
+#define WL_ECONOMY_WORKERS_QUEUE_H
+
+#include <vector>
+#include "logic/map_objects/immovable.h"
+#include "logic/widelands.h"
+
+namespace Widelands {
+
+class EditorGameBase;
+class Game;
+class MapObjectLoader;
+struct MapObjectSaver;
+class Player;
+class Request;
+class Worker;
+
+/**
+ * Similar to WaresQueue but for workers.
+ */
+class WorkersQueue {
+public:
+
+ /**
+ * Default constructor
+ */
+ WorkersQueue(PlayerImmovable &, DescriptionIndex, uint8_t size);
+
+ /**
+ * The type of workers required here.
+ */
+ DescriptionIndex get_worker() const {return worker_type_;}
+
+ /**
+ * \return a list of workers that are currently in the building.
+ */
+ std::vector<Worker *> workers() const {return workers_;};
+
+ /**
+ * \return the maximum number of workers that this building can be
+ * configured to hold.
+ */
+ Quantity max_capacity() const {return max_capacity_;};
+
+ /**
+ * Is in [0, max_capacity()].
+ * \return the number of workers this building is configured to hold
+ * right now.
+ */
+ Quantity capacity() const {return capacity_;};
+
+ /**
+ * Sets the capacity for workers of this building.
+ * Has to be in [0, max_capacity()].
+ *
+ * New workers will be requested and old workers will be evicted
+ * as necessary.
+ */
+ void set_capacity(Quantity capacity);
+
+ void change_capacity(int32_t const difference) {
+ Quantity const old_capacity = capacity();
+ Quantity const new_capacity =
+ std::min
+ (static_cast<Quantity>
+ (std::max
+ (static_cast<int32_t>(old_capacity) + difference,
+ 0)),
+ max_capacity_);
+ if (old_capacity != new_capacity)
+ set_capacity(new_capacity);
+ }
+
+ /**
+ * Evict the given worker from the building immediately,
+ * without changing the building's capacity.
+ */
+ void drop(Worker &);
+
+ /**
+ * Removes the given amount of workers from the game.
+ * There have to be at least the given amount of workers in the queue.
+ */
+ void remove_workers(Quantity amount);
+
+ /**
+ * Returns the amount of workers currently in this building.
+ * @return The number of workers.
+ */
+ Quantity get_filled() const;
+
+ /**
+ * Adds new workers to the queue.
+ * The current capacity is not modified, the maximal capacity is respected.
+ * If the given amount is smaller than the current one, the workers will
+ * be removed silently.
+ * @param amount The number of workers which should be inside.
+ */
+ void set_filled(Quantity amount);
+
+ /**
+ * Add a new worker into this site.
+ * \return -1 if there is no space for him, 0 on success.
+ */
+ int incorporate_worker(EditorGameBase &, Worker &);
+
+ void remove_from_economy(Economy &);
+ void add_to_economy(Economy &);
+
+ Player & owner() const {return owner_.owner();}
+
+ void read (FileRead &, Game &, MapObjectLoader &);
+ void write(FileWrite &, Game &, MapObjectSaver &);
+
+private:
+
+ /**
+ * Callback when a request is fulfilled and a worker enters the queue.
+ */
+ static void request_callback
+ (Game &, Request &, DescriptionIndex, Worker *, PlayerImmovable &);
+
+ /**
+ * Updates the request for further workers.
+ * Should be called when a worker is added or removed.
+ */
+ void update_request();
+
+ PlayerImmovable & owner_;
+ /// Type of the stored worker
+ DescriptionIndex worker_type_;
+ /// Number of workers that fit into the queue maximum.
+ Quantity max_capacity_;
+ /// Number of workers that fit currently into the queue.
+ Quantity capacity_;
+
+ /// The workers currently in the queue
+ std::vector<Worker *> workers_;
+
+ /// Currently pending request
+ Request * request_;
+};
+
+}
+
+#endif // WL_ECONOMY_WORKERS_QUEUE_H
=== modified file 'src/logic/game.cc'
--- src/logic/game.cc 2016-10-15 16:36:12 +0000
+++ src/logic/game.cc 2016-11-01 14:41:18 +0000
@@ -717,6 +717,17 @@
*new CmdChangeTrainingOptions(get_gametime(), ts.owner().player_number(), ts, attr, val));
}
+void Game::send_player_drop_worker(Building& b, int32_t const ser) {
+ assert(ser != -1);
+ send_player_command(
+ *new CmdDropWorker(get_gametime(), b.owner().player_number(), b, ser));
+}
+
+void Game::send_player_change_worker_capacity(Building& b, DescriptionIndex worker_type, int16_t const val) {
+ send_player_command(
+ *new CmdChangeWorkerCapacity(get_gametime(), b.owner().player_number(), b, worker_type, val));
+}
+
void Game::send_player_drop_soldier(Building& b, int32_t const ser) {
assert(ser != -1);
send_player_command(*new CmdDropSoldier(get_gametime(), b.owner().player_number(), b, ser));
=== modified file 'src/logic/game.h'
--- src/logic/game.h 2016-08-04 15:49:05 +0000
+++ src/logic/game.h 2016-11-01 14:41:18 +0000
@@ -200,6 +200,8 @@
int32_t prio);
void send_player_set_ware_max_fill(PlayerImmovable&, DescriptionIndex index, uint32_t);
void send_player_change_training_options(TrainingSite&, TrainingAttribute, int32_t);
+ void send_player_drop_worker(Building&, int32_t serial);
+ void send_player_change_worker_capacity(Building&, DescriptionIndex worker_type, int16_t delta);
void send_player_drop_soldier(Building&, int32_t);
void send_player_change_soldier_capacity(Building&, int32_t);
void send_player_enemyflagaction(const Flag&, PlayerNumber, uint32_t count);
=== modified file 'src/logic/map_objects/tribes/building.cc'
--- src/logic/map_objects/tribes/building.cc 2016-10-26 19:43:40 +0000
+++ src/logic/map_objects/tribes/building.cc 2016-11-01 14:41:18 +0000
@@ -470,6 +470,10 @@
throw wexception("%s (%u) has no WaresQueue for %u", descr().name().c_str(), serial(), wi);
}
+WorkersQueue & Building::workersqueue(DescriptionIndex const wi) {
+ throw wexception("%s (%u) has no WorkersQueue for %u", descr().name().c_str(), serial(), wi);
+}
+
/*
===============
This function is called by workers in the buildingwork task.
=== modified file 'src/logic/map_objects/tribes/building.h'
--- src/logic/map_objects/tribes/building.h 2016-10-26 19:43:40 +0000
+++ src/logic/map_objects/tribes/building.h 2016-11-01 14:41:18 +0000
@@ -50,6 +50,7 @@
struct Message;
class TribeDescr;
class WaresQueue;
+class WorkersQueue;
class Building;
@@ -238,6 +239,9 @@
/// \returns the queue for a ware type or \throws WException.
virtual WaresQueue& waresqueue(DescriptionIndex);
+ /// \returns the queue for a worker type or \throws WException.
+ virtual WorkersQueue & workersqueue(DescriptionIndex);
+
virtual bool burn_on_destroy();
void destroy(EditorGameBase&) override;
=== modified file 'src/logic/map_objects/tribes/production_program.cc'
--- src/logic/map_objects/tribes/production_program.cc 2016-09-27 06:30:47 +0000
+++ src/logic/map_objects/tribes/production_program.cc 2016-11-01 14:41:18 +0000
@@ -32,6 +32,7 @@
#include "economy/economy.h"
#include "economy/flag.h"
#include "economy/wares_queue.h"
+#include "economy/workers_queue.h"
#include "graphic/graphic.h"
#include "helper.h"
#include "io/filesystem/layered_filesystem.h"
@@ -200,8 +201,9 @@
void ProductionProgram::parse_ware_type_group(char*& parameters,
WareTypeGroup& group,
const Tribes& tribes,
- const BillOfMaterials& inputs) {
- std::set<DescriptionIndex>::iterator last_insert_pos = group.first.end();
+ const BillOfMaterials& inputs,
+ const BillOfMaterials& input_workers) {
+ std::set<std::pair<DescriptionIndex, WareWorker>>::iterator last_insert_pos = group.first.end();
uint8_t count = 1;
uint8_t count_max = 0;
for (;;) {
@@ -211,25 +213,51 @@
char const terminator = *parameters;
*parameters = '\0';
- DescriptionIndex const ware_index = tribes.safe_ware_index(ware);
-
- for (BillOfMaterials::const_iterator input_it = inputs.begin(); input_it != inputs.end();
- ++input_it) {
- if (input_it == inputs.end()) {
- throw GameDataError("%s is not declared as an input (\"%s=<count>\" was not "
- "found in the [inputs] section)",
- ware, ware);
- } else if (input_it->first == ware_index) {
- count_max += input_it->second;
- break;
- }
- }
-
- if (group.first.size() && ware_index <= *group.first.begin())
- throw GameDataError("wrong order of ware types within group: ware type %s appears "
- "after ware type %s (fix order!)",
- ware, tribes.get_ware_descr(*group.first.begin())->name().c_str());
- last_insert_pos = group.first.insert(last_insert_pos, ware_index);
+ WareWorker type = wwWARE;
+
+ // Try as ware
+ DescriptionIndex ware_index = tribes.ware_index(ware);
+ if (tribes.ware_exists(ware_index)) {
+ for (BillOfMaterials::const_iterator input_it = inputs.begin();
+ input_it != inputs.end(); ++input_it) {
+ if (input_it == inputs.end()) {
+ throw GameDataError
+ ("%s is not declared as an input (\"%s=<count>\" was not "
+ "found in the [inputs] section)",
+ ware, ware);
+ } else if (input_it->first == ware_index) {
+ count_max += input_it->second;
+ break;
+ }
+ }
+ } else {
+ ware_index = tribes.worker_index(ware);
+ if (tribes.worker_exists(ware_index)) {
+ // It is a worker
+ type = wwWORKER;
+ for (BillOfMaterials::const_iterator input_it = input_workers.begin();
+ input_it != input_workers.end(); ++input_it) {
+ if (input_it == input_workers.end()) {
+ throw GameDataError
+ ("%s is not declared as an input (\"%s=<count>\" was not "
+ "found in the [inputs] section)",
+ ware, ware);
+ } else if (input_it->first == ware_index) {
+ count_max += input_it->second;
+ break;
+ }
+ }
+ } else {
+ throw GameDataError("Unknown ware or worker type \"%s\"", ware);
+ }
+ }
+
+ if (group.first.size() && ware_index <= group.first.begin()->first)
+ throw GameDataError
+ ("wrong order of ware types within group: ware type %s appears "
+ "after ware type %s (fix order!)",
+ ware, tribes.get_ware_descr(group.first.begin()->first)->name().c_str());
+ last_insert_pos = group.first.insert(last_insert_pos, std::make_pair(ware_index, type));
*parameters = terminator;
switch (terminator) {
case ':': {
@@ -331,7 +359,7 @@
const ProductionSiteDescr& descr,
const Tribes& tribes) {
try {
- parse_ware_type_group(parameters, group, tribes, descr.inputs());
+ parse_ware_type_group(parameters, group, tribes, descr.inputs(), descr.input_workers());
} catch (const WException& e) {
throw GameDataError("has ware_type1[,ware_type2[,...]][:N]: %s", e.what());
}
@@ -339,11 +367,14 @@
bool ProductionProgram::ActReturn::SiteHas::evaluate(const ProductionSite& ps) const {
uint8_t count = group.second;
for (WaresQueue* ip_queue : ps.warequeues()) {
- if (group.first.count(ip_queue->get_ware())) {
- uint8_t const filled = ip_queue->get_filled();
- if (count <= filled)
- return true;
- count -= filled;
+ for (auto it = group.first.begin(); it != group.first.end(); it++) {
+ if (it->first == ip_queue->get_ware() && it->second == wwWARE) {
+ uint8_t const filled = ip_queue->get_filled();
+ if (count <= filled)
+ return true;
+ count -= filled;
+ break;
+ }
}
}
return false;
@@ -351,8 +382,11 @@
std::string ProductionProgram::ActReturn::SiteHas::description(const Tribes& tribes) const {
std::vector<std::string> condition_list;
- for (const DescriptionIndex& temp_ware : group.first) {
- condition_list.push_back(tribes.get_ware_descr(temp_ware)->descname());
+ for (const auto& entry : group.first) {
+ if (entry.second == wwWARE)
+ condition_list.push_back(tribes.get_ware_descr(entry.first)->descname());
+ else
+ condition_list.push_back(tribes.get_worker_descr(entry.first)->descname());
}
std::string condition = i18n::localize_list(condition_list, i18n::ConcatenateWith::AND);
if (1 < group.second) {
@@ -373,8 +407,11 @@
std::string
ProductionProgram::ActReturn::SiteHas::description_negation(const Tribes& tribes) const {
std::vector<std::string> condition_list;
- for (const DescriptionIndex& temp_ware : group.first) {
- condition_list.push_back(tribes.get_ware_descr(temp_ware)->descname());
+ for (const auto& entry : group.first) {
+ if (entry.second == wwWARE)
+ condition_list.push_back(tribes.get_ware_descr(entry.first)->descname());
+ else
+ condition_list.push_back(tribes.get_worker_descr(entry.first)->descname());
}
std::string condition = i18n::localize_list(condition_list, i18n::ConcatenateWith::AND);
if (1 < group.second) {
@@ -760,7 +797,8 @@
try {
for (;;) {
consumed_wares_.resize(consumed_wares_.size() + 1);
- parse_ware_type_group(parameters, *consumed_wares_.rbegin(), tribes, descr.inputs());
+ parse_ware_type_group
+ (parameters, *consumed_wares_.rbegin(), tribes, descr.inputs(), descr.input_workers());
if (!*parameters)
break;
force_skip(parameters);
@@ -775,38 +813,76 @@
void ProductionProgram::ActConsume::execute(Game& game, ProductionSite& ps) const {
std::vector<WaresQueue*> const warequeues = ps.warequeues();
- size_t const nr_warequeues = warequeues.size();
- std::vector<uint8_t> consumption_quantities(nr_warequeues, 0);
+ std::vector<WorkersQueue*> const workerqueues = ps.workerqueues();
+ std::vector<uint8_t> consumption_quantities_wares(warequeues.size(), 0);
+ std::vector<uint8_t> consumption_quantities_workers(workerqueues.size(), 0);
Groups l_groups = consumed_wares_; // make a copy for local modification
// Iterate over all input queues and see how much we should consume from
// each of them.
- for (size_t i = 0; i < nr_warequeues; ++i) {
+ bool found;
+ for (size_t i = 0; i < warequeues.size(); ++i) {
DescriptionIndex const ware_type = warequeues[i]->get_ware();
uint8_t nr_available = warequeues[i]->get_filled();
- consumption_quantities[i] = 0;
+ consumption_quantities_wares[i] = 0;
// Iterate over all consume groups and see if they want us to consume
// any thing from the currently considered input queue.
- for (Groups::iterator it = l_groups.begin(); it != l_groups.end();)
- if (it->first.count(ware_type)) {
- if (it->second <= nr_available) {
- // There are enough wares of the currently considered type
- // to fulfill the requirements of the current group. We can
- // therefore erase the group.
- consumption_quantities[i] += it->second;
- nr_available -= it->second;
- it = l_groups.erase(it);
- // No increment here, erase moved next element to the position
- // pointed to by it.
- } else {
- consumption_quantities[i] += nr_available;
- it->second -= nr_available;
- ++it; // Now check if the next group includes this ware type.
- }
- } else
- ++it;
+ for (Groups::iterator it = l_groups.begin(); it != l_groups.end();) {
+ found = false;
+ for (auto it2 = it->first.begin(); it2 != it->first.end(); it2++) {
+ if (it2->first == ware_type && it2->second == wwWARE) {
+ found = true;
+ if (it->second <= nr_available) {
+ // There are enough wares of the currently considered type
+ // to fulfill the requirements of the current group. We can
+ // therefore erase the group.
+ consumption_quantities_wares[i] += it->second;
+ nr_available -= it->second;
+ it = l_groups.erase(it);
+ // No increment here, erase moved next element to the position
+ // pointed to by it.
+ } else {
+ consumption_quantities_wares[i] += nr_available;
+ it->second -= nr_available;
+ ++it; // Now check if the next group includes this ware type.
+ }
+ break;
+ }
+ }
+ // group does not request ware
+ if (!found)
+ ++it;
+ }
+ }
+
+ // Same for workers
+ for (size_t i = 0; i < workerqueues.size(); ++i) {
+ DescriptionIndex const worker_type = workerqueues[i]->get_worker();
+ uint8_t nr_available = workerqueues[i]->workers().size();
+ consumption_quantities_workers[i] = 0;
+
+ for (Groups::iterator it = l_groups.begin(); it != l_groups.end();) {
+ found = false;
+ for (auto it2 = it->first.begin(); it2 != it->first.end(); it2++) {
+ if (it2->first == worker_type && it2->second == wwWORKER) {
+ found = true;
+ if (it->second <= nr_available) {
+ consumption_quantities_workers[i] += it->second;
+ nr_available -= it->second;
+ it = l_groups.erase(it);
+ } else {
+ consumption_quantities_workers[i] += nr_available;
+ it->second -= nr_available;
+ ++it;
+ }
+ break;
+ }
+ }
+ if (!found)
+ ++it;
+ }
}
// "Did not start working because .... is/are missing"
@@ -818,8 +894,11 @@
assert(group.first.size());
std::vector<std::string> ware_list;
- for (const DescriptionIndex& ware : group.first) {
- ware_list.push_back(tribe.get_ware_descr(ware)->descname());
+ for (const auto& entry : group.first) {
+ if (entry.second == wwWARE)
+ ware_list.push_back(tribe.get_ware_descr(entry.first)->descname());
+ else
+ ware_list.push_back(tribe.get_worker_descr(entry.first)->descname());
}
std::string ware_string = i18n::localize_list(ware_list, i18n::ConcatenateWith::OR);
@@ -861,15 +940,20 @@
ps.set_production_result(result_string);
return ps.program_end(game, Failed);
- } else { // we fulfilled all consumption requirements
- for (size_t i = 0; i < nr_warequeues; ++i)
- if (uint8_t const q = consumption_quantities[i]) {
+ } else { // we fulfilled all consumption requirements
+ for (size_t i = 0; i < warequeues.size(); ++i)
+ if (uint8_t const q = consumption_quantities_wares[i]) {
assert(q <= warequeues[i]->get_filled());
warequeues[i]->set_filled(warequeues[i]->get_filled() - q);
// Update consumption statistic
ps.owner().ware_consumed(warequeues[i]->get_ware(), q);
}
+ for (size_t i = 0; i < workerqueues.size(); ++i)
+ if (uint8_t const q = consumption_quantities_workers[i]) {
+ assert(q <= workerqueues[i]->workers().size());
+ workerqueues[i]->remove_workers(q);
+ }
return ps.program_step(game);
}
}
=== modified file 'src/logic/map_objects/tribes/production_program.h'
--- src/logic/map_objects/tribes/production_program.h 2016-08-04 15:49:05 +0000
+++ src/logic/map_objects/tribes/production_program.h 2016-11-01 14:41:18 +0000
@@ -52,7 +52,7 @@
struct ProductionProgram {
/// A group of ware types with a count.
- using WareTypeGroup = std::pair<std::set<DescriptionIndex>, uint8_t>;
+ using WareTypeGroup = std::pair<std::set<std::pair<DescriptionIndex, WareWorker>>, uint8_t>;
using Groups = std::vector<WareTypeGroup>;
/// Can be executed on a ProductionSite.
@@ -100,7 +100,8 @@
static void parse_ware_type_group(char*& parameters,
WareTypeGroup& group,
const Tribes& tribes,
- const BillOfMaterials& inputs);
+ const BillOfMaterials& inputs,
+ const BillOfMaterials& input_workers);
/// Returns from the program.
///
=== modified file 'src/logic/map_objects/tribes/productionsite.cc'
--- src/logic/map_objects/tribes/productionsite.cc 2016-09-02 11:48:37 +0000
+++ src/logic/map_objects/tribes/productionsite.cc 2016-11-01 14:41:18 +0000
@@ -30,6 +30,7 @@
#include "economy/request.h"
#include "economy/ware_instance.h"
#include "economy/wares_queue.h"
+#include "economy/workers_queue.h"
#include "graphic/text_constants.h"
#include "logic/editor_game_base.h"
#include "logic/game.h"
@@ -115,7 +116,7 @@
if (amount < 1 || 255 < amount) {
throw wexception("amount is out of range 1 .. 255");
}
- DescriptionIndex const idx = egbase.tribes().ware_index(ware_name);
+ DescriptionIndex idx = egbase.tribes().ware_index(ware_name);
if (egbase.tribes().ware_exists(idx)) {
for (const auto& temp_inputs : inputs()) {
if (temp_inputs.first == idx) {
@@ -124,7 +125,17 @@
}
inputs_.push_back(WareAmount(idx, amount));
} else {
- throw wexception("tribes do not define a ware type with this name");
+ 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");
+ }
+ }
+ input_workers_.push_back(WareAmount(idx, amount));
+ } else {
+ throw wexception("tribes do not define a ware or worker type with this name");
+ }
}
} catch (const WException& e) {
throw wexception("input \"%s=%d\": %s", ware_name.c_str(), amount, e.what());
@@ -334,6 +345,22 @@
throw wexception("%s (%u) has no WaresQueue for %u", descr().name().c_str(), serial(), wi);
}
+WorkersQueue & ProductionSite::workersqueue(DescriptionIndex const wi) {
+ // Check for perfect match first
+ for (WorkersQueue * ip_queue : input_worker_queues_) {
+ if (ip_queue->get_worker() == wi) {
+ return *ip_queue;
+ }
+ }
+ // No perfect match, check for similar jobs
+ for (WorkersQueue * ip_queue : input_worker_queues_) {
+ if (owner().egbase().tribes().get_worker_descr(wi)->can_act_as(ip_queue->get_worker())) {
+ return *ip_queue;
+ }
+ }
+ throw wexception("%s (%u) has no WorkersQueue for %u", descr().name().c_str(), serial(), wi);
+}
+
/**
* Calculate statistic.
*/
@@ -406,6 +433,16 @@
for (WareRange i(inputs); i; ++i)
input_queues_[i.i] = new WaresQueue(*this, i.current->first, i.current->second);
+ const BillOfMaterials & input_workers = descr().input_workers();
+ input_worker_queues_.resize(input_workers.size());
+ for (WareRange i(input_workers); i; ++i) {
+ input_worker_queues_[i.i] =
+ new WorkersQueue
+ (*this,
+ i.current->first,
+ i.current->second);
+ }
+
// Request missing workers.
WorkingPosition* wp = working_positions_;
for (const auto& temp_wp : descr().working_positions()) {
@@ -431,6 +468,9 @@
for (WaresQueue* ip_queue : input_queues_) {
ip_queue->remove_from_economy(*old);
}
+ for (WorkersQueue * ip_queue : input_worker_queues_) {
+ ip_queue->remove_from_economy(*old);
+ }
}
Building::set_economy(e);
@@ -442,6 +482,9 @@
for (WaresQueue* ip_queue : input_queues_) {
ip_queue->add_to_economy(*e);
}
+ for (WorkersQueue * ip_queue : input_worker_queues_) {
+ ip_queue->add_to_economy(*e);
+ }
}
}
=== modified file 'src/logic/map_objects/tribes/productionsite.h'
--- src/logic/map_objects/tribes/productionsite.h 2016-08-04 15:49:05 +0000
+++ src/logic/map_objects/tribes/productionsite.h 2016-11-01 14:41:18 +0000
@@ -88,6 +88,9 @@
const BillOfMaterials& inputs() const {
return inputs_;
}
+ const BillOfMaterials & input_workers() const {
+ return input_workers_;
+ }
using Output = std::set<DescriptionIndex>;
const Output& output_ware_types() const {
return output_ware_types_;
@@ -119,6 +122,7 @@
private:
BillOfMaterials working_positions_;
BillOfMaterials inputs_;
+ BillOfMaterials input_workers_;
Output output_ware_types_;
Output output_worker_types_;
Programs programs_;
@@ -192,6 +196,7 @@
}
WaresQueue& waresqueue(DescriptionIndex) override;
+ WorkersQueue& workersqueue(DescriptionIndex) override;
void init(EditorGameBase&) override;
void cleanup(EditorGameBase&) override;
@@ -209,6 +214,12 @@
const InputQueues& warequeues() const {
return input_queues_;
}
+
+ using InputWorkerQueues = std::vector<WorkersQueue*>;
+ const InputWorkerQueues& workerqueues() const {
+ return input_worker_queues_;
+ }
+
const std::vector<Worker*>& workers() const;
bool can_start_working() const;
@@ -303,6 +314,7 @@
BillOfMaterials produced_wares_;
BillOfMaterials recruited_workers_;
InputQueues input_queues_; ///< input queues for all inputs
+ InputWorkerQueues input_worker_queues_; ///< input queues for workers
std::vector<bool> statistics_;
uint8_t last_stat_percent_;
// integer 0-10000000, to be divided by 10000 to get a percent, to avoid float (target range:
=== modified file 'src/logic/playercommand.cc'
--- src/logic/playercommand.cc 2016-08-04 15:49:05 +0000
+++ src/logic/playercommand.cc 2016-11-01 14:41:18 +0000
@@ -24,6 +24,7 @@
#include "base/wexception.h"
#include "economy/economy.h"
#include "economy/wares_queue.h"
+#include "economy/workers_queue.h"
#include "io/fileread.h"
#include "io/filewrite.h"
#include "io/streamwrite.h"
@@ -88,7 +89,9 @@
PLCMD_SHIP_EXPLORE = 27,
PLCMD_SHIP_CONSTRUCT = 28,
PLCMD_SHIP_SINK = 29,
- PLCMD_SHIP_CANCELEXPEDITION = 30
+ PLCMD_SHIP_CANCELEXPEDITION = 30,
+ PLCMD_DROPWORKER = 31,
+ PLCMD_CHANGEWORKERCAPACITY = 32
};
/*** class PlayerCommand ***/
@@ -127,6 +130,10 @@
return new CmdEnhanceBuilding(des);
case PLCMD_CHANGETRAININGOPTIONS:
return new CmdChangeTrainingOptions(des);
+ case PLCMD_DROPWORKER:
+ return new CmdDropWorker(des);
+ case PLCMD_CHANGEWORKERCAPACITY:
+ return new CmdChangeWorkerCapacity(des);
case PLCMD_DROPSOLDIER:
return new CmdDropSoldier(des);
case PLCMD_CHANGESOLDIERCAPACITY:
@@ -1431,6 +1438,121 @@
fw.unsigned_16(value);
}
+/*** class Cmd_DropWorker ***/
+
+CmdDropWorker::CmdDropWorker(StreamRead & des) :
+PlayerCommand (0, des.unsigned_8()) {
+ serial = des.unsigned_32(); // Serial of the building
+ worker = des.unsigned_32(); // Serial of worker
+}
+
+void CmdDropWorker::execute (Game & game) {
+ if (upcast(ProductionSite, building, game.objects().get_object(serial))) {
+ if (&building->owner() == game.get_player(sender())) {
+ if (upcast(Worker, w, game.objects().get_object(worker)))
+ building->workersqueue(w->descr().worker_index()).drop(*w);
+ }
+ }
+}
+
+void CmdDropWorker::serialize (StreamWrite & ser) {
+ ser.unsigned_8 (PLCMD_DROPWORKER);
+ ser.unsigned_8 (sender());
+ ser.unsigned_32(serial);
+ ser.unsigned_32(worker);
+}
+
+constexpr uint16_t kCurrentPacketVersionCmdDropWorker = 1;
+
+void CmdDropWorker::read(FileRead & fr, EditorGameBase & egbase, MapObjectLoader & mol) {
+ try {
+ const uint16_t packet_version = fr.unsigned_16();
+ if (packet_version == kCurrentPacketVersionCmdDropWorker) {
+ PlayerCommand::read(fr, egbase, mol);
+ serial = get_object_serial_or_zero<PlayerImmovable>(fr.unsigned_32(), mol);
+ worker = get_object_serial_or_zero<Worker>(fr.unsigned_32(), mol);
+ } else {
+ throw UnhandledVersionError("CmdDropWorker",
+ packet_version, kCurrentPacketVersionCmdDropWorker);
+ }
+ } catch (const WException & e) {
+ throw GameDataError("drop worker: %s", e.what());
+ }
+}
+
+void CmdDropWorker::write(FileWrite & fw, EditorGameBase & egbase, MapObjectSaver & mos) {
+ // First, write version
+ fw.unsigned_16(kCurrentPacketVersionCmdDropWorker);
+ // Write base classes
+ PlayerCommand::write(fw, egbase, mos);
+
+ // site serial
+ fw.unsigned_32(mos.get_object_file_index_or_zero(egbase.objects().get_object(serial)));
+ // worker serial
+ fw.unsigned_32(mos.get_object_file_index_or_zero(egbase.objects().get_object(worker)));
+}
+
+/*** Cmd_ChangeWorkerCapacity ***/
+
+CmdChangeWorkerCapacity::CmdChangeWorkerCapacity(StreamRead & des)
+ : PlayerCommand (0, des.unsigned_8()) {
+ serial = des.unsigned_32();
+ worker_type_ = des.signed_32();
+ val = des.signed_16();
+}
+
+void CmdChangeWorkerCapacity::execute (Game & game) {
+ if (upcast(ProductionSite, building, game.objects().get_object(serial))) {
+ if (&building->owner() == game.get_player(sender())) {
+ building->workersqueue(worker_type_).change_capacity(val);
+ }
+ }
+}
+
+void CmdChangeWorkerCapacity::serialize (StreamWrite & ser) {
+ ser.unsigned_8 (PLCMD_CHANGEWORKERCAPACITY);
+ ser.unsigned_8 (sender());
+ ser.unsigned_32(serial);
+ ser.signed_32(worker_type_);
+ ser.signed_16(val);
+}
+
+constexpr uint16_t kCurrentPacketVersionChangeWorkerCapacity = 1;
+
+void CmdChangeWorkerCapacity::read(FileRead & fr, EditorGameBase & egbase, MapObjectLoader & mol) {
+ try {
+ const uint16_t packet_version = fr.unsigned_16();
+ if (packet_version == kCurrentPacketVersionChangeWorkerCapacity) {
+ PlayerCommand::read(fr, egbase, mol);
+ serial = get_object_serial_or_zero<Building>(fr.unsigned_32(), mol);
+ worker_type_ = fr.signed_32();
+ val = fr.signed_16();
+ } else {
+ throw UnhandledVersionError("CmdChangeWorkerCapacity",
+ packet_version, kCurrentPacketVersionChangeWorkerCapacity);
+ }
+ } catch (const WException & e) {
+ throw GameDataError("change worker capacity: %s", e.what());
+ }
+}
+
+void CmdChangeWorkerCapacity::write(FileWrite & fw, EditorGameBase & egbase, MapObjectSaver & mos) {
+ // First, write version
+ fw.unsigned_16(kCurrentPacketVersionChangeWorkerCapacity);
+ // Write base classes
+ PlayerCommand::write(fw, egbase, mos);
+
+ // Now serial
+ fw.unsigned_32(mos.get_object_file_index_or_zero(egbase.objects().get_object(serial)));
+
+ // Now queue index
+ fw.signed_32(worker_type_);
+
+ // Now capacity
+ fw.signed_16(val);
+
+}
+
/*** class Cmd_DropSoldier ***/
CmdDropSoldier::CmdDropSoldier(StreamRead& des) : PlayerCommand(0, des.unsigned_8()) {
=== modified file 'src/logic/playercommand.h'
--- src/logic/playercommand.h 2016-08-04 15:49:05 +0000
+++ src/logic/playercommand.h 2016-11-01 14:41:18 +0000
@@ -674,6 +674,56 @@
int32_t value;
};
+struct CmdDropWorker : public PlayerCommand {
+ CmdDropWorker () : PlayerCommand(), serial(0), worker(0) {} // for savegames
+ CmdDropWorker
+ (const uint32_t t,
+ const int32_t p,
+ Building & b,
+ const int32_t init_worker)
+ : PlayerCommand(t, p), serial(b.serial()), worker(init_worker)
+ { }
+
+ // Write these commands to a file (for savegames)
+ void write(FileWrite &, EditorGameBase &, MapObjectSaver &) override;
+ void read (FileRead &, EditorGameBase &, MapObjectLoader &) override;
+
+ QueueCommandTypes id() const override {return QueueCommandTypes::kDropWorker;}
+
+ CmdDropWorker(StreamRead &);
+
+ void execute (Game &) override;
+ void serialize (StreamWrite &) override;
+
+private:
+ Serial serial;
+ Serial worker;
+};
+
+struct CmdChangeWorkerCapacity : public PlayerCommand {
+ CmdChangeWorkerCapacity () : PlayerCommand(), serial(0), worker_type_(), val(0) {} // for savegames
+ CmdChangeWorkerCapacity
+ (const uint32_t t, const int32_t p, Building & b, DescriptionIndex worker_type, const int16_t delta)
+ : PlayerCommand(t, p), serial(b.serial()), worker_type_(worker_type), val(delta)
+ { }
+
+ // Write these commands to a file (for savegames)
+ void write(FileWrite &, EditorGameBase &, MapObjectSaver &) override;
+ void read (FileRead &, EditorGameBase &, MapObjectLoader &) override;
+
+ QueueCommandTypes id() const override {return QueueCommandTypes::kChangeWorkerCapacity;}
+
+ CmdChangeWorkerCapacity (StreamRead &);
+
+ void execute (Game &) override;
+ void serialize (StreamWrite &) override;
+
+private:
+ Serial serial;
+ DescriptionIndex worker_type_;
+ int16_t val;
+};
+
struct CmdDropSoldier : public PlayerCommand {
CmdDropSoldier() : PlayerCommand(), serial(0), soldier(0) {
} // for savegames
=== modified file 'src/logic/queue_cmd_factory.cc'
--- src/logic/queue_cmd_factory.cc 2016-08-04 15:49:05 +0000
+++ src/logic/queue_cmd_factory.cc 2016-11-01 14:41:18 +0000
@@ -46,6 +46,10 @@
return *new CmdEnhanceBuilding();
case QueueCommandTypes::kBulldoze:
return *new CmdBulldoze();
+ case QueueCommandTypes::kDropWorker:
+ return *new CmdDropWorker();
+ case QueueCommandTypes::kChangeWorkerCapacity:
+ return *new CmdChangeWorkerCapacity();
case QueueCommandTypes::kChangeTrainingOptions:
return *new CmdChangeTrainingOptions();
case QueueCommandTypes::kDropSoldier:
=== modified file 'src/logic/queue_cmd_ids.h'
--- src/logic/queue_cmd_ids.h 2016-08-04 15:49:05 +0000
+++ src/logic/queue_cmd_ids.h 2016-11-01 14:41:18 +0000
@@ -72,6 +72,9 @@
kMilitarysiteSetSoldierPreference, // 26
+ kDropWorker,
+ kChangeWorkerCapacity,
+
kSinkShip = 121,
kShipCancelExpedition,
kPortStartExpedition,
=== modified file 'src/map_io/map_buildingdata_packet.cc'
--- src/map_io/map_buildingdata_packet.cc 2016-08-04 15:49:05 +0000
+++ src/map_io/map_buildingdata_packet.cc 2016-11-01 14:41:18 +0000
@@ -30,6 +30,7 @@
#include "economy/request.h"
#include "economy/warehousesupply.h"
#include "economy/wares_queue.h"
+#include "economy/workers_queue.h"
#include "io/fileread.h"
#include "io/filewrite.h"
#include "logic/editor_game_base.h"
@@ -685,6 +686,19 @@
}
}
+ nr_queues = fr.unsigned_16();
+ assert(!productionsite.input_worker_queues_.size());
+ for (uint16_t i = 0; i < nr_queues; ++i) {
+ WorkersQueue * wq = new WorkersQueue(productionsite, INVALID_INDEX, 0);
+ wq->read(fr, game, mol);
+
+ if (!game.tribes().worker_exists(wq->get_worker())) {
+ delete wq;
+ } else {
+ productionsite.input_worker_queues_.push_back(wq);
+ }
+ }
+
uint16_t const stats_size = fr.unsigned_16();
productionsite.statistics_.resize(stats_size);
for (uint32_t i = 0; i < productionsite.statistics_.size(); ++i)
@@ -1129,6 +1143,11 @@
for (uint16_t i = 0; i < input_queues_size; ++i)
productionsite.input_queues_[i]->write(fw, game, mos);
+ const uint16_t input_worker_queues_size = productionsite.input_worker_queues_.size();
+ fw.unsigned_16(input_worker_queues_size);
+ for (uint16_t i = 0; i < input_worker_queues_size; ++i)
+ productionsite.input_worker_queues_[i]->write(fw, game, mos);
+
const uint16_t statistics_size = productionsite.statistics_.size();
fw.unsigned_16(statistics_size);
for (uint32_t i = 0; i < statistics_size; ++i)
=== modified file 'src/scripting/lua_map.cc'
--- src/scripting/lua_map.cc 2016-10-16 09:31:42 +0000
+++ src/scripting/lua_map.cc 2016-11-01 14:41:18 +0000
@@ -27,6 +27,7 @@
#include "base/macros.h"
#include "base/wexception.h"
#include "economy/wares_queue.h"
+#include "economy/workers_queue.h"
#include "graphic/graphic.h"
#include "logic/findimmovable.h"
#include "logic/map_objects/checkstep.h"
@@ -148,11 +149,13 @@
using SoldiersMap = std::map<SoldierMapDescr, Widelands::Quantity>;
using WaresMap = std::map<Widelands::DescriptionIndex, Widelands::Quantity>;
+using InputMap = std::map<std::pair<Widelands::DescriptionIndex, Widelands::WareWorker>, Widelands::Quantity>;
using WorkersMap = std::map<Widelands::DescriptionIndex, Widelands::Quantity>;
using SoldierAmount = std::pair<SoldierMapDescr, Widelands::Quantity>;
using WorkerAmount = std::pair<Widelands::DescriptionIndex, Widelands::Quantity>;
using PlrInfluence = std::pair<Widelands::PlayerNumber, Widelands::MilitaryInfluence>;
using WaresSet = std::set<Widelands::DescriptionIndex>;
+using InputSet = std::set<std::pair<Widelands::DescriptionIndex, Widelands::WareWorker>>;
using WorkersSet = std::set<Widelands::DescriptionIndex>;
using SoldiersList = std::vector<Widelands::Soldier*>;
@@ -227,6 +230,105 @@
PARSERS(worker, Worker)
#undef PARSERS
+// Versions of the above macros which accept wares and workers
+InputSet parse_get_input_arguments(
+ lua_State* L, const TribeDescr& tribe, bool* return_number) {
+ /* takes either "all", a name or an array of names */
+ int32_t nargs = lua_gettop(L);
+ if (nargs != 2)
+ report_error(L, "Wrong number of arguments to get_inputs!");
+ *return_number = false;
+ InputSet rv;
+ if (lua_isstring(L, 2)) {
+ std::string what = luaL_checkstring(L, -1);
+ if (what == "all") {
+ for (const DescriptionIndex& i : tribe.wares()) {
+ rv.insert(std::make_pair(i, wwWARE));
+ }
+ for (const DescriptionIndex& i : tribe.workers()) {
+ rv.insert(std::make_pair(i, wwWORKER));
+ }
+ } else {
+ /* Only one item requested */
+ DescriptionIndex index = tribe.ware_index(what);
+ if (tribe.has_ware(index)) {
+ rv.insert(std::make_pair(index, wwWARE));
+ *return_number = true;
+ } else {
+ index = tribe.worker_index(what);
+ if (tribe.has_worker(index)) {
+ rv.insert(std::make_pair(index, wwWORKER));
+ *return_number = true;
+ } else {
+ report_error(L, "Invalid input: <%s>", what.c_str());
+ }
+ }
+ }
+ } else {
+ /* array of names */
+ luaL_checktype(L, 2, LUA_TTABLE);
+ lua_pushnil(L);
+ while (lua_next(L, 2) != 0) {
+ std::string what = luaL_checkstring(L, -1);
+ DescriptionIndex index = tribe.ware_index(what);
+ if (tribe.has_ware(index)) {
+ rv.insert(std::make_pair(index, wwWARE));
+ } else {
+ index = tribe.worker_index(what);
+ if (tribe.has_worker(index)) {
+ rv.insert(std::make_pair(index, wwWORKER));
+ } else {
+ report_error(L, "Invalid input: <%s>", what.c_str());
+ }
+ }
+ lua_pop(L, 1);
+ }
+ }
+ return rv;
+}
+
+InputMap parse_set_input_arguments(lua_State* L, const TribeDescr& tribe) {
+ int32_t nargs = lua_gettop(L);
+ if (nargs != 2 && nargs != 3)
+ report_error(L, "Wrong number of arguments to set_inputs!");
+ InputMap rv;
+ if (nargs == 3) {
+ /* name amount */
+ std::string what = luaL_checkstring(L, 2);
+ DescriptionIndex index = tribe.ware_index(what);
+ if (tribe.has_ware(index)) {
+ rv.insert(std::make_pair(std::make_pair(index, wwWARE), luaL_checkuint32(L, 3)));
+ } else {
+ index = tribe.worker_index(what);
+ if (tribe.has_worker(index)) {
+ rv.insert(std::make_pair(std::make_pair(index, wwWORKER), luaL_checkuint32(L, 3)));
+ } else {
+ report_error(L, "Invalid input: <%s>", what.c_str());
+ }
+ }
+ } else {
+ /* array of (name, count) */
+ luaL_checktype(L, 2, LUA_TTABLE);
+ lua_pushnil(L);
+ while (lua_next(L, 2) != 0) {
+ std::string what = luaL_checkstring(L, -2);
+ DescriptionIndex index = tribe.ware_index(what);
+ if (tribe.has_ware(index)) {
+ rv.insert(std::make_pair(std::make_pair(index, wwWARE), luaL_checkuint32(L, -1)));
+ } else {
+ index = tribe.worker_index(what);
+ if (tribe.has_worker(index)) {
+ rv.insert(std::make_pair(std::make_pair(index, wwWORKER), luaL_checkuint32(L, -1)));
+ } else {
+ report_error(L, "Invalid input: <%s>", what.c_str());
+ }
+ }
+ lua_pop(L, 1);
+ }
+ }
+ return rv;
+}
+
WaresMap count_wares_on_flag_(Flag& f, const Tribes& tribes) {
WaresMap rv;
@@ -1998,8 +2100,12 @@
for (const auto& group : program.consumed_wares()) {
lua_pushuint32(L, ++counter);
lua_newtable(L);
- for (const DescriptionIndex& ware_index : group.first) {
- lua_pushstring(L, get_egbase(L).tribes().get_ware_descr(ware_index)->name());
+ for (const auto& entry : group.first) {
+ const DescriptionIndex& index = entry.first;
+ if (entry.second == wwWARE)
+ lua_pushstring(L, get_egbase(L).tribes().get_ware_descr(index)->name());
+ else
+ lua_pushstring(L, get_egbase(L).tribes().get_worker_descr(index)->name());
lua_pushuint32(L, group.second);
lua_settable(L, -3);
}
@@ -4065,15 +4171,15 @@
*/
const char LuaProductionSite::className[] = "ProductionSite";
const MethodType<LuaProductionSite> LuaProductionSite::Methods[] = {
- METHOD(LuaProductionSite, set_wares),
- METHOD(LuaProductionSite, get_wares),
+ METHOD(LuaProductionSite, set_inputs),
+ METHOD(LuaProductionSite, get_inputs),
METHOD(LuaProductionSite, get_workers),
METHOD(LuaProductionSite, set_workers),
{nullptr, nullptr},
};
const PropertyType<LuaProductionSite> LuaProductionSite::Properties[] = {
PROP_RO(LuaProductionSite, valid_workers),
- PROP_RO(LuaProductionSite, valid_wares),
+ PROP_RO(LuaProductionSite, valid_inputs),
{nullptr, nullptr, nullptr},
};
@@ -4083,7 +4189,7 @@
==========================================================
*/
// documented in parent class
-int LuaProductionSite::get_valid_wares(lua_State* L) {
+int LuaProductionSite::get_valid_inputs(lua_State* L) {
EditorGameBase& egbase = get_egbase(L);
ProductionSite* ps = get(L, egbase);
@@ -4094,6 +4200,12 @@
lua_pushuint32(L, input_ware.second);
lua_rawset(L, -3);
}
+ for (const auto& input_worker : ps->descr().input_workers()) {
+ const WorkerDescr* descr = egbase.tribes().get_worker_descr(input_worker.first);
+ lua_pushstring(L, descr->name());
+ lua_pushuint32(L, input_worker.second);
+ lua_rawset(L, -3);
+ }
return 1;
}
@@ -4108,62 +4220,88 @@
LUA METHODS
==========================================================
*/
-
// documented in parent class
-int LuaProductionSite::set_wares(lua_State* L) {
+int LuaProductionSite::set_inputs(lua_State* L) {
ProductionSite* ps = get(L, get_egbase(L));
const TribeDescr& tribe = ps->owner().tribe();
- WaresMap setpoints = parse_set_wares_arguments(L, tribe);
+ InputMap setpoints = parse_set_input_arguments(L, tribe);
- WaresSet valid_wares;
+ InputSet valid_inputs;
for (const auto& input_ware : ps->descr().inputs()) {
- valid_wares.insert(input_ware.first);
+ valid_inputs.insert(std::make_pair(input_ware.first, wwWARE));
+ }
+ for (const auto& input_worker : ps->descr().input_workers()) {
+ valid_inputs.insert(std::make_pair(input_worker.first, wwWORKER));
}
for (const auto& sp : setpoints) {
- if (!valid_wares.count(sp.first)) {
- report_error(L, "<%s> can't be stored in this building: %s!",
- tribe.get_ware_descr(sp.first)->name().c_str(), ps->descr().name().c_str());
- }
- WaresQueue& wq = ps->waresqueue(sp.first);
- if (sp.second > wq.get_max_size()) {
- report_error(
- L, "Not enough space for %u items, only for %i", sp.second, wq.get_max_size());
- }
- wq.set_filled(sp.second);
+ if (!valid_inputs.count(sp.first)) {
+ if (sp.first.second == wwWARE)
+ report_error(L, "<%s> can't be stored in this building: %s!",
+ tribe.get_ware_descr(sp.first.first)->name().c_str(), ps->descr().name().c_str());
+ else
+ report_error(L, "<%s> can't be stored in this building: %s!",
+ tribe.get_worker_descr(sp.first.first)->name().c_str(), ps->descr().name().c_str());
+ }
+ if (sp.first.second == wwWARE) {
+ WaresQueue& wq = ps->waresqueue(sp.first.first);
+ if (sp.second > wq.get_max_size()) {
+ report_error(
+ L, "Not enough space for %u items, only for %i", sp.second, wq.get_max_size());
+ }
+ wq.set_filled(sp.second);
+ } else {
+ assert(sp.first.second == wwWORKER);
+ WorkersQueue& wq = ps->workersqueue(sp.first.first);
+ if (sp.second > wq.max_capacity()) {
+ report_error(
+ L, "Not enough space for %u workers, only for %i", sp.second, wq.max_capacity());
+ }
+ wq.set_filled(sp.second);
+ }
}
return 0;
}
// documented in parent class
-int LuaProductionSite::get_wares(lua_State* L) {
+int LuaProductionSite::get_inputs(lua_State* L) {
ProductionSite* ps = get(L, get_egbase(L));
const TribeDescr& tribe = ps->owner().tribe();
bool return_number = false;
- WaresSet wares_set = parse_get_wares_arguments(L, tribe, &return_number);
+ InputSet input_set = parse_get_input_arguments(L, tribe, &return_number);
- WaresSet valid_wares;
+ InputSet valid_inputs;
for (const auto& input_ware : ps->descr().inputs()) {
- valid_wares.insert(input_ware.first);
+ valid_inputs.insert(std::make_pair(input_ware.first, wwWARE));
+ }
+ for (const auto& input_worker : ps->descr().input_workers()) {
+ valid_inputs.insert(std::make_pair(input_worker.first, wwWORKER));
}
- if (wares_set.size() == tribe.get_nrwares()) // Want all returned
- wares_set = valid_wares;
+ if (input_set.size() == tribe.get_nrwares() + tribe.get_nrworkers()) // Want all returned
+ input_set = valid_inputs;
if (!return_number)
lua_newtable(L);
- for (const Widelands::DescriptionIndex& ware : wares_set) {
+ for (const auto& input : input_set) {
uint32_t cnt = 0;
- if (valid_wares.count(ware))
- cnt = ps->waresqueue(ware).get_filled();
+ if (valid_inputs.count(input)) {
+ if (input.second == wwWARE)
+ cnt = ps->waresqueue(input.first).get_filled();
+ else
+ cnt = ps->workersqueue(input.first).get_filled();
+ }
if (return_number) { // this is the only thing the customer wants to know
lua_pushuint32(L, cnt);
break;
} else {
- lua_pushstring(L, tribe.get_ware_descr(ware)->name());
+ if (input.second == wwWARE)
+ lua_pushstring(L, tribe.get_ware_descr(input.first)->name());
+ else
+ lua_pushstring(L, tribe.get_worker_descr(input.first)->name());
lua_pushuint32(L, cnt);
lua_rawset(L, -3);
}
=== modified file 'src/scripting/lua_map.h'
--- src/scripting/lua_map.h 2016-08-04 15:49:05 +0000
+++ src/scripting/lua_map.h 2016-11-01 14:41:18 +0000
@@ -1033,15 +1033,15 @@
/*
* Properties
*/
- int get_valid_wares(lua_State* L);
+ int get_valid_inputs(lua_State* L);
int get_valid_workers(lua_State* L);
/*
* Lua Methods
*/
- int get_wares(lua_State* L);
+ int get_inputs(lua_State* L);
int get_workers(lua_State* L);
- int set_wares(lua_State* L);
+ int set_inputs(lua_State* L);
int set_workers(lua_State* L);
/*
=== modified file 'src/wui/CMakeLists.txt'
--- src/wui/CMakeLists.txt 2016-10-25 07:07:14 +0000
+++ src/wui/CMakeLists.txt 2016-11-01 14:41:18 +0000
@@ -179,11 +179,15 @@
productionsitewindow.cc
productionsitewindow.h
shipwindow.cc
+ workercapacitycontrol.cc
+ workercapacitycontrol.h
shipwindow.h
soldiercapacitycontrol.cc
soldiercapacitycontrol.h
soldierlist.cc
soldierlist.h
+ workerpanel.cc
+ workerpanel.h
stock_menu.cc
stock_menu.h
story_message_box.cc
=== modified file 'src/wui/productionsitewindow.cc'
--- src/wui/productionsitewindow.cc 2016-08-04 15:49:05 +0000
+++ src/wui/productionsitewindow.cc 2016-11-01 14:41:18 +0000
@@ -31,6 +31,7 @@
#include "ui_basic/tabpanel.h"
#include "ui_basic/textarea.h"
#include "wui/waresqueuedisplay.h"
+#include "wui/workerpanel.h"
using Widelands::ProductionSite;
@@ -97,6 +98,12 @@
(ngettext("Worker", "Workers", productionsite().descr().nr_working_positions())));
update_worker_table();
}
+
+ // Input workers. Make "worker-worker"-panel first to match order in training sites
+ const std::vector<Widelands::WorkersQueue*>& workerqueues = ps.workerqueues();
+ for (uint32_t i = 0; i < workerqueues.size(); ++i)
+ add_worker_panel(get_tabs(), parent, ps, i, *workerqueues[i]);
+
}
void ProductionSiteWindow::think() {
=== added file 'src/wui/workercapacitycontrol.cc'
--- src/wui/workercapacitycontrol.cc 1970-01-01 00:00:00 +0000
+++ src/wui/workercapacitycontrol.cc 2016-11-01 14:41:18 +0000
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2002-2004, 2006-2010 by the Widelands Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "wui/workercapacitycontrol.h"
+
+#include "economy/workers_queue.h"
+#include "graphic/graphic.h"
+#include "logic/player.h"
+#include "ui_basic/button.h"
+#include "ui_basic/radiobutton.h"
+#include "wui/interactive_gamebase.h"
+
+/**
+ * Widget to control the capacity of \ref ProductionBuilding
+ * Adapted copy of \ref SoldierCapacityControl
+ */
+struct WorkerCapacityControl : UI::Box {
+ WorkerCapacityControl
+ (UI::Panel * parent, InteractiveGameBase & igb,
+ Widelands::Building & building,
+ Widelands::DescriptionIndex index,
+ Widelands::WorkersQueue & workers);
+
+protected:
+ void think() override;
+
+private:
+ void change_worker_capacity(int16_t delta);
+ void click_decrease();
+ void click_increase();
+
+ InteractiveGameBase & igbase_;
+ Widelands::Building & building_;
+ Widelands::DescriptionIndex index_;
+ Widelands::WorkersQueue & workers_;
+
+ UI::Button decrease_;
+ UI::Button increase_;
+ UI::Textarea value_;
+};
+
+WorkerCapacityControl::WorkerCapacityControl
+ (UI::Panel * parent, InteractiveGameBase & igb,
+ Widelands::Building & building,
+ Widelands::DescriptionIndex index,
+ Widelands::WorkersQueue & workers)
+:
+Box(parent, 0, 0, Horizontal),
+igbase_(igb),
+building_(building),
+index_(index),
+workers_(workers),
+decrease_
+ (this, "decrease", 0, 0, 32, 32,
+ g_gr->images().get("images/ui_basic/but4.png"),
+ g_gr->images().get("images/wui/buildings/menu_down_train.png"), _("Decrease capacity")),
+increase_
+ (this, "increase", 0, 0, 32, 32,
+ g_gr->images().get("images/ui_basic/but4.png"),
+ g_gr->images().get("images/wui/buildings/menu_up_train.png"), _("Increase capacity")),
+value_(this, "199", UI::Align::kCenter)
+{
+ decrease_.sigclicked.connect(boost::bind(&WorkerCapacityControl::click_decrease, boost::ref(*this)));
+ increase_.sigclicked.connect(boost::bind(&WorkerCapacityControl::click_increase, boost::ref(*this)));
+
+ add(new UI::Textarea(this, _("Capacity")), UI::Align::kHCenter);
+ add(&decrease_, UI::Align::kHCenter);
+ add(&value_, UI::Align::kHCenter);
+ add(&increase_, UI::Align::kHCenter);
+
+ decrease_.set_repeating(true);
+ increase_.set_repeating(true);
+
+ set_thinks(true);
+}
+
+void WorkerCapacityControl::think()
+{
+ uint32_t const capacity = workers_.capacity();
+ char buffer[sizeof("4294967295")];
+
+ sprintf(buffer, "%2u", capacity);
+ value_.set_text(buffer);
+
+ bool const can_act = igbase_.can_act(building_.owner().player_number());
+ decrease_.set_enabled(can_act && 0 < capacity);
+ increase_.set_enabled(can_act && workers_.max_capacity() > capacity);
+}
+
+void WorkerCapacityControl::change_worker_capacity(int16_t delta)
+{
+ igbase_.game().send_player_change_worker_capacity(building_, workers_.get_worker(), delta);
+}
+
+void WorkerCapacityControl::click_decrease()
+{
+ change_worker_capacity(-1);
+}
+
+void WorkerCapacityControl::click_increase()
+{
+ change_worker_capacity(1);
+}
+
+UI::Panel * create_worker_capacity_control
+ (UI::Panel & parent, InteractiveGameBase & igb,
+ Widelands::Building & building,
+ Widelands::DescriptionIndex index,
+ Widelands::WorkersQueue & workers)
+{
+ return new WorkerCapacityControl(&parent, igb, building, index, workers);
+}
=== added file 'src/wui/workercapacitycontrol.h'
--- src/wui/workercapacitycontrol.h 1970-01-01 00:00:00 +0000
+++ src/wui/workercapacitycontrol.h 2016-11-01 14:41:18 +0000
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2002-2004, 2006-2013 by the Widelands Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef WL_WUI_WORKERCAPACITYCONTROL_H
+#define WL_WUI_WORKERCAPACITYCONTROL_H
+
+#include "logic/widelands.h"
+
+class InteractiveGameBase;
+
+namespace UI {
+class Panel;
+}
+
+namespace Widelands {
+class Building;
+class WorkersQueue;
+}
+
+UI::Panel * create_worker_capacity_control
+ (UI::Panel & parent,
+ InteractiveGameBase & igb,
+ Widelands::Building & building,
+ Widelands::DescriptionIndex index,
+ Widelands::WorkersQueue & workers);
+
+#endif // end of include guard: WL_WUI_WORKERCAPACITYCONTROL_H
=== added file 'src/wui/workerpanel.cc'
--- src/wui/workerpanel.cc 1970-01-01 00:00:00 +0000
+++ src/wui/workerpanel.cc 2016-11-01 14:41:18 +0000
@@ -0,0 +1,475 @@
+/*
+ * Copyright (C) 2002-2016 by the Widelands Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "wui/workerpanel.h"
+
+#include <boost/bind.hpp>
+#include <boost/format.hpp>
+
+#include "base/macros.h"
+#include "economy/workers_queue.h"
+#include "graphic/font_handler1.h"
+#include "graphic/graphic.h"
+#include "graphic/rendertarget.h"
+#include "logic/map_objects/tribes/building.h"
+#include "logic/map_objects/tribes/soldier.h"
+#include "logic/map_objects/tribes/worker.h"
+#include "logic/map_objects/tribes/worker_descr.h"
+#include "logic/player.h"
+#include "logic/widelands.h"
+#include "ui_basic/box.h"
+#include "ui_basic/button.h"
+#include "ui_basic/tabpanel.h"
+#include "wlapplication.h"
+#include "wui/interactive_gamebase.h"
+#include "wui/workercapacitycontrol.h"
+
+
+using Widelands::Worker;
+using Widelands::WorkersQueue;
+using Widelands::DescriptionIndex;
+using Widelands::Building;
+
+namespace {
+
+constexpr uint32_t kMaxColumns = 6;
+constexpr uint32_t kAnimateSpeed = 300; ///< in pixels per second
+constexpr uint32_t kIconBorder = 2;
+
+} // namespace
+
+/**
+ * Iconic representation of workers in a queue.
+ * Adapted copy of \ref SoldierPanel
+ */
+struct WorkerPanel : UI::Panel {
+ using WorkerFn = boost::function<void (const Worker *)>;
+
+ WorkerPanel(UI::Panel & parent, Widelands::EditorGameBase & egbase, WorkersQueue & workers_queue);
+
+ Widelands::EditorGameBase & egbase() const {return egbase_;}
+
+ void think() override;
+ void draw(RenderTarget &) override;
+
+ void set_mouseover(const WorkerFn & fn);
+ void set_click(const WorkerFn & fn);
+
+protected:
+ void handle_mousein(bool inside) override;
+ bool handle_mousemove(uint8_t state, int32_t x, int32_t y, int32_t xdiff, int32_t ydiff) override;
+ bool handle_mousepress(uint8_t btn, int32_t x, int32_t y) override;
+
+private:
+ Vector2i calc_pos(uint32_t row, uint32_t col) const;
+ const Worker * find_worker(int32_t x, int32_t y) const;
+
+ struct Icon {
+ Widelands::OPtr<Worker> worker;
+ uint32_t row;
+ uint32_t col;
+ Vector2i pos;
+
+ /**
+ * Experience when last drawing
+ */
+ uint32_t cache_experience;
+ // The experience is currently not shown on top of the icon,
+ // this remains as further work.
+ };
+
+ Widelands::EditorGameBase & egbase_;
+ WorkersQueue& workers_;
+
+ WorkerFn mouseover_fn_;
+ WorkerFn click_fn_;
+
+ std::vector<Icon> icons_;
+
+ uint32_t rows_;
+ uint32_t cols_;
+
+ uint32_t icon_width_;
+ uint32_t icon_height_;
+
+ int32_t last_animate_time_;
+};
+
+WorkerPanel::WorkerPanel
+ (UI::Panel & parent,
+ Widelands::EditorGameBase & gegbase,
+ WorkersQueue & workers_queue)
+:
+Panel(&parent, 0, 0, 0, 0),
+egbase_(gegbase),
+workers_(workers_queue),
+last_animate_time_(0)
+{
+ {
+ // Fetch the icon of some worker and retrieve its size
+ assert(egbase_.tribes().nrtribes() > 0);
+ assert(egbase_.tribes().nrworkers() > 0);
+ // ID=0 should be the first worker created and anyone is good enough
+ const Image * some_icon = egbase_.tribes().get_worker_descr(0)->icon();
+ icon_width_ = some_icon->width();
+ icon_height_ = some_icon->height();
+ }
+ icon_width_ += 2 * kIconBorder;
+ icon_height_ += 2 * kIconBorder;
+
+ Widelands::Quantity maxcapacity = workers_.max_capacity();
+ if (maxcapacity <= kMaxColumns) {
+ cols_ = maxcapacity;
+ rows_ = 1;
+ } else {
+ cols_ = kMaxColumns;
+ rows_ = (maxcapacity + cols_ - 1) / cols_;
+ }
+
+ set_size(cols_ * icon_width_, rows_ * icon_height_);
+ set_desired_size(cols_ * icon_width_, rows_ * icon_height_);
+ set_thinks(true);
+
+ // Initialize the icons
+ uint32_t row = 0;
+ uint32_t col = 0;
+ for (Worker * worker : workers_.workers()) {
+ Icon icon;
+ icon.worker = worker;
+ icon.row = row;
+ icon.col = col;
+ icon.pos = calc_pos(row, col);
+ icons_.push_back(icon);
+
+ if (++col >= cols_) {
+ col = 0;
+ row++;
+ }
+ }
+}
+
+/**
+ * Set the callback function that indicates which worker the mouse is over.
+ */
+void WorkerPanel::set_mouseover(const WorkerPanel::WorkerFn & fn)
+{
+ mouseover_fn_ = fn;
+}
+
+/**
+ * Set the callback function that is called when a worker is clicked.
+ */
+void WorkerPanel::set_click(const WorkerPanel::WorkerFn & fn)
+{
+ click_fn_ = fn;
+}
+
+void WorkerPanel::think()
+{
+ bool changes = false;
+ uint32_t capacity = workers_.capacity();
+
+ // Update worker list and target row/col:
+ std::vector<Worker *> workerlist = workers_.workers();
+ std::vector<uint32_t> row_occupancy;
+ row_occupancy.resize(rows_);
+
+ // First pass: check whether existing icons are still valid, and compact them
+ for (uint32_t idx = 0; idx < icons_.size(); ++idx) {
+ Icon & icon = icons_[idx];
+ Worker * worker = icon.worker.get(egbase());
+ if (worker) {
+ std::vector<Worker *>::iterator it = std::find(workerlist.begin(), workerlist.end(), worker);
+ if (it != workerlist.end())
+ workerlist.erase(it);
+ else
+ worker = nullptr;
+ }
+
+ if (!worker) {
+ icons_.erase(icons_.begin() + idx);
+ idx--;
+ changes = true;
+ continue;
+ }
+
+ while
+ (icon.row &&
+ (row_occupancy[icon.row] >= kMaxColumns ||
+ icon.row * kMaxColumns + row_occupancy[icon.row] >= capacity))
+ icon.row--;
+
+ icon.col = row_occupancy[icon.row]++;
+ }
+
+ // Second pass: add new workers
+ while (!workerlist.empty()) {
+ Icon icon;
+ icon.worker = workerlist.back();
+ workerlist.pop_back();
+ icon.row = 0;
+ while (row_occupancy[icon.row] >= kMaxColumns)
+ icon.row++;
+ icon.col = row_occupancy[icon.row]++;
+ icon.pos = calc_pos(icon.row, icon.col);
+
+ // Let workers slide in from the right border
+ icon.pos.x = get_w();
+
+ std::vector<Icon>::iterator insertpos = icons_.begin();
+
+ for (std::vector<Icon>::iterator icon_iter = icons_.begin();
+ icon_iter != icons_.end();
+ ++icon_iter) {
+
+ if (icon_iter->row <= icon.row)
+ insertpos = icon_iter + 1;
+
+ icon.pos.x = std::max<int32_t>(icon.pos.x, icon_iter->pos.x + icon_width_);
+ }
+
+ icon.cache_experience = 0;
+
+ icons_.insert(insertpos, icon);
+ changes = true;
+ }
+
+ // Third pass: animate icons
+ int32_t curtime = SDL_GetTicks();
+ int32_t dt = std::min(std::max(curtime - last_animate_time_, 0), 1000);
+ int32_t maxdist = dt * kAnimateSpeed / 1000;
+ last_animate_time_ = curtime;
+
+ for (Icon& icon : icons_) {
+ Vector2i goal = calc_pos(icon.row, icon.col);
+ Vector2i dp = goal - icon.pos;
+
+ dp.x = std::min(std::max(dp.x, -maxdist), maxdist);
+ dp.y = std::min(std::max(dp.y, -maxdist), maxdist);
+
+ if (dp.x != 0 || dp.y != 0)
+ changes = true;
+
+ icon.pos += dp;
+
+ // Check whether experience of the worker has changed
+ Worker * worker = icon.worker.get(egbase());
+ uint32_t experience = worker->get_current_experience();
+
+ if (experience != icon.cache_experience) {
+ icon.cache_experience = experience;
+ changes = true;
+ }
+ }
+
+ if (changes) {
+ Vector2i mousepos = get_mouse_position();
+ mouseover_fn_(find_worker(mousepos.x, mousepos.y));
+ }
+}
+
+void WorkerPanel::draw(RenderTarget & dst)
+{
+ // Fill a region matching the current site capacity with black
+ uint32_t capacity = workers_.capacity();
+ uint32_t fullrows = capacity / kMaxColumns;
+
+ if (fullrows)
+ dst.fill_rect(Rectf(0, 0, get_w(), icon_height_ * fullrows), RGBAColor(0, 0, 0, 0));
+ if (capacity % kMaxColumns)
+ dst.fill_rect(Rectf(0, icon_height_ * fullrows,
+ icon_width_ * (capacity % kMaxColumns), icon_height_), RGBAColor(0, 0, 0, 0));
+
+ // Draw icons
+ for (const Icon& icon : icons_) {
+ const Worker * worker = icon.worker.get(egbase());
+ if (!worker)
+ continue;
+
+ // This should probably call something similar to soldier::draw_info_icon()
+ dst.blit(icon.pos.cast<float>() + Vector2f(kIconBorder, kIconBorder), worker->descr().icon());
+ // TODO(Notabilis): Maybe print experience on top of icon
+ }
+}
+
+Vector2i WorkerPanel::calc_pos(uint32_t row, uint32_t col) const
+{
+ return Vector2i(col * icon_width_, row * icon_height_);
+}
+
+/**
+ * Return the worker (if any) at the given coordinates.
+ */
+const Worker * WorkerPanel::find_worker(int32_t x, int32_t y) const
+{
+ for (const Icon& icon : icons_) {
+ Rectf r(icon.pos, icon_width_, icon_height_);
+ if (r.contains(Vector2i(x, y))) {
+ return icon.worker.get(egbase());
+ }
+ }
+
+ return nullptr;
+}
+
+void WorkerPanel::handle_mousein(bool inside)
+{
+ if (!inside && mouseover_fn_)
+ mouseover_fn_(nullptr);
+}
+
+bool WorkerPanel::handle_mousemove
+ (uint8_t /* state */,
+ int32_t x,
+ int32_t y,
+ int32_t /* xdiff */,
+ int32_t /* ydiff */)
+{
+ if (mouseover_fn_)
+ mouseover_fn_(find_worker(x, y));
+ return true;
+}
+
+bool WorkerPanel::handle_mousepress(uint8_t btn, int32_t x, int32_t y)
+{
+ if (btn == SDL_BUTTON_LEFT) {
+ if (click_fn_) {
+ if (const Worker * worker = find_worker(x, y))
+ click_fn_(worker);
+ }
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * List of workers
+ */
+struct WorkerList : UI::Box {
+ WorkerList
+ (UI::Panel & parent,
+ InteractiveGameBase & igb,
+ Building & building,
+ DescriptionIndex index,
+ WorkersQueue & workers_queue);
+
+private:
+ void mouseover(const Worker * worker);
+ void eject(const Worker * worker);
+
+ InteractiveGameBase& igbase_;
+ Building& building_;
+ DescriptionIndex index_;
+ WorkersQueue & workers_queue_;
+ WorkerPanel workerpanel_;
+ UI::Textarea infotext_;
+};
+
+WorkerList::WorkerList
+ (UI::Panel & parent,
+ InteractiveGameBase & igb,
+ Building & building,
+ DescriptionIndex index,
+ WorkersQueue & workers_queue)
+:
+UI::Box(&parent, 0, 0, UI::Box::Vertical),
+
+igbase_(igb),
+building_(building),
+index_(index),
+workers_queue_(workers_queue),
+workerpanel_(*this, igb.egbase(), workers_queue),
+infotext_(this, _("Click worker to send away"))
+{
+ add(&workerpanel_, UI::Align::kHCenter);
+
+ add_space(2);
+
+ add(&infotext_, UI::Align::kHCenter);
+
+ workerpanel_.set_mouseover(boost::bind(&WorkerList::mouseover, this, _1));
+ workerpanel_.set_click(boost::bind(&WorkerList::eject, this, _1));
+
+ // We don't want translators to translate this twice, so it's a bit involved.
+ int w = UI::g_fh1->render(
+ as_uifont((boost::format("%s ") // We need some extra space to fix bug 724169
+ /** TRANSLATORS: Workertype (current experience / required experience) */
+ % (boost::format(_("%1$s (Exp: %2$u/%3$u)"))
+ % 8 % 8 % 8)).str()))->width();
+ uint32_t maxtextwidth = std::max(w,
+ UI::g_fh1->render(as_uifont(_("Click worker to send away")))->width());
+ set_min_desired_breadth(maxtextwidth + 4);
+
+ UI::Box * buttons = new UI::Box(this, 0, 0, UI::Box::Horizontal);
+
+ buttons->add_inf_space();
+ buttons->add
+ (create_worker_capacity_control(*buttons, igb, building, index_, workers_queue_),
+ UI::Align::kRight);
+
+ add(buttons, UI::Align::kHCenter, true);
+}
+
+void WorkerList::mouseover(const Worker * worker)
+{
+ if (!worker) {
+ infotext_.set_text(_("Click worker to send away"));
+ return;
+ }
+
+ if (worker->needs_experience()) {
+ infotext_.set_text(
+ (boost::format(_("%1$s (Exp: %2$u/%3$u)"))
+ % worker->descr().descname()
+ % worker->get_current_experience()
+ % worker->descr().get_needed_experience()
+ ).str()
+ );
+ } else {
+ infotext_.set_text(
+ (boost::format(_("%1$s (Exp: -/-)"))
+ % worker->descr().descname()
+ ).str());
+ }
+}
+
+void WorkerList::eject(const Worker * worker)
+{
+ uint32_t const capacity_min = 0;
+ bool can_act = igbase_.can_act(building_.owner().player_number());
+ bool over_min = capacity_min < workers_queue_.workers().size();
+
+ if (can_act && over_min)
+ igbase_.game().send_player_drop_worker(building_, worker->serial());
+}
+
+void add_worker_panel
+ (UI::TabPanel * parent,
+ InteractiveGameBase & igb,
+ Widelands::Building & building,
+ DescriptionIndex index,
+ Widelands::WorkersQueue & workers_queue)
+{
+
+ const Widelands::WorkerDescr * desc = igb.egbase().tribes().get_worker_descr(workers_queue.get_worker());
+ parent->add(desc->name(), desc->icon(),
+ new WorkerList(*parent, igb, building, index, workers_queue),
+ desc->descname());
+}
=== added file 'src/wui/workerpanel.h'
--- src/wui/workerpanel.h 1970-01-01 00:00:00 +0000
+++ src/wui/workerpanel.h 2016-11-01 14:41:18 +0000
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2002-2004, 2006-2013 by the Widelands Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef WL_WUI_WORKERPANEL_H
+#define WL_WUI_WORKERPANEL_H
+
+#include "logic/widelands.h"
+
+class InteractiveGameBase;
+
+namespace UI {
+class TabPanel;
+}
+
+namespace Widelands {
+class Building;
+class WorkersQueue;
+}
+
+void add_worker_panel
+ (UI::TabPanel * parent,
+ InteractiveGameBase & igb,
+ Widelands::Building & building,
+ Widelands::DescriptionIndex index,
+ Widelands::WorkersQueue & workers_queue);
+
+#endif // end of include guard: WL_WUI_WORKERPANEL_H
Follow ups
-
Re: [Merge] lp:~widelands-dev/widelands/casern_workersqueue into lp:widelands
From: GunChleoc, 2016-12-05
-
Re: [Merge] lp:~widelands-dev/widelands/casern_workersqueue into lp:widelands
From: Notabilis, 2016-12-04
-
Re: [Merge] lp:~widelands-dev/widelands/casern_workersqueue into lp:widelands
From: GunChleoc, 2016-12-04
-
Re: [Merge] lp:~widelands-dev/widelands/casern_workersqueue into lp:widelands
From: Notabilis, 2016-12-04
-
[Merge] lp:~widelands-dev/widelands/casern_workersqueue into lp:widelands
From: bunnybot, 2016-12-04
-
[Merge] lp:~widelands-dev/widelands/casern_workersqueue into lp:widelands
From: bunnybot, 2016-11-28
-
[Merge] lp:~widelands-dev/widelands/casern_workersqueue into lp:widelands
From: bunnybot, 2016-11-28
-
Re: [Merge] lp:~widelands-dev/widelands/casern_workersqueue into lp:widelands
From: Notabilis, 2016-11-28
-
Re: [Merge] lp:~widelands-dev/widelands/casern_workersqueue into lp:widelands
From: SirVer, 2016-11-28
-
Re: [Merge] lp:~widelands-dev/widelands/casern_workersqueue into lp:widelands
From: GunChleoc, 2016-11-28
-
[Merge] lp:~widelands-dev/widelands/casern_workersqueue into lp:widelands
From: bunnybot, 2016-11-26
-
[Merge] lp:~widelands-dev/widelands/casern_workersqueue into lp:widelands
From: bunnybot, 2016-11-26
-
[Merge] lp:~widelands-dev/widelands/casern_workersqueue into lp:widelands
From: bunnybot, 2016-11-23
-
Re: [Merge] lp:~widelands-dev/widelands/casern_workersqueue into lp:widelands
From: Notabilis, 2016-11-23
-
Re: [Merge] lp:~widelands-dev/widelands/casern_workersqueue into lp:widelands
From: Notabilis, 2016-11-23
-
[Merge] lp:~widelands-dev/widelands/casern_workersqueue into lp:widelands
From: bunnybot, 2016-11-15
-
[Merge] lp:~widelands-dev/widelands/casern_workersqueue into lp:widelands
From: bunnybot, 2016-11-14
-
Re: [Merge] lp:~widelands-dev/widelands/casern_workersqueue into lp:widelands
From: GunChleoc, 2016-11-14
-
[Merge] lp:~widelands-dev/widelands/casern_workersqueue into lp:widelands
From: bunnybot, 2016-11-14
-
[Merge] lp:~widelands-dev/widelands/casern_workersqueue into lp:widelands
From: bunnybot, 2016-11-13
-
Re: [Merge] lp:~widelands-dev/widelands/casern_workersqueue into lp:widelands
From: Notabilis, 2016-11-13
-
[Merge] lp:~widelands-dev/widelands/casern_workersqueue into lp:widelands
From: bunnybot, 2016-11-12
-
Re: [Merge] lp:~widelands-dev/widelands/casern_workersqueue into lp:widelands
From: GunChleoc, 2016-11-12
-
Re: [Merge] lp:~widelands-dev/widelands/casern_workersqueue into lp:widelands
From: Notabilis, 2016-11-12
-
Re: [Merge] lp:~widelands-dev/widelands/casern_workersqueue into lp:widelands
From: GunChleoc, 2016-11-12
-
Re: [Merge] lp:~widelands-dev/widelands/casern_workersqueue into lp:widelands
From: Notabilis, 2016-11-09
-
Re: [Merge] lp:~widelands-dev/widelands/casern_workersqueue into lp:widelands
From: GunChleoc, 2016-11-09
-
Re: [Merge] lp:~widelands-dev/widelands/casern_workersqueue into lp:widelands
From: GunChleoc, 2016-11-09
-
Re: [Merge] lp:~widelands-dev/widelands/casern_workersqueue into lp:widelands
From: Notabilis, 2016-11-08
-
Re: [Merge] lp:~widelands-dev/widelands/casern_workersqueue into lp:widelands
From: GunChleoc, 2016-11-08
-
Re: [Merge] lp:~widelands-dev/widelands/casern_workersqueue into lp:widelands
From: SirVer, 2016-11-08
-
Re: [Merge] lp:~widelands-dev/widelands/casern_workersqueue into lp:widelands
From: Notabilis, 2016-11-07
-
[Merge] lp:~widelands-dev/widelands/casern_workersqueue into lp:widelands
From: bunnybot, 2016-11-07
-
[Merge] lp:~widelands-dev/widelands/casern_workersqueue into lp:widelands
From: bunnybot, 2016-11-07
-
Re: [Merge] lp:~widelands-dev/widelands/casern_workersqueue into lp:widelands
From: GunChleoc, 2016-11-03
-
[Merge] lp:~widelands-dev/widelands/casern_workersqueue into lp:widelands
From: bunnybot, 2016-11-03
-
[Merge] lp:~notabilis27/widelands/casern into lp:widelands
From: bunnybot, 2016-11-02