Merge lp:~widelands-dev/widelands/bug-1675179-lua-economy into lp:widelands

Proposed by GunChleoc
Status: Merged
Merged at revision: 8330
Proposed branch: lp:~widelands-dev/widelands/bug-1675179-lua-economy
Merge into: lp:widelands
Diff against target: 321 lines (+248/-1)
4 files modified
src/scripting/lua_map.cc (+145/-1)
src/scripting/lua_map.h (+50/-0)
test/maps/lua_testsuite.wmf/scripting/geconomy.lua (+52/-0)
test/maps/lua_testsuite.wmf/scripting/init.lua (+1/-0)
To merge this branch: bzr merge lp:~widelands-dev/widelands/bug-1675179-lua-economy
Reviewer Review Type Date Requested Status
Klaus Halfmann compile, review, test Approve
Review via email: mp+321008@code.launchpad.net

Commit message

Added a new object LuaEconomy to LuaMap.

Description of the change

This is for the Empire scenario scripting. The new functions aren't used anywhere in the game yet, so the unit test will have to cover the testing for now.

To post a comment you must log in.
Revision history for this message
Klaus Halfmann (klaus-halfmann) wrote :

Id like to put in some comments, so we can do the review along them.
Please be patient ...

Revision history for this message
Klaus Halfmann (klaus-halfmann) wrote :

Where does a lua programme "find" that flag to fetch the economy from?
would it not be easier to get some "default" economy for the headquarter
(like you do in that test :-). Or some warehouses?

Can we have a test for a bare flag? Is there an ecomony for every
disconnected flag?

For the tutotrial this may be enough, but is this stable enoough
for general usage. Can wee add some test where the economies change?

See my inline comments, too

will compile this and let the test run.

Revision history for this message
GunChleoc (gunchleoc) wrote :

Regarding your comments: The economy number is the array index of the economies owned by a player. So, we need both the player number and he economy index to identify it.

When an economy is merged with another economy or its last flag deleted, the vector gets reshuffled. I don't expect it to be a problem with the persistence code though, because the game is paused while its being saved, so economies can't disappear on us then.

Outside of saveloading, we reference the Economy object itself and not the economy & player numbers, so the worst thing that it can do to us is to become nil. Becoming nil can happen to any map object though, so this is something that a scenario Lua script will need to test for when necessary.

> Where does a lua programme "find" that flag to fetch the economy from?

It doesn't. Whoever writes a scenario will need to tell it which flag to use.

> would it not be easier to get some "default" economy for the headquarter
(like you do in that test :-). Or some warehouses?

No, because a scenario author might need to be able to access all economies, not just the "default" one. Also, if the headquarters gets conquered and the player still has 2 other economies with warehouses in them, which one will become the "default" economy?

> Can we have a test for a bare flag? Is there an ecomony for every
disconnected flag?

A disconnected flag has an economy that consists of that flag only. Unless you connect it to a warehouse, it will be an economy without a warehouse, but it's still an economy.

Revision history for this message
Klaus Halfmann (klaus-halfmann) wrote :

Mhh, ./regression_test.py does not execute geconomy.lua? is there something missing?

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 2064. State: passed. Details: https://travis-ci.org/widelands/widelands/builds/215195452.
Appveyor build 1899. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_bug_1675179_lua_economy-1899.

Revision history for this message
GunChleoc (gunchleoc) wrote :

Why do you think that?

It is definitely executed when I run

    ./widelands --scenario=test/maps/lua_testsuite.wmf --datadir_for_testing=.

because it blew up in my face quite a few times until I fixed everything up. And it's registered in the same init.lua that ./regression_test.py will run.

From Travis:

    test/maps/lua_testsuite.wmf/scripting/test_lua_in_game.lua ...
      Running Widelands ... done.
    ok

If you want to make sure that it's run, add a nonsense assert to make it fail. I just did that and it definitely fails then, so it is run.

Revision history for this message
Klaus Halfmann (klaus-halfmann) wrote :

OK, checke that testsuite and foud how it is used.
updated again and run the tests agin, all fine.

Sorry fo taking so long.

@bunnybot merge

review: Approve (compile, review, test)
Revision history for this message
GunChleoc (gunchleoc) wrote :

Don't apologize and thanks for reviewing! :)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/scripting/lua_map.cc'
2--- src/scripting/lua_map.cc 2017-01-30 14:40:12 +0000
3+++ src/scripting/lua_map.cc 2017-03-29 16:02:45 +0000
4@@ -3342,6 +3342,134 @@
5 */
6
7 /* RST
8+Economy
9+-------
10+.. class:: LuaEconomy
11+
12+ Provides access to an economy. A player can have multiple economies;
13+ you can get an economy from a :class:`Flag`.
14+*/
15+const char LuaEconomy::className[] = "Economy";
16+const MethodType<LuaEconomy> LuaEconomy::Methods[] = {
17+ METHOD(LuaEconomy, ware_target_quantity),
18+ METHOD(LuaEconomy, worker_target_quantity),
19+ METHOD(LuaEconomy, set_ware_target_quantity),
20+ METHOD(LuaEconomy, set_worker_target_quantity),
21+ {nullptr, nullptr},
22+};
23+const PropertyType<LuaEconomy> LuaEconomy::Properties[] = {
24+ {nullptr, nullptr, nullptr},
25+};
26+
27+void LuaEconomy::__persist(lua_State* L) {
28+ const Widelands::Economy* economy = get();
29+ const Widelands::Player& player = economy->owner();
30+ PERS_UINT32("player", player.player_number());
31+ PERS_UINT32("economy", player.get_economy_number(economy));
32+}
33+
34+void LuaEconomy::__unpersist(lua_State* L) {
35+ Widelands::PlayerNumber player_number;
36+ size_t economy_number;
37+ UNPERS_UINT32("player", player_number);
38+ UNPERS_UINT32("economy", economy_number);
39+ const Widelands::Player& player = get_egbase(L).player(player_number);
40+ set_economy_pointer(player.get_economy_by_number(economy_number));
41+}
42+
43+/* RST
44+ .. method:: ware_target_quantity(warename)
45+
46+ Returns the amount of the given ware that should be kept in stock for this economy.
47+
48+ :arg warename: the name of the ware.
49+ :type warename: :class:`string`
50+*/
51+int LuaEconomy::ware_target_quantity(lua_State* L) {
52+ const std::string warename = luaL_checkstring(L, 2);
53+ const Widelands::DescriptionIndex index = get_egbase(L).tribes().ware_index(warename);
54+ if (get_egbase(L).tribes().ware_exists(index)) {
55+ const Widelands::Economy::TargetQuantity& quantity = get()->ware_target_quantity(index);
56+ lua_pushinteger(L, quantity.permanent);
57+ } else {
58+ report_error(L, "There is no ware '%s'.", warename.c_str());
59+ }
60+ return 1;
61+}
62+
63+/* RST
64+ .. method:: worker_target_quantity(workername)
65+
66+ Returns the amount of the given worker that should be kept in stock for this economy.
67+
68+ :arg workername: the name of the worker.
69+ :type workername: :class:`string`
70+*/
71+int LuaEconomy::worker_target_quantity(lua_State* L) {
72+ const std::string workername = luaL_checkstring(L, 2);
73+ const Widelands::DescriptionIndex index = get_egbase(L).tribes().worker_index(workername);
74+ if (get_egbase(L).tribes().worker_exists(index)) {
75+ const Widelands::Economy::TargetQuantity& quantity = get()->worker_target_quantity(index);
76+ lua_pushinteger(L, quantity.permanent);
77+ } else {
78+ report_error(L, "There is no worker '%s'.", workername.c_str());
79+ }
80+ return 1;
81+}
82+
83+/* RST
84+ .. method:: set_ware_target_quantity(warename)
85+
86+ Sets the amount of the given ware type that should be kept in stock for this economy.
87+
88+ :arg warename: the name of the ware type.
89+ :type warename: :class:`string`
90+
91+ :arg amount: the new target amount for the ware. Needs to be >= 0.
92+ :type amount: :class:`integer`
93+*/
94+int LuaEconomy::set_ware_target_quantity(lua_State* L) {
95+ const std::string warename = luaL_checkstring(L, 2);
96+ const Widelands::DescriptionIndex index = get_egbase(L).tribes().ware_index(warename);
97+ if (get_egbase(L).tribes().ware_exists(index)) {
98+ const int quantity = luaL_checkinteger(L, 3);
99+ if (quantity < 0) {
100+ report_error(L, "Target ware quantity needs to be >= 0 but was '%d'.", quantity);
101+ }
102+ get()->set_ware_target_quantity(index, quantity, get_egbase(L).get_gametime());
103+ } else {
104+ report_error(L, "There is no ware '%s'.", warename.c_str());
105+ }
106+ return 1;
107+}
108+
109+/* RST
110+ .. method:: set_worker_target_quantity(workername)
111+
112+ Sets the amount of the given worker type that should be kept in stock for this economy.
113+
114+ :arg workername: the name of the worker type.
115+ :type workername: :class:`string`
116+
117+ :arg amount: the new target amount for the worker. Needs to be >= 0.
118+ :type amount: :class:`integer`
119+*/
120+int LuaEconomy::set_worker_target_quantity(lua_State* L) {
121+ const std::string workername = luaL_checkstring(L, 2);
122+ const Widelands::DescriptionIndex index = get_egbase(L).tribes().worker_index(workername);
123+ if (get_egbase(L).tribes().worker_exists(index)) {
124+ const int quantity = luaL_checkinteger(L, 3);
125+ if (quantity < 0) {
126+ report_error(L, "Target worker quantity needs to be >= 0 but was '%d'.", quantity);
127+ }
128+ get()->set_worker_target_quantity(index, quantity, get_egbase(L).get_gametime());
129+ } else {
130+ report_error(L, "There is no worker '%s'.", workername.c_str());
131+ }
132+ return 1;
133+}
134+
135+/* RST
136 MapObject
137 ---------
138
139@@ -3682,7 +3810,10 @@
140 METHOD(LuaFlag, set_wares), METHOD(LuaFlag, get_wares), {nullptr, nullptr},
141 };
142 const PropertyType<LuaFlag> LuaFlag::Properties[] = {
143- PROP_RO(LuaFlag, roads), PROP_RO(LuaFlag, building), {nullptr, nullptr, nullptr},
144+ PROP_RO(LuaFlag, economy),
145+ PROP_RO(LuaFlag, roads),
146+ PROP_RO(LuaFlag, building),
147+ {nullptr, nullptr, nullptr},
148 };
149
150 /*
151@@ -3691,6 +3822,18 @@
152 ==========================================================
153 */
154 /* RST
155+ .. attribute:: economy
156+
157+ (RO) Returns the economy that this flag belongs to.
158+
159+ :returns: The :class:`Economy` associated with the flag.
160+*/
161+int LuaFlag::get_economy(lua_State* L) {
162+ const Flag* f = get(L, get_egbase(L));
163+ return to_lua<LuaEconomy>(L, new LuaEconomy(f->get_economy()));
164+}
165+
166+/* RST
167 .. attribute:: roads
168
169 (RO) Array of roads leading to the flag. Directions
170@@ -6075,6 +6218,7 @@
171
172 register_class<LuaField>(L, "map");
173 register_class<LuaPlayerSlot>(L, "map");
174+ register_class<LuaEconomy>(L, "map");
175 register_class<LuaMapObject>(L, "map");
176
177 register_class<LuaBob>(L, "map", true);
178
179=== modified file 'src/scripting/lua_map.h'
180--- src/scripting/lua_map.h 2017-01-25 18:55:59 +0000
181+++ src/scripting/lua_map.h 2017-03-29 16:02:45 +0000
182@@ -22,6 +22,7 @@
183
184 #include <set>
185
186+#include "economy/economy.h"
187 #include "economy/flag.h"
188 #include "economy/portdock.h"
189 #include "economy/road.h"
190@@ -728,6 +729,54 @@
191 const Widelands::TerrainDescription* terraindescr_;
192 };
193
194+class LuaEconomy : public LuaMapModuleClass {
195+public:
196+ LUNA_CLASS_HEAD(LuaEconomy);
197+
198+ virtual ~LuaEconomy() {
199+ }
200+
201+ LuaEconomy() : economy_(nullptr) {
202+ }
203+ LuaEconomy(Widelands::Economy* economy) : economy_(economy) {
204+ }
205+ LuaEconomy(lua_State* L) : economy_(nullptr) {
206+ report_error(L, "Cannot instantiate a 'LuaEconomy' directly!");
207+ }
208+
209+ void __persist(lua_State* L) override;
210+ void __unpersist(lua_State* L) override;
211+
212+ /*
213+ * Properties
214+ */
215+
216+ /*
217+ * Lua methods
218+ */
219+ int ware_target_quantity(lua_State*);
220+ int worker_target_quantity(lua_State*);
221+ int set_ware_target_quantity(lua_State*);
222+ int set_worker_target_quantity(lua_State*);
223+
224+ /*
225+ * C methods
226+ */
227+
228+protected:
229+ Widelands::Economy* get() const {
230+ assert(economy_ != nullptr);
231+ return economy_;
232+ }
233+ // For persistence.
234+ void set_economy_pointer(Widelands::Economy* pointer) {
235+ economy_ = pointer;
236+ }
237+
238+private:
239+ Widelands::Economy* economy_;
240+};
241+
242 #define CASTED_GET(klass) \
243 Widelands::klass* get(lua_State* L, Widelands::EditorGameBase& egbase) { \
244 return static_cast<Widelands::klass*>(LuaMapObject::get(L, egbase, #klass)); \
245@@ -904,6 +953,7 @@
246 /*
247 * Properties
248 */
249+ int get_economy(lua_State* L);
250 int get_roads(lua_State* L);
251 int get_building(lua_State* L);
252 /*
253
254=== added file 'test/maps/lua_testsuite.wmf/scripting/geconomy.lua'
255--- test/maps/lua_testsuite.wmf/scripting/geconomy.lua 1970-01-01 00:00:00 +0000
256+++ test/maps/lua_testsuite.wmf/scripting/geconomy.lua 2017-03-29 16:02:45 +0000
257@@ -0,0 +1,52 @@
258+-- ==================================================
259+-- Tests for Economy that are only useful in the Game
260+-- ==================================================
261+
262+economy_tests = lunit.TestCase("Economy test")
263+function economy_tests:test_instantiation_forbidden()
264+ assert_error("Cannot instantiate", function()
265+ wl.map.Economy()
266+ end)
267+end
268+
269+function economy_tests:test_ware_target_quantity()
270+
271+ -- Get the economy off a flag
272+ local sf = map:get_field(10, 10)
273+ local hq = player1:place_building("barbarians_headquarters", sf, false, true)
274+ local hq_flag = hq.flag
275+ local eco = hq_flag.economy
276+
277+ -- Test illegal parameters
278+ assert_error("Nonexisting ware",function() eco:ware_target_quantity("foobar") end)
279+ assert_error("Quantity for nonexisting ware",function() eco:worker_target_quantity("foobar", 1) end)
280+ assert_error("Negative ware quantity",function() eco:set_ware_target_quantity("log", -1) end)
281+
282+ -- Now set and confirm ware quantity
283+ quantity = eco:ware_target_quantity("log")
284+ quantity = quantity + 1
285+ eco:set_ware_target_quantity("log", quantity)
286+ assert_equal(quantity, eco:ware_target_quantity("log"))
287+
288+ hq_flag:remove()
289+end
290+
291+function economy_tests:test_worker_target_quantity()
292+ -- Get the economy off a flag
293+ local sf = map:get_field(10, 10)
294+ local hq = player1:place_building("barbarians_headquarters", sf, false, true)
295+ local hq_flag = hq.flag
296+ local eco = hq_flag.economy
297+
298+ -- Test illegal parameters
299+ assert_error("Nonexisting worker",function() eco:worker_target_quantity("foobar") end)
300+ assert_error("Quantity for nonexisting worker",function() eco:worker_target_quantity("foobar", 1) end)
301+ assert_error("Negative worker quantity",function() eco:set_worker_target_quantity("barbarians_soldier", -1) end)
302+
303+ -- Now set and confirm worker quantity
304+ quantity = eco:worker_target_quantity("barbarians_soldier")
305+ quantity = quantity + 1
306+ eco:set_worker_target_quantity("barbarians_soldier", quantity)
307+ assert_equal(quantity, eco:worker_target_quantity("barbarians_soldier"))
308+ hq_flag:remove()
309+end
310
311=== modified file 'test/maps/lua_testsuite.wmf/scripting/init.lua'
312--- test/maps/lua_testsuite.wmf/scripting/init.lua 2016-04-12 07:35:33 +0000
313+++ test/maps/lua_testsuite.wmf/scripting/init.lua 2017-03-29 16:02:45 +0000
314@@ -40,6 +40,7 @@
315 if not wl.editor then
316 include "map:scripting/game.lua"
317
318+ include "map:scripting/geconomy.lua"
319 include "map:scripting/gplayer.lua"
320 include "map:scripting/gfield.lua"
321 include "map:scripting/gplr_access.lua"

Subscribers

People subscribed via source and target branches

to status/vote changes: