Merge lp:~widelands-dev/widelands/unify-program-parsers into lp:widelands

Proposed by GunChleoc
Status: Work in progress
Proposed branch: lp:~widelands-dev/widelands/unify-program-parsers
Merge into: lp:widelands
Prerequisite: lp:~widelands-dev/widelands/list-directories-in-cpp
Diff against target: 4800 lines (+1466/-1837)
52 files modified
data/tribes/immovables/shipconstruction_atlanteans/init.lua (+1/-1)
data/tribes/immovables/shipconstruction_barbarians/init.lua (+1/-1)
data/tribes/immovables/shipconstruction_empire/init.lua (+1/-1)
data/tribes/immovables/shipconstruction_frisians/init.lua (+1/-1)
data/tribes/workers/empire/stonemason/init.lua (+1/-1)
src/CMakeLists.txt (+0/-10)
src/editor/editorinteractive.cc (+1/-2)
src/editor/ui_menus/main_menu_new_map.cc (+1/-2)
src/editor/ui_menus/main_menu_random_map.cc (+1/-2)
src/helper.cc (+0/-55)
src/helper.h (+0/-44)
src/logic/editor_game_base.cc (+49/-55)
src/logic/editor_game_base.h (+2/-1)
src/logic/map_objects/CMakeLists.txt (+3/-1)
src/logic/map_objects/bob.cc (+3/-3)
src/logic/map_objects/bob.h (+5/-15)
src/logic/map_objects/immovable.cc (+15/-474)
src/logic/map_objects/immovable_program.cc (+344/-0)
src/logic/map_objects/immovable_program.h (+27/-39)
src/logic/map_objects/map_object.h (+1/-0)
src/logic/map_objects/map_object_program.cc (+125/-0)
src/logic/map_objects/map_object_program.h (+98/-0)
src/logic/map_objects/tribes/production_program.cc (+503/-790)
src/logic/map_objects/tribes/production_program.h (+34/-40)
src/logic/map_objects/tribes/productionsite.cc (+20/-4)
src/logic/map_objects/tribes/productionsite.h (+9/-0)
src/logic/map_objects/tribes/soldier.cc (+0/-1)
src/logic/map_objects/tribes/trainingsite.cc (+0/-1)
src/logic/map_objects/tribes/tribes.cc (+35/-10)
src/logic/map_objects/tribes/tribes.h (+1/-4)
src/logic/map_objects/tribes/worker.cc (+1/-2)
src/logic/map_objects/tribes/worker.h (+1/-1)
src/logic/map_objects/tribes/worker_descr.cc (+9/-10)
src/logic/map_objects/tribes/worker_program.cc (+151/-210)
src/logic/map_objects/tribes/worker_program.h (+3/-11)
src/logic/map_objects/world/critter.cc (+9/-15)
src/logic/map_objects/world/critter.h (+6/-3)
src/logic/map_objects/world/critter_program.h (+4/-11)
src/logic/map_objects/world/resource_description.cc (+0/-1)
src/network/CMakeLists.txt (+0/-1)
src/network/gameclient.cc (+0/-1)
src/network/gamehost.cc (+0/-1)
src/scripting/CMakeLists.txt (+0/-1)
src/scripting/lua_path.cc (+0/-1)
src/sound/CMakeLists.txt (+0/-1)
src/sound/fxset.cc (+0/-1)
src/sound/songset.cc (+0/-1)
src/ui_fsmenu/CMakeLists.txt (+0/-2)
src/ui_fsmenu/launch_spg.cc (+0/-1)
src/ui_fsmenu/options.cc (+0/-1)
src/wui/CMakeLists.txt (+0/-1)
src/wui/load_or_save_game.cc (+0/-2)
To merge this branch: bzr merge lp:~widelands-dev/widelands/unify-program-parsers
Reviewer Review Type Date Requested Status
Notabilis Approve
Review via email: mp+367936@code.launchpad.net

Commit message

Refactor program parsers
- Pull out common code into new class MapObjectProgram
- Fix ware demand checks so that they only affect the correct tribes
- Get rid of extraneous calls to EditorGameBase::postload()
- Small performance tweak for trainingsite programs
- Get rid of now empty helper library

Description of the change

The goal of this branch is to make the code easier to read, and to get rid of code duplication. This also fixes a bug with the ware demand checks.

To post a comment you must log in.
Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 5061. State: failed. Details: https://travis-ci.org/widelands/widelands/builds/537401669.
Appveyor build 4841. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_unify_program_parsers-4841.

Revision history for this message
Notabilis (notabilis27) wrote :

Looking mostly good, two comments in the diff.

I went through it commit by commit while ignoring the merges of trunk and list-directories-in-cpp. I hope that is okay? I am a bit unsure about that since there seem to be changes in the full diff that I can't find in the single commits.

Revision history for this message
GunChleoc (gunchleoc) wrote :

Thanks for the review! It should be OK. There were some merge conflicts, which is why there might be some small additional stuff, but nothing serious.

I have pushed a commit to address your comments.

Revision history for this message
Notabilis (notabilis27) wrote :

Looking good now, thanks.

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

Thanks for reviewing again :)

@bunnybot merge

Revision history for this message
bunnybot (widelandsofficial) wrote :

Refusing to merge, since Travis is not green. Use @bunnybot merge force for merging anyways.

Travis build 5191. State: failed. Details: https://travis-ci.org/widelands/widelands/builds/545623652.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 5242. State: errored. Details: https://travis-ci.org/widelands/widelands/builds/549900235.
Appveyor build 5021. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_unify_program_parsers-5021.

Revision history for this message
Toni Förster (stonerl) wrote :

Is it okay to force merge this branch?

test/maps/expedition.wmf/scripting/test_check_transportation_works_one_ship.lua ...

  Running Widelands ...

Seems to time out in some builds.

Revision history for this message
GunChleoc (gunchleoc) wrote :

No, I still need to find the bug that occurs with some compilers.

Revision history for this message
GunChleoc (gunchleoc) wrote :

I can reproduce the problem now in an Ubuntu trusty VM.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'data/tribes/immovables/shipconstruction_atlanteans/init.lua'
--- data/tribes/immovables/shipconstruction_atlanteans/init.lua 2019-05-28 19:37:11 +0000
+++ data/tribes/immovables/shipconstruction_atlanteans/init.lua 2019-08-31 15:58:01 +0000
@@ -12,7 +12,7 @@
12 programs = {12 programs = {
13 program = {13 program = {
14 "construct=idle 5000 210000",14 "construct=idle 5000 210000",
15 "transform=bob tribe:atlanteans_ship",15 "transform=bob atlanteans_ship",
16 }16 }
17 },17 },
18 buildcost = {18 buildcost = {
1919
=== modified file 'data/tribes/immovables/shipconstruction_barbarians/init.lua'
--- data/tribes/immovables/shipconstruction_barbarians/init.lua 2019-05-28 19:37:11 +0000
+++ data/tribes/immovables/shipconstruction_barbarians/init.lua 2019-08-31 15:58:01 +0000
@@ -12,7 +12,7 @@
12 programs = {12 programs = {
13 program = {13 program = {
14 "construct=idle 5000 210000",14 "construct=idle 5000 210000",
15 "transform=bob tribe:barbarians_ship",15 "transform=bob barbarians_ship",
16 }16 }
17 },17 },
18 buildcost = {18 buildcost = {
1919
=== modified file 'data/tribes/immovables/shipconstruction_empire/init.lua'
--- data/tribes/immovables/shipconstruction_empire/init.lua 2019-05-28 19:37:11 +0000
+++ data/tribes/immovables/shipconstruction_empire/init.lua 2019-08-31 15:58:01 +0000
@@ -12,7 +12,7 @@
12 programs = {12 programs = {
13 program = {13 program = {
14 "construct=idle 5000 210000",14 "construct=idle 5000 210000",
15 "transform=bob tribe:empire_ship",15 "transform=bob empire_ship",
16 }16 }
17 },17 },
18 buildcost = {18 buildcost = {
1919
=== modified file 'data/tribes/immovables/shipconstruction_frisians/init.lua'
--- data/tribes/immovables/shipconstruction_frisians/init.lua 2019-05-28 19:37:11 +0000
+++ data/tribes/immovables/shipconstruction_frisians/init.lua 2019-08-31 15:58:01 +0000
@@ -12,7 +12,7 @@
12 programs = {12 programs = {
13 program = {13 program = {
14 "construct=idle 5000 210000",14 "construct=idle 5000 210000",
15 "transform=bob tribe:frisians_ship",15 "transform=bob frisians_ship",
16 }16 }
17 },17 },
18 buildcost = {18 buildcost = {
1919
=== modified file 'data/tribes/workers/empire/stonemason/init.lua'
--- data/tribes/workers/empire/stonemason/init.lua 2019-06-02 14:45:28 +0000
+++ data/tribes/workers/empire/stonemason/init.lua 2019-08-31 15:58:01 +0000
@@ -41,7 +41,7 @@
41 "return"41 "return"
42 },42 },
43 cut_marble = {43 cut_marble = {
44 "findobject= attrib:rocks radius:6",44 "findobject=attrib:rocks radius:6",
45 "walk=object",45 "walk=object",
46 "playsound=sound/stonecutting/stonecutter 220",46 "playsound=sound/stonecutting/stonecutter 220",
47 "animate=hacking 17500",47 "animate=hacking 17500",
4848
=== modified file 'src/CMakeLists.txt'
--- src/CMakeLists.txt 2019-07-20 14:32:57 +0000
+++ src/CMakeLists.txt 2019-08-31 15:58:01 +0000
@@ -132,16 +132,6 @@
132 wui132 wui
133)133)
134134
135# TODO(sirver): Split into libs with useful names.
136wl_library(helper
137 SRCS
138 helper.cc
139 helper.h
140 USES_SDL2
141 DEPENDS
142 base_exceptions
143)
144
145if (CMAKE_SYSTEM_NAME MATCHES "FreeBSD" OR CMAKE_SYSTEM_NAME MATCHES "OpenBSD")135if (CMAKE_SYSTEM_NAME MATCHES "FreeBSD" OR CMAKE_SYSTEM_NAME MATCHES "OpenBSD")
146 target_link_libraries(widelands_ball_of_mud ${EXECINFO_LIBRARY})136 target_link_libraries(widelands_ball_of_mud ${EXECINFO_LIBRARY})
147endif (CMAKE_SYSTEM_NAME MATCHES "FreeBSD" OR CMAKE_SYSTEM_NAME MATCHES "OpenBSD")137endif (CMAKE_SYSTEM_NAME MATCHES "FreeBSD" OR CMAKE_SYSTEM_NAME MATCHES "OpenBSD")
148138
=== modified file 'src/editor/editorinteractive.cc'
--- src/editor/editorinteractive.cc 2019-08-28 06:12:07 +0000
+++ src/editor/editorinteractive.cc 2019-08-31 15:58:01 +0000
@@ -474,8 +474,7 @@
474 }474 }
475475
476 ml->load_map_complete(egbase(), Widelands::MapLoader::LoadType::kEditor);476 ml->load_map_complete(egbase(), Widelands::MapLoader::LoadType::kEditor);
477 egbase().postload();477 egbase().create_tempfile_and_save_mapdata(FileSystem::ZIP);
478 egbase().load_graphics(loader_ui);
479 map_changed(MapWas::kReplaced);478 map_changed(MapWas::kReplaced);
480}479}
481480
482481
=== modified file 'src/editor/ui_menus/main_menu_new_map.cc'
--- src/editor/ui_menus/main_menu_new_map.cc 2019-08-28 06:12:07 +0000
+++ src/editor/ui_menus/main_menu_new_map.cc 2019-08-31 15:58:01 +0000
@@ -110,8 +110,7 @@
110 map_size_box_.selected_height(), list_.get_selected(), _("No Name"),110 map_size_box_.selected_height(), list_.get_selected(), _("No Name"),
111 get_config_string("realname", pgettext("author_name", "Unknown")));111 get_config_string("realname", pgettext("author_name", "Unknown")));
112112
113 egbase.postload();113 egbase.create_tempfile_and_save_mapdata(FileSystem::ZIP);
114 egbase.load_graphics(loader_ui);
115114
116 map->recalc_whole_map(egbase.world());115 map->recalc_whole_map(egbase.world());
117 parent.map_changed(EditorInteractive::MapWas::kReplaced);116 parent.map_changed(EditorInteractive::MapWas::kReplaced);
118117
=== modified file 'src/editor/ui_menus/main_menu_random_map.cc'
--- src/editor/ui_menus/main_menu_random_map.cc 2019-08-28 06:12:07 +0000
+++ src/editor/ui_menus/main_menu_random_map.cc 2019-08-31 15:58:01 +0000
@@ -516,8 +516,7 @@
516516
517 gen.create_random_map();517 gen.create_random_map();
518518
519 egbase.postload();519 egbase.create_tempfile_and_save_mapdata(FileSystem::ZIP);
520 egbase.load_graphics(loader_ui);
521520
522 map->recalc_whole_map(egbase.world());521 map->recalc_whole_map(egbase.world());
523 eia.map_changed(EditorInteractive::MapWas::kReplaced);522 eia.map_changed(EditorInteractive::MapWas::kReplaced);
524523
=== removed file 'src/helper.cc'
--- src/helper.cc 2019-02-23 11:00:49 +0000
+++ src/helper.cc 1970-01-01 00:00:00 +0000
@@ -1,55 +0,0 @@
1/*
2 * Copyright (C) 2002-2019 by the Widelands Development Team
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 */
19
20#include "helper.h"
21
22#include <cstdarg>
23#include <memory>
24#include <string>
25
26#include <boost/algorithm/string/replace.hpp>
27#include <boost/format.hpp>
28#include <boost/lexical_cast.hpp>
29
30std::vector<std::string> split_string(const std::string& s, const char* const separators) {
31 std::vector<std::string> result;
32 for (std::string::size_type pos = 0, endpos;
33 (pos = s.find_first_not_of(separators, pos)) != std::string::npos; pos = endpos) {
34 endpos = s.find_first_of(separators, pos);
35 result.push_back(s.substr(pos, endpos - pos));
36 }
37 return result;
38}
39
40char* next_word(char*& p, bool& reached_end, char const terminator) {
41 assert(terminator);
42 char* const result = p;
43 for (; *p != terminator; ++p)
44 if (*p == '\0') {
45 reached_end = true;
46 goto end;
47 }
48 reached_end = false;
49 *p = '\0'; // terminate the word
50 ++p; // move past the terminator
51end:
52 if (result < p)
53 return result;
54 throw wexception("expected word");
55}
560
=== removed file 'src/helper.h'
--- src/helper.h 2019-03-27 07:20:26 +0000
+++ src/helper.h 1970-01-01 00:00:00 +0000
@@ -1,44 +0,0 @@
1/*
2 * Copyright (C) 2006-2019 by the Widelands Development Team
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 */
19
20#ifndef WL_HELPER_H
21#define WL_HELPER_H
22
23#include <cassert>
24#include <cstring>
25#include <string>
26#include <vector>
27
28#include <SDL_keyboard.h>
29#include <boost/utility.hpp>
30
31#include "base/wexception.h"
32
33/// Returns the word starting at the character that p points to and ending
34/// before the first terminator character. Replaces the terminator with null.
35// TODO(sirver): move into a logic/strings lib or so.
36char* next_word(char*& p, bool& reached_end, char terminator = ' ');
37
38/// Split a string by separators.
39/// \note This ignores empty elements, so do not use this for example to split
40/// a string with newline characters into lines, because it would ignore empty
41/// lines.
42std::vector<std::string> split_string(const std::string&, char const* separators);
43
44#endif // end of include guard: WL_HELPER_H
450
=== modified file 'src/logic/editor_game_base.cc'
--- src/logic/editor_game_base.cc 2019-06-23 11:41:17 +0000
+++ src/logic/editor_game_base.cc 2019-08-31 15:58:01 +0000
@@ -114,50 +114,53 @@
114 * throws an exception if something goes wrong114 * throws an exception if something goes wrong
115 */115 */
116void EditorGameBase::create_tempfile_and_save_mapdata(FileSystem::Type const type) {116void EditorGameBase::create_tempfile_and_save_mapdata(FileSystem::Type const type) {
117 // should only be called when a map was already loaded117 // save map data to temporary file and reassign map fs
118 assert(map_.filesystem());118 try {
119119 g_fs->ensure_directory_exists(kTempFileDir);
120 g_fs->ensure_directory_exists(kTempFileDir);120
121121 std::string filename = kTempFileDir + g_fs->file_separator() + timestring() + "_mapdata";
122 std::string filename = kTempFileDir + g_fs->file_separator() + timestring() + "_mapdata";122 std::string complete_filename = filename + kTempFileExtension;
123 std::string complete_filename = filename + kTempFileExtension;123
124124 // if a file with that name already exists, then try a few name modifications
125 // if a file with that name already exists, then try a few name modifications125 if (g_fs->file_exists(complete_filename)) {
126 if (g_fs->file_exists(complete_filename)) {126 int suffix;
127 int suffix;127 for (suffix = 0; suffix <= 9; suffix++) {
128 for (suffix = 0; suffix <= 9; suffix++) {128 complete_filename = filename + "-" + std::to_string(suffix) + kTempFileExtension;
129 complete_filename = filename + "-" + std::to_string(suffix) + kTempFileExtension;129 if (!g_fs->file_exists(complete_filename))
130 if (!g_fs->file_exists(complete_filename))130 break;
131 break;131 }
132 }132 if (suffix > 9) {
133 if (suffix > 9) {133 throw wexception("EditorGameBase::create_tempfile_and_save_mapdata(): for all considered "
134 throw wexception("EditorGameBase::create_tempfile_and_save_mapdata(): for all considered "134 "filenames a file already existed");
135 "filenames a file already existed");135 }
136 }136 }
137
138 // create tmp_fs_
139 tmp_fs_.reset(g_fs->create_sub_file_system(complete_filename, type));
140
141 // save necessary map data (we actually save the whole map)
142 std::unique_ptr<Widelands::MapSaver> wms(new Widelands::MapSaver(*tmp_fs_, *this));
143 wms->save();
144
145 // swap map fs
146 std::unique_ptr<FileSystem> mapfs(tmp_fs_->make_sub_file_system("."));
147 map_.swap_filesystem(mapfs);
148 mapfs.reset();
149
150 // This is just a convenience hack:
151 // If tmp_fs_ is a zip filesystem then - because of the way zip filesystems are currently
152 // implemented -
153 // the file is still in zip mode right now, which means that the file isn't finalized yet, i.e.,
154 // not even a valid zip file until zip mode ends. To force ending the zip mode (thus finalizing
155 // the file)
156 // we simply perform a (otherwise useless) filesystem request.
157 // It's not strictly necessary, but this way we get a valid zip file immediately istead of
158 // at some unkown later point (when an unzip operation happens or a filesystem object destructs).
159 tmp_fs_->file_exists("binary");
160 } catch (const WException& e) {
161 log("EditorGameBase: saving map to temporary file failed: %s", e.what());
162 throw;
137 }163 }
138
139 // create tmp_fs_
140 tmp_fs_.reset(g_fs->create_sub_file_system(complete_filename, type));
141
142 // save necessary map data (we actually save the whole map)
143 std::unique_ptr<Widelands::MapSaver> wms(new Widelands::MapSaver(*tmp_fs_, *this));
144 wms->save();
145
146 // swap map fs
147 std::unique_ptr<FileSystem> mapfs(tmp_fs_->make_sub_file_system("."));
148 map_.swap_filesystem(mapfs);
149 mapfs.reset();
150
151 // This is just a convenience hack:
152 // If tmp_fs_ is a zip filesystem then - because of the way zip filesystems are currently
153 // implemented -
154 // the file is still in zip mode right now, which means that the file isn't finalized yet, i.e.,
155 // not even a valid zip file until zip mode ends. To force ending the zip mode (thus finalizing
156 // the file)
157 // we simply perform a (otherwise useless) filesystem request.
158 // It's not strictly necessary, but this way we get a valid zip file immediately istead of
159 // at some unkown later point (when an unzip operation happens or a filesystem object destructs).
160 tmp_fs_->file_exists("binary");
161}164}
162165
163void EditorGameBase::think() {166void EditorGameBase::think() {
@@ -276,20 +279,11 @@
276}279}
277280
278/**281/**
279 * Load and prepare detailed game data.282 * Load and prepare detailed game and map data.
280 * This happens once just after the host has started the game and before the283 * This happens once just after the host has started the game / the editor has started and before the graphics are loaded.
281 * graphics are loaded.
282 */284 */
283void EditorGameBase::postload() {285void EditorGameBase::postload() {
284 if (map_.filesystem()) {286 create_tempfile_and_save_mapdata(FileSystem::ZIP);
285 // save map data to temporary file and reassign map fs
286 try {
287 create_tempfile_and_save_mapdata(FileSystem::ZIP);
288 } catch (const WException& e) {
289 log("EditorGameBase::postload: saving map to temporary file failed: %s", e.what());
290 throw;
291 }
292 }
293287
294 // Postload tribes288 // Postload tribes
295 assert(tribes_);289 assert(tribes_);
296290
=== modified file 'src/logic/editor_game_base.h'
--- src/logic/editor_game_base.h 2019-05-14 16:25:57 +0000
+++ src/logic/editor_game_base.h 2019-08-31 15:58:01 +0000
@@ -200,6 +200,8 @@
200 // Returns the mutable tribes. Prefer tribes() whenever possible.200 // Returns the mutable tribes. Prefer tribes() whenever possible.
201 Tribes* mutable_tribes();201 Tribes* mutable_tribes();
202202
203 void create_tempfile_and_save_mapdata(FileSystem::Type type);
204
203private:205private:
204 /// Common function for create_critter and create_ship.206 /// Common function for create_critter and create_ship.
205 Bob& create_bob(Coords, const BobDescr&, Player* owner = nullptr);207 Bob& create_bob(Coords, const BobDescr&, Player* owner = nullptr);
@@ -264,7 +266,6 @@
264 /// a temporary file (in a special dir) is created for such data.266 /// a temporary file (in a special dir) is created for such data.
265 std::unique_ptr<FileSystem> tmp_fs_;267 std::unique_ptr<FileSystem> tmp_fs_;
266 void delete_tempfile();268 void delete_tempfile();
267 void create_tempfile_and_save_mapdata(FileSystem::Type type);
268269
269 DISALLOW_COPY_AND_ASSIGN(EditorGameBase);270 DISALLOW_COPY_AND_ASSIGN(EditorGameBase);
270};271};
271272
=== modified file 'src/logic/map_objects/CMakeLists.txt'
--- src/logic/map_objects/CMakeLists.txt 2019-08-09 17:29:40 +0000
+++ src/logic/map_objects/CMakeLists.txt 2019-08-31 15:58:01 +0000
@@ -39,8 +39,11 @@
39 immovable.cc39 immovable.cc
40 immovable.h40 immovable.h
41 immovable_program.h41 immovable_program.h
42 immovable_program.cc
42 map_object.cc43 map_object.cc
43 map_object.h44 map_object.h
45 map_object_program.cc
46 map_object_program.h
44 terrain_affinity.cc47 terrain_affinity.cc
45 terrain_affinity.h48 terrain_affinity.h
46 tribes/attack_target.h49 tribes/attack_target.h
@@ -129,7 +132,6 @@
129 graphic_surface132 graphic_surface
130 graphic_text_layout133 graphic_text_layout
131 graphic_toolbar_imageset134 graphic_toolbar_imageset
132 helper
133 io_fileread135 io_fileread
134 io_filesystem136 io_filesystem
135 logic # TODO(GunChleoc): Circular dependency137 logic # TODO(GunChleoc): Circular dependency
136138
=== modified file 'src/logic/map_objects/bob.cc'
--- src/logic/map_objects/bob.cc 2019-05-11 13:48:12 +0000
+++ src/logic/map_objects/bob.cc 2019-08-31 15:58:01 +0000
@@ -1092,8 +1092,8 @@
1092 throw GameDataError("unknown bob task '%s'", name.c_str());1092 throw GameDataError("unknown bob task '%s'", name.c_str());
1093}1093}
10941094
1095const BobProgramBase* Bob::Loader::get_program(const std::string& name) {1095const MapObjectProgram* Bob::Loader::get_program(const std::string& name) {
1096 throw GameDataError("unknown bob program '%s'", name.c_str());1096 throw GameDataError("unknown map object program '%s'", name.c_str());
1097}1097}
10981098
1099void Bob::save(EditorGameBase& eg, MapObjectSaver& mos, FileWrite& fw) {1099void Bob::save(EditorGameBase& eg, MapObjectSaver& mos, FileWrite& fw) {
@@ -1156,7 +1156,7 @@
1156 fw.unsigned_8(0);1156 fw.unsigned_8(0);
1157 }1157 }
11581158
1159 fw.c_string(state.program ? state.program->get_name() : "");1159 fw.c_string(state.program ? state.program->name() : "");
1160 }1160 }
1161}1161}
1162} // namespace Widelands1162} // namespace Widelands
11631163
=== modified file 'src/logic/map_objects/bob.h'
--- src/logic/map_objects/bob.h 2019-04-24 06:01:37 +0000
+++ src/logic/map_objects/bob.h 2019-08-31 15:58:01 +0000
@@ -27,29 +27,19 @@
27#include "graphic/diranimations.h"27#include "graphic/diranimations.h"
28#include "logic/map_objects/draw_text.h"28#include "logic/map_objects/draw_text.h"
29#include "logic/map_objects/map_object.h"29#include "logic/map_objects/map_object.h"
30#include "logic/map_objects/map_object_program.h"
30#include "logic/map_objects/walkingdir.h"31#include "logic/map_objects/walkingdir.h"
31#include "logic/widelands_geometry.h"32#include "logic/widelands_geometry.h"
3233
33namespace Widelands {34namespace Widelands {
35
36class Bob;
34class Map;37class Map;
35struct Route;38struct Route;
36struct Transfer;39struct Transfer;
37class TribeDescr;40class TribeDescr;
3841
39/**42/**
40 * BobProgramBase is only used that
41 * get_name always works
42 */
43
44struct BobProgramBase {
45 virtual ~BobProgramBase() {
46 }
47 virtual std::string get_name() const = 0;
48};
49
50class Bob;
51
52/**
53 * Implement MapObjectDescr for the following \ref Bob class.43 * Implement MapObjectDescr for the following \ref Bob class.
54 */44 */
55class BobDescr : public MapObjectDescr {45class BobDescr : public MapObjectDescr {
@@ -223,7 +213,7 @@
223 DirAnimations diranims;213 DirAnimations diranims;
224 Path* path;214 Path* path;
225 Route* route;215 Route* route;
226 const BobProgramBase* program; ///< pointer to current program216 const MapObjectProgram* program; ///< pointer to current program
227 };217 };
228218
229 MO_DESCR(BobDescr)219 MO_DESCR(BobDescr)
@@ -419,7 +409,7 @@
419409
420 protected:410 protected:
421 virtual const Task* get_task(const std::string& name);411 virtual const Task* get_task(const std::string& name);
422 virtual const BobProgramBase* get_program(const std::string& name);412 virtual const MapObjectProgram* get_program(const std::string& name);
423413
424 private:414 private:
425 struct LoadState {415 struct LoadState {
426416
=== modified file 'src/logic/map_objects/immovable.cc'
--- src/logic/map_objects/immovable.cc 2019-05-28 17:01:30 +0000
+++ src/logic/map_objects/immovable.cc 2019-08-31 15:58:01 +0000
@@ -19,41 +19,17 @@
1919
20#include "logic/map_objects/immovable.h"20#include "logic/map_objects/immovable.h"
2121
22#include <cstdio>
23#include <cstring>
24#include <memory>22#include <memory>
2523
26#include <boost/algorithm/string.hpp>
27#include <boost/format.hpp>
28
29#include "base/log.h"
30#include "base/macros.h"
31#include "base/wexception.h"
32#include "config.h"
33#include "graphic/graphic.h"
34#include "graphic/rendertarget.h"
35#include "helper.h"
36#include "io/fileread.h"24#include "io/fileread.h"
37#include "io/filewrite.h"25#include "io/filewrite.h"
38#include "logic/editor_game_base.h"
39#include "logic/field.h"
40#include "logic/game.h"
41#include "logic/game_data_error.h"26#include "logic/game_data_error.h"
42#include "logic/map.h"
43#include "logic/map_objects/immovable_program.h"27#include "logic/map_objects/immovable_program.h"
44#include "logic/map_objects/terrain_affinity.h"28#include "logic/map_objects/terrain_affinity.h"
45#include "logic/map_objects/tribes/tribe_descr.h"
46#include "logic/map_objects/tribes/worker.h"
47#include "logic/map_objects/world/world.h"29#include "logic/map_objects/world/world.h"
48#include "logic/mapfringeregion.h"
49#include "logic/player.h"30#include "logic/player.h"
50#include "logic/widelands_geometry_io.h"31#include "logic/widelands_geometry_io.h"
51#include "map_io/tribes_legacy_lookup_table.h"
52#include "map_io/world_legacy_lookup_table.h"32#include "map_io/world_legacy_lookup_table.h"
53#include "notifications/notifications.h"
54#include "scripting/lua_table.h"
55#include "sound/note_sound.h"
56#include "sound/sound_handler.h"
5733
58namespace Widelands {34namespace Widelands {
5935
@@ -140,52 +116,6 @@
140/*116/*
141==============================================================================117==============================================================================
142118
143ImmovableProgram IMPLEMENTATION
144
145==============================================================================
146*/
147
148ImmovableProgram::ImmovableProgram(const std::string& init_name,
149 const std::vector<std::string>& lines,
150 ImmovableDescr* immovable)
151 : name_(init_name) {
152 for (const std::string& line : lines) {
153 std::vector<std::string> parts;
154 boost::split(parts, line, boost::is_any_of("="));
155 if (parts.size() != 2) {
156 throw GameDataError("invalid line: %s.", line.c_str());
157 }
158 std::unique_ptr<char[]> arguments(new char[parts[1].size() + 1]);
159 strncpy(arguments.get(), parts[1].c_str(), parts[1].size() + 1);
160
161 Action* action;
162 if (parts[0] == "animate") {
163 action = new ActAnimate(arguments.get(), *immovable);
164 } else if (parts[0] == "transform") {
165 action = new ActTransform(arguments.get(), *immovable);
166 } else if (parts[0] == "grow") {
167 action = new ActGrow(arguments.get(), *immovable);
168 } else if (parts[0] == "remove") {
169 action = new ActRemove(arguments.get(), *immovable);
170 } else if (parts[0] == "seed") {
171 action = new ActSeed(arguments.get(), *immovable);
172 } else if (parts[0] == "playsound") {
173 action = new ActPlaySound(arguments.get(), *immovable);
174 } else if (parts[0] == "construct") {
175 action = new ActConstruct(arguments.get(), *immovable);
176 } else {
177 throw GameDataError("unknown command type \"%s\" in immovable \"%s\"", parts[0].c_str(),
178 immovable->name().c_str());
179 }
180 actions_.push_back(action);
181 }
182 if (actions_.empty())
183 throw GameDataError("no actions");
184}
185
186/*
187==============================================================================
188
189ImmovableDescr IMPLEMENTATION119ImmovableDescr IMPLEMENTATION
190120
191==============================================================================121==============================================================================
@@ -249,12 +179,16 @@
249 }179 }
250180
251 std::unique_ptr<LuaTable> programs = table.get_table("programs");181 std::unique_ptr<LuaTable> programs = table.get_table("programs");
252 for (const std::string& program_name : programs->keys<std::string>()) {182 for (std::string program_name : programs->keys<std::string>()) {
183 std::transform(program_name.begin(), program_name.end(), program_name.begin(), tolower);
184 if (programs_.count(program_name)) {
185 throw GameDataError("Program '%s' has already been declared for immovable '%s'", program_name.c_str(), name().c_str());
186 }
253 try {187 try {
254 programs_[program_name] = new ImmovableProgram(188 programs_[program_name] = new ImmovableProgram(
255 program_name, programs->get_table(program_name)->array_entries<std::string>(), this);189 program_name, programs->get_table(program_name)->array_entries<std::string>(), *this);
256 } catch (const std::exception& e) {190 } catch (const std::exception& e) {
257 throw wexception("Error in program %s: %s", program_name.c_str(), e.what());191 throw GameDataError("%s: Error in immovable program %s: %s", name().c_str(), program_name.c_str(), e.what());
258 }192 }
259 }193 }
260194
@@ -308,9 +242,9 @@
308void ImmovableDescr::make_sure_default_program_is_there() {242void ImmovableDescr::make_sure_default_program_is_there() {
309 if (!programs_.count("program")) { // default program243 if (!programs_.count("program")) { // default program
310 assert(is_animation_known("idle"));244 assert(is_animation_known("idle"));
311 char parameters[] = "idle";245 std::vector<std::string> arguments{"idle"};
312 programs_["program"] =246 programs_["program"] =
313 new ImmovableProgram("program", new ImmovableProgram::ActAnimate(parameters, *this));247 new ImmovableProgram("program", std::unique_ptr<ImmovableProgram::Action>(new ImmovableProgram::ActAnimate(arguments, *this)));
314 }248 }
315}249}
316250
@@ -418,10 +352,12 @@
418 ImmovableProgram const* prog = program_;352 ImmovableProgram const* prog = program_;
419 if (!prog) {353 if (!prog) {
420 prog = descr().get_program("program");354 prog = descr().get_program("program");
421 assert(prog != nullptr);355 }
422 }356 assert(prog != nullptr);
423 if (upcast(ImmovableProgram::ActAnimate const, act_animate, &(*prog)[program_ptr_]))357
424 start_animation(egbase, descr().get_animation(act_animate->animation(), this));358 if (upcast(ImmovableProgram::ActAnimate const, act_animate, &(*prog)[program_ptr_])) {
359 start_animation(egbase, act_animate->animation());
360 }
425361
426 if (upcast(Game, game, &egbase)) {362 if (upcast(Game, game, &egbase)) {
427 switch_program(*game, "program");363 switch_program(*game, "program");
@@ -761,391 +697,6 @@
761 return loader.release();697 return loader.release();
762}698}
763699
764ImmovableProgram::Action::~Action() {
765}
766
767ImmovableProgram::ActAnimate::ActAnimate(char* parameters, ImmovableDescr& descr) {
768 try {
769 bool reached_end;
770 animation_name_ = std::string(next_word(parameters, reached_end));
771 if (!descr.is_animation_known(animation_name_)) {
772 throw GameDataError("Unknown animation: %s.", animation_name_.c_str());
773 }
774
775 if (!reached_end) { // The next parameter is the duration.
776 char* endp;
777 long int const value = strtol(parameters, &endp, 0);
778 if (*endp || value <= 0)
779 throw GameDataError("expected %s but found \"%s\"", "duration in ms", parameters);
780 duration_ = value;
781 } else {
782 duration_ = 0; // forever
783 }
784 } catch (const WException& e) {
785 throw GameDataError("animate: %s", e.what());
786 }
787}
788
789/// Use convolutuion to make the animation time a random variable with binomial
790/// distribution and the configured time as the expected value.
791void ImmovableProgram::ActAnimate::execute(Game& game, Immovable& immovable) const {
792 immovable.start_animation(game, immovable.descr().get_animation(animation_name_, &immovable));
793 immovable.program_step(
794 game, duration_ ? 1 + game.logic_rand() % duration_ + game.logic_rand() % duration_ : 0);
795}
796
797ImmovableProgram::ActPlaySound::ActPlaySound(char* parameters, const ImmovableDescr&) {
798 try {
799 bool reached_end;
800 std::string name = next_word(parameters, reached_end);
801
802 if (!reached_end) {
803 char* endp;
804 unsigned long long int const value = strtoull(parameters, &endp, 0);
805 priority = value;
806 if (*endp || priority != value)
807 throw GameDataError("expected %s but found \"%s\"", "priority", parameters);
808 } else
809 priority = 127;
810
811 fx = g_sh->register_fx(SoundType::kAmbient, name);
812 } catch (const WException& e) {
813 throw GameDataError("playsound: %s", e.what());
814 }
815}
816
817/** Demand from the g_sound_handler to play a certain sound effect.
818 * Whether the effect actually gets played
819 * is decided only by the sound server*/
820void ImmovableProgram::ActPlaySound::execute(Game& game, Immovable& immovable) const {
821 Notifications::publish(NoteSound(SoundType::kAmbient, fx, immovable.get_position(), priority));
822 immovable.program_step(game);
823}
824
825ImmovableProgram::ActTransform::ActTransform(char* parameters, ImmovableDescr& descr) {
826 try {
827 tribe = true;
828 bob = false;
829 probability = 0;
830
831 std::vector<std::string> params = split_string(parameters, " ");
832 for (uint32_t i = 0; i < params.size(); ++i) {
833 if (params[i] == "bob")
834 bob = true;
835 else if (params[i] == "immovable")
836 bob = false;
837 else if (params[i][0] >= '0' && params[i][0] <= '9') {
838 long int const value = atoi(params[i].c_str());
839 if (value < 1 || 254 < value)
840 throw GameDataError("expected %s but found \"%s\"", "probability in range [1, 254]",
841 params[i].c_str());
842 probability = value;
843 } else {
844 std::vector<std::string> segments = split_string(params[i], ":");
845
846 if (segments.size() > 2)
847 throw GameDataError("object type has more than 2 segments");
848 if (segments.size() == 2) {
849 if (segments[0] == "world")
850 tribe = false;
851 else if (segments[0] == "tribe") {
852 if (descr.owner_type() != MapObjectDescr::OwnerType::kTribe)
853 throw GameDataError("scope \"tribe\" does not match the immovable type");
854 tribe = true;
855 } else
856 throw GameDataError("unknown scope \"%s\" given for target type (must be "
857 "\"world\" or \"tribe\")",
858 parameters);
859
860 type_name = segments[1];
861 } else {
862 type_name = segments[0];
863 }
864 }
865 }
866 if (type_name == descr.name())
867 throw GameDataError("illegal transformation to the same type");
868 } catch (const WException& e) {
869 throw GameDataError("transform: %s", e.what());
870 }
871}
872
873void ImmovableProgram::ActTransform::execute(Game& game, Immovable& immovable) const {
874 if (probability == 0 || game.logic_rand() % 256 < probability) {
875 Player* player = immovable.get_owner();
876 Coords const c = immovable.get_position();
877 MapObjectDescr::OwnerType owner_type = immovable.descr().owner_type();
878 immovable.remove(game); // Now immovable is a dangling reference!
879
880 if (bob) {
881 game.create_ship(c, type_name, player);
882 } else {
883 game.create_immovable_with_name(
884 c, type_name, owner_type, player, nullptr /* former_building_descr */);
885 }
886 } else
887 immovable.program_step(game);
888}
889
890ImmovableProgram::ActGrow::ActGrow(char* parameters, ImmovableDescr& descr) {
891 if (!descr.has_terrain_affinity()) {
892 throw GameDataError(
893 "Immovable %s can 'grow', but has no terrain_affinity entry.", descr.name().c_str());
894 }
895
896 try {
897 tribe = true;
898 for (char* p = parameters;;)
899 switch (*p) {
900 case ':': {
901 *p = '\0';
902 ++p;
903 if (descr.owner_type() != MapObjectDescr::OwnerType::kTribe)
904 throw GameDataError("immovable type not in tribes but target type has scope "
905 "(\"%s\")",
906 parameters);
907 else if (strcmp(parameters, "world"))
908 throw GameDataError("scope \"%s\" given for target type (must be "
909 "\"world\")",
910 parameters);
911 tribe = false;
912 parameters = p;
913 break;
914 }
915 case '\0':
916 goto end;
917 default:
918 ++p;
919 break;
920 }
921 end:
922 type_name = parameters;
923 } catch (const WException& e) {
924 throw GameDataError("grow: %s", e.what());
925 }
926}
927
928void ImmovableProgram::ActGrow::execute(Game& game, Immovable& immovable) const {
929 const Map& map = game.map();
930 FCoords const f = map.get_fcoords(immovable.get_position());
931 const ImmovableDescr& descr = immovable.descr();
932
933 if ((game.logic_rand() % TerrainAffinity::kPrecisionFactor) <
934 probability_to_grow(descr.terrain_affinity(), f, map, game.world().terrains())) {
935 MapObjectDescr::OwnerType owner_type = descr.owner_type();
936 Player* owner = immovable.get_owner();
937 immovable.remove(game); // Now immovable is a dangling reference!
938 game.create_immovable_with_name(
939 f, type_name, owner_type, owner, nullptr /* former_building_descr */);
940 } else {
941 immovable.program_step(game);
942 }
943}
944
945/**
946 * remove
947 */
948ImmovableProgram::ActRemove::ActRemove(char* parameters, ImmovableDescr&) {
949 try {
950 if (*parameters) {
951 char* endp;
952 long int const value = strtol(parameters, &endp, 0);
953 if (*endp || value < 1 || 254 < value)
954 throw GameDataError(
955 "expected %s but found \"%s\"", "probability in range [1, 254]", parameters);
956 probability = value;
957 } else
958 probability = 0;
959 } catch (const WException& e) {
960 throw GameDataError("remove: %s", e.what());
961 }
962}
963
964void ImmovableProgram::ActRemove::execute(Game& game, Immovable& immovable) const {
965 if (probability == 0 || game.logic_rand() % 256 < probability)
966 immovable.remove(game); // Now immovable is a dangling reference!
967 else
968 immovable.program_step(game);
969}
970
971ImmovableProgram::ActSeed::ActSeed(char* parameters, ImmovableDescr& descr) {
972 try {
973 probability = 0;
974 for (char* p = parameters;;)
975 switch (*p) {
976 case ':': {
977 *p = '\0';
978 ++p;
979 if (descr.owner_type() != MapObjectDescr::OwnerType::kTribe)
980 throw GameDataError("immovable type not in tribes but target type has scope "
981 "(\"%s\")",
982 parameters);
983 else if (strcmp(parameters, "world"))
984 throw GameDataError("scope \"%s\" given for target type (must be "
985 "\"world\")",
986 parameters);
987 parameters = p;
988 break;
989 }
990 case ' ': {
991 *p = '\0';
992 ++p;
993 char* endp;
994 long int const value = strtol(p, &endp, 0);
995 if (*endp || value < 1 || 254 < value)
996 throw GameDataError(
997 "expected %s but found \"%s\"", "probability in range [1, 254]", p);
998 probability = value;
999 // fallthrough
1000 }
1001 FALLS_THROUGH;
1002 case '\0':
1003 goto end;
1004 default:
1005 ++p;
1006 break;
1007 }
1008 end:
1009 type_name = parameters;
1010 } catch (const WException& e) {
1011 throw GameDataError("seed: %s", e.what());
1012 }
1013}
1014
1015void ImmovableProgram::ActSeed::execute(Game& game, Immovable& immovable) const {
1016 const Map& map = game.map();
1017 FCoords const f = map.get_fcoords(immovable.get_position());
1018 const ImmovableDescr& descr = immovable.descr();
1019
1020 if ((game.logic_rand() % TerrainAffinity::kPrecisionFactor) <
1021 probability_to_grow(descr.terrain_affinity(), f, map, game.world().terrains())) {
1022 // Seed a new tree.
1023 MapFringeRegion<> mr(map, Area<>(f, 0));
1024 uint32_t fringe_size = 0;
1025 do {
1026 mr.extend(map);
1027 fringe_size += 6;
1028 } while (game.logic_rand() % std::numeric_limits<uint8_t>::max() < probability);
1029
1030 for (uint32_t n = game.logic_rand() % fringe_size; n; --n) {
1031 mr.advance(map);
1032 }
1033
1034 const FCoords new_location = map.get_fcoords(mr.location());
1035 if (!new_location.field->get_immovable() &&
1036 (new_location.field->nodecaps() & MOVECAPS_WALK) &&
1037 (game.logic_rand() % TerrainAffinity::kPrecisionFactor) <
1038 probability_to_grow(
1039 descr.terrain_affinity(), new_location, map, game.world().terrains())) {
1040 game.create_immovable_with_name(mr.location(), type_name, descr.owner_type(),
1041 nullptr /* owner */, nullptr /* former_building_descr */);
1042 }
1043 }
1044
1045 immovable.program_step(game);
1046}
1047
1048ImmovableProgram::ActConstruct::ActConstruct(char* parameters, ImmovableDescr& descr) {
1049 try {
1050 if (descr.owner_type() != MapObjectDescr::OwnerType::kTribe)
1051 throw GameDataError("only usable for tribe immovable");
1052
1053 std::vector<std::string> params = split_string(parameters, " ");
1054
1055 if (params.size() != 3)
1056 throw GameDataError("usage: animation-name buildtime decaytime");
1057
1058 buildtime_ = atoi(params[1].c_str());
1059 decaytime_ = atoi(params[2].c_str());
1060
1061 animation_name_ = params[0];
1062 if (!descr.is_animation_known(animation_name_)) {
1063 throw GameDataError("unknown animation \"%s\" in immovable program for immovable \"%s\"",
1064 animation_name_.c_str(), descr.name().c_str());
1065 }
1066 } catch (const WException& e) {
1067 throw GameDataError("construct: %s", e.what());
1068 }
1069}
1070
1071constexpr uint8_t kCurrentPacketVersionConstructionData = 1;
1072
1073struct ActConstructData : ImmovableActionData {
1074 const char* name() const override {
1075 return "construct";
1076 }
1077 void save(FileWrite& fw, Immovable& imm) override {
1078 fw.unsigned_8(kCurrentPacketVersionConstructionData);
1079 delivered.save(fw, imm.get_owner()->tribe());
1080 }
1081
1082 static ActConstructData* load(FileRead& fr, Immovable& imm) {
1083 ActConstructData* d = new ActConstructData;
1084
1085 try {
1086 uint8_t packet_version = fr.unsigned_8();
1087 if (packet_version == kCurrentPacketVersionConstructionData) {
1088 d->delivered.load(fr, imm.get_owner()->tribe());
1089 } else {
1090 throw UnhandledVersionError(
1091 "ActConstructData", packet_version, kCurrentPacketVersionConstructionData);
1092 }
1093 } catch (const WException& e) {
1094 delete d;
1095 d = nullptr;
1096 throw GameDataError("ActConstructData: %s", e.what());
1097 }
1098
1099 return d;
1100 }
1101
1102 Buildcost delivered;
1103};
1104
1105void ImmovableProgram::ActConstruct::execute(Game& g, Immovable& imm) const {
1106 ActConstructData* d = imm.get_action_data<ActConstructData>();
1107 if (!d) {
1108 // First execution
1109 d = new ActConstructData;
1110 imm.set_action_data(d);
1111
1112 imm.start_animation(g, imm.descr().get_animation(animation_name_, &imm));
1113 imm.anim_construction_total_ = imm.descr().buildcost().total();
1114 } else {
1115 // Perhaps we are called due to the construction timeout of the last construction step
1116 Buildcost remaining;
1117 imm.construct_remaining_buildcost(g, &remaining);
1118 if (remaining.empty()) {
1119 imm.program_step(g);
1120 return;
1121 }
1122
1123 // Otherwise, this is a decay timeout
1124 uint32_t totaldelivered = 0;
1125 for (Buildcost::const_iterator it = d->delivered.begin(); it != d->delivered.end(); ++it)
1126 totaldelivered += it->second;
1127
1128 if (!totaldelivered) {
1129 imm.remove(g);
1130 return;
1131 }
1132
1133 uint32_t randdecay = g.logic_rand() % totaldelivered;
1134 for (Buildcost::iterator it = d->delivered.begin(); it != d->delivered.end(); ++it) {
1135 if (randdecay < it->second) {
1136 it->second--;
1137 break;
1138 }
1139
1140 randdecay -= it->second;
1141 }
1142
1143 imm.anim_construction_done_ = d->delivered.total();
1144 }
1145
1146 imm.program_step_ = imm.schedule_act(g, decaytime_);
1147}
1148
1149/**700/**
1150 * For an immovable that is currently in construction mode, return \c true and701 * For an immovable that is currently in construction mode, return \c true and
1151 * compute the remaining buildcost.702 * compute the remaining buildcost.
@@ -1208,16 +759,6 @@
1208 return true;759 return true;
1209}760}
1210761
1211ImmovableActionData*
1212ImmovableActionData::load(FileRead& fr, Immovable& imm, const std::string& name) {
1213 // TODO(GunChleoc): Use "construct" only after Build 20
1214 if (name == "construction" || name == "construct")
1215 return ActConstructData::load(fr, imm);
1216 else {
1217 log("ImmovableActionData::load: type %s not known", name.c_str());
1218 return nullptr;
1219 }
1220}
1221762
1222/*763/*
1223==============================================================================764==============================================================================
1224765
=== added file 'src/logic/map_objects/immovable_program.cc'
--- src/logic/map_objects/immovable_program.cc 1970-01-01 00:00:00 +0000
+++ src/logic/map_objects/immovable_program.cc 2019-08-31 15:58:01 +0000
@@ -0,0 +1,344 @@
1/*
2 * Copyright (C) 2002-2019 by the Widelands Development Team
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 */
19
20#include "logic/map_objects/immovable_program.h"
21
22#include <memory>
23
24#include "logic/game.h"
25#include "logic/game_data_error.h"
26#include "logic/map_objects/terrain_affinity.h"
27#include "logic/map_objects/world/world.h"
28#include "logic/mapfringeregion.h"
29#include "logic/player.h"
30#include "sound/note_sound.h"
31
32namespace Widelands {
33
34ImmovableProgram::ImmovableProgram(const std::string& init_name, std::unique_ptr<Action> action) : MapObjectProgram(init_name) {
35 actions_.push_back(std::move(action));
36}
37
38ImmovableProgram::ImmovableProgram(const std::string& init_name,
39 const std::vector<std::string>& lines,
40 const ImmovableDescr& immovable)
41 : MapObjectProgram(init_name) {
42 for (const std::string& line : lines) {
43 if (line.empty()) {
44 throw GameDataError("Empty line");
45 }
46 try {
47 ProgramParseInput parseinput = parse_program_string(line);
48
49 if (parseinput.name == "animate") {
50 actions_.push_back(std::unique_ptr<Action>(new ActAnimate(parseinput.arguments, immovable)));
51 } else if (parseinput.name == "transform") {
52 actions_.push_back(std::unique_ptr<Action>(new ActTransform(parseinput.arguments, immovable)));
53 } else if (parseinput.name == "grow") {
54 actions_.push_back(std::unique_ptr<Action>(new ActGrow(parseinput.arguments, immovable)));
55 } else if (parseinput.name == "remove") {
56 actions_.push_back(std::unique_ptr<Action>(new ActRemove(parseinput.arguments)));
57 } else if (parseinput.name == "seed") {
58 actions_.push_back(std::unique_ptr<Action>(new ActSeed(parseinput.arguments, immovable)));
59 } else if (parseinput.name == "playsound") {
60 actions_.push_back(std::unique_ptr<Action>(new ActPlaySound(parseinput.arguments)));
61 } else if (parseinput.name == "construct") {
62 actions_.push_back(std::unique_ptr<Action>(new ActConstruct(parseinput.arguments, immovable)));
63 } else {
64 throw GameDataError("Unknown command '%s' in line '%s'", parseinput.name.c_str(), line.c_str());
65 }
66 } catch (const GameDataError& e) {
67 throw GameDataError("Error reading line '%s': %s", line.c_str(), e.what());
68 }
69 }
70 if (actions_.empty()) {
71 throw GameDataError("No actions found");
72 }
73}
74
75ImmovableProgram::Action::~Action() {
76}
77
78ImmovableProgram::ActAnimate::ActAnimate(const std::vector<std::string>& arguments, const ImmovableDescr& descr) {
79 parameters = MapObjectProgram::parse_act_animate(arguments, descr, true);
80}
81
82/// Use convolution to make the animation time a random variable with binomial
83/// distribution and the configured time as the expected value.
84void ImmovableProgram::ActAnimate::execute(Game& game, Immovable& immovable) const {
85 immovable.start_animation(game, parameters.animation);
86 immovable.program_step(
87 game, parameters.duration ? 1 + game.logic_rand() % parameters.duration + game.logic_rand() % parameters.duration : 0);
88}
89
90ImmovableProgram::ActPlaySound::ActPlaySound(const std::vector<std::string>& arguments) {
91 parameters = MapObjectProgram::parse_act_play_sound(arguments, kFxPriorityAllowMultiple - 1);
92}
93
94/**
95 * Send request to the g_sound_handler to play a certain sound effect.
96 * Whether the effect actually gets played is decided by the sound server itself.
97 */
98void ImmovableProgram::ActPlaySound::execute(Game& game, Immovable& immovable) const {
99 Notifications::publish(NoteSound(SoundType::kAmbient, parameters.fx, immovable.get_position(), parameters.priority));
100 immovable.program_step(game);
101}
102
103ImmovableProgram::ActTransform::ActTransform(std::vector<std::string>& arguments, const ImmovableDescr& descr) {
104 if (arguments.empty()) {
105 throw GameDataError("Usage: transform=[bob] <name> [<probability>]");
106 }
107 try {
108 bob = false;
109 probability = 0;
110
111 for (const std::string& argument : arguments) {
112 if (argument == "bob") {
113 bob = true;
114 } else if (argument[0] >= '0' && argument[0] <= '9') {
115 probability = read_positive(argument, 254);
116 } else {
117 // TODO(GunChleoc): If would be nice to check if target exists, but we can't guarantee the load order. Maybe in postload() one day.
118 type_name = argument;
119 }
120 }
121 if (type_name == descr.name()) {
122 throw GameDataError("illegal transformation to the same type");
123 }
124 } catch (const WException& e) {
125 throw GameDataError("transform: %s", e.what());
126 }
127}
128
129void ImmovableProgram::ActTransform::execute(Game& game, Immovable& immovable) const {
130 if (probability == 0 || game.logic_rand() % 256 < probability) {
131 Player* player = immovable.get_owner();
132 Coords const c = immovable.get_position();
133 MapObjectDescr::OwnerType owner_type = immovable.descr().owner_type();
134 immovable.remove(game); // Now immovable is a dangling reference!
135
136 if (bob) {
137 game.create_ship(c, type_name, player);
138 } else {
139 game.create_immovable_with_name(
140 c, type_name, owner_type, player, nullptr /* former_building_descr */);
141 }
142 } else
143 immovable.program_step(game);
144}
145
146ImmovableProgram::ActGrow::ActGrow(std::vector<std::string>& arguments, const ImmovableDescr& descr) {
147 if (arguments.size() != 1) {
148 throw GameDataError("Usage: grow=<immovable name>");
149 }
150 if (!descr.has_terrain_affinity()) {
151 throw GameDataError(
152 "Immovable %s can 'grow', but has no terrain_affinity entry.", descr.name().c_str());
153 }
154
155 // TODO(GunChleoc): If would be nice to check if target exists, but we can't guarantee the load order. Maybe in postload() one day.
156 type_name = arguments.front();
157}
158
159void ImmovableProgram::ActGrow::execute(Game& game, Immovable& immovable) const {
160 const Map& map = game.map();
161 FCoords const f = map.get_fcoords(immovable.get_position());
162 const ImmovableDescr& descr = immovable.descr();
163
164 if ((game.logic_rand() % TerrainAffinity::kPrecisionFactor) <
165 probability_to_grow(descr.terrain_affinity(), f, map, game.world().terrains())) {
166 MapObjectDescr::OwnerType owner_type = descr.owner_type();
167 Player* owner = immovable.get_owner();
168 immovable.remove(game); // Now immovable is a dangling reference!
169 game.create_immovable_with_name(
170 f, type_name, owner_type, owner, nullptr /* former_building_descr */);
171 } else {
172 immovable.program_step(game);
173 }
174}
175
176/**
177 * remove
178 */
179ImmovableProgram::ActRemove::ActRemove(std::vector<std::string>& arguments) {
180 if (arguments.size() > 1) {
181 throw GameDataError("Usage: remove=[<probability>]");
182 }
183 probability = arguments.empty() ? 0 : read_positive(arguments.front(), 254);
184}
185
186void ImmovableProgram::ActRemove::execute(Game& game, Immovable& immovable) const {
187 if (probability == 0 || game.logic_rand() % 256 < probability) {
188 immovable.remove(game); // Now immovable is a dangling reference!
189 } else {
190 immovable.program_step(game);
191 }
192}
193
194ImmovableProgram::ActSeed::ActSeed(std::vector<std::string>& arguments, const ImmovableDescr& descr) {
195 if (arguments.size() != 1) {
196 throw GameDataError("Usage: seed=<immovable name>");
197 }
198 if (!descr.has_terrain_affinity()) {
199 throw GameDataError(
200 "Immovable %s can 'seed', but has no terrain_affinity entry.", descr.name().c_str());
201 }
202
203 // TODO(GunChleoc): If would be nice to check if target exists, but we can't guarantee the load order. Maybe in postload() one day.
204 type_name = arguments.front();
205}
206
207void ImmovableProgram::ActSeed::execute(Game& game, Immovable& immovable) const {
208 const Map& map = game.map();
209 FCoords const f = map.get_fcoords(immovable.get_position());
210 const ImmovableDescr& descr = immovable.descr();
211
212 if ((game.logic_rand() % TerrainAffinity::kPrecisionFactor) <
213 probability_to_grow(descr.terrain_affinity(), f, map, game.world().terrains())) {
214 // Seed a new tree.
215 MapFringeRegion<> mr(map, Area<>(f, 0));
216 uint32_t fringe_size = 0;
217 do {
218 mr.extend(map);
219 fringe_size += 6;
220 } while (game.logic_rand() % std::numeric_limits<uint8_t>::max() < probability);
221
222 for (uint32_t n = game.logic_rand() % fringe_size; n; --n) {
223 mr.advance(map);
224 }
225
226 const FCoords new_location = map.get_fcoords(mr.location());
227 if (!new_location.field->get_immovable() &&
228 (new_location.field->nodecaps() & MOVECAPS_WALK) &&
229 (game.logic_rand() % TerrainAffinity::kPrecisionFactor) <
230 probability_to_grow(
231 descr.terrain_affinity(), new_location, map, game.world().terrains())) {
232 game.create_immovable_with_name(mr.location(), type_name, descr.owner_type(),
233 nullptr /* owner */, nullptr /* former_building_descr */);
234 }
235 }
236
237 immovable.program_step(game);
238}
239
240ImmovableProgram::ActConstruct::ActConstruct(std::vector<std::string>& arguments, const ImmovableDescr& descr) {
241 if (arguments.size() != 3) {
242 throw GameDataError("Usage: construct=<animation> <build duration> <decay duration>");
243 }
244 try {
245 animation_name_ = arguments[0];
246 if (!descr.is_animation_known(animation_name_)) {
247 throw GameDataError("Unknown animation '%s' in immovable program for immovable '%s'",
248 animation_name_.c_str(), descr.name().c_str());
249 }
250
251 buildtime_ = read_positive(arguments[1]);
252 decaytime_ = read_positive(arguments[2]);
253 } catch (const WException& e) {
254 throw GameDataError("construct: %s", e.what());
255 }
256}
257
258constexpr uint8_t kCurrentPacketVersionConstructionData = 1;
259
260
261const char* ActConstructData::name() const {
262 return "construct";
263}
264void ActConstructData::save(FileWrite& fw, Immovable& imm) const {
265 fw.unsigned_8(kCurrentPacketVersionConstructionData);
266 delivered.save(fw, imm.get_owner()->tribe());
267}
268
269ActConstructData* ActConstructData::load(FileRead& fr, Immovable& imm) {
270 ActConstructData* d = new ActConstructData;
271
272 try {
273 uint8_t packet_version = fr.unsigned_8();
274 if (packet_version == kCurrentPacketVersionConstructionData) {
275 d->delivered.load(fr, imm.get_owner()->tribe());
276 } else {
277 throw UnhandledVersionError(
278 "ActConstructData", packet_version, kCurrentPacketVersionConstructionData);
279 }
280 } catch (const WException& e) {
281 delete d;
282 d = nullptr;
283 throw GameDataError("ActConstructData: %s", e.what());
284 }
285
286 return d;
287}
288
289
290void ImmovableProgram::ActConstruct::execute(Game& g, Immovable& imm) const {
291 ActConstructData* d = imm.get_action_data<ActConstructData>();
292 if (!d) {
293 // First execution
294 d = new ActConstructData;
295 imm.set_action_data(d);
296
297 imm.start_animation(g, imm.descr().get_animation(animation_name_, &imm));
298 imm.anim_construction_total_ = imm.descr().buildcost().total();
299 } else {
300 // Perhaps we are called due to the construction timeout of the last construction step
301 Buildcost remaining;
302 imm.construct_remaining_buildcost(g, &remaining);
303 if (remaining.empty()) {
304 imm.program_step(g);
305 return;
306 }
307
308 // Otherwise, this is a decay timeout
309 uint32_t totaldelivered = 0;
310 for (const auto& addme : d->delivered) {
311 totaldelivered += addme.second;
312 }
313
314 if (!totaldelivered) {
315 imm.remove(g);
316 return;
317 }
318
319 uint32_t randdecay = g.logic_rand() % totaldelivered;
320 for (Buildcost::iterator it = d->delivered.begin(); it != d->delivered.end(); ++it) {
321 if (randdecay < it->second) {
322 it->second--;
323 break;
324 }
325
326 randdecay -= it->second;
327 }
328
329 imm.anim_construction_done_ = d->delivered.total();
330 }
331
332 imm.program_step_ = imm.schedule_act(g, decaytime_);
333}
334
335ImmovableActionData*
336ImmovableActionData::load(FileRead& fr, Immovable& imm, const std::string& name) {
337 if (name == "construct") {
338 return ActConstructData::load(fr, imm);
339 } else {
340 log("ImmovableActionData::load: type %s not known", name.c_str());
341 return nullptr;
342 }
343}
344} // namespace Widelands
0345
=== modified file 'src/logic/map_objects/immovable_program.h'
--- src/logic/map_objects/immovable_program.h 2019-04-26 12:46:40 +0000
+++ src/logic/map_objects/immovable_program.h 2019-08-31 15:58:01 +0000
@@ -21,21 +21,19 @@
21#define WL_LOGIC_MAP_OBJECTS_IMMOVABLE_PROGRAM_H21#define WL_LOGIC_MAP_OBJECTS_IMMOVABLE_PROGRAM_H
2222
23#include <cstring>23#include <cstring>
24#include <memory>
24#include <string>25#include <string>
2526
26#include "base/macros.h"27#include "base/macros.h"
2728
28/*
29 * Implementation is in immovable.cc
30 */
31
32#include "logic/map_objects/buildcost.h"29#include "logic/map_objects/buildcost.h"
33#include "logic/map_objects/immovable.h"30#include "logic/map_objects/immovable.h"
31#include "logic/map_objects/map_object_program.h"
3432
35namespace Widelands {33namespace Widelands {
3634
37/// Ordered sequence of actions (at least 1). Has a name.35/// Ordered sequence of actions (at least 1). Has a name.
38struct ImmovableProgram {36struct ImmovableProgram : public MapObjectProgram {
3937
40 /// Can be executed on an Immovable.38 /// Can be executed on an Immovable.
41 struct Action {39 struct Action {
@@ -63,15 +61,14 @@
63 /// will not be stopped by this command. It will run until another animation61 /// will not be stopped by this command. It will run until another animation
64 /// is started.)62 /// is started.)
65 struct ActAnimate : public Action {63 struct ActAnimate : public Action {
66 ActAnimate(char* parameters, ImmovableDescr&);64 ActAnimate(const std::vector<std::string>& arguments, const ImmovableDescr&);
67 void execute(Game&, Immovable&) const override;65 void execute(Game&, Immovable&) const override;
68 const std::string& animation() const {66 uint32_t animation() const {
69 return animation_name_;67 return parameters.animation;
70 }68 }
7169
72 private:70 private:
73 std::string animation_name_;71 AnimationParameters parameters;
74 Duration duration_;
75 };72 };
7673
77 /// Transforms the immovable into another immovable or into a bob74 /// Transforms the immovable into another immovable or into a bob
@@ -91,28 +88,26 @@
91 /// name:88 /// name:
92 /// name of the replacement object89 /// name of the replacement object
93 struct ActTransform : public Action {90 struct ActTransform : public Action {
94 ActTransform(char* parameters, ImmovableDescr&);91 ActTransform(std::vector<std::string>& arguments, const ImmovableDescr&);
95 void execute(Game&, Immovable&) const override;92 void execute(Game&, Immovable&) const override;
9693
97 private:94 private:
98 std::string type_name;95 std::string type_name;
99 bool bob;96 bool bob;
100 bool tribe;
101 uint8_t probability;97 uint8_t probability;
102 };98 };
10399
104 /// Like ActTransform but the probability is determined by the suitability.100 /// Like ActTransform but the probability is determined by the suitability.
105 struct ActGrow : public Action {101 struct ActGrow : public Action {
106 ActGrow(char* parameters, ImmovableDescr&);102 ActGrow(std::vector<std::string>& arguments, const ImmovableDescr&);
107 void execute(Game&, Immovable&) const override;103 void execute(Game&, Immovable&) const override;
108104
109 private:105 private:
110 std::string type_name;106 std::string type_name;
111 bool tribe;
112 };107 };
113108
114 struct ActRemove : public Action {109 struct ActRemove : public Action {
115 ActRemove(char* parameters, ImmovableDescr&);110 ActRemove(std::vector<std::string>& arguments);
116 void execute(Game&, Immovable&) const override;111 void execute(Game&, Immovable&) const override;
117112
118 private:113 private:
@@ -120,7 +115,7 @@
120 };115 };
121116
122 struct ActSeed : public Action {117 struct ActSeed : public Action {
123 ActSeed(char* parameters, ImmovableDescr&);118 ActSeed(std::vector<std::string>& arguments, const ImmovableDescr&);
124 void execute(Game&, Immovable&) const override;119 void execute(Game&, Immovable&) const override;
125120
126 private:121 private:
@@ -142,12 +137,11 @@
142 /// Plays the specified sound effect with the specified priority. Whether the137 /// Plays the specified sound effect with the specified priority. Whether the
143 /// sound effect is actually played is determined by the sound handler.138 /// sound effect is actually played is determined by the sound handler.
144 struct ActPlaySound : public Action {139 struct ActPlaySound : public Action {
145 ActPlaySound(char* parameters, const ImmovableDescr&);140 ActPlaySound(const std::vector<std::string>& arguments);
146 void execute(Game&, Immovable&) const override;141 void execute(Game&, Immovable&) const override;
147142
148 private:143 private:
149 FxId fx;144 PlaySoundParameters parameters;
150 uint8_t priority;
151 };145 };
152146
153 /**147 /**
@@ -164,7 +158,7 @@
164 * Time until construction decays one step if no progress has been made.158 * Time until construction decays one step if no progress has been made.
165 */159 */
166 struct ActConstruct : public Action {160 struct ActConstruct : public Action {
167 ActConstruct(char* parameters, ImmovableDescr&);161 ActConstruct(std::vector<std::string>& arguments, const ImmovableDescr&);
168 void execute(Game&, Immovable&) const override;162 void execute(Game&, Immovable&) const override;
169163
170 Duration buildtime() const {164 Duration buildtime() const {
@@ -181,24 +175,16 @@
181 };175 };
182176
183 /// Create a program with a single action.177 /// Create a program with a single action.
184 ImmovableProgram(char const* const init_name, Action* const action) : name_(init_name) {178 ImmovableProgram(const std::string& init_name, std::unique_ptr<Action> action);
185 actions_.push_back(action);
186 }
187179
188 // Create an immovable program from a number of lines.180 /// Create an immovable program from a number of lines.
189 ImmovableProgram(const std::string& init_name,181 ImmovableProgram(const std::string& init_name,
190 const std::vector<std::string>& lines,182 const std::vector<std::string>& lines,
191 ImmovableDescr* immovable);183 const ImmovableDescr& immovable);
192184
193 ~ImmovableProgram() {185 ~ImmovableProgram() {
194 for (Action* action : actions_) {
195 delete action;
196 }
197 }186 }
198187
199 const std::string& name() const {
200 return name_;
201 }
202 size_t size() const {188 size_t size() const {
203 return actions_.size();189 return actions_.size();
204 }190 }
@@ -207,14 +193,8 @@
207 return *actions_[idx];193 return *actions_[idx];
208 }194 }
209195
210 using Actions = std::vector<Action*>;
211 const Actions& actions() const {
212 return actions_;
213 }
214
215private:196private:
216 std::string name_;197 std::vector<std::unique_ptr<Action>> actions_;
217 Actions actions_;
218};198};
219199
220struct ImmovableActionData {200struct ImmovableActionData {
@@ -224,10 +204,18 @@
224 }204 }
225205
226 virtual const char* name() const = 0;206 virtual const char* name() const = 0;
227 virtual void save(FileWrite& fw, Immovable& imm) = 0;207 virtual void save(FileWrite& fw, Immovable& imm) const = 0;
228208
229 static ImmovableActionData* load(FileRead& fr, Immovable& imm, const std::string& name);209 static ImmovableActionData* load(FileRead& fr, Immovable& imm, const std::string& name);
230};210};
211
212struct ActConstructData : ImmovableActionData {
213 const char* name() const override;
214 void save(FileWrite& fw, Immovable& imm) const override;
215 static ActConstructData* load(FileRead& fr, Immovable& imm);
216
217 Buildcost delivered;
218};
231} // namespace Widelands219} // namespace Widelands
232220
233#endif // end of include guard: WL_LOGIC_MAP_OBJECTS_IMMOVABLE_PROGRAM_H221#endif // end of include guard: WL_LOGIC_MAP_OBJECTS_IMMOVABLE_PROGRAM_H
234222
=== modified file 'src/logic/map_objects/map_object.h'
--- src/logic/map_objects/map_object.h 2019-05-25 08:51:42 +0000
+++ src/logic/map_objects/map_object.h 2019-08-31 15:58:01 +0000
@@ -125,6 +125,7 @@
125 }125 }
126126
127 virtual uint32_t get_animation(const std::string& animname, const MapObject* mo) const;127 virtual uint32_t get_animation(const std::string& animname, const MapObject* mo) const;
128
128 uint32_t main_animation() const;129 uint32_t main_animation() const;
129 std::string get_animation_name(uint32_t) const; ///< needed for save, debug130 std::string get_animation_name(uint32_t) const; ///< needed for save, debug
130131
131132
=== added file 'src/logic/map_objects/map_object_program.cc'
--- src/logic/map_objects/map_object_program.cc 1970-01-01 00:00:00 +0000
+++ src/logic/map_objects/map_object_program.cc 2019-08-31 15:58:01 +0000
@@ -0,0 +1,125 @@
1/*
2 * Copyright (C) 2002-2019 by the Widelands Development Team
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 */
19
20#include "logic/map_objects/map_object_program.h"
21
22#include "io/filesystem/layered_filesystem.h"
23#include "logic/game_data_error.h"
24#include "logic/map_objects/map_object.h"
25#include "sound/sound_handler.h"
26
27namespace Widelands {
28
29MapObjectProgram::MapObjectProgram(const std::string& init_name) : name_ (init_name) {}
30
31const std::string& MapObjectProgram::name() const {
32 return name_;
33}
34
35std::vector<std::string> MapObjectProgram::split_string(const std::string& s, const char* const separators) {
36 std::vector<std::string> result;
37 for (std::string::size_type pos = 0, endpos;
38 (pos = s.find_first_not_of(separators, pos)) != std::string::npos; pos = endpos) {
39 endpos = s.find_first_of(separators, pos);
40 result.push_back(s.substr(pos, endpos - pos));
41 }
42 return result;
43}
44
45
46unsigned int MapObjectProgram::read_int(const std::string& input, int min_value, int max_value) {
47 unsigned int result = 0U;
48 char* endp;
49 long int const value = strtol(input.c_str(), &endp, 0);
50 result = value;
51 if (*endp || result != value) {
52 throw GameDataError("Expected a number but found \"%s\"", input.c_str());
53 }
54 if (value < min_value) {
55 throw GameDataError("Expected a number >= %d but found \"%s\"", min_value, input.c_str());
56 }
57 if (value > max_value) {
58 throw GameDataError("Expected a number <= %d but found \"%s\"", max_value, input.c_str());
59 }
60 return result;
61}
62
63unsigned int MapObjectProgram::read_positive(const std::string& input, int max_value) {
64 return read_int(input, 1, max_value);
65}
66
67MapObjectProgram::ProgramParseInput MapObjectProgram::parse_program_string(const std::string& line) {
68 const std::pair<std::string, std::string> key_values = MapObjectProgram::read_key_value_pair(line, '=');
69 return ProgramParseInput{key_values.first, split_string(key_values.second, " \t\n")};
70}
71
72const std::pair<std::string, std::string> MapObjectProgram::read_key_value_pair(const std::string& input, const char separator, const std::string& default_value, const std::string& expected_key) {
73 const size_t idx = input.find(separator);
74 const std::string key = input.substr(0, idx);
75
76 if (!expected_key.empty()) {
77 if (idx == input.npos) {
78 throw GameDataError("Empty value in '%s' for separator '%c'\n", input.c_str(), separator);
79 }
80 if (key != expected_key) {
81 throw GameDataError("Expected key '%s' but found '%s' in '%s'\n", expected_key.c_str(), key.c_str(), input.c_str());
82 }
83 }
84
85 return std::make_pair(key, idx == input.npos ? default_value : input.substr(idx + 1));
86}
87
88MapObjectProgram::AnimationParameters MapObjectProgram::parse_act_animate(const std::vector<std::string>& arguments, const MapObjectDescr& descr, bool is_idle_allowed) {
89 if (arguments.size() < 1 || arguments.size() > 2) {
90 throw GameDataError("Usage: animate=<name> [<duration>]");
91 }
92
93 AnimationParameters result;
94 const std::string& animation_name = arguments.at(0);
95
96 if (!is_idle_allowed && animation_name == "idle") {
97 throw GameDataError("'idle' animation is default; calling is not allowed");
98 }
99 if (!descr.is_animation_known(animation_name)) {
100 throw GameDataError("Unknown animation '%s'", animation_name.c_str());
101 }
102 result.animation = descr.get_animation(animation_name, nullptr);
103
104 if (arguments.size() == 2) {
105 result.duration = read_positive(arguments.at(1));
106 }
107 return result;
108}
109
110MapObjectProgram::PlaySoundParameters MapObjectProgram::parse_act_play_sound(const std::vector<std::string>& arguments, uint8_t default_priority) {
111 if (arguments.size() < 1 || arguments.size() > 2) {
112 throw GameDataError("Usage: playsound=<sound_dir/sound_name> [priority]");
113 }
114 PlaySoundParameters result;
115 result.fx = SoundHandler::register_fx(SoundType::kAmbient, arguments.at(0));
116
117 result.priority = arguments.size() == 2 ? read_positive(arguments.at(1)) : default_priority;
118 if (result.priority < kFxPriorityLowest) {
119 throw GameDataError("Minmum priority for sounds is %d, but only %d was specified for %s",
120 kFxPriorityLowest, result.priority, arguments.at(0).c_str());
121 }
122 return result;
123}
124
125} // namespace Widelands
0126
=== added file 'src/logic/map_objects/map_object_program.h'
--- src/logic/map_objects/map_object_program.h 1970-01-01 00:00:00 +0000
+++ src/logic/map_objects/map_object_program.h 2019-08-31 15:58:01 +0000
@@ -0,0 +1,98 @@
1/*
2 * Copyright (C) 2002-2019 by the Widelands Development Team
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 */
19
20#ifndef WL_LOGIC_MAP_OBJECTS_MAP_OBJECT_PROGRAM_H
21#define WL_LOGIC_MAP_OBJECTS_MAP_OBJECT_PROGRAM_H
22
23#include <limits>
24#include <string>
25#include <vector>
26
27#include "logic/widelands.h"
28#include "sound/constants.h"
29
30namespace Widelands {
31
32struct MapObjectDescr;
33
34/// Superclass for Worker, Immovable and Productionsite programs. Includes a program name and diverse parsing convenience functions. The creation and execution of program actions is left to the sub-classes.
35struct MapObjectProgram {
36 const std::string& name() const;
37
38 explicit MapObjectProgram(const std::string& init_name);
39 virtual ~MapObjectProgram() = default;
40
41protected:
42 /// Splits a string by separators.
43 /// \note This ignores empty elements, so do not use this for example to split
44 /// a string with newline characters into lines, because it would ignore empty
45 /// lines.
46 static std::vector<std::string> split_string(const std::string&, char const* separators);
47
48 /// Reads an int value from a string. Throws a GameDataError if 'min_value' or 'max_value' are exceeded
49 static unsigned int read_int(const std::string& input, int min_value, int max_value = std::numeric_limits<int32_t>::max());
50 /// Same as 'read_int', with 'min_value' == 1
51 static unsigned int read_positive(const std::string& input, int max_value = std::numeric_limits<int32_t>::max());
52
53 /**
54 * @brief Reads a key-value pair from a string using the given separator, e.g. "attrib:tree", "meat:2", "return=skipped unless economy needs meal"
55 * @param input The string to parse
56 * @param separator The separator for splitting the string, e.g. ':' or '='
57 * @param default_value A default to assign to the right-hand value if the separator is not found
58 * @param expected_key If this is not empty, the left-hand key must match this string
59 * @return A key, value pair
60 */
61 static const std::pair<std::string, std::string> read_key_value_pair(const std::string& input, const char separator, const std::string& default_value = "", const std::string& expected_key = "");
62
63 /// Left-hand and right-hand elements of a line in a program, e.g. parsed from "return=skipped unless economy needs meal"
64 struct ProgramParseInput {
65 /// Program name, e.g. "return"
66 std::string name;
67 /// Program arguments, e.g. { "skipped", "unless", "economy", "needs", "meal" }
68 std::vector<std::string> arguments;
69 };
70 /// Reads the program name and arguments from a string
71 static ProgramParseInput parse_program_string(const std::string& line);
72
73 /// Animation information
74 struct AnimationParameters {
75 /// Animation ID
76 uint32_t animation = 0;
77 /// Animation duration. 0 will play the animation forever.
78 Duration duration = 0;
79 };
80 /// Parses the arguments for an animation action, e.g. { "working", "24000" }. If 'is_idle_allowed' == false, throws a GameDataError if the animation is called "idle".
81 static AnimationParameters parse_act_animate(const std::vector<std::string>& arguments, const MapObjectDescr& descr, bool is_idle_allowed);
82
83 /// Sound effect information
84 struct PlaySoundParameters {
85 /// Sound effect ID
86 FxId fx;
87 /// Sound effect priority
88 uint8_t priority = 0;
89 };
90 /// Parses the arguments for a play_sound action, e.g. { "sound/smiths/sharpening", "120" }
91 static PlaySoundParameters parse_act_play_sound(const std::vector<std::string>& arguments, uint8_t default_priority);
92
93private:
94 const std::string name_;
95};
96} // namespace Widelands
97
98#endif // end of include guard: WL_LOGIC_MAP_OBJECTS_MAP_OBJECT_PROGRAM_H
099
=== modified file 'src/logic/map_objects/tribes/production_program.cc'
--- src/logic/map_objects/tribes/production_program.cc 2019-05-18 11:58:43 +0000
+++ src/logic/map_objects/tribes/production_program.cc 2019-08-31 15:58:01 +0000
@@ -20,10 +20,6 @@
20#include "logic/map_objects/tribes/production_program.h"20#include "logic/map_objects/tribes/production_program.h"
2121
22#include <memory>22#include <memory>
23#include <sstream>
24
25#include <boost/algorithm/string.hpp>
26#include <boost/format.hpp>
2723
28#include "base/i18n.h"24#include "base/i18n.h"
29#include "base/macros.h"25#include "base/macros.h"
@@ -32,7 +28,6 @@
32#include "economy/economy.h"28#include "economy/economy.h"
33#include "economy/flag.h"29#include "economy/flag.h"
34#include "economy/input_queue.h"30#include "economy/input_queue.h"
35#include "helper.h"
36#include "io/filesystem/layered_filesystem.h"31#include "io/filesystem/layered_filesystem.h"
37#include "logic/game.h"32#include "logic/game.h"
38#include "logic/game_data_error.h"33#include "logic/game_data_error.h"
@@ -56,135 +51,42 @@
56namespace Widelands {51namespace Widelands {
5752
58namespace {53namespace {
5954/// If the iterator contents match the string, increment the iterator. Returns whether it matched.
60/// Matches the string that candidate points to against the string that55bool match_and_skip(const std::vector<std::string>& args, std::vector<std::string>::const_iterator& it, const std::string& matchme) {
61/// template points to. Stops at when reaching a null character or the56 const bool result = (it != args.end()) && (*it == matchme);
62/// character terminator. If a match is found, candidate is moved beyond the57 if (result) {
63/// matched part.58 ++it;
64///59 }
65/// example:60 return result;
66/// char const * candidate = "return 75";61}
67/// bool const result = match(candidate, "return");62
68/// now candidate points to " 75" and result is true63ProductionProgram::ActReturn::Condition* create_economy_condition(const std::string& item, const ProductionSiteDescr& descr, const Tribes& tribes) {
69bool match(char*& candidate, const char* pattern) {64 DescriptionIndex index = tribes.ware_index(item);
70 for (char* p = candidate;; ++p, ++pattern)65 if (tribes.ware_exists(index)) {
71 if (!*pattern) {66 descr.ware_demand_checks()->insert(index);
72 candidate = p;67 return new ProductionProgram::ActReturn::EconomyNeedsWare(index);
73 return true;68 } else if (tribes.worker_exists(tribes.worker_index(item))) {
74 } else if (*p != *pattern)69 index = tribes.worker_index(item);
75 break;70 descr.worker_demand_checks()->insert(index);
76 return false;71 return new ProductionProgram::ActReturn::EconomyNeedsWorker(index);
77}72 } else {
7873 throw GameDataError("Expected ware or worker type but found '%s'", item.c_str());
79/// Skips a sequence of consecutive characters with the value c, starting at p.74 }
80/// Throws WException if no characters were skipped.75}
81void force_skip(char*& p, char const c = ' ') {76
82 char* t = p;77TrainingAttribute parse_training_attribute(const std::string& argument) {
83 while (*t == c)78 if (argument == "health") {
84 ++t;79 return TrainingAttribute::kHealth;
85 if (p < t)80 } else if (argument == "attack") {
86 p = t;81 return TrainingAttribute::kAttack;
87 else82 } else if (argument == "defense") {
88 throw wexception("expected '%c' but found \"%s\"", c, p);83 return TrainingAttribute::kDefense;
89}84 } else if (argument == "evade") {
9085 return TrainingAttribute::kEvade;
91/// Skips a sequence of consecutive characters with the value c, starting at p.86 } else {
92/// Returns whether any characters were skipped.87 throw GameDataError("Expected health|attack|defense|evade after 'soldier' but found '%s'", argument.c_str());
93bool skip(char*& p, char const c = ' ') {88 }
94 char* t = p;89}
95 while (*t == c)
96 ++t;
97 if (p < t) {
98 p = t;
99 return true;
100 } else
101 return false;
102}
103
104/// Combines match and force_skip.
105///
106/// example:
107/// char const * candidate = "return 75";
108/// bool const result = match_force_skip(candidate, "return");
109/// now candidate points to "75" and result is true
110///
111/// example:
112/// char const * candidate = "return75";
113/// bool const result = match_force_skip(candidate, "return");
114/// throws WException
115bool match_force_skip(char*& candidate, const char* pattern) {
116 for (char* p = candidate;; ++p, ++pattern)
117 if (!*pattern) {
118 force_skip(p);
119 candidate = p;
120 return true;
121 } else if (*p != *pattern)
122 return false;
123
124 NEVER_HERE();
125}
126
127ProductionProgram::ActReturn::Condition* create_economy_condition(char*& parameters,
128 const Tribes& tribes) {
129 try {
130 if (match_force_skip(parameters, "needs"))
131 try {
132 bool reached_end;
133 char const* const type_name = next_word(parameters, reached_end);
134 const DescriptionIndex& wareindex = tribes.ware_index(type_name);
135 if (tribes.ware_exists(wareindex)) {
136 for (size_t i = 0; i < tribes.nrtribes(); ++i) {
137 const TribeDescr& tribe_descr = *tribes.get_tribe_descr(i);
138 if (tribe_descr.has_ware(wareindex)) {
139 tribes.set_ware_type_has_demand_check(wareindex, tribe_descr.name());
140 }
141 }
142 return new ProductionProgram::ActReturn::EconomyNeedsWare(wareindex);
143 } else if (tribes.worker_exists(tribes.worker_index(type_name))) {
144 const DescriptionIndex& workerindex = tribes.worker_index(type_name);
145 for (size_t i = 0; i < tribes.nrtribes(); ++i) {
146 const TribeDescr* tribe_descr = tribes.get_tribe_descr(i);
147 if (tribe_descr->has_worker(workerindex)) {
148 tribes.set_worker_type_has_demand_check(workerindex);
149 }
150 }
151 return new ProductionProgram::ActReturn::EconomyNeedsWorker(workerindex);
152 } else
153 throw GameDataError(
154 "expected %s but found \"%s\"", "ware type or worker type", type_name);
155 } catch (const WException& e) {
156 throw GameDataError("needs: %s", e.what());
157 }
158 else
159 throw GameDataError("expected %s but found \"%s\"", "\"needs\"", parameters);
160 } catch (const WException& e) {
161 throw GameDataError("economy: %s", e.what());
162 }
163}
164
165ProductionProgram::ActReturn::Condition*
166create_site_condition(char*& parameters, const ProductionSiteDescr& descr, const Tribes& tribes) {
167 try {
168 if (match_force_skip(parameters, "has"))
169 return new ProductionProgram::ActReturn::SiteHas(parameters, descr, tribes);
170 else
171 throw GameDataError("expected %s but found \"%s\"", "\"has\"", parameters);
172 } catch (const WException& e) {
173 throw GameDataError("site: %s", e.what());
174 }
175}
176
177ProductionProgram::ActReturn::Condition* create_workers_condition(char*& parameters) {
178 try {
179 if (match(parameters, "need experience"))
180 return new ProductionProgram::ActReturn::WorkersNeedExperience;
181 else
182 throw GameDataError("expected %s but found \"%s\"", "\"need experience\"", parameters);
183 } catch (const WException& e) {
184 throw GameDataError("workers: %s", e.what());
185 }
186}
187
188} // namespace90} // namespace
18991
190ProductionProgram::Action::~Action() {92ProductionProgram::Action::~Action() {
@@ -197,90 +99,99 @@
197void ProductionProgram::Action::building_work_failed(Game&, ProductionSite&, Worker&) const {99void ProductionProgram::Action::building_work_failed(Game&, ProductionSite&, Worker&) const {
198}100}
199101
200void ProductionProgram::parse_ware_type_group(char*& parameters,102ProductionProgram::Groups ProductionProgram::parse_ware_type_groups(std::vector<std::string>::const_iterator begin, std::vector<std::string>::const_iterator end, const ProductionSiteDescr& descr, const Tribes& tribes) {
201 WareTypeGroup& group,103 ProductionProgram::Groups result;
202 const Tribes& tribes,104
203 const BillOfMaterials& input_wares,105 for (auto& it = begin; it != end; ++it) {
204 const BillOfMaterials& input_workers) {106 const std::pair<std::string, std::string> names_to_amount = read_key_value_pair(*it, ':', "1");
205 std::set<std::pair<DescriptionIndex, WareWorker>>::iterator last_insert_pos = group.first.end();107 const uint8_t amount = read_positive(names_to_amount.second);
206 uint8_t count = 1;108 uint8_t max_amount = 0;
207 uint8_t count_max = 0;109 std::set<std::pair<DescriptionIndex, WareWorker>> ware_worker_names;
208 for (;;) {110 for (const std::string& item_name : split_string(names_to_amount.first, ",")) {
209 char const* ware = parameters;111 // Try as ware
210 while (*parameters && *parameters != ',' && *parameters != ':' && *parameters != ' ')112 WareWorker type = wwWARE;
211 ++parameters;113 DescriptionIndex item_index = tribes.ware_index(item_name);
212 char const terminator = *parameters;114 if (!tribes.ware_exists(item_index)) {
213 *parameters = '\0';115 item_index = tribes.worker_index(item_name);
214116 if (tribes.worker_exists(item_index)) {
215 // Try as ware117 // It is a worker
216 WareWorker type = wwWARE;118 type = wwWORKER;
217 const BillOfMaterials* input_list = &input_wares;119 } else {
218 DescriptionIndex ware_index = tribes.ware_index(ware);120 throw GameDataError("Expected ware or worker type but found '%s'", item_name.c_str());
219 if (!tribes.ware_exists(ware_index)) {121 }
220 ware_index = tribes.worker_index(ware);122 }
221 if (tribes.worker_exists(ware_index)) {123
222 // It is a worker124 // Sanity checks
223 type = wwWORKER;125 bool found = false;
224 input_list = &input_workers;126 const BillOfMaterials& inputs = (type == wwWARE) ? descr.input_wares() : descr.input_workers();
225 } else {127 for (const WareAmount& input : inputs) {
226 throw GameDataError("Unknown ware or worker type \"%s\"", ware);128 if (input.first == item_index) {
227 }129 max_amount += input.second;
228 }130 found = true;
229131 break;
230 bool found = false;132 }
231 for (const WareAmount& input : *input_list) {133 }
232 if (input.first == ware_index) {134 if (!found) {
233 count_max += input.second;135 throw GameDataError("%s was not declared in the building's 'inputs' table", item_name.c_str());
234 found = true;136 }
235 break;137
236 }138 if (max_amount < amount) {
237 }139 throw GameDataError("Ware/worker count is %u but (total) input storage capacity of "
238 if (!found) {140 "the specified ware type(s) is only %u, so the ware/worker requirement can "
239 throw GameDataError("%s is not declared as an input (\"%s=<count>\" was not "
240 "found in the [inputs] section)",
241 ware, ware);
242 }
243
244 if (group.first.size() && ware_index <= group.first.begin()->first)
245 throw GameDataError("wrong order of ware types within group: ware type %s appears "
246 "after ware type %s (fix order!)",
247 ware,
248 tribes.get_ware_descr(group.first.begin()->first)->name().c_str());
249 last_insert_pos = group.first.insert(last_insert_pos, std::make_pair(ware_index, type));
250 *parameters = terminator;
251 switch (terminator) {
252 case ':': {
253 ++parameters;
254 char* endp;
255 unsigned long long int const value = strtoull(parameters, &endp, 0);
256 count = value;
257 if ((*endp && *endp != ' ') || value < 1 || count != value)
258 throw GameDataError("expected %s but found \"%s\"", "count", parameters);
259 parameters = endp;
260 if (count_max < count)
261 throw GameDataError("group count is %u but (total) input storage capacity of "
262 "the specified ware type(s) is only %u, so the group can "
263 "never be fulfilled by the site",141 "never be fulfilled by the site",
264 count, count_max);142 static_cast<unsigned int>(amount), static_cast<unsigned int>(max_amount));
265 }143 }
266 FALLS_THROUGH;144 // Add item
267 case '\0':145 ware_worker_names.insert(std::make_pair(item_index, type));
268 case ' ':146 }
269 group.second = count;147 // Add set
270 return;148 result.push_back(std::make_pair(ware_worker_names, amount));
271 case ',':149 }
272 ++parameters;150 if (result.empty()) {
273 break;151 throw GameDataError("No wares or workers found");
274 default:152 }
275 // scan for terminator should ensure that this cannot happen153 return result;
276 NEVER_HERE();154}
277 }155
278 }156
157BillOfMaterials ProductionProgram::parse_bill_of_materials(const std::vector<std::string>& arguments,
158 WareWorker ww,
159 const ProductionSiteDescr& descr,
160 const Tribes& tribes) {
161 BillOfMaterials result;
162 for (const std::string& argument : arguments) {
163 const std::pair<std::string, std::string> produceme = read_key_value_pair(argument, ':', "1");
164
165 const DescriptionIndex index = ww == WareWorker::wwWARE ?
166 tribes.safe_ware_index(produceme.first) :
167 tribes.safe_worker_index(produceme.first);
168
169 // Verify the building outputs
170 switch (ww) {
171 case WareWorker::wwWARE:
172 if (!descr.is_output_ware_type(index)) {
173 throw GameDataError("Ware '%s' is not listed in the building's 'outputs' table",
174 produceme.first.c_str());
175 } break;
176 case WareWorker::wwWORKER:
177 if (!descr.is_output_worker_type(index)) {
178 throw GameDataError("Worker '%s' is not listed in the building's 'outputs' table",
179 produceme.first.c_str());
180 } break;
181 }
182
183 result.push_back(std::make_pair(index, read_positive(produceme.second)));
184 }
185 return result;
279}186}
280187
281ProductionProgram::ActReturn::Condition::~Condition() {188ProductionProgram::ActReturn::Condition::~Condition() {
282}189}
283190
191ProductionProgram::ActReturn::Negation::Negation(const std::vector<std::string>& arguments, std::vector<std::string>::const_iterator& begin, std::vector<std::string>::const_iterator& end, const ProductionSiteDescr& descr, const Tribes& tribes)
192 : operand(create_condition(arguments, begin, end, descr, tribes)) {
193}
194
284ProductionProgram::ActReturn::Negation::~Negation() {195ProductionProgram::ActReturn::Negation::~Negation() {
285 delete operand;196 delete operand;
286}197}
@@ -343,13 +254,14 @@
343 return result;254 return result;
344}255}
345256
346ProductionProgram::ActReturn::SiteHas::SiteHas(char*& parameters,257ProductionProgram::ActReturn::SiteHas::SiteHas(std::vector<std::string>::const_iterator begin,
258 std::vector<std::string>::const_iterator end,
347 const ProductionSiteDescr& descr,259 const ProductionSiteDescr& descr,
348 const Tribes& tribes) {260 const Tribes& tribes) {
349 try {261 try {
350 parse_ware_type_group(parameters, group, tribes, descr.input_wares(), descr.input_workers());262 group = parse_ware_type_groups(begin, end, descr, tribes).front();
351 } catch (const WException& e) {263 } catch (const GameDataError& e) {
352 throw GameDataError("has ware_type1[,ware_type2[,...]][:N]: %s", e.what());264 throw GameDataError("Expected <ware or worker>[,<ware or worker>[,...]][:<amount>] after 'site has' but got %s", e.what());
353 }265 }
354}266}
355bool ProductionProgram::ActReturn::SiteHas::evaluate(const ProductionSite& ps) const {267bool ProductionProgram::ActReturn::SiteHas::evaluate(const ProductionSite& ps) const {
@@ -438,76 +350,89 @@
438 return _("the workers need no experience");350 return _("the workers need no experience");
439}351}
440352
441ProductionProgram::ActReturn::Condition* ProductionProgram::ActReturn::create_condition(353ProductionProgram::ActReturn::Condition* ProductionProgram::ActReturn::create_condition(const std::vector<std::string>& arguments,
442 char*& parameters, const ProductionSiteDescr& descr, const Tribes& tribes) {354 std::vector<std::string>::const_iterator& begin, std::vector<std::string>::const_iterator& end, const ProductionSiteDescr& descr, const Tribes& tribes) {
355 if (begin == end) {
356 throw GameDataError("Expected a condition after '%s'", (begin - 1)->c_str());
357 }
443 try {358 try {
444 if (match_force_skip(parameters, "not"))359 if (match_and_skip(arguments, begin, "not")) {
445 return new ActReturn::Negation(parameters, descr, tribes);360 return new ActReturn::Negation(arguments, begin, end, descr, tribes);
446 else if (match_force_skip(parameters, "economy"))361 } else if (match_and_skip(arguments, begin, "economy")) {
447 return create_economy_condition(parameters, tribes);362 if (!match_and_skip(arguments, begin, "needs")) {
448 else if (match_force_skip(parameters, "site"))363 throw GameDataError("Expected 'needs' after 'economy' but found '%s'", begin->c_str());
449 return create_site_condition(parameters, descr, tribes);364 }
450 else if (match_force_skip(parameters, "workers"))365 return create_economy_condition(*begin, descr, tribes);
451 return create_workers_condition(parameters);366 } else if (match_and_skip(arguments, begin, "site")) {
452 else367 if (!match_and_skip(arguments, begin, "has")) {
368 throw GameDataError("Expected 'has' after 'site' but found '%s'", begin->c_str());
369 }
370 return new ProductionProgram::ActReturn::SiteHas(begin, end, descr, tribes);
371 } else if (match_and_skip(arguments, begin, "workers")) {
372 if (!match_and_skip(arguments, begin, "need")) {
373 throw GameDataError("Expected 'need experience' after 'workers' but found '%s'", begin->c_str());
374 }
375 if (!match_and_skip(arguments, begin, "experience")) {
376 throw GameDataError("Expected 'experience' after 'workers need' but found '%s'", begin->c_str());
377 }
378 return new ProductionProgram::ActReturn::WorkersNeedExperience();
379 } else {
453 throw GameDataError(380 throw GameDataError(
454 "expected %s but found \"%s\"", "{\"not\"|\"economy\"|\"workers\"}", parameters);381 "Expected not|economy|site|workers after '%s' but found '%s'", (begin - 1)->c_str(), begin->c_str());
382 }
455 } catch (const WException& e) {383 } catch (const WException& e) {
456 throw GameDataError("invalid condition: %s", e.what());384 throw GameDataError("Invalid condition. %s", e.what());
457 }385 }
458}386}
459387
460ProductionProgram::ActReturn::ActReturn(char* parameters,388ProductionProgram::ActReturn::ActReturn(const std::vector<std::string>& arguments,
461 const ProductionSiteDescr& descr,389 const ProductionSiteDescr& descr,
462 const Tribes& tribes) {390 const Tribes& tribes) {
463 try {391 if (arguments.empty()) {
464 if (match(parameters, "failed"))392 throw GameDataError("Usage: return=failed|completed|skipped|no_stats [when|unless <conditions>]");
465 result_ = ProgramResult::kFailed;393 }
466 else if (match(parameters, "completed"))394 auto begin = arguments.begin();
467 result_ = ProgramResult::kCompleted;395
468 else if (match(parameters, "skipped"))396 if (match_and_skip(arguments, begin, "failed")) {
469 result_ = ProgramResult::kSkipped;397 result_ = ProgramResult::kFailed;
470 else if (match(parameters, "no_stats"))398 } else if (match_and_skip(arguments, begin, "completed")) {
471 result_ = ProgramResult::kNone;399 result_ = ProgramResult::kCompleted;
472 else400 } else if (match_and_skip(arguments, begin, "skipped")) {
473 throw GameDataError("expected %s but found \"%s\"",401 result_ = ProgramResult::kSkipped;
474 "{\"failed\"|\"completed\"|\"skipped\"|\"no_stats\"}", parameters);402 } else if (match_and_skip(arguments, begin, "no_stats")) {
475403 result_ = ProgramResult::kNone;
476 if (skip(parameters)) {404 } else {
477 if (match_force_skip(parameters, "when")) {405 throw GameDataError("Usage: return=failed|completed|skipped|no_stats [when|unless <conditions>]");
478 is_when_ = true;406 }
479 for (;;) {407
480 conditions_.push_back(create_condition(parameters, descr, tribes));408
481 if (*parameters) {409 // Parse all arguments starting from the given iterator into our 'conditions_', splitting individual conditions by the given 'separator'
482 skip(parameters);410 auto parse_conditions = [this, &descr, &tribes] (const std::vector<std::string>& args, std::vector<std::string>::const_iterator it, const std::string& separator) {
483 if (!match_force_skip(parameters, "and"))411 while (it != args.end()) {
484 throw GameDataError("expected \"%s\" or end of input", "and");412 auto end = it + 1;
485 } else413 while (end != args.end() && *end != separator) {
486 break;414 ++end;
487 }415 }
488 } else if (match_force_skip(parameters, "unless")) {416 if (it == end) {
489 is_when_ = false;417 throw GameDataError("Expected: [%s] <condition> after '%s'", separator.c_str(), (it - 1)->c_str());
490 for (;;) {418 }
491 if (!*parameters)419
492 throw GameDataError("expected condition at end of input");420 conditions_.push_back(create_condition(args, it, end, descr, tribes));
493 conditions_.push_back(create_condition(parameters, descr, tribes));421 match_and_skip(args, end, separator);
494 if (*parameters) {422 it = end;
495 skip(parameters);423 }
496 if (!match_force_skip(parameters, "or"))424 };
497 throw GameDataError("expected \"%s\" or end of input", "or");425
498 } else426 is_when_ = true;
499 break;427 if (begin != arguments.end()) {
500 }428 if (match_and_skip(arguments, begin, "when")) {
501 } else429 parse_conditions(arguments, begin, "and");
502 throw GameDataError(430 } else if (match_and_skip(arguments, begin, "unless")) {
503 "expected %s but found \"%s\"", "{\"when\"|\"unless\"}", parameters);431 is_when_ = false;
504 } else if (*parameters)432 parse_conditions(arguments, begin, "or");
505 throw GameDataError("expected %s but found \"%s\"", ("space or end of input"), parameters);433 } else {
506 else434 throw GameDataError("Expected when|unless but found '%s'", begin->c_str());
507 is_when_ = true;435 }
508
509 } catch (const WException& e) {
510 throw GameDataError("return: %s", e.what());
511 }436 }
512}437}
513438
@@ -573,7 +498,11 @@
573 return ps.program_end(game, result_);498 return ps.program_end(game, result_);
574}499}
575500
576ProductionProgram::ActCall::ActCall(char* parameters, const ProductionSiteDescr& descr) {501ProductionProgram::ActCall::ActCall(const std::vector<std::string>& arguments, const ProductionSiteDescr& descr) {
502 if (arguments.size() < 1 || arguments.size() > 4) {
503 throw GameDataError("Usage: call=<program name> [on failure|completion|skip fail|complete|skip|repeat]");
504 }
505
577 // Initialize with default handling methods.506 // Initialize with default handling methods.
578 handling_methods_[program_result_index(ProgramResult::kFailed)] =507 handling_methods_[program_result_index(ProgramResult::kFailed)] =
579 ProgramResultHandlingMethod::kContinue;508 ProgramResultHandlingMethod::kContinue;
@@ -582,61 +511,60 @@
582 handling_methods_[program_result_index(ProgramResult::kSkipped)] =511 handling_methods_[program_result_index(ProgramResult::kSkipped)] =
583 ProgramResultHandlingMethod::kContinue;512 ProgramResultHandlingMethod::kContinue;
584513
585 try {514 // Fetch program to call
586 bool reached_end;515 const std::string& program_name = arguments.front();
587 {516 const ProductionSiteDescr::Programs& programs = descr.programs();
588 char const* const program_name = next_word(parameters, reached_end);517 ProductionSiteDescr::Programs::const_iterator const it = programs.find(program_name);
589 const ProductionSiteDescr::Programs& programs = descr.programs();518 if (it == programs.end()) {
590 ProductionSiteDescr::Programs::const_iterator const it = programs.find(program_name);519 throw GameDataError("The program '%s' has not (yet) been declared in %s "
591 if (it == programs.end())520 "(wrong declaration order?)",
592 throw GameDataError("the program \"%s\" has not (yet) been declared in %s "521 program_name.c_str(), descr.name().c_str());
593 "(wrong declaration order?)",522 }
594 program_name, descr.name().c_str());523 program_ = it->second.get();
595 program_ = it->second.get();524
596 }525 // Override with specified handling methods.
597526 if (arguments.size() > 1) {
598 // Override with specified handling methods.527 if (arguments.at(1) != "on") {
599 while (!reached_end) {528 throw GameDataError("Expected 'on' keyword in second position");
600 skip(parameters);529 }
601 match_force_skip(parameters, "on");530
602531 ProgramResult result_to_set_method_for;
603 ProgramResult result_to_set_method_for;532 if (arguments.at(2) == "failure") {
604 if (match_force_skip(parameters, "failure")) {533 if (handling_methods_[program_result_index(ProgramResult::kFailed)] !=
605 if (handling_methods_[program_result_index(ProgramResult::kFailed)] !=534 ProgramResultHandlingMethod::kContinue) {
606 ProgramResultHandlingMethod::kContinue)535 throw GameDataError("%s handling method already defined", "failure");
607 throw GameDataError("%s handling method already defined", "failure");536 }
608 result_to_set_method_for = ProgramResult::kFailed;537 result_to_set_method_for = ProgramResult::kFailed;
609 } else if (match_force_skip(parameters, "completion")) {538 } else if (arguments.at(2) == "completion") {
610 if (handling_methods_[program_result_index(ProgramResult::kCompleted)] !=539 if (handling_methods_[program_result_index(ProgramResult::kCompleted)] !=
611 ProgramResultHandlingMethod::kContinue)540 ProgramResultHandlingMethod::kContinue) {
612 throw GameDataError("%s handling method already defined", "completion");541 throw GameDataError("%s handling method already defined", "completion");
613 result_to_set_method_for = ProgramResult::kCompleted;542 }
614 } else if (match_force_skip(parameters, "skip")) {543 result_to_set_method_for = ProgramResult::kCompleted;
615 if (handling_methods_[program_result_index(ProgramResult::kSkipped)] !=544 } else if (arguments.at(2) == "skip") {
616 ProgramResultHandlingMethod::kContinue)545 if (handling_methods_[program_result_index(ProgramResult::kSkipped)] !=
617 throw GameDataError("%s handling method already defined", "skip");546 ProgramResultHandlingMethod::kContinue) {
618 result_to_set_method_for = ProgramResult::kSkipped;547 throw GameDataError("%s handling method already defined", "skip");
619 } else548 }
620 throw GameDataError(549 result_to_set_method_for = ProgramResult::kSkipped;
621 "expected %s but found \"%s\"", "{\"failure\"|\"completion\"|\"skip\"}", parameters);550 } else {
622551 throw GameDataError(
623 ProgramResultHandlingMethod handling_method;552 "Expected failure|completion|skip after 'on' but found '%s'", arguments.at(2).c_str());
624 if (match(parameters, "fail"))553 }
625 handling_method = ProgramResultHandlingMethod::kFail;554
626 else if (match(parameters, "complete"))555 ProgramResultHandlingMethod handling_method;
627 handling_method = ProgramResultHandlingMethod::kComplete;556 if (arguments.at(3) == "fail") {
628 else if (match(parameters, "skip"))557 handling_method = ProgramResultHandlingMethod::kFail;
629 handling_method = ProgramResultHandlingMethod::kSkip;558 } else if (arguments.at(3) == "complete") {
630 else if (match(parameters, "repeat"))559 handling_method = ProgramResultHandlingMethod::kComplete;
631 handling_method = ProgramResultHandlingMethod::kRepeat;560 } else if (arguments.at(3) == "skip") {
632 else561 handling_method = ProgramResultHandlingMethod::kSkip;
633 throw GameDataError("expected %s but found \"%s\"",562 } else if (arguments.at(3) == "repeat") {
634 "{\"fail\"|\"complete\"|\"skip\"|\"repeat\"}", parameters);563 handling_method = ProgramResultHandlingMethod::kRepeat;
635 handling_methods_[program_result_index(result_to_set_method_for)] = handling_method;564 } else {
636 reached_end = !*parameters;565 throw GameDataError("Expected fail|complete|skip|repeat in final position but found '%s'", arguments.at(3).c_str());
637 }566 }
638 } catch (const WException& e) {567 handling_methods_[program_result_index(result_to_set_method_for)] = handling_method;
639 throw GameDataError("call: %s", e.what());
640 }568 }
641}569}
642570
@@ -662,38 +590,38 @@
662 }590 }
663}591}
664592
665ProductionProgram::ActCallWorker::ActCallWorker(char* parameters,593ProductionProgram::ActCallWorker::ActCallWorker(const std::vector<std::string>& arguments,
666 const std::string& production_program_name,594 const std::string& production_program_name,
667 ProductionSiteDescr* descr,595 ProductionSiteDescr* descr,
668 const Tribes& tribes) {596 const Tribes& tribes) {
669 try {597 if (arguments.size() != 1) {
670 program_ = parameters;598 throw GameDataError("Usage: callworker=<worker program name>");
671599 }
672 // Quote form "void ProductionSite::program_act(Game &)":600
673 // "Always main worker is doing stuff"601 program_ = arguments.front();
674 const WorkerDescr& main_worker_descr =602
675 *tribes.get_worker_descr(descr->working_positions()[0].first);603 // Quote from "void ProductionSite::program_act(Game &)":
676604 // "Always main worker is doing stuff"
677 // This will fail unless the main worker has a program with the given605 const WorkerDescr& main_worker_descr =
678 // name, so it also validates the parameter.606 *tribes.get_worker_descr(descr->working_positions().front().first);
679 const WorkareaInfo& worker_workarea_info =607
680 main_worker_descr.get_program(program_)->get_workarea_info();608 // This will fail unless the main worker has a program with the given
681609 // name, so it also validates the parameter.
682 for (const auto& area_info : worker_workarea_info) {610 const WorkareaInfo& worker_workarea_info =
683 std::set<std::string>& building_radius_infos = descr->workarea_info_[area_info.first];611 main_worker_descr.get_program(program_)->get_workarea_info();
684612
685 for (const std::string& worker_name : area_info.second) {613 for (const auto& area_info : worker_workarea_info) {
686 std::string description = descr->name();614 std::set<std::string>& building_radius_infos = descr->workarea_info_[area_info.first];
687 description += ' ';615
688 description += production_program_name;616 for (const std::string& worker_name : area_info.second) {
689 description += " worker ";617 std::string description = descr->name();
690 description += main_worker_descr.name();618 description += ' ';
691 description += worker_name;619 description += production_program_name;
692 building_radius_infos.insert(description);620 description += " worker ";
693 }621 description += main_worker_descr.name();
622 description += worker_name;
623 building_radius_infos.insert(description);
694 }624 }
695 } catch (const WException& e) {
696 throw GameDataError("worker: %s", e.what());
697 }625 }
698}626}
699627
@@ -722,42 +650,27 @@
722 psite.program_end(game, ProgramResult::kFailed);650 psite.program_end(game, ProgramResult::kFailed);
723}651}
724652
725ProductionProgram::ActSleep::ActSleep(char* parameters) {653ProductionProgram::ActSleep::ActSleep(const std::vector<std::string>& arguments) {
726 try {654 if (arguments.size() != 1) {
727 if (*parameters) {655 throw GameDataError("Usage: sleep=<duration>");
728 char* endp;
729 long long int const value = strtoll(parameters, &endp, 0);
730 duration_ = value;
731 if (*endp || value <= 0 || duration_ != value)
732 throw GameDataError("expected %s but found \"%s\"", "duration in ms", parameters);
733 } else
734 duration_ = 0; // Get duration from the result of a previous action.
735 } catch (const WException& e) {
736 throw GameDataError("sleep: %s", e.what());
737 }656 }
657 duration_ = read_positive(arguments.front());
738}658}
739659
740void ProductionProgram::ActSleep::execute(Game& game, ProductionSite& ps) const {660void ProductionProgram::ActSleep::execute(Game& game, ProductionSite& ps) const {
741 return ps.program_step(game, duration_ ? duration_ : 0, ps.top_state().phase);661 return ps.program_step(game, duration_ ? duration_ : 0, ps.top_state().phase);
742}662}
743663
744ProductionProgram::ActCheckMap::ActCheckMap(char* parameters) {664ProductionProgram::ActCheckMap::ActCheckMap(const std::vector<std::string>& arguments) {
745 try {665 if (arguments.size() != 1 || arguments.front() != "seafaring") {
746 if (*parameters) {666 throw GameDataError("Usage: checkmap=seafaring");
747 if (!strcmp(parameters, "seafaring"))
748 feature_ = SEAFARING;
749 else
750 throw GameDataError("Unknown parameter \"%s\"", parameters);
751 } else
752 throw GameDataError("No parameter given!");
753 } catch (const WException& e) {
754 throw GameDataError("sleep: %s", e.what());
755 }667 }
668 feature_ = Feature::kSeafaring;
756}669}
757670
758void ProductionProgram::ActCheckMap::execute(Game& game, ProductionSite& ps) const {671void ProductionProgram::ActCheckMap::execute(Game& game, ProductionSite& ps) const {
759 switch (feature_) {672 switch (feature_) {
760 case SEAFARING: {673 case Feature::kSeafaring: {
761 if (game.map().allows_seafaring()) {674 if (game.map().allows_seafaring()) {
762 return ps.program_step(game, 0);675 return ps.program_step(game, 0);
763 } else {676 } else {
@@ -765,57 +678,26 @@
765 return ps.program_end(game, ProgramResult::kFailed);678 return ps.program_end(game, ProgramResult::kFailed);
766 }679 }
767 }680 }
768 default:
769 NEVER_HERE();
770 }681 }
682 NEVER_HERE();
771}683}
772684
773ProductionProgram::ActAnimate::ActAnimate(char* parameters, ProductionSiteDescr* descr) {685ProductionProgram::ActAnimate::ActAnimate(const std::vector<std::string>& arguments, ProductionSiteDescr* descr) {
774 try {686 parameters = MapObjectProgram::parse_act_animate(arguments, *descr, false);
775 bool reached_end;
776 animation_name_ = std::string(next_word(parameters, reached_end));
777 if (animation_name_ == "idle") {
778 throw GameDataError("idle animation is default; calling is not allowed");
779 }
780 if (!descr->is_animation_known(animation_name_)) {
781 throw GameDataError("Unknown animation '%s'", animation_name_.c_str());
782 }
783 if (!reached_end) { // The next parameter is the duration.
784 char* endp;
785 long long int const value = strtoll(parameters, &endp, 0);
786 duration_ = value;
787 if (*endp || value <= 0 || duration_ != value)
788 throw GameDataError("expected %s but found \"%s\"", "duration in ms", parameters);
789 } else
790 duration_ = 0; // Get duration from the result of a previous action.
791 } catch (const WException& e) {
792 throw GameDataError("animate: %s", e.what());
793 }
794}687}
795688
796void ProductionProgram::ActAnimate::execute(Game& game, ProductionSite& ps) const {689void ProductionProgram::ActAnimate::execute(Game& game, ProductionSite& ps) const {
797 ps.start_animation(game, ps.descr().get_animation(animation_name_, &ps));690 ps.start_animation(game, parameters.animation);
798 return ps.program_step(game, duration_ ? duration_ : 0, ps.top_state().phase);691 return ps.program_step(game, parameters.duration ? parameters.duration : 0, ps.top_state().phase);
799}692}
800693
801ProductionProgram::ActConsume::ActConsume(char* parameters,694ProductionProgram::ActConsume::ActConsume(const std::vector<std::string>& arguments,
802 const ProductionSiteDescr& descr,695 const ProductionSiteDescr& descr,
803 const Tribes& tribes) {696 const Tribes& tribes) {
804 try {697 if (arguments.empty()) {
805 for (;;) {698 throw GameDataError("Usage: consume=<ware or worker>[,<ware or worker>[,...]][:<amount>] ...");
806 consumed_wares_workers_.resize(consumed_wares_workers_.size() + 1);
807 parse_ware_type_group(parameters, *consumed_wares_workers_.rbegin(), tribes,
808 descr.input_wares(), descr.input_workers());
809 if (!*parameters)
810 break;
811 force_skip(parameters);
812 }
813 if (consumed_wares_workers_.empty()) {
814 throw GameDataError("expected ware_type1[,ware_type2[,...]][:N] ...");
815 }
816 } catch (const WException& e) {
817 throw GameDataError("consume: %s", e.what());
818 }699 }
700 consumed_wares_workers_ = parse_ware_type_groups(arguments.begin(), arguments.end(), descr, tribes);
819}701}
820702
821void ProductionProgram::ActConsume::execute(Game& game, ProductionSite& ps) const {703void ProductionProgram::ActConsume::execute(Game& game, ProductionSite& ps) const {
@@ -938,47 +820,13 @@
938 }820 }
939}821}
940822
941ProductionProgram::ActProduce::ActProduce(char* parameters,823ProductionProgram::ActProduce::ActProduce(const std::vector<std::string>& arguments,
942 const ProductionSiteDescr& descr,824 const ProductionSiteDescr& descr,
943 const Tribes& tribes) {825 const Tribes& tribes) {
944 try {826 if (arguments.empty()) {
945 for (bool more = true; more; ++parameters) {827 throw GameDataError("Usage: produce=<ware name>[:<amount>] [<ware name>[:<amount>]...]");
946 produced_wares_.resize(produced_wares_.size() + 1);
947 WareAmount& item = *produced_wares_.rbegin();
948 skip(parameters);
949 char const* ware = parameters;
950 for (;; ++parameters) {
951 switch (*parameters) {
952 default:
953 break;
954 case '\0':
955 case ' ':
956 item.second = 1;
957 goto item_end;
958 case ':': {
959 *parameters = '\0';
960 ++parameters;
961 char* endp;
962 unsigned long long int const value = strtoull(parameters, &endp, 0);
963 item.second = value;
964 if ((*endp && *endp != ' ') || value < 1 || item.second != value)
965 throw GameDataError("expected %s but found \"%s\"", "count", parameters);
966 parameters = endp;
967 goto item_end;
968 }
969 }
970 }
971 item_end:
972 more = *parameters != '\0';
973 *parameters = '\0';
974 if (!descr.is_output_ware_type(item.first = tribes.safe_ware_index(ware)))
975 throw GameDataError("%s is not declared as an output (\"%s\" was not "
976 "found in the \"outputs\" table)",
977 ware, ware);
978 }
979 } catch (const WException& e) {
980 throw GameDataError("produce: %s", e.what());
981 }828 }
829 produced_wares_ = parse_bill_of_materials(arguments, WareWorker::wwWARE, descr, tribes);
982}830}
983831
984void ProductionProgram::ActProduce::execute(Game& game, ProductionSite& ps) const {832void ProductionProgram::ActProduce::execute(Game& game, ProductionSite& ps) const {
@@ -1024,47 +872,13 @@
1024 return false;872 return false;
1025}873}
1026874
1027ProductionProgram::ActRecruit::ActRecruit(char* parameters,875ProductionProgram::ActRecruit::ActRecruit(const std::vector<std::string>& arguments,
1028 const ProductionSiteDescr& descr,876 const ProductionSiteDescr& descr,
1029 const Tribes& tribes) {877 const Tribes& tribes) {
1030 try {878 if (arguments.empty()) {
1031 for (bool more = true; more; ++parameters) {879 throw GameDataError("Usage: recruit=<worker name>[:<amount>] [<worker name>[:<amount>]...]");
1032 recruited_workers_.resize(recruited_workers_.size() + 1);
1033 WareAmount& item = *recruited_workers_.rbegin();
1034 skip(parameters);
1035 char const* worker = parameters;
1036 for (;; ++parameters) {
1037 switch (*parameters) {
1038 default:
1039 break;
1040 case '\0':
1041 case ' ':
1042 item.second = 1;
1043 goto item_end;
1044 case ':': {
1045 *parameters = '\0';
1046 ++parameters;
1047 char* endp;
1048 unsigned long long int const value = strtoull(parameters, &endp, 0);
1049 item.second = value;
1050 if ((*endp && *endp != ' ') || value < 1 || item.second != value)
1051 throw GameDataError("expected %s but found \"%s\"", "count", parameters);
1052 parameters = endp;
1053 goto item_end;
1054 }
1055 }
1056 }
1057 item_end:
1058 more = *parameters != '\0';
1059 *parameters = '\0';
1060 if (!descr.is_output_worker_type(item.first = tribes.safe_worker_index(worker)))
1061 throw GameDataError("%s is not declared as an output (\"%s\" was not "
1062 "found in the \"outputs\" table)",
1063 worker, worker);
1064 }
1065 } catch (const WException& e) {
1066 throw GameDataError("recruit: %s", e.what());
1067 }880 }
881 recruited_workers_ = parse_bill_of_materials(arguments, WareWorker::wwWORKER, descr, tribes);
1068}882}
1069883
1070void ProductionProgram::ActRecruit::execute(Game& game, ProductionSite& ps) const {884void ProductionProgram::ActRecruit::execute(Game& game, ProductionSite& ps) const {
@@ -1106,55 +920,23 @@
1106 return false;920 return false;
1107}921}
1108922
1109ProductionProgram::ActMine::ActMine(char* parameters,923ProductionProgram::ActMine::ActMine(const std::vector<std::string>& arguments,
1110 const World& world,924 const World& world,
1111 const std::string& production_program_name,925 const std::string& production_program_name,
1112 ProductionSiteDescr* descr) {926 ProductionSiteDescr* descr) {
1113 try {927 if (arguments.size() != 5) {
1114 bool reached_end;928 throw GameDataError("Usage: mine=resource <workarea radius> <max> <chance> <worker experience gained>");
1115 resource_ = world.safe_resource_index(next_word(parameters, reached_end));
1116
1117 {
1118 char* endp;
1119 unsigned long long int const value = strtoull(parameters, &endp, 0);
1120 distance_ = value;
1121 if (*endp != ' ' || distance_ != value)
1122 throw GameDataError("expected %s but found \"%s\"", "distance", parameters);
1123 parameters = endp;
1124 }
1125
1126 {
1127 char* endp;
1128 unsigned long long int const value = strtoull(parameters, &endp, 0);
1129 max_ = value;
1130 if (*endp != ' ' || value < 1 || 100 < value)
1131 throw GameDataError("expected %s but found \"%s\"", "percentage", parameters);
1132 parameters = endp;
1133 }
1134
1135 {
1136 char* endp;
1137 unsigned long long int const value = strtoull(parameters, &endp, 0);
1138 chance_ = value;
1139 if (*endp != ' ' || value < 1 || 100 < value)
1140 throw GameDataError("expected %s but found \"%s\"", "percentage", parameters);
1141 parameters = endp;
1142 }
1143 {
1144 char* endp;
1145 unsigned long long int const value = strtoull(parameters, &endp, 0);
1146 training_ = value;
1147 if (*endp || value < 1 || 100 < value)
1148 throw GameDataError("expected %s but found \"%s\"", "percentage", parameters);
1149 }
1150 std::string description = (boost::format("%1$s %2$s mine %3$s") % descr->name() %
1151 production_program_name % world.get_resource(resource_)->name())
1152 .str();
1153
1154 descr->workarea_info_[distance_].insert(description);
1155 } catch (const WException& e) {
1156 throw GameDataError("mine: %s", e.what());
1157 }929 }
930
931 resource_ = world.safe_resource_index(arguments.front().c_str());
932 distance_ = read_positive(arguments.at(1));
933 max_ = read_positive(arguments.at(2));
934 chance_ = read_positive(arguments.at(3));
935 training_ = read_positive(arguments.at(4));
936
937 const std::string description = descr->name() + " " +
938 production_program_name + " mine " + world.get_resource(resource_)->name();
939 descr->workarea_info_[distance_].insert(description);
1158}940}
1159941
1160void ProductionProgram::ActMine::execute(Game& game, ProductionSite& ps) const {942void ProductionProgram::ActMine::execute(Game& game, ProductionSite& ps) const {
@@ -1268,32 +1050,16 @@
1268 return ps.program_step(game);1050 return ps.program_step(game);
1269}1051}
12701052
1271ProductionProgram::ActCheckSoldier::ActCheckSoldier(char* parameters) {1053ProductionProgram::ActCheckSoldier::ActCheckSoldier(const std::vector<std::string>& arguments) {
1272 // TODO(unknown): This is currently hardcoded for "soldier", but should allow any1054 if (arguments.size() != 3) {
1273 // soldier type name.1055 throw GameDataError("Usage: checksoldier=soldier <training attribute> <level>");
1274 if (!match_force_skip(parameters, "soldier"))1056 }
1275 throw GameDataError("expected %s but found \"%s\"", "soldier type", parameters);
1276 try {
1277 if (match_force_skip(parameters, "health"))
1278 attribute = TrainingAttribute::kHealth;
1279 else if (match_force_skip(parameters, "attack"))
1280 attribute = TrainingAttribute::kAttack;
1281 else if (match_force_skip(parameters, "defense"))
1282 attribute = TrainingAttribute::kDefense;
1283 else if (match_force_skip(parameters, "evade"))
1284 attribute = TrainingAttribute::kEvade;
1285 else
1286 throw GameDataError("expected %s but found \"%s\"",
1287 "{\"health\"|\"attack\"|\"defense\"|\"evade\"}", parameters);
12881057
1289 char* endp;1058 if (arguments.front() != "soldier") {
1290 unsigned long long int const value = strtoull(parameters, &endp, 0);1059 throw GameDataError("Expected 'soldier' but found '%s'", arguments.front().c_str());
1291 level = value;
1292 if (*endp || level != value)
1293 throw GameDataError("expected %s but found \"%s\"", "level", parameters);
1294 } catch (const WException& e) {
1295 throw GameDataError("checksoldier: %s", e.what());
1296 }1060 }
1061 attribute_ = parse_training_attribute(arguments.at(1));
1062 level_ = read_int(arguments.at(2), 0);
1297}1063}
12981064
1299void ProductionProgram::ActCheckSoldier::execute(Game& game, ProductionSite& ps) const {1065void ProductionProgram::ActCheckSoldier::execute(Game& game, ProductionSite& ps) const {
@@ -1304,8 +1070,8 @@
1304 ps.set_production_result(_("No soldier to train!"));1070 ps.set_production_result(_("No soldier to train!"));
1305 return ps.program_end(game, ProgramResult::kSkipped);1071 return ps.program_end(game, ProgramResult::kSkipped);
1306 }1072 }
1307 ps.molog(" Checking soldier (%u) level %d)\n", static_cast<unsigned int>(attribute),1073 ps.molog(" Checking soldier (%u) level %d)\n", static_cast<unsigned int>(attribute_),
1308 static_cast<unsigned int>(level));1074 static_cast<unsigned int>(level_));
13091075
1310 const std::vector<Soldier*>::const_iterator soldiers_end = soldiers.end();1076 const std::vector<Soldier*>::const_iterator soldiers_end = soldiers.end();
1311 for (std::vector<Soldier*>::const_iterator it = soldiers.begin();; ++it) {1077 for (std::vector<Soldier*>::const_iterator it = soldiers.begin();; ++it) {
@@ -1313,114 +1079,93 @@
1313 ps.set_production_result(_("No soldier found for this training level!"));1079 ps.set_production_result(_("No soldier found for this training level!"));
1314 return ps.program_end(game, ProgramResult::kSkipped);1080 return ps.program_end(game, ProgramResult::kSkipped);
1315 }1081 }
1316 if (attribute == TrainingAttribute::kHealth) {1082
1317 if ((*it)->get_health_level() == level)1083 if (attribute_ == TrainingAttribute::kHealth) {
1318 break;1084 if ((*it)->get_health_level() == level_) {
1319 } else if (attribute == TrainingAttribute::kAttack) {1085 break;
1320 if ((*it)->get_attack_level() == level)1086 }
1321 break;1087 } else if (attribute_ == TrainingAttribute::kAttack) {
1322 } else if (attribute == TrainingAttribute::kDefense) {1088 if ((*it)->get_attack_level() == level_) {
1323 if ((*it)->get_defense_level() == level)1089 break;
1324 break;1090 }
1325 } else if (attribute == TrainingAttribute::kEvade) {1091 } else if (attribute_ == TrainingAttribute::kDefense) {
1326 if ((*it)->get_evade_level() == level)1092 if ((*it)->get_defense_level() == level_) {
1327 break;1093 break;
1094 }
1095 } else if (attribute_ == TrainingAttribute::kEvade) {
1096 if ((*it)->get_evade_level() == level_) {
1097 break;
1098 }
1328 }1099 }
1329 }1100 }
1330 ps.molog(" okay\n"); // okay, do nothing1101 ps.molog(" okay\n"); // okay, do nothing
13311102
1332 upcast(TrainingSite, ts, &ps);1103 upcast(TrainingSite, ts, &ps);
1333 ts->training_attempted(attribute, level);1104 ts->training_attempted(attribute_, level_);
13341105
1335 ps.molog(" Check done!\n");1106 ps.molog(" Check done!\n");
13361107
1337 return ps.program_step(game);1108 return ps.program_step(game);
1338}1109}
13391110
1340ProductionProgram::ActTrain::ActTrain(char* parameters) {1111ProductionProgram::ActTrain::ActTrain(const std::vector<std::string>& arguments) {
1341 // TODO(unknown): This is currently hardcoded for "soldier", but should allow any1112 if (arguments.size() != 4) {
1342 // soldier type name.1113 throw GameDataError("Usage: checksoldier=soldier <training attribute> <level before> <level after>");
1343 if (!match_force_skip(parameters, "soldier"))1114 }
1344 throw GameDataError("expected %s but found \"%s\"", "soldier type", parameters);1115
1345 try {1116 if (arguments.front() != "soldier") {
1346 if (match_force_skip(parameters, "health"))1117 throw GameDataError("Expected 'soldier' but found '%s'", arguments.front().c_str());
1347 attribute = TrainingAttribute::kHealth;1118 }
1348 else if (match_force_skip(parameters, "attack"))1119
1349 attribute = TrainingAttribute::kAttack;1120 attribute_ = parse_training_attribute(arguments.at(1));
1350 else if (match_force_skip(parameters, "defense"))1121 level_ = read_int(arguments.at(2), 0);
1351 attribute = TrainingAttribute::kDefense;1122 target_level_ = read_positive(arguments.at(3));
1352 else if (match_force_skip(parameters, "evade"))
1353 attribute = TrainingAttribute::kEvade;
1354 else
1355 throw GameDataError("expected %s but found \"%s\"",
1356 "{\"health\"|\"attack\"|\"defense\"|\"evade\"}", parameters);
1357
1358 {
1359 char* endp;
1360 unsigned long long int const value = strtoull(parameters, &endp, 0);
1361 level = value;
1362 if (*endp != ' ' || level != value)
1363 throw GameDataError("expected %s but found \"%s\"", "level", parameters);
1364 parameters = endp;
1365 }
1366
1367 {
1368 char* endp;
1369 unsigned long long int const value = strtoull(parameters, &endp, 0);
1370 target_level = value;
1371 if (*endp || target_level != value || target_level <= level)
1372 throw GameDataError("expected level > %u but found \"%s\"", level, parameters);
1373 }
1374 } catch (const WException& e) {
1375 throw GameDataError("train: %s", e.what());
1376 }
1377}1123}
13781124
1379void ProductionProgram::ActTrain::execute(Game& game, ProductionSite& ps) const {1125void ProductionProgram::ActTrain::execute(Game& game, ProductionSite& ps) const {
1380 const SoldierControl* ctrl = ps.soldier_control();1126 const SoldierControl* ctrl = ps.soldier_control();
1381 const std::vector<Soldier*> soldiers = ctrl->present_soldiers();1127 const std::vector<Soldier*> soldiers = ctrl->present_soldiers();
1382 const std::vector<Soldier*>::const_iterator soldiers_end = soldiers.end();1128 const std::vector<Soldier*>::const_iterator soldiers_end = soldiers.end();
1383 std::vector<Soldier*>::const_iterator it = soldiers.begin();1129
13841130 ps.molog(" Training soldier's %u (%d to %d)", static_cast<unsigned int>(attribute_),
1385 ps.molog(" Training soldier's %u (%d to %d)", static_cast<unsigned int>(attribute),1131 static_cast<unsigned int>(level_), static_cast<unsigned int>(target_level_));
1386 static_cast<unsigned int>(level), static_cast<unsigned int>(target_level));1132
13871133 bool training_done = false;
1388 for (;; ++it) {1134 for (auto it = soldiers.begin(); !training_done; ++it) {
1389 if (it == soldiers_end) {1135 if (it == soldiers_end) {
1390 ps.set_production_result(_("No soldier found for this training level!"));1136 ps.set_production_result(_("No soldier found for this training level!"));
1391 return ps.program_end(game, ProgramResult::kSkipped);1137 return ps.program_end(game, ProgramResult::kSkipped);
1392 }1138 }
1393 if (attribute == TrainingAttribute::kHealth) {1139 try {
1394 if ((*it)->get_health_level() == level)1140 switch (attribute_) {
1395 break;1141 case TrainingAttribute::kHealth:
1396 } else if (attribute == TrainingAttribute::kAttack) {1142 if ((*it)->get_health_level() == level_) {
1397 if ((*it)->get_attack_level() == level)1143 (*it)->set_health_level(target_level_);
1398 break;1144 training_done = true;
1399 } else if (attribute == TrainingAttribute::kDefense) {1145 } break;
1400 if ((*it)->get_defense_level() == level)1146 case TrainingAttribute::kAttack:
1401 break;1147 if ((*it)->get_attack_level() == level_) {
1402 } else if (attribute == TrainingAttribute::kEvade) {1148 (*it)->set_attack_level(target_level_);
1403 if ((*it)->get_evade_level() == level)1149 training_done = true;
1404 break;1150 } break;
1151 case TrainingAttribute::kDefense:
1152 if ((*it)->get_defense_level() == level_) {
1153 (*it)->set_defense_level(target_level_);
1154 training_done = true;
1155 } break;
1156 case TrainingAttribute::kEvade:
1157 if ((*it)->get_evade_level() == level_) {
1158 (*it)->set_evade_level(target_level_);
1159 training_done = true;
1160 } break;
1161 default:
1162 throw wexception("Unknown training attribute index %d", static_cast<unsigned int>(attribute_));
1163 }
1164 } catch (...) {
1165 throw wexception("Fail training soldier!!");
1405 }1166 }
1406 }1167 }
1407 ps.molog(" okay\n"); // okay, do nothing1168
1408
1409 try {
1410 if (attribute == TrainingAttribute::kHealth)
1411 (*it)->set_health_level(target_level);
1412 else if (attribute == TrainingAttribute::kAttack)
1413 (*it)->set_attack_level(target_level);
1414
1415 else if (attribute == TrainingAttribute::kDefense)
1416 (*it)->set_defense_level(target_level);
1417
1418 else if (attribute == TrainingAttribute::kEvade)
1419 (*it)->set_evade_level(target_level);
1420
1421 } catch (...) {
1422 throw wexception("Fail training soldier!!");
1423 }
1424 ps.molog(" Training done!\n");1169 ps.molog(" Training done!\n");
1425 ps.set_production_result(1170 ps.set_production_result(
1426 /** TRANSLATORS: Success message of a trainingsite '%s' stands for the description of the1171 /** TRANSLATORS: Success message of a trainingsite '%s' stands for the description of the
@@ -1428,61 +1173,32 @@
1428 (boost::format(_("Completed %s")) % ps.top_state().program->descname()).str());1173 (boost::format(_("Completed %s")) % ps.top_state().program->descname()).str());
14291174
1430 upcast(TrainingSite, ts, &ps);1175 upcast(TrainingSite, ts, &ps);
1431 ts->training_successful(attribute, level);1176 ts->training_successful(attribute_, level_);
14321177
1433 return ps.program_step(game);1178 return ps.program_step(game);
1434}1179}
14351180
1436ProductionProgram::ActPlaySound::ActPlaySound(char* parameters) {1181ProductionProgram::ActPlaySound::ActPlaySound(const std::vector<std::string>& arguments) {
1437 try {1182 parameters = MapObjectProgram::parse_act_play_sound(arguments, kFxPriorityAllowMultiple - 1);
1438 bool reached_end;
1439 const char* const name = next_word(parameters, reached_end);
1440 fx = SoundHandler::register_fx(SoundType::kAmbient, name);
1441
1442 if (!reached_end) {
1443 char* endp;
1444 unsigned long long int const value = strtoull(parameters, &endp, 0);
1445 priority = value;
1446 if (*endp || priority != value)
1447 throw GameDataError("expected %s but found \"%s\"", "priority", parameters);
1448 } else {
1449 priority = kFxPriorityAllowMultiple - 1;
1450 }
1451 if (priority < kFxPriorityLowest) {
1452 throw GameDataError("Minmum priority for sounds is %d, but only %d was specified for %s",
1453 kFxPriorityLowest, priority, name);
1454 }
1455 } catch (const WException& e) {
1456 throw GameDataError("playsound: %s", e.what());
1457 }
1458}1183}
14591184
1460void ProductionProgram::ActPlaySound::execute(Game& game, ProductionSite& ps) const {1185void ProductionProgram::ActPlaySound::execute(Game& game, ProductionSite& ps) const {
1461 Notifications::publish(NoteSound(SoundType::kAmbient, fx, ps.position_, priority));1186 Notifications::publish(NoteSound(SoundType::kAmbient, parameters.fx, ps.position_, parameters.priority));
1462 return ps.program_step(game);1187 return ps.program_step(game);
1463}1188}
14641189
1465ProductionProgram::ActConstruct::ActConstruct(char* parameters,1190ProductionProgram::ActConstruct::ActConstruct(const std::vector<std::string>& arguments,
1466 const std::string& production_program_name,1191 const std::string& production_program_name,
1467 ProductionSiteDescr* descr) {1192 ProductionSiteDescr* descr) {
1468 try {1193 if (arguments.size() != 3) {
1469 std::vector<std::string> params = split_string(parameters, " ");1194 throw GameDataError("Usage: construct=<object name> <worker program> <workarea radius>");
1470
1471 if (params.size() != 3)
1472 throw GameDataError("usage: construct object-name worker-program radius:NN");
1473
1474 objectname = params[0];
1475 workerprogram = params[1];
1476 radius = boost::lexical_cast<uint32_t>(params[2]);
1477
1478 std::set<std::string>& building_radius_infos = descr->workarea_info_[radius];
1479 std::string description = descr->name() + ' ' + production_program_name;
1480 description += " construct ";
1481 description += objectname;
1482 building_radius_infos.insert(description);
1483 } catch (const WException& e) {
1484 throw GameDataError("construct: %s", e.what());
1485 }1195 }
1196 objectname = arguments.at(0);
1197 workerprogram = arguments.at(1);
1198 radius = read_positive(arguments.at(2));
1199
1200 const std::string description = descr->name() + ' ' + production_program_name + " construct " + objectname;
1201 descr->workarea_info_[radius].insert(description);
1486}1202}
14871203
1488const ImmovableDescr&1204const ImmovableDescr&
@@ -1632,96 +1348,93 @@
1632 const Tribes& tribes,1348 const Tribes& tribes,
1633 const World& world,1349 const World& world,
1634 ProductionSiteDescr* building)1350 ProductionSiteDescr* building)
1635 : name_(init_name), descname_(init_descname) {1351 : MapObjectProgram(init_name), descname_(init_descname) {
16361352
1637 for (const std::string& action_string : actions_table->array_entries<std::string>()) {1353 for (const std::string& line : actions_table->array_entries<std::string>()) {
1638 std::vector<std::string> parts;1354 if (line.empty()) {
1639 boost::split(parts, action_string, boost::is_any_of("="));1355 throw GameDataError("Empty line");
1640 if (parts.size() != 2) {1356 }
1641 throw GameDataError(1357 try {
1642 "invalid line: \"%s\" in production program \"%s\" for building \"%s\"",1358 ProgramParseInput parseinput = parse_program_string(line);
1643 action_string.c_str(), name().c_str(), building->name().c_str());1359
1644 }1360 if (parseinput.name == "return") {
1645 std::unique_ptr<char[]> arguments(new char[parts[1].size() + 1]);1361 actions_.push_back(std::unique_ptr<ProductionProgram::Action>(
1646 strncpy(arguments.get(), parts[1].c_str(), parts[1].size() + 1);1362 new ActReturn(parseinput.arguments, *building, tribes)));
16471363 } else if (parseinput.name == "call") {
1648 if (boost::iequals(parts[0], "return")) {1364 actions_.push_back(
1649 actions_.push_back(std::unique_ptr<ProductionProgram::Action>(1365 std::unique_ptr<ProductionProgram::Action>(new ActCall(parseinput.arguments, *building)));
1650 new ActReturn(arguments.get(), *building, tribes)));1366 } else if (parseinput.name == "sleep") {
1651 } else if (boost::iequals(parts[0], "call")) {1367 actions_.push_back(
1652 actions_.push_back(1368 std::unique_ptr<ProductionProgram::Action>(new ActSleep(parseinput.arguments)));
1653 std::unique_ptr<ProductionProgram::Action>(new ActCall(arguments.get(), *building)));1369 } else if (parseinput.name == "animate") {
1654 } else if (boost::iequals(parts[0], "sleep")) {1370 actions_.push_back(
1655 actions_.push_back(1371 std::unique_ptr<ProductionProgram::Action>(new ActAnimate(parseinput.arguments, building)));
1656 std::unique_ptr<ProductionProgram::Action>(new ActSleep(arguments.get())));1372 } else if (parseinput.name =="consume") {
1657 } else if (boost::iequals(parts[0], "animate")) {1373 actions_.push_back(std::unique_ptr<ProductionProgram::Action>(
1658 actions_.push_back(1374 new ActConsume(parseinput.arguments, *building, tribes)));
1659 std::unique_ptr<ProductionProgram::Action>(new ActAnimate(arguments.get(), building)));1375 } else if (parseinput.name == "produce") {
1660 } else if (boost::iequals(parts[0], "consume")) {1376 actions_.push_back(std::unique_ptr<ProductionProgram::Action>(
1661 actions_.push_back(std::unique_ptr<ProductionProgram::Action>(1377 new ActProduce(parseinput.arguments, *building, tribes)));
1662 new ActConsume(arguments.get(), *building, tribes)));1378 } else if (parseinput.name == "recruit") {
1663 } else if (boost::iequals(parts[0], "produce")) {1379 actions_.push_back(std::unique_ptr<ProductionProgram::Action>(
1664 actions_.push_back(std::unique_ptr<ProductionProgram::Action>(1380 new ActRecruit(parseinput.arguments, *building, tribes)));
1665 new ActProduce(arguments.get(), *building, tribes)));1381 } else if (parseinput.name =="callworker") {
1666 } else if (boost::iequals(parts[0], "recruit")) {1382 actions_.push_back(std::unique_ptr<ProductionProgram::Action>(
1667 actions_.push_back(std::unique_ptr<ProductionProgram::Action>(1383 new ActCallWorker(parseinput.arguments, name(), building, tribes)));
1668 new ActRecruit(arguments.get(), *building, tribes)));1384 } else if (parseinput.name == "mine") {
1669 } else if (boost::iequals(parts[0], "callworker")) {1385 actions_.push_back(std::unique_ptr<ProductionProgram::Action>(
1670 actions_.push_back(std::unique_ptr<ProductionProgram::Action>(1386 new ActMine(parseinput.arguments, world, name(), building)));
1671 new ActCallWorker(arguments.get(), name(), building, tribes)));1387 } else if (parseinput.name == "checksoldier") {
1672 } else if (boost::iequals(parts[0], "mine")) {1388 actions_.push_back(
1673 actions_.push_back(std::unique_ptr<ProductionProgram::Action>(1389 std::unique_ptr<ProductionProgram::Action>(new ActCheckSoldier(parseinput.arguments)));
1674 new ActMine(arguments.get(), world, name(), building)));1390 } else if (parseinput.name == "train") {
1675 } else if (boost::iequals(parts[0], "checksoldier")) {1391 actions_.push_back(
1676 actions_.push_back(1392 std::unique_ptr<ProductionProgram::Action>(new ActTrain(parseinput.arguments)));
1677 std::unique_ptr<ProductionProgram::Action>(new ActCheckSoldier(arguments.get())));1393 } else if (parseinput.name == "playsound") {
1678 } else if (boost::iequals(parts[0], "train")) {1394 actions_.push_back(
1679 actions_.push_back(1395 std::unique_ptr<ProductionProgram::Action>(new ActPlaySound(parseinput.arguments)));
1680 std::unique_ptr<ProductionProgram::Action>(new ActTrain(arguments.get())));1396 } else if (parseinput.name == "construct") {
1681 } else if (boost::iequals(parts[0], "playsound")) {1397 actions_.push_back(std::unique_ptr<ProductionProgram::Action>(
1682 actions_.push_back(1398 new ActConstruct(parseinput.arguments, name(), building)));
1683 std::unique_ptr<ProductionProgram::Action>(new ActPlaySound(arguments.get())));1399 } else if (parseinput.name == "checkmap") {
1684 } else if (boost::iequals(parts[0], "construct")) {1400 actions_.push_back(
1685 actions_.push_back(std::unique_ptr<ProductionProgram::Action>(1401 std::unique_ptr<ProductionProgram::Action>(new ActCheckMap(parseinput.arguments)));
1686 new ActConstruct(arguments.get(), name(), building)));1402 } else {
1687 } else if (boost::iequals(parts[0], "checkmap")) {1403 throw GameDataError("Unknown command '%s' in line '%s'", parseinput.name.c_str(), line.c_str());
1688 actions_.push_back(1404 }
1689 std::unique_ptr<ProductionProgram::Action>(new ActCheckMap(arguments.get())));1405
1690 } else {1406 const ProductionProgram::Action& action = *actions_.back();
1691 throw GameDataError(1407 for (const auto& group : action.consumed_wares_workers()) {
1692 "unknown command type \"%s\" in production program \"%s\" for building \"%s\"",1408 consumed_wares_workers_.push_back(group);
1693 arguments.get(), name().c_str(), building->name().c_str());1409 }
1694 }1410
16951411 // Add produced wares. If the ware already exists, increase the amount
1696 const ProductionProgram::Action& action = *actions_.back();1412 for (const auto& ware : action.produced_wares()) {
1697 for (const auto& group : action.consumed_wares_workers()) {1413 if (produced_wares_.count(ware.first) == 1) {
1698 consumed_wares_workers_.push_back(group);1414 produced_wares_.at(ware.first) += ware.second;
1699 }1415 } else {
1700 // Add produced wares. If the ware already exists, increase the amount1416 produced_wares_.insert(ware);
1701 for (const auto& ware : action.produced_wares()) {1417 }
1702 if (produced_wares_.count(ware.first) == 1) {1418 }
1703 produced_wares_.at(ware.first) += ware.second;1419
1704 } else {1420 // Add recruited workers. If the worker already exists, increase the amount
1705 produced_wares_.insert(ware);1421 for (const auto& worker : action.recruited_workers()) {
1706 }1422 if (recruited_workers_.count(worker.first) == 1) {
1707 }1423 recruited_workers_.at(worker.first) += worker.second;
1708 // Add recruited workers. If the worker already exists, increase the amount1424 } else {
1709 for (const auto& worker : action.recruited_workers()) {1425 recruited_workers_.insert(worker);
1710 if (recruited_workers_.count(worker.first) == 1) {1426 }
1711 recruited_workers_.at(worker.first) += worker.second;1427 }
1712 } else {1428 } catch (const std::exception& e) {
1713 recruited_workers_.insert(worker);1429 throw GameDataError("Error reading line '%s': %s", line.c_str(), e.what());
1714 }1430 }
1715 }1431 }
1716 }1432
1717 if (actions_.empty())1433 if (actions_.empty()) {
1718 throw GameDataError("no actions in production program \"%s\" for building \"%s\"",1434 throw GameDataError("No actions found");
1719 name().c_str(), building->name().c_str());1435 }
1720}1436}
17211437
1722const std::string& ProductionProgram::name() const {
1723 return name_;
1724}
1725const std::string& ProductionProgram::descname() const {1438const std::string& ProductionProgram::descname() const {
1726 return descname_;1439 return descname_;
1727}1440}
17281441
=== modified file 'src/logic/map_objects/tribes/production_program.h'
--- src/logic/map_objects/tribes/production_program.h 2019-05-11 13:48:12 +0000
+++ src/logic/map_objects/tribes/production_program.h 2019-08-31 15:58:01 +0000
@@ -32,6 +32,7 @@
32#include "base/log.h"32#include "base/log.h"
33#include "base/macros.h"33#include "base/macros.h"
34#include "logic/map_objects/buildcost.h"34#include "logic/map_objects/buildcost.h"
35#include "logic/map_objects/map_object_program.h"
35#include "logic/map_objects/tribes/bill_of_materials.h"36#include "logic/map_objects/tribes/bill_of_materials.h"
36#include "logic/map_objects/tribes/program_result.h"37#include "logic/map_objects/tribes/program_result.h"
37#include "logic/map_objects/tribes/training_attribute.h"38#include "logic/map_objects/tribes/training_attribute.h"
@@ -51,7 +52,7 @@
51class World;52class World;
5253
53/// Ordered sequence of actions (at least 1). Has a name.54/// Ordered sequence of actions (at least 1). Has a name.
54struct ProductionProgram {55struct ProductionProgram : public MapObjectProgram {
5556
56 /// A group of ware types with a count.57 /// A group of ware types with a count.
57 using WareTypeGroup = std::pair<std::set<std::pair<DescriptionIndex, WareWorker>>, uint8_t>;58 using WareTypeGroup = std::pair<std::set<std::pair<DescriptionIndex, WareWorker>>, uint8_t>;
@@ -97,13 +98,11 @@
97 DISALLOW_COPY_AND_ASSIGN(Action);98 DISALLOW_COPY_AND_ASSIGN(Action);
98 };99 };
99100
100 /// Parse a group of ware types followed by an optional count and terminated101 /// Parse a group of ware types followed by an optional count within a vector range. Example: "fish,meat:2".
101 /// by a space or null. Example: "fish,meat:2".102 static Groups parse_ware_type_groups(std::vector<std::string>::const_iterator begin, std::vector<std::string>::const_iterator end, const ProductionSiteDescr& descr, const Tribes& tribes);
102 static void parse_ware_type_group(char*& parameters,103
103 WareTypeGroup& group,104 /// Parse a ware or worker list with optional amounts and ensure that the building's outputs match. Example: "fish:2".
104 const Tribes& tribes,105 static BillOfMaterials parse_bill_of_materials(const std::vector<std::string>& arguments, WareWorker ww, const ProductionSiteDescr& descr, const Tribes& tribes);
105 const BillOfMaterials& input_wares,
106 const BillOfMaterials& input_workers);
107106
108 /// Returns from the program.107 /// Returns from the program.
109 ///108 ///
@@ -156,7 +155,7 @@
156 /// Note: If the execution reaches the end of the program. the return value155 /// Note: If the execution reaches the end of the program. the return value
157 /// is implicitly set to Completed.156 /// is implicitly set to Completed.
158 struct ActReturn : public Action {157 struct ActReturn : public Action {
159 ActReturn(char* parameters, const ProductionSiteDescr&, const Tribes& tribes);158 ActReturn(const std::vector<std::string>& arguments, const ProductionSiteDescr&, const Tribes& tribes);
160 ~ActReturn() override;159 ~ActReturn() override;
161 void execute(Game&, ProductionSite&) const override;160 void execute(Game&, ProductionSite&) const override;
162161
@@ -167,11 +166,10 @@
167 virtual std::string description_negation(const Tribes&) const = 0;166 virtual std::string description_negation(const Tribes&) const = 0;
168 };167 };
169 static Condition*168 static Condition*
170 create_condition(char*& parameters, const ProductionSiteDescr&, const Tribes& tribes);169 create_condition(const std::vector<std::string>& arguments, std::vector<std::string>::const_iterator& begin, std::vector<std::string>::const_iterator& end, const ProductionSiteDescr&, const Tribes& tribes);
170
171 struct Negation : public Condition {171 struct Negation : public Condition {
172 Negation(char*& parameters, const ProductionSiteDescr& descr, const Tribes& tribes)172 Negation(const std::vector<std::string>& arguments, std::vector<std::string>::const_iterator& begin, std::vector<std::string>::const_iterator& end, const ProductionSiteDescr& descr, const Tribes& tribes);
173 : operand(create_condition(parameters, descr, tribes)) {
174 }
175 ~Negation() override;173 ~Negation() override;
176 bool evaluate(const ProductionSite&) const override;174 bool evaluate(const ProductionSite&) const override;
177 // Just a dummy to satisfy the superclass interface. Do not use.175 // Just a dummy to satisfy the superclass interface. Do not use.
@@ -211,7 +209,7 @@
211 /// wares, combining from any of the types specified, in its input209 /// wares, combining from any of the types specified, in its input
212 /// queues.210 /// queues.
213 struct SiteHas : public Condition {211 struct SiteHas : public Condition {
214 SiteHas(char*& parameters, const ProductionSiteDescr&, const Tribes& tribes);212 SiteHas(std::vector<std::string>::const_iterator begin, std::vector<std::string>::const_iterator end, const ProductionSiteDescr& descr, const Tribes& tribes);
215 bool evaluate(const ProductionSite&) const override;213 bool evaluate(const ProductionSite&) const override;
216 std::string description(const Tribes& tribes) const override;214 std::string description(const Tribes& tribes) const override;
217 std::string description_negation(const Tribes& tribes) const override;215 std::string description_negation(const Tribes& tribes) const override;
@@ -269,7 +267,7 @@
269 /// but no statistics are calculated (with the same effect as267 /// but no statistics are calculated (with the same effect as
270 /// executing "return=no_stats")268 /// executing "return=no_stats")
271 struct ActCall : public Action {269 struct ActCall : public Action {
272 ActCall(char* parameters, const ProductionSiteDescr&);270 ActCall(const std::vector<std::string>& arguments, const ProductionSiteDescr&);
273 void execute(Game&, ProductionSite&) const override;271 void execute(Game&, ProductionSite&) const override;
274272
275 private:273 private:
@@ -285,7 +283,7 @@
285 /// program:283 /// program:
286 /// The name of a program defined in the productionsite's main worker.284 /// The name of a program defined in the productionsite's main worker.
287 struct ActCallWorker : public Action {285 struct ActCallWorker : public Action {
288 ActCallWorker(char* parameters,286 ActCallWorker(const std::vector<std::string>& arguments,
289 const std::string& production_program_name,287 const std::string& production_program_name,
290 ProductionSiteDescr*,288 ProductionSiteDescr*,
291 const Tribes& tribes);289 const Tribes& tribes);
@@ -311,7 +309,7 @@
311 ///309 ///
312 /// Blocks the execution of the program for the specified duration.310 /// Blocks the execution of the program for the specified duration.
313 struct ActSleep : public Action {311 struct ActSleep : public Action {
314 explicit ActSleep(char* parameters);312 explicit ActSleep(const std::vector<std::string>& arguments);
315 void execute(Game&, ProductionSite&) const override;313 void execute(Game&, ProductionSite&) const override;
316314
317 private:315 private:
@@ -329,12 +327,12 @@
329 ///327 ///
330 /// Ends the program if the feature is not enabled.328 /// Ends the program if the feature is not enabled.
331 struct ActCheckMap : public Action {329 struct ActCheckMap : public Action {
332 explicit ActCheckMap(char* parameters);330 explicit ActCheckMap(const std::vector<std::string>& arguments);
333 void execute(Game&, ProductionSite&) const override;331 void execute(Game&, ProductionSite&) const override;
334332
335 private:333 private:
336 enum { SEAFARING = 1 };334 enum class Feature { kSeafaring = 1 };
337 uint8_t feature_;335 Feature feature_;
338 };336 };
339337
340 /// Runs an animation.338 /// Runs an animation.
@@ -354,12 +352,11 @@
354 /// animation will not be stopped by this command. It will run until another352 /// animation will not be stopped by this command. It will run until another
355 /// animation is started.)353 /// animation is started.)
356 struct ActAnimate : public Action {354 struct ActAnimate : public Action {
357 ActAnimate(char* parameters, ProductionSiteDescr*);355 ActAnimate(const std::vector<std::string>& arguments, ProductionSiteDescr*);
358 void execute(Game&, ProductionSite&) const override;356 void execute(Game&, ProductionSite&) const override;
359357
360 private:358 private:
361 std::string animation_name_;359 AnimationParameters parameters;
362 Duration duration_;
363 };360 };
364361
365 /// Consumes wares from the input storages.362 /// Consumes wares from the input storages.
@@ -406,7 +403,7 @@
406 /// types of a group are sorted.403 /// types of a group are sorted.
407 // TODO(unknown): change this!404 // TODO(unknown): change this!
408 struct ActConsume : public Action {405 struct ActConsume : public Action {
409 ActConsume(char* parameters, const ProductionSiteDescr&, const Tribes& tribes);406 ActConsume(const std::vector<std::string>& arguments, const ProductionSiteDescr& descr, const Tribes& tribes);
410 void execute(Game&, ProductionSite&) const override;407 void execute(Game&, ProductionSite&) const override;
411 };408 };
412409
@@ -426,7 +423,7 @@
426 /// produced wares are of the type specified in the group. How the produced423 /// produced wares are of the type specified in the group. How the produced
427 /// wares are handled is defined by the productionsite.424 /// wares are handled is defined by the productionsite.
428 struct ActProduce : public Action {425 struct ActProduce : public Action {
429 ActProduce(char* parameters, const ProductionSiteDescr&, const Tribes& tribes);426 ActProduce(const std::vector<std::string>& arguments, const ProductionSiteDescr&, const Tribes& tribes);
430 void execute(Game&, ProductionSite&) const override;427 void execute(Game&, ProductionSite&) const override;
431 bool get_building_work(Game&, ProductionSite&, Worker&) const override;428 bool get_building_work(Game&, ProductionSite&, Worker&) const override;
432 };429 };
@@ -447,13 +444,13 @@
447 /// The recruited workers are of the type specified in the group. How the444 /// The recruited workers are of the type specified in the group. How the
448 /// recruited workers are handled is defined by the productionsite.445 /// recruited workers are handled is defined by the productionsite.
449 struct ActRecruit : public Action {446 struct ActRecruit : public Action {
450 ActRecruit(char* parameters, const ProductionSiteDescr&, const Tribes& tribes);447 ActRecruit(const std::vector<std::string>& arguments, const ProductionSiteDescr&, const Tribes& tribes);
451 void execute(Game&, ProductionSite&) const override;448 void execute(Game&, ProductionSite&) const override;
452 bool get_building_work(Game&, ProductionSite&, Worker&) const override;449 bool get_building_work(Game&, ProductionSite&, Worker&) const override;
453 };450 };
454451
455 struct ActMine : public Action {452 struct ActMine : public Action {
456 ActMine(char* parameters,453 ActMine(const std::vector<std::string>& arguments,
457 const World&,454 const World&,
458 const std::string& production_program_name,455 const std::string& production_program_name,
459 ProductionSiteDescr*);456 ProductionSiteDescr*);
@@ -468,22 +465,22 @@
468 };465 };
469466
470 struct ActCheckSoldier : public Action {467 struct ActCheckSoldier : public Action {
471 explicit ActCheckSoldier(char* parameters);468 explicit ActCheckSoldier(const std::vector<std::string>& arguments);
472 void execute(Game&, ProductionSite&) const override;469 void execute(Game&, ProductionSite&) const override;
473470
474 private:471 private:
475 TrainingAttribute attribute;472 TrainingAttribute attribute_;
476 uint8_t level;473 uint8_t level_;
477 };474 };
478475
479 struct ActTrain : public Action {476 struct ActTrain : public Action {
480 explicit ActTrain(char* parameters);477 explicit ActTrain(const std::vector<std::string>& arguments);
481 void execute(Game&, ProductionSite&) const override;478 void execute(Game&, ProductionSite&) const override;
482479
483 private:480 private:
484 TrainingAttribute attribute;481 TrainingAttribute attribute_;
485 uint8_t level;482 uint8_t level_;
486 uint8_t target_level;483 uint8_t target_level_;
487 };484 };
488485
489 /// Plays a sound effect.486 /// Plays a sound effect.
@@ -501,12 +498,11 @@
501 /// Plays the specified sound effect with the specified priority. Whether the498 /// Plays the specified sound effect with the specified priority. Whether the
502 /// sound effect is actually played is determined by the sound handler.499 /// sound effect is actually played is determined by the sound handler.
503 struct ActPlaySound : public Action {500 struct ActPlaySound : public Action {
504 explicit ActPlaySound(char* parameters);501 explicit ActPlaySound(const std::vector<std::string>& arguments);
505 void execute(Game&, ProductionSite&) const override;502 void execute(Game&, ProductionSite&) const override;
506503
507 private:504 private:
508 FxId fx;505 PlaySoundParameters parameters;
509 uint8_t priority;
510 };506 };
511507
512 /// Sends a building worker to construct at an immovable.508 /// Sends a building worker to construct at an immovable.
@@ -522,7 +518,7 @@
522 /// radius518 /// radius
523 /// Activity radius519 /// Activity radius
524 struct ActConstruct : public Action {520 struct ActConstruct : public Action {
525 ActConstruct(char* parameters,521 ActConstruct(const std::vector<std::string>& arguments,
526 const std::string& production_program_name,522 const std::string& production_program_name,
527 ProductionSiteDescr*);523 ProductionSiteDescr*);
528 void execute(Game&, ProductionSite&) const override;524 void execute(Game&, ProductionSite&) const override;
@@ -544,7 +540,6 @@
544 const World& world,540 const World& world,
545 ProductionSiteDescr* building);541 ProductionSiteDescr* building);
546542
547 const std::string& name() const;
548 const std::string& descname() const;543 const std::string& descname() const;
549544
550 size_t size() const;545 size_t size() const;
@@ -555,7 +550,6 @@
555 const Buildcost& recruited_workers() const;550 const Buildcost& recruited_workers() const;
556551
557private:552private:
558 std::string name_;
559 std::string descname_;553 std::string descname_;
560 std::vector<std::unique_ptr<Action>> actions_;554 std::vector<std::unique_ptr<Action>> actions_;
561 ProductionProgram::Groups consumed_wares_workers_;555 ProductionProgram::Groups consumed_wares_workers_;
562556
=== modified file 'src/logic/map_objects/tribes/productionsite.cc'
--- src/logic/map_objects/tribes/productionsite.cc 2019-08-20 17:35:33 +0000
+++ src/logic/map_objects/tribes/productionsite.cc 2019-08-31 15:58:01 +0000
@@ -93,6 +93,8 @@
93 const Tribes& tribes,93 const Tribes& tribes,
94 const World& world)94 const World& world)
95 : BuildingDescr(init_descname, init_type, table, tribes),95 : BuildingDescr(init_descname, init_type, table, tribes),
96 ware_demand_checks_(new std::set<DescriptionIndex>()),
97 worker_demand_checks_(new std::set<DescriptionIndex>()),
96 out_of_resource_productivity_threshold_(100) {98 out_of_resource_productivity_threshold_(100) {
97 if (msgctxt.empty()) {99 if (msgctxt.empty()) {
98 throw Widelands::GameDataError(100 throw Widelands::GameDataError(
@@ -187,10 +189,11 @@
187 items_table = table.get_table("programs");189 items_table = table.get_table("programs");
188 for (std::string program_name : items_table->keys<std::string>()) {190 for (std::string program_name : items_table->keys<std::string>()) {
189 std::transform(program_name.begin(), program_name.end(), program_name.begin(), tolower);191 std::transform(program_name.begin(), program_name.end(), program_name.begin(), tolower);
192 if (programs_.count(program_name)) {
193 throw GameDataError("Program '%s' has already been declared for productionsite '%s'",
194 program_name.c_str(), name().c_str());
195 }
190 try {196 try {
191 if (programs_.count(program_name)) {
192 throw wexception("this program has already been declared");
193 }
194 std::unique_ptr<LuaTable> program_table = items_table->get_table(program_name);197 std::unique_ptr<LuaTable> program_table = items_table->get_table(program_name);
195198
196 // Allow use of both gettext and pgettext. This way, we can have a lower workload on199 // Allow use of both gettext and pgettext. This way, we can have a lower workload on
@@ -204,7 +207,7 @@
204 new ProductionProgram(program_name, program_descname,207 new ProductionProgram(program_name, program_descname,
205 program_table->get_table("actions"), tribes, world, this));208 program_table->get_table("actions"), tribes, world, this));
206 } catch (const std::exception& e) {209 } catch (const std::exception& e) {
207 throw wexception("program %s: %s", program_name.c_str(), e.what());210 throw GameDataError("%s: Error in productionsite program %s: %s", name().c_str(), program_name.c_str(), e.what());
208 }211 }
209 }212 }
210213
@@ -272,6 +275,19 @@
272 return *new ProductionSite(*this);275 return *new ProductionSite(*this);
273}276}
274277
278std::set<DescriptionIndex>* ProductionSiteDescr::ware_demand_checks() const {
279 return ware_demand_checks_.get();
280}
281std::set<DescriptionIndex>* ProductionSiteDescr::worker_demand_checks() const {
282 return worker_demand_checks_.get();
283}
284void ProductionSiteDescr::clear_demand_checks() {
285 ware_demand_checks_->clear();
286 ware_demand_checks_.reset(nullptr);
287 worker_demand_checks_->clear();
288 worker_demand_checks_.reset(nullptr);
289}
290
275/*291/*
276==============================292==============================
277293
278294
=== modified file 'src/logic/map_objects/tribes/productionsite.h'
--- src/logic/map_objects/tribes/productionsite.h 2019-06-23 12:45:29 +0000
+++ src/logic/map_objects/tribes/productionsite.h 2019-08-31 15:58:01 +0000
@@ -73,6 +73,13 @@
7373
74 Building& create_object() const override;74 Building& create_object() const override;
7575
76 // List of wares to register having economy checks. Parsed by the tribes during postload and must be nullptr after loading has finished
77 std::set<DescriptionIndex>* ware_demand_checks() const;
78 // List of workers to register having economy checks. Parsed by the tribes during postload and must be nullptr after loading has finished
79 std::set<DescriptionIndex>* worker_demand_checks() const;
80 // Clear ware and worker demand check info
81 void clear_demand_checks();
82
76 uint32_t nr_working_positions() const {83 uint32_t nr_working_positions() const {
77 uint32_t result = 0;84 uint32_t result = 0;
78 for (const auto& working_pos : working_positions()) {85 for (const auto& working_pos : working_positions()) {
@@ -143,6 +150,8 @@
143 }150 }
144151
145private:152private:
153 std::unique_ptr<std::set<DescriptionIndex>> ware_demand_checks_;
154 std::unique_ptr<std::set<DescriptionIndex>> worker_demand_checks_;
146 BillOfMaterials working_positions_;155 BillOfMaterials working_positions_;
147 BillOfMaterials input_wares_;156 BillOfMaterials input_wares_;
148 BillOfMaterials input_workers_;157 BillOfMaterials input_workers_;
149158
=== modified file 'src/logic/map_objects/tribes/soldier.cc'
--- src/logic/map_objects/tribes/soldier.cc 2019-05-27 14:25:47 +0000
+++ src/logic/map_objects/tribes/soldier.cc 2019-08-31 15:58:01 +0000
@@ -32,7 +32,6 @@
32#include "economy/flag.h"32#include "economy/flag.h"
33#include "graphic/graphic.h"33#include "graphic/graphic.h"
34#include "graphic/rendertarget.h"34#include "graphic/rendertarget.h"
35#include "helper.h"
36#include "io/fileread.h"35#include "io/fileread.h"
37#include "io/filewrite.h"36#include "io/filewrite.h"
38#include "logic/editor_game_base.h"37#include "logic/editor_game_base.h"
3938
=== modified file 'src/logic/map_objects/tribes/trainingsite.cc'
--- src/logic/map_objects/tribes/trainingsite.cc 2019-06-23 11:41:17 +0000
+++ src/logic/map_objects/tribes/trainingsite.cc 2019-08-31 15:58:01 +0000
@@ -28,7 +28,6 @@
28#include "base/macros.h"28#include "base/macros.h"
29#include "base/wexception.h"29#include "base/wexception.h"
30#include "economy/request.h"30#include "economy/request.h"
31#include "helper.h"
32#include "logic/editor_game_base.h"31#include "logic/editor_game_base.h"
33#include "logic/game.h"32#include "logic/game.h"
34#include "logic/map_objects/tribes/production_program.h"33#include "logic/map_objects/tribes/production_program.h"
3534
=== modified file 'src/logic/map_objects/tribes/tribes.cc'
--- src/logic/map_objects/tribes/tribes.cc 2019-06-23 12:45:29 +0000
+++ src/logic/map_objects/tribes/tribes.cc 2019-08-31 15:58:01 +0000
@@ -293,15 +293,6 @@
293 return tribes_->get_mutable(tribeindex);293 return tribes_->get_mutable(tribeindex);
294}294}
295295
296void Tribes::set_ware_type_has_demand_check(const DescriptionIndex& wareindex,
297 const std::string& tribename) const {
298 wares_->get_mutable(wareindex)->set_has_demand_check(tribename);
299}
300
301void Tribes::set_worker_type_has_demand_check(const DescriptionIndex& workerindex) const {
302 workers_->get_mutable(workerindex)->set_has_demand_check();
303}
304
305void Tribes::load_graphics() {296void Tribes::load_graphics() {
306 for (size_t tribeindex = 0; tribeindex < nrtribes(); ++tribeindex) {297 for (size_t tribeindex = 0; tribeindex < nrtribes(); ++tribeindex) {
307 TribeDescr* tribe = tribes_->get_mutable(tribeindex);298 TribeDescr* tribe = tribes_->get_mutable(tribeindex);
@@ -366,6 +357,11 @@
366 // Some final checks on the gamedata357 // Some final checks on the gamedata
367 for (DescriptionIndex i = 0; i < tribes_->size(); ++i) {358 for (DescriptionIndex i = 0; i < tribes_->size(); ++i) {
368 TribeDescr* tribe_descr = tribes_->get_mutable(i);359 TribeDescr* tribe_descr = tribes_->get_mutable(i);
360
361 // Register which wares and workers have economy demand checks for each tribe
362 for (const DescriptionIndex bi : tribe_descr->buildings()) {
363 postload_register_economy_demand_checks(*buildings_->get_mutable(bi), *tribe_descr);
364 }
369 // Verify that the preciousness has been set for all of the tribe's wares365 // Verify that the preciousness has been set for all of the tribe's wares
370 for (const DescriptionIndex wi : tribe_descr->wares()) {366 for (const DescriptionIndex wi : tribe_descr->wares()) {
371 if (tribe_descr->get_ware_descr(wi)->ai_hints().preciousness(tribe_descr->name()) ==367 if (tribe_descr->get_ware_descr(wi)->ai_hints().preciousness(tribe_descr->name()) ==
@@ -378,7 +374,36 @@
378 }374 }
379}375}
380376
381// Set default trainingsites proportions for AI. Make sure that we get a sum of ca. 100377/// Register wares and workers that have economy demand checks for a building
378void Tribes::postload_register_economy_demand_checks(BuildingDescr& building_descr, const TribeDescr& tribe_descr)
379{
380 if (upcast(ProductionSiteDescr, prodsite, &building_descr)) {
381 // This function can be called only once per loading of tribes
382 assert(prodsite->ware_demand_checks() != nullptr);
383
384 for (const DescriptionIndex wi : *prodsite->ware_demand_checks()) {
385 if (!tribe_descr.has_ware(wi)) {
386 throw GameDataError("Productionsite '%s' for tribe '%s' has an economy demand check for ware '%s', but the tribe does not use this ware",
387 prodsite->name().c_str(),
388 tribe_descr.name().c_str(),
389 get_ware_descr(wi)->name().c_str());
390 }
391 wares_->get_mutable(wi)->set_has_demand_check(tribe_descr.name());
392 }
393 for (const DescriptionIndex wi : *prodsite->worker_demand_checks()) {
394 if (!tribe_descr.has_worker(wi)) {
395 throw GameDataError("Productionsite '%s' for tribe '%s' has an economy demand check for worker '%s', but the tribe does not use this worker",
396 prodsite->name().c_str(),
397 tribe_descr.name().c_str(),
398 get_worker_descr(wi)->name().c_str());
399 }
400 workers_->get_mutable(wi)->set_has_demand_check();
401 }
402 prodsite->clear_demand_checks();
403 }
404}
405
406/// Set default trainingsites proportions for AI. Make sure that we get a sum of ca. 100
382void Tribes::postload_calculate_trainingsites_proportions() {407void Tribes::postload_calculate_trainingsites_proportions() {
383 for (DescriptionIndex i = 0; i < tribes_->size(); ++i) {408 for (DescriptionIndex i = 0; i < tribes_->size(); ++i) {
384 TribeDescr* tribe_descr = tribes_->get_mutable(i);409 TribeDescr* tribe_descr = tribes_->get_mutable(i);
385410
=== modified file 'src/logic/map_objects/tribes/tribes.h'
--- src/logic/map_objects/tribes/tribes.h 2019-05-16 09:15:03 +0000
+++ src/logic/map_objects/tribes/tribes.h 2019-08-31 15:58:01 +0000
@@ -133,10 +133,6 @@
133 const WorkerDescr* get_worker_descr(DescriptionIndex worker_index) const;133 const WorkerDescr* get_worker_descr(DescriptionIndex worker_index) const;
134 const TribeDescr* get_tribe_descr(DescriptionIndex tribe_index) const;134 const TribeDescr* get_tribe_descr(DescriptionIndex tribe_index) const;
135135
136 void set_ware_type_has_demand_check(const DescriptionIndex& ware_index,
137 const std::string& tribename) const;
138 void set_worker_type_has_demand_check(const DescriptionIndex& worker_index) const;
139
140 /// Load tribes' graphics136 /// Load tribes' graphics
141 void load_graphics();137 void load_graphics();
142138
@@ -147,6 +143,7 @@
147143
148private:144private:
149 void postload_calculate_trainingsites_proportions();145 void postload_calculate_trainingsites_proportions();
146 void postload_register_economy_demand_checks(BuildingDescr& building_descr, const TribeDescr& tribe_descr);
150147
151 std::unique_ptr<DescriptionMaintainer<BuildingDescr>> buildings_;148 std::unique_ptr<DescriptionMaintainer<BuildingDescr>> buildings_;
152 std::unique_ptr<DescriptionMaintainer<ImmovableDescr>> immovables_;149 std::unique_ptr<DescriptionMaintainer<ImmovableDescr>> immovables_;
153150
=== modified file 'src/logic/map_objects/tribes/worker.cc'
--- src/logic/map_objects/tribes/worker.cc 2019-05-28 17:01:30 +0000
+++ src/logic/map_objects/tribes/worker.cc 2019-08-31 15:58:01 +0000
@@ -36,7 +36,6 @@
36#include "graphic/graphic.h"36#include "graphic/graphic.h"
37#include "graphic/rendertarget.h"37#include "graphic/rendertarget.h"
38#include "graphic/text_layout.h"38#include "graphic/text_layout.h"
39#include "helper.h"
40#include "io/fileread.h"39#include "io/fileread.h"
41#include "io/filewrite.h"40#include "io/filewrite.h"
42#include "logic/cmd_incorporate.h"41#include "logic/cmd_incorporate.h"
@@ -3146,7 +3145,7 @@
3146 return Bob::Loader::get_task(name);3145 return Bob::Loader::get_task(name);
3147}3146}
31483147
3149const BobProgramBase* Worker::Loader::get_program(const std::string& name) {3148const MapObjectProgram* Worker::Loader::get_program(const std::string& name) {
3150 Worker& worker = get<Worker>();3149 Worker& worker = get<Worker>();
3151 return worker.descr().get_program(name);3150 return worker.descr().get_program(name);
3152}3151}
31533152
=== modified file 'src/logic/map_objects/tribes/worker.h'
--- src/logic/map_objects/tribes/worker.h 2019-04-24 06:01:37 +0000
+++ src/logic/map_objects/tribes/worker.h 2019-08-31 15:58:01 +0000
@@ -303,7 +303,7 @@
303303
304 protected:304 protected:
305 const Task* get_task(const std::string& name) override;305 const Task* get_task(const std::string& name) override;
306 const BobProgramBase* get_program(const std::string& name) override;306 const MapObjectProgram* get_program(const std::string& name) override;
307307
308 private:308 private:
309 uint32_t location_;309 uint32_t location_;
310310
=== modified file 'src/logic/map_objects/tribes/worker_descr.cc'
--- src/logic/map_objects/tribes/worker_descr.cc 2019-05-25 08:51:42 +0000
+++ src/logic/map_objects/tribes/worker_descr.cc 2019-08-31 15:58:01 +0000
@@ -106,16 +106,14 @@
106 std::unique_ptr<LuaTable> programs_table = table.get_table("programs");106 std::unique_ptr<LuaTable> programs_table = table.get_table("programs");
107 for (std::string program_name : programs_table->keys<std::string>()) {107 for (std::string program_name : programs_table->keys<std::string>()) {
108 std::transform(program_name.begin(), program_name.end(), program_name.begin(), tolower);108 std::transform(program_name.begin(), program_name.end(), program_name.begin(), tolower);
109109 if (programs_.count(program_name)) {
110 throw GameDataError("Program '%s' has already been declared for worker '%s'", program_name.c_str(), name().c_str());
111 }
110 try {112 try {
111 if (programs_.count(program_name))113 programs_[program_name] = std::unique_ptr<WorkerProgram>(
112 throw wexception("this program has already been declared");114 new WorkerProgram(program_name, *programs_table->get_table(program_name), *this, tribes_));
113
114 programs_[program_name] =
115 std::unique_ptr<WorkerProgram>(new WorkerProgram(program_name, *this, tribes_));
116 programs_[program_name]->parse(*programs_table->get_table(program_name));
117 } catch (const std::exception& e) {115 } catch (const std::exception& e) {
118 throw wexception("program %s: %s", program_name.c_str(), e.what());116 throw GameDataError("%s: Error in worker program %s: %s", name().c_str(), program_name.c_str(), e.what());
119 }117 }
120 }118 }
121 }119 }
@@ -136,8 +134,9 @@
136WorkerProgram const* WorkerDescr::get_program(const std::string& programname) const {134WorkerProgram const* WorkerDescr::get_program(const std::string& programname) const {
137 Programs::const_iterator it = programs_.find(programname);135 Programs::const_iterator it = programs_.find(programname);
138136
139 if (it == programs_.end())137 if (it == programs_.end()) {
140 throw wexception("%s has no program '%s'", name().c_str(), programname.c_str());138 throw GameDataError("%s has no program '%s'", name().c_str(), programname.c_str());
139 }
141140
142 return it->second.get();141 return it->second.get();
143}142}
144143
=== modified file 'src/logic/map_objects/tribes/worker_program.cc'
--- src/logic/map_objects/tribes/worker_program.cc 2019-05-11 13:48:12 +0000
+++ src/logic/map_objects/tribes/worker_program.cc 2019-08-31 15:58:01 +0000
@@ -23,7 +23,6 @@
23#include <string>23#include <string>
2424
25#include "base/log.h"25#include "base/log.h"
26#include "helper.h"
27#include "logic/game_data_error.h"26#include "logic/game_data_error.h"
28#include "logic/map_objects/findnode.h"27#include "logic/map_objects/findnode.h"
29#include "sound/sound_handler.h"28#include "sound/sound_handler.h"
@@ -98,54 +97,45 @@
9897
99 {nullptr, nullptr}};98 {nullptr, nullptr}};
10099
100
101/**101/**
102 * Parse a program102 * Parse a program
103 */103 */
104void WorkerProgram::parse(const LuaTable& table) {104WorkerProgram::WorkerProgram(const std::string& init_name, const LuaTable& actions_table, const WorkerDescr& worker, const Tribes& tribes)
105 for (const std::string& string : table.array_entries<std::string>()) {105 : MapObjectProgram(init_name), worker_(worker), tribes_(tribes) {
106 if (string.empty()) {106
107 log("Worker program %s for worker %s contains empty string\n", name_.c_str(),107 for (const std::string& line : actions_table.array_entries<std::string>()) {
108 worker_.name().c_str());108 if (line.empty()) {
109 break;109 throw GameDataError("Empty line");
110 }110 }
111 try {111 try {
112 std::vector<std::string> cmd(split_string(string, "="));112
113 if (cmd.empty()) {113 ProgramParseInput parseinput = parse_program_string(line);
114 continue;
115 }
116114
117 // Find the appropriate parser115 // Find the appropriate parser
118 Worker::Action act;116 Worker::Action act;
119 uint32_t mapidx;117 uint32_t mapidx;
120118
121 for (mapidx = 0; parsemap_[mapidx].name; ++mapidx) {119 for (mapidx = 0; parsemap_[mapidx].name; ++mapidx) {
122 if (cmd[0] == parsemap_[mapidx].name) {120 if (parseinput.name == parsemap_[mapidx].name) {
123 break;121 break;
124 }122 }
125 }123 }
126124
127 if (!parsemap_[mapidx].name) {125 if (!parsemap_[mapidx].name) {
128 throw wexception("unknown command type \"%s\"", cmd[0].c_str());126 throw GameDataError("Unknown command '%s' in line '%s'", parseinput.name.c_str(), line.c_str());
129 }127 }
130128
131 // TODO(GunChleoc): Quick and dirty solution, don't do it like that when we unify the129 (this->*parsemap_[mapidx].function)(&act, parseinput.arguments);
132 // program parsers
133 if (cmd.size() == 2) {
134 const std::vector<std::string> parameters(split_string(cmd[1], " \t\n"));
135 cmd.pop_back();
136 for (const std::string& parameter : parameters) {
137 cmd.push_back(parameter);
138 }
139 }
140
141 (this->*parsemap_[mapidx].function)(&act, cmd);
142130
143 actions_.push_back(act);131 actions_.push_back(act);
144 } catch (const std::exception& e) {132 } catch (const std::exception& e) {
145 throw wexception("Error reading line '%s' in worker program %s for worker %s: %s",133 throw GameDataError("Error reading line '%s': %s", line.c_str(), e.what());
146 string.c_str(), name_.c_str(), worker_.name().c_str(), e.what());
147 }134 }
148 }135 }
136 if (actions_.empty()) {
137 throw GameDataError("No actions found");
138 }
149}139}
150140
151/* RST141/* RST
@@ -172,11 +162,12 @@
172 * iparam1 = ware index162 * iparam1 = ware index
173 */163 */
174void WorkerProgram::parse_createware(Worker::Action* act, const std::vector<std::string>& cmd) {164void WorkerProgram::parse_createware(Worker::Action* act, const std::vector<std::string>& cmd) {
175 if (cmd.size() != 2)165 if (cmd.size() != 1) {
176 throw wexception("Usage: createware=<ware type>");166 throw wexception("Usage: createware=<ware type>");
167 }
177168
178 act->function = &Worker::run_createware;169 act->function = &Worker::run_createware;
179 act->iparam1 = tribes_.safe_ware_index(cmd[1]);170 act->iparam1 = tribes_.safe_ware_index(cmd[0]);
180}171}
181172
182/* RST173/* RST
@@ -207,16 +198,13 @@
207 * sparam1 = resource198 * sparam1 = resource
208 */199 */
209void WorkerProgram::parse_mine(Worker::Action* act, const std::vector<std::string>& cmd) {200void WorkerProgram::parse_mine(Worker::Action* act, const std::vector<std::string>& cmd) {
210 if (cmd.size() != 3)201 if (cmd.size() != 2) {
211 throw wexception("Usage: mine=<ware type> <area>");202 throw GameDataError("Usage: mine=<ware type> <workarea radius>");
203 }
212204
213 act->function = &Worker::run_mine;205 act->function = &Worker::run_mine;
214 act->sparam1 = cmd[1];206 act->sparam1 = cmd[0];
215 char* endp;207 act->iparam1 = read_positive(cmd[1]);
216 act->iparam1 = strtol(cmd[2].c_str(), &endp, 0);
217
218 if (*endp)
219 throw wexception("Bad area '%s'", cmd[2].c_str());
220}208}
221209
222/* RST210/* RST
@@ -244,16 +232,13 @@
244 * sparam1 = resource232 * sparam1 = resource
245 */233 */
246void WorkerProgram::parse_breed(Worker::Action* act, const std::vector<std::string>& cmd) {234void WorkerProgram::parse_breed(Worker::Action* act, const std::vector<std::string>& cmd) {
247 if (cmd.size() != 3)235 if (cmd.size() != 2) {
248 throw wexception("Usage: breed=<ware type> <area>");236 throw GameDataError("Usage: breed=<ware type> <workarea radius>");
237 }
249238
250 act->function = &Worker::run_breed;239 act->function = &Worker::run_breed;
251 act->sparam1 = cmd[1];240 act->sparam1 = cmd[0];
252 char* endp;241 act->iparam1 = read_positive(cmd[1]);
253 act->iparam1 = strtol(cmd[2].c_str(), &endp, 0);
254
255 if (*endp)
256 throw wexception("Bad area '%s'", cmd[2].c_str());
257}242}
258243
259/* RST244/* RST
@@ -296,37 +281,26 @@
296 * sparam1 = type281 * sparam1 = type
297 */282 */
298void WorkerProgram::parse_findobject(Worker::Action* act, const std::vector<std::string>& cmd) {283void WorkerProgram::parse_findobject(Worker::Action* act, const std::vector<std::string>& cmd) {
299 uint32_t i;
300
301 act->function = &Worker::run_findobject;284 act->function = &Worker::run_findobject;
302 act->iparam1 = -1;285 act->iparam1 = -1;
303 act->iparam2 = -1;286 act->iparam2 = -1;
304 act->sparam1 = "immovable";287 act->sparam1 = "immovable";
305288
306 // Parse predicates289 // Parse predicates
307 for (i = 1; i < cmd.size(); ++i) {290 for (const std::string& argument : cmd) {
308 uint32_t idx = cmd[i].find(':');291 const std::pair<std::string, std::string> item = read_key_value_pair(argument, ':');
309 std::string const key = cmd[i].substr(0, idx);292
310 std::string const value = cmd[i].substr(idx + 1);293 if (item.first == "radius") {
311294 act->iparam1 = read_positive(item.second);
312 if (key == "radius") {295 } else if (item.first == "attrib") {
313 char* endp;296 act->iparam2 = MapObjectDescr::get_attribute_id(item.second);
314297 } else if (item.first == "type") {
315 act->iparam1 = strtol(value.c_str(), &endp, 0);298 act->sparam1 = item.second;
316 if (*endp)299 } else {
317 throw wexception("Bad findobject radius '%s'", value.c_str());300 throw GameDataError("Unknown findobject predicate %s", argument.c_str());
318301 }
319 } else if (key == "attrib") {
320 act->iparam2 = MapObjectDescr::get_attribute_id(value);
321 } else if (key == "type") {
322 act->sparam1 = value;
323 } else
324 throw wexception("Bad findobject predicate %s:%s", key.c_str(), value.c_str());
325 }302 }
326303
327 if (act->iparam1 <= 0)
328 throw wexception("findobject: must specify radius");
329
330 workarea_info_[act->iparam1].insert(" findobject");304 workarea_info_[act->iparam1].insert(" findobject");
331}305}
332306
@@ -408,8 +382,6 @@
408 * sparam1 = Resource382 * sparam1 = Resource
409 */383 */
410void WorkerProgram::parse_findspace(Worker::Action* act, const std::vector<std::string>& cmd) {384void WorkerProgram::parse_findspace(Worker::Action* act, const std::vector<std::string>& cmd) {
411 uint32_t i;
412
413 act->function = &Worker::run_findspace;385 act->function = &Worker::run_findspace;
414 act->iparam1 = -1;386 act->iparam1 = -1;
415 act->iparam2 = -1;387 act->iparam2 = -1;
@@ -420,61 +392,58 @@
420 act->sparam1 = "";392 act->sparam1 = "";
421393
422 // Parse predicates394 // Parse predicates
423 for (i = 1; i < cmd.size(); ++i) {395 for (const std::string& argument : cmd) {
424 uint32_t idx = cmd[i].find(':');396 try {
425 std::string key = cmd[i].substr(0, idx);397 const std::pair<std::string, std::string> item = read_key_value_pair(argument, ':');
426 std::string value = cmd[i].substr(idx + 1);398
427399 if (item.first == "radius") {
428 if (key == "radius") {400 act->iparam1 = read_positive(item.second);
429 char* endp;401 } else if (item.first == "size") {
430402 static const struct {
431 act->iparam1 = strtol(value.c_str(), &endp, 0);403 char const* name;
432 if (*endp)404 int32_t val;
433 throw wexception("Bad findspace radius '%s'", value.c_str());405 } sizenames[] = {{"any", FindNodeSize::sizeAny}, {"build", FindNodeSize::sizeBuild},
434406 {"small", FindNodeSize::sizeSmall}, {"medium", FindNodeSize::sizeMedium},
435 } else if (key == "size") {407 {"big", FindNodeSize::sizeBig}, {"mine", FindNodeSize::sizeMine},
436 static const struct {408 {"port", FindNodeSize::sizePort}, {nullptr, 0}};
437 char const* name;409
438 int32_t val;410 int32_t index;
439 } sizenames[] = {{"any", FindNodeSize::sizeAny}, {"build", FindNodeSize::sizeBuild},411
440 {"small", FindNodeSize::sizeSmall}, {"medium", FindNodeSize::sizeMedium},412 for (index = 0; sizenames[index].name; ++index) {
441 {"big", FindNodeSize::sizeBig}, {"mine", FindNodeSize::sizeMine},413 if (item.second == sizenames[index].name) {
442 {"port", FindNodeSize::sizePort}, {nullptr, 0}};414 break;
443415 }
444 int32_t index;416 }
445417
446 for (index = 0; sizenames[index].name; ++index)418 if (!sizenames[index].name) {
447 if (value == sizenames[index].name)419 throw GameDataError("Bad findspace size '%s'", item.second.c_str());
448 break;420 }
449421
450 if (!sizenames[index].name)422 act->iparam2 = sizenames[index].val;
451 throw wexception("Bad findspace size '%s'", value.c_str());423 } else if (item.first == "breed") {
452424 act->iparam4 = 1;
453 act->iparam2 = sizenames[index].val;425 } else if (item.first == "resource") {
454 } else if (key == "breed") {426 act->sparam1 = item.second;
455 act->iparam4 = 1;427 } else if (item.first == "space") {
456 } else if (key == "resource") {428 act->iparam3 = 1;
457 act->sparam1 = value;429 } else if (item.first == "avoid") {
458 } else if (key == "space") {430 act->iparam5 = MapObjectDescr::get_attribute_id(item.second);
459 act->iparam3 = 1;431 } else if (item.first == "saplingsearches") {
460 } else if (key == "avoid") {432 act->iparam6 = read_positive(item.second);
461 act->iparam5 = MapObjectDescr::get_attribute_id(value);
462 } else if (key == "saplingsearches") {
463 int ival = strtol(value.c_str(), nullptr, 10);
464 if (1 != act->iparam6 || 1 > ival) {
465 throw wexception("Findspace: Forester should consider at least one spot.%d %d %s",
466 act->iparam6, ival, value.c_str());
467 } else {433 } else {
468 act->iparam6 = ival;434 throw GameDataError("Unknown findspace predicate %s", item.first.c_str());
469 }435 }
470 } else436 } catch (const GameDataError& e) {
471 throw wexception("Bad findspace predicate %s:%s", key.c_str(), value.c_str());437 throw GameDataError("Malformed findspace argument %s: %s", argument.c_str(), e.what());
438 }
472 }439 }
473440
474 if (act->iparam1 <= 0)441 if (act->iparam1 <= 0) {
475 throw wexception("findspace: must specify radius");442 throw GameDataError("findspace: must specify radius");
476 if (act->iparam2 < 0)443 }
477 throw wexception("findspace: must specify size");444 if (act->iparam2 < 0) {
445 throw GameDataError("findspace: must specify size");
446 }
478 workarea_info_[act->iparam1].insert(" findspace");447 workarea_info_[act->iparam1].insert(" findspace");
479}448}
480449
@@ -526,19 +495,21 @@
526 * iparam1 = walkXXX495 * iparam1 = walkXXX
527 */496 */
528void WorkerProgram::parse_walk(Worker::Action* act, const std::vector<std::string>& cmd) {497void WorkerProgram::parse_walk(Worker::Action* act, const std::vector<std::string>& cmd) {
529 if (cmd.size() != 2)498 if (cmd.size() != 1) {
530 throw wexception("Usage: walk=<where>");499 throw GameDataError("Usage: walk=object|coords|object-or-coords");
500 }
531501
532 act->function = &Worker::run_walk;502 act->function = &Worker::run_walk;
533503
534 if (cmd[1] == "object")504 if (cmd[0] == "object") {
535 act->iparam1 = Worker::Action::walkObject;505 act->iparam1 = Worker::Action::walkObject;
536 else if (cmd[1] == "coords")506 } else if (cmd[0] == "coords") {
537 act->iparam1 = Worker::Action::walkCoords;507 act->iparam1 = Worker::Action::walkCoords;
538 else if (cmd[1] == "object-or-coords")508 } else if (cmd[0] == "object-or-coords") {
539 act->iparam1 = Worker::Action::walkObject | Worker::Action::walkCoords;509 act->iparam1 = Worker::Action::walkObject | Worker::Action::walkCoords;
540 else510 } else {
541 throw wexception("Bad walk destination '%s'", cmd[1].c_str());511 throw GameDataError("Bad walk destination '%s'", cmd[0].c_str());
512 }
542}513}
543514
544/* RST515/* RST
@@ -565,28 +536,14 @@
565 * iparam2 = duration536 * iparam2 = duration
566 */537 */
567void WorkerProgram::parse_animate(Worker::Action* act, const std::vector<std::string>& cmd) {538void WorkerProgram::parse_animate(Worker::Action* act, const std::vector<std::string>& cmd) {
568 char* endp;539 AnimationParameters parameters = MapObjectProgram::parse_act_animate(cmd, worker_, true);
569
570 if (cmd.size() != 3)
571 throw GameDataError("Usage: animate=<name> <time>");
572
573 if (!worker_.is_animation_known(cmd[1])) {
574 throw GameDataError("unknown animation \"%s\" in worker program for worker \"%s\"",
575 cmd[1].c_str(), worker_.name().c_str());
576 }
577540
578 act->function = &Worker::run_animate;541 act->function = &Worker::run_animate;
579 // If the second parameter to MapObjectDescr::get_animation is ever used for anything other than542 // If the second parameter to MapObjectDescr::get_animation is ever used for anything other than
580 // level-dependent soldier animations, or we want to write a worker program for a soldier,543 // level-dependent soldier animations, or we want to write a worker program for a soldier,
581 // we will need to store the animation name as a string in an iparam544 // we will need to store the animation name as a string in an sparam
582 act->iparam1 = worker_.get_animation(cmd[1], nullptr);545 act->iparam1 = parameters.animation;
583546 act->iparam2 = parameters.duration;
584 act->iparam2 = strtol(cmd[2].c_str(), &endp, 0);
585 if (*endp)
586 throw GameDataError("Bad duration '%s'", cmd[2].c_str());
587
588 if (act->iparam2 <= 0)
589 throw GameDataError("animation duration must be positive");
590}547}
591548
592/* RST549/* RST
@@ -604,7 +561,10 @@
604/**561/**
605 * iparam1 = 0: don't drop ware on flag, 1: do drop ware on flag562 * iparam1 = 0: don't drop ware on flag, 1: do drop ware on flag
606 */563 */
607void WorkerProgram::parse_return(Worker::Action* act, const std::vector<std::string>&) {564void WorkerProgram::parse_return(Worker::Action* act, const std::vector<std::string>& cmd) {
565 if (!cmd.empty()) {
566 throw GameDataError("Usage: return");
567 }
608 act->function = &Worker::run_return;568 act->function = &Worker::run_return;
609 act->iparam1 = 1; // drop a ware on our owner's flag569 act->iparam1 = 1; // drop a ware on our owner's flag
610}570}
@@ -634,11 +594,12 @@
634 * sparam1 = callobject command name594 * sparam1 = callobject command name
635 */595 */
636void WorkerProgram::parse_callobject(Worker::Action* act, const std::vector<std::string>& cmd) {596void WorkerProgram::parse_callobject(Worker::Action* act, const std::vector<std::string>& cmd) {
637 if (cmd.size() != 2)597 if (cmd.size() != 1) {
638 throw wexception("Usage: callobject=<program name>");598 throw GameDataError("Usage: callobject=<program name>");
599 }
639600
640 act->function = &Worker::run_callobject;601 act->function = &Worker::run_callobject;
641 act->sparam1 = cmd[1];602 act->sparam1 = cmd[0];
642}603}
643604
644/* RST605/* RST
@@ -691,36 +652,28 @@
691 * iparam1 one of plantXXX652 * iparam1 one of plantXXX
692 */653 */
693void WorkerProgram::parse_plant(Worker::Action* act, const std::vector<std::string>& cmd) {654void WorkerProgram::parse_plant(Worker::Action* act, const std::vector<std::string>& cmd) {
694 if (cmd.size() < 2)655 if (cmd.empty()) {
695 throw wexception(656 throw GameDataError(
696 "Usage: plant plant=attrib:<attribute> [attrib:<attribute> ...] [unless object]");657 "Usage: plant=attrib:<attribute> [attrib:<attribute> ...] [unless object]");
658 }
697659
698 act->function = &Worker::run_plant;660 act->function = &Worker::run_plant;
699 act->iparam1 = Worker::Action::plantAlways;661 act->iparam1 = Worker::Action::plantAlways;
700 for (uint32_t i = 1; i < cmd.size(); ++i) {662 for (uint32_t i = 0; i < cmd.size(); ++i) {
701 if (i >= 2 && cmd[i] == "unless") {663 if (cmd[i] == "unless") {
702 ++i;664 ++i;
703 if (i >= cmd.size())665 if (i >= cmd.size()) {
704 throw GameDataError("plant: something expected after unless");666 throw GameDataError("plant: something expected after 'unless'");
705 if (cmd[i] == "object")667 }
668 if (cmd[i] == "object") {
706 act->iparam1 = Worker::Action::plantUnlessObject;669 act->iparam1 = Worker::Action::plantUnlessObject;
707 else670 } else {
708 throw GameDataError("plant: 'unless %s' not understood", cmd[i].c_str());671 throw GameDataError("plant: 'unless %s' not understood", cmd[i].c_str());
709672 }
710 continue;673 continue;
711 }674 }
712675
713 // Check if immovable type exists676 const std::string attrib_name = read_key_value_pair(cmd[i], ':', "", "attrib").second;
714 std::vector<std::string> const list(split_string(cmd[i], ":"));
715 if (list.size() != 2 || list.at(0) != "attrib") {
716 throw GameDataError("plant takes a list of attrib:<attribute> that reference world and/or "
717 "tribe immovables");
718 }
719
720 const std::string& attrib_name = list[1];
721 if (attrib_name.empty()) {
722 throw GameDataError("plant has an empty attrib:<attribute> at position %d", i);
723 }
724677
725 // This will throw a GameDataError if the attribute doesn't exist.678 // This will throw a GameDataError if the attribute doesn't exist.
726 ImmovableDescr::get_attribute_id(attrib_name);679 ImmovableDescr::get_attribute_id(attrib_name);
@@ -750,14 +703,12 @@
750*/703*/
751// TODO(GunChleoc): attrib:eatable would be much better, then depend on terrain too704// TODO(GunChleoc): attrib:eatable would be much better, then depend on terrain too
752void WorkerProgram::parse_createbob(Worker::Action* act, const std::vector<std::string>& cmd) {705void WorkerProgram::parse_createbob(Worker::Action* act, const std::vector<std::string>& cmd) {
753 if (cmd.size() < 2)706 if (cmd.empty()) {
754 throw wexception("Usage: createbob=<bob name> <bob name> ...");707 throw GameDataError("Usage: createbob=<bob name> <bob name> ...");
708 }
755709
756 act->function = &Worker::run_createbob;710 act->function = &Worker::run_createbob;
757711 act->sparamv = std::move(cmd);
758 for (uint32_t i = 1; i < cmd.size(); ++i) {
759 act->sparamv.push_back(cmd[i]);
760 }
761}712}
762713
763/* RST714/* RST
@@ -804,22 +755,14 @@
804 * sparam1 = subcommand755 * sparam1 = subcommand
805 */756 */
806void WorkerProgram::parse_repeatsearch(Worker::Action* act, const std::vector<std::string>& cmd) {757void WorkerProgram::parse_repeatsearch(Worker::Action* act, const std::vector<std::string>& cmd) {
807 char* endp;758 if (cmd.size() != 3) {
808759 throw GameDataError("Usage: repeatsearch=<repeat #> <radius> <subcommand>");
809 if (cmd.size() != 4)760 }
810 throw wexception("Usage: repeatsearch=<repeat #> <radius> <subcommand>");
811761
812 act->function = &Worker::run_repeatsearch;762 act->function = &Worker::run_repeatsearch;
813763 act->iparam1 = read_positive(cmd[0]);
814 act->iparam1 = strtol(cmd[1].c_str(), &endp, 0);764 act->iparam2 = read_positive(cmd[1]);
815 if (*endp)765 act->sparam1 = cmd[2];
816 throw wexception("Bad repeat count '%s'", cmd[1].c_str());
817
818 act->iparam2 = strtol(cmd[2].c_str(), &endp, 0);
819 if (*endp)
820 throw wexception("Bad radius '%s'", cmd[2].c_str());
821
822 act->sparam1 = cmd[3];
823}766}
824767
825/* RST768/* RST
@@ -840,8 +783,9 @@
840 }783 }
841*/784*/
842void WorkerProgram::parse_findresources(Worker::Action* act, const std::vector<std::string>& cmd) {785void WorkerProgram::parse_findresources(Worker::Action* act, const std::vector<std::string>& cmd) {
843 if (cmd.size() != 1)786 if (!cmd.empty()) {
844 throw wexception("Usage: findresources");787 throw GameDataError("Usage: findresources");
788 }
845789
846 act->function = &Worker::run_findresources;790 act->function = &Worker::run_findresources;
847}791}
@@ -867,11 +811,12 @@
867 * iparam2 = time811 * iparam2 = time
868 */812 */
869void WorkerProgram::parse_scout(Worker::Action* act, const std::vector<std::string>& cmd) {813void WorkerProgram::parse_scout(Worker::Action* act, const std::vector<std::string>& cmd) {
870 if (cmd.size() != 3)814 if (cmd.size() != 2) {
871 throw wexception("Usage: scout=<radius> <time>");815 throw GameDataError("Usage: scout=<radius> <time>");
816 }
872817
873 act->iparam1 = atoi(cmd[1].c_str());818 act->iparam1 = read_positive(cmd[0]);
874 act->iparam2 = atoi(cmd[2].c_str());819 act->iparam2 = read_positive(cmd[1]);
875 act->function = &Worker::run_scout;820 act->function = &Worker::run_scout;
876}821}
877822
@@ -902,17 +847,12 @@
902 }847 }
903*/848*/
904void WorkerProgram::parse_playsound(Worker::Action* act, const std::vector<std::string>& cmd) {849void WorkerProgram::parse_playsound(Worker::Action* act, const std::vector<std::string>& cmd) {
905 if (cmd.size() < 3 || cmd.size() > 4)850 // 50% chance to play, only one instance at a time
906 throw wexception("Usage: playsound <sound_dir> <sound_name> [priority]");851 PlaySoundParameters parameters = MapObjectProgram::parse_act_play_sound(cmd, kFxPriorityMedium);
907852
908 act->iparam2 = SoundHandler::register_fx(SoundType::kAmbient, cmd[1]);853 act->iparam1 = parameters.priority;
909854 act->iparam2 = parameters.fx;
910 act->function = &Worker::run_playsound;855 act->function = &Worker::run_playsound;
911 act->iparam1 = cmd.size() == 2 ? kFxPriorityMedium : atoi(cmd[2].c_str());
912 if (act->iparam1 < kFxPriorityLowest) {
913 throw GameDataError("Minmum priority for sounds is %d, but only %d was specified for %s",
914 kFxPriorityLowest, act->iparam1, cmd[1].c_str());
915 }
916}856}
917857
918/* RST858/* RST
@@ -943,8 +883,9 @@
943 * for construction. This is used in ship building.883 * for construction. This is used in ship building.
944 */884 */
945void WorkerProgram::parse_construct(Worker::Action* act, const std::vector<std::string>& cmd) {885void WorkerProgram::parse_construct(Worker::Action* act, const std::vector<std::string>& cmd) {
946 if (cmd.size() != 1)886 if (!cmd.empty()) {
947 throw wexception("Usage: construct");887 throw GameDataError("Usage: construct");
888 }
948889
949 act->function = &Worker::run_construct;890 act->function = &Worker::run_construct;
950}891}
951892
=== modified file 'src/logic/map_objects/tribes/worker_program.h'
--- src/logic/map_objects/tribes/worker_program.h 2019-02-23 11:00:49 +0000
+++ src/logic/map_objects/tribes/worker_program.h 2019-08-31 15:58:01 +0000
@@ -24,6 +24,7 @@
2424
25#include "base/macros.h"25#include "base/macros.h"
26#include "logic/map_objects/bob.h"26#include "logic/map_objects/bob.h"
27#include "logic/map_objects/map_object_program.h"
27#include "logic/map_objects/tribes/tribes.h"28#include "logic/map_objects/tribes/tribes.h"
28#include "logic/map_objects/tribes/workarea_info.h"29#include "logic/map_objects/tribes/workarea_info.h"
29#include "logic/map_objects/tribes/worker.h"30#include "logic/map_objects/tribes/worker.h"
@@ -35,20 +36,13 @@
35// declaration (Chicken-and-egg problem)36// declaration (Chicken-and-egg problem)
36class WorkerDescr;37class WorkerDescr;
3738
38struct WorkerProgram : public BobProgramBase {39struct WorkerProgram : public MapObjectProgram {
3940
40 using ParseWorkerProgramFn = void (WorkerProgram::*)(Worker::Action*,41 using ParseWorkerProgramFn = void (WorkerProgram::*)(Worker::Action*,
41 const std::vector<std::string>&);42 const std::vector<std::string>&);
4243
43 WorkerProgram(const std::string& name, const WorkerDescr& worker, const Tribes& tribes)44 WorkerProgram(const std::string& init_name, const LuaTable& actions_table, const WorkerDescr& worker, const Tribes& tribes);
44 : name_(name), worker_(worker), tribes_(tribes) {
45 }
46 ~WorkerProgram() override {
47 }
4845
49 std::string get_name() const override {
50 return name_;
51 }
52 using Actions = std::vector<Worker::Action>;46 using Actions = std::vector<Worker::Action>;
53 Actions::size_type get_size() const {47 Actions::size_type get_size() const {
54 return actions_.size();48 return actions_.size();
@@ -62,7 +56,6 @@
62 return &actions_[idx];56 return &actions_[idx];
63 }57 }
6458
65 void parse(const LuaTable& table);
66 const WorkareaInfo& get_workarea_info() const {59 const WorkareaInfo& get_workarea_info() const {
67 return workarea_info_;60 return workarea_info_;
68 }61 }
@@ -92,7 +85,6 @@
92 void parse_playsound(Worker::Action* act, const std::vector<std::string>& cmd);85 void parse_playsound(Worker::Action* act, const std::vector<std::string>& cmd);
93 void parse_construct(Worker::Action* act, const std::vector<std::string>& cmd);86 void parse_construct(Worker::Action* act, const std::vector<std::string>& cmd);
9487
95 const std::string name_;
96 const WorkerDescr& worker_;88 const WorkerDescr& worker_;
97 const Tribes& tribes_;89 const Tribes& tribes_;
98 Actions actions_;90 Actions actions_;
9991
=== modified file 'src/logic/map_objects/world/critter.cc'
--- src/logic/map_objects/world/critter.cc 2019-06-23 10:30:26 +0000
+++ src/logic/map_objects/world/critter.cc 2019-08-31 15:58:01 +0000
@@ -27,22 +27,21 @@
27#include <stdint.h>27#include <stdint.h>
2828
29#include "base/wexception.h"29#include "base/wexception.h"
30#include "helper.h"
31#include "io/fileread.h"30#include "io/fileread.h"
32#include "io/filewrite.h"31#include "io/filewrite.h"
33#include "logic/field.h"32#include "logic/field.h"
34#include "logic/game.h"33#include "logic/game.h"
35#include "logic/game_data_error.h"34#include "logic/game_data_error.h"
35#include "logic/map_objects/map_object_program.h"
36#include "logic/map_objects/tribes/tribe_descr.h"36#include "logic/map_objects/tribes/tribe_descr.h"
37#include "logic/map_objects/world/critter_program.h"
38#include "logic/map_objects/world/world.h"37#include "logic/map_objects/world/world.h"
39#include "map_io/world_legacy_lookup_table.h"38#include "map_io/world_legacy_lookup_table.h"
40#include "scripting/lua_table.h"39#include "scripting/lua_table.h"
4140
42namespace Widelands {41namespace Widelands {
4342
44void CritterProgram::parse(const std::vector<std::string>& lines) {43CritterProgram::CritterProgram(const std::string& program_name, const LuaTable& actions_table) : MapObjectProgram(program_name) {
45 for (const std::string& line : lines) {44 for (const std::string& line : actions_table.array_entries<std::string>()) {
46 try {45 try {
47 const std::vector<std::string> cmd(split_string(line, " \t\r\n"));46 const std::vector<std::string> cmd(split_string(line, " \t\r\n"));
48 if (cmd.empty())47 if (cmd.empty())
@@ -109,9 +108,7 @@
109 std::unique_ptr<LuaTable> programs = table.get_table("programs");108 std::unique_ptr<LuaTable> programs = table.get_table("programs");
110 for (const std::string& program_name : programs->keys<std::string>()) {109 for (const std::string& program_name : programs->keys<std::string>()) {
111 try {110 try {
112 std::unique_ptr<CritterProgram> prog(new CritterProgram(program_name));111 programs_[program_name] = std::unique_ptr<CritterProgram>(new CritterProgram(program_name, *programs->get_table(program_name).get()));
113 prog->parse(programs->get_table(program_name)->array_entries<std::string>());
114 programs_[program_name] = prog.release();
115 } catch (const std::exception& e) {112 } catch (const std::exception& e) {
116 throw wexception("Parse error in program %s: %s", program_name.c_str(), e.what());113 throw wexception("Parse error in program %s: %s", program_name.c_str(), e.what());
117 }114 }
@@ -126,9 +123,6 @@
126}123}
127124
128CritterDescr::~CritterDescr() {125CritterDescr::~CritterDescr() {
129 for (auto program : programs_) {
130 delete program.second;
131 }
132}126}
133127
134bool CritterDescr::is_swimming() const {128bool CritterDescr::is_swimming() const {
@@ -141,11 +135,11 @@
141Get a program from the workers description.135Get a program from the workers description.
142===============136===============
143*/137*/
144CritterProgram const* CritterDescr::get_program(const std::string& programname) const {138CritterProgram const* CritterDescr::get_program(const std::string& program_name) const {
145 Programs::const_iterator const it = programs_.find(programname);139 Programs::const_iterator const it = programs_.find(program_name);
146 if (it == programs_.end())140 if (it == programs_.end())
147 throw wexception("%s has no program '%s'", name().c_str(), programname.c_str());141 throw wexception("%s has no program '%s'", name().c_str(), program_name.c_str());
148 return it->second;142 return it->second.get();
149}143}
150144
151uint32_t CritterDescr::movecaps() const {145uint32_t CritterDescr::movecaps() const {
@@ -283,7 +277,7 @@
283 return Bob::Loader::get_task(name);277 return Bob::Loader::get_task(name);
284}278}
285279
286const BobProgramBase* Critter::Loader::get_program(const std::string& name) {280const MapObjectProgram* Critter::Loader::get_program(const std::string& name) {
287 Critter& critter = get<Critter>();281 Critter& critter = get<Critter>();
288 return critter.descr().get_program(name);282 return critter.descr().get_program(name);
289}283}
290284
=== modified file 'src/logic/map_objects/world/critter.h'
--- src/logic/map_objects/world/critter.h 2019-02-23 11:00:49 +0000
+++ src/logic/map_objects/world/critter.h 2019-08-31 15:58:01 +0000
@@ -20,9 +20,12 @@
20#ifndef WL_LOGIC_MAP_OBJECTS_WORLD_CRITTER_H20#ifndef WL_LOGIC_MAP_OBJECTS_WORLD_CRITTER_H
21#define WL_LOGIC_MAP_OBJECTS_WORLD_CRITTER_H21#define WL_LOGIC_MAP_OBJECTS_WORLD_CRITTER_H
2222
23#include <memory>
24
23#include "base/macros.h"25#include "base/macros.h"
24#include "graphic/diranimations.h"26#include "graphic/diranimations.h"
25#include "logic/map_objects/bob.h"27#include "logic/map_objects/bob.h"
28#include "logic/map_objects/world/critter_program.h"
2629
27class LuaTable;30class LuaTable;
28class WorldLegacyLookupTable;31class WorldLegacyLookupTable;
@@ -48,13 +51,13 @@
48 return walk_anims_;51 return walk_anims_;
49 }52 }
5053
51 CritterProgram const* get_program(const std::string&) const;54 CritterProgram const* get_program(const std::string& program_name) const;
5255
53 const EditorCategory* editor_category() const;56 const EditorCategory* editor_category() const;
5457
55private:58private:
56 DirAnimations walk_anims_;59 DirAnimations walk_anims_;
57 using Programs = std::map<std::string, CritterProgram*>;60 using Programs = std::map<std::string, std::unique_ptr<const CritterProgram>>;
58 Programs programs_;61 Programs programs_;
59 EditorCategory* editor_category_; // not owned.62 EditorCategory* editor_category_; // not owned.
60 DISALLOW_COPY_AND_ASSIGN(CritterDescr);63 DISALLOW_COPY_AND_ASSIGN(CritterDescr);
@@ -83,7 +86,7 @@
83 Loader();86 Loader();
8487
85 const Task* get_task(const std::string& name) override;88 const Task* get_task(const std::string& name) override;
86 const BobProgramBase* get_program(const std::string& name) override;89 const MapObjectProgram* get_program(const std::string& name) override;
87 };90 };
8891
89private:92private:
9093
=== modified file 'src/logic/map_objects/world/critter_program.h'
--- src/logic/map_objects/world/critter_program.h 2019-02-23 11:00:49 +0000
+++ src/logic/map_objects/world/critter_program.h 2019-08-31 15:58:01 +0000
@@ -21,8 +21,10 @@
21#define WL_LOGIC_MAP_OBJECTS_WORLD_CRITTER_PROGRAM_H21#define WL_LOGIC_MAP_OBJECTS_WORLD_CRITTER_PROGRAM_H
2222
23#include "logic/map_objects/bob.h"23#include "logic/map_objects/bob.h"
24#include "logic/map_objects/map_object_program.h"
2425
25namespace Widelands {26namespace Widelands {
27class Critter;
2628
27struct CritterAction {29struct CritterAction {
28 using CritterExecuteActionFn = bool (Critter::*)(Game&, Bob::State&, const CritterAction&);30 using CritterExecuteActionFn = bool (Critter::*)(Game&, Bob::State&, const CritterAction&);
@@ -40,15 +42,9 @@
40 std::vector<std::string> sparamv;42 std::vector<std::string> sparamv;
41};43};
4244
43struct CritterProgram : public BobProgramBase {45struct CritterProgram : public MapObjectProgram {
44 explicit CritterProgram(const std::string& name) : name_(name) {46 explicit CritterProgram(const std::string& program_name, const LuaTable& actions_table);
45 }
46 ~CritterProgram() override {
47 }
4847
49 std::string get_name() const override {
50 return name_;
51 }
52 int32_t get_size() const {48 int32_t get_size() const {
53 return actions_.size();49 return actions_.size();
54 }50 }
@@ -57,10 +53,7 @@
57 return actions_[idx];53 return actions_[idx];
58 }54 }
5955
60 void parse(const std::vector<std::string>& lines);
61
62private:56private:
63 std::string name_;
64 std::vector<CritterAction> actions_;57 std::vector<CritterAction> actions_;
65};58};
66} // namespace Widelands59} // namespace Widelands
6760
=== modified file 'src/logic/map_objects/world/resource_description.cc'
--- src/logic/map_objects/world/resource_description.cc 2019-02-23 11:00:49 +0000
+++ src/logic/map_objects/world/resource_description.cc 2019-08-31 15:58:01 +0000
@@ -21,7 +21,6 @@
2121
22#include <memory>22#include <memory>
2323
24#include "helper.h"
25#include "logic/game_data_error.h"24#include "logic/game_data_error.h"
26#include "scripting/lua_table.h"25#include "scripting/lua_table.h"
2726
2827
=== modified file 'src/network/CMakeLists.txt'
--- src/network/CMakeLists.txt 2019-08-27 19:00:30 +0000
+++ src/network/CMakeLists.txt 2019-08-31 15:58:01 +0000
@@ -44,7 +44,6 @@
44 build_info44 build_info
45 chat45 chat
46 game_io46 game_io
47 helper
48 io_fileread47 io_fileread
49 io_filesystem48 io_filesystem
50 io_stream49 io_stream
5150
=== modified file 'src/network/gameclient.cc'
--- src/network/gameclient.cc 2019-08-28 06:12:07 +0000
+++ src/network/gameclient.cc 2019-08-31 15:58:01 +0000
@@ -31,7 +31,6 @@
31#include "build_info.h"31#include "build_info.h"
32#include "config.h"32#include "config.h"
33#include "game_io/game_loader.h"33#include "game_io/game_loader.h"
34#include "helper.h"
35#include "io/fileread.h"34#include "io/fileread.h"
36#include "io/filesystem/filesystem_exceptions.h"35#include "io/filesystem/filesystem_exceptions.h"
37#include "io/filewrite.h"36#include "io/filewrite.h"
3837
=== modified file 'src/network/gamehost.cc'
--- src/network/gamehost.cc 2019-08-28 06:12:07 +0000
+++ src/network/gamehost.cc 2019-08-31 15:58:01 +0000
@@ -40,7 +40,6 @@
40#include "chat/chat.h"40#include "chat/chat.h"
41#include "game_io/game_loader.h"41#include "game_io/game_loader.h"
42#include "game_io/game_preload_packet.h"42#include "game_io/game_preload_packet.h"
43#include "helper.h"
44#include "io/fileread.h"43#include "io/fileread.h"
45#include "io/filesystem/layered_filesystem.h"44#include "io/filesystem/layered_filesystem.h"
46#include "logic/filesystem_constants.h"45#include "logic/filesystem_constants.h"
4746
=== modified file 'src/scripting/CMakeLists.txt'
--- src/scripting/CMakeLists.txt 2019-08-27 19:00:30 +0000
+++ src/scripting/CMakeLists.txt 2019-08-31 15:58:01 +0000
@@ -70,7 +70,6 @@
70 base_i18n70 base_i18n
71 base_macros71 base_macros
72 build_info72 build_info
73 helper
74 io_filesystem73 io_filesystem
75 scripting_base74 scripting_base
76 scripting_errors75 scripting_errors
7776
=== modified file 'src/scripting/lua_path.cc'
--- src/scripting/lua_path.cc 2019-04-28 10:14:42 +0000
+++ src/scripting/lua_path.cc 2019-08-31 15:58:01 +0000
@@ -24,7 +24,6 @@
24#include <boost/lexical_cast.hpp>24#include <boost/lexical_cast.hpp>
2525
26#include "base/macros.h"26#include "base/macros.h"
27#include "helper.h"
28#include "io/filesystem/layered_filesystem.h"27#include "io/filesystem/layered_filesystem.h"
2928
30namespace {29namespace {
3130
=== modified file 'src/sound/CMakeLists.txt'
--- src/sound/CMakeLists.txt 2019-08-27 19:00:30 +0000
+++ src/sound/CMakeLists.txt 2019-08-31 15:58:01 +0000
@@ -28,7 +28,6 @@
28 DEPENDS28 DEPENDS
29 base_i18n29 base_i18n
30 base_log30 base_log
31 helper
32 io_fileread31 io_fileread
33 io_filesystem32 io_filesystem
34 logic_exceptions33 logic_exceptions
3534
=== modified file 'src/sound/fxset.cc'
--- src/sound/fxset.cc 2019-05-25 08:52:09 +0000
+++ src/sound/fxset.cc 2019-08-31 15:58:01 +0000
@@ -25,7 +25,6 @@
25#include <boost/regex.hpp>25#include <boost/regex.hpp>
2626
27#include "base/log.h"27#include "base/log.h"
28#include "helper.h"
29#include "io/fileread.h"28#include "io/fileread.h"
30#include "io/filesystem/layered_filesystem.h"29#include "io/filesystem/layered_filesystem.h"
31#include "logic/game_data_error.h"30#include "logic/game_data_error.h"
3231
=== modified file 'src/sound/songset.cc'
--- src/sound/songset.cc 2019-04-28 09:14:59 +0000
+++ src/sound/songset.cc 2019-08-31 15:58:01 +0000
@@ -25,7 +25,6 @@
25#include <boost/regex.hpp>25#include <boost/regex.hpp>
2626
27#include "base/log.h"27#include "base/log.h"
28#include "helper.h"
29#include "io/fileread.h"28#include "io/fileread.h"
30#include "io/filesystem/layered_filesystem.h"29#include "io/filesystem/layered_filesystem.h"
3130
3231
=== modified file 'src/ui_fsmenu/CMakeLists.txt'
--- src/ui_fsmenu/CMakeLists.txt 2019-08-27 19:00:30 +0000
+++ src/ui_fsmenu/CMakeLists.txt 2019-08-31 15:58:01 +0000
@@ -10,7 +10,6 @@
10 graphic_fonthandler10 graphic_fonthandler
11 graphic_text11 graphic_text
12 graphic_text_layout12 graphic_text_layout
13 helper
14 io_filesystem13 io_filesystem
15 logic_filesystem_constants14 logic_filesystem_constants
16 scripting_lua_interface15 scripting_lua_interface
@@ -101,7 +100,6 @@
101 base_i18n100 base_i18n
102 graphic101 graphic
103 graphic_playercolor102 graphic_playercolor
104 helper
105 io_filesystem103 io_filesystem
106 io_profile104 io_profile
107 logic105 logic
108106
=== modified file 'src/ui_fsmenu/launch_spg.cc'
--- src/ui_fsmenu/launch_spg.cc 2019-06-01 14:16:25 +0000
+++ src/ui_fsmenu/launch_spg.cc 2019-08-31 15:58:01 +0000
@@ -26,7 +26,6 @@
26#include "base/i18n.h"26#include "base/i18n.h"
27#include "base/warning.h"27#include "base/warning.h"
28#include "base/wexception.h"28#include "base/wexception.h"
29#include "helper.h"
30#include "io/filesystem/layered_filesystem.h"29#include "io/filesystem/layered_filesystem.h"
31#include "logic/game.h"30#include "logic/game.h"
32#include "logic/game_controller.h"31#include "logic/game_controller.h"
3332
=== modified file 'src/ui_fsmenu/options.cc'
--- src/ui_fsmenu/options.cc 2019-08-25 14:50:16 +0000
+++ src/ui_fsmenu/options.cc 2019-08-31 15:58:01 +0000
@@ -36,7 +36,6 @@
36#include "graphic/text/bidi.h"36#include "graphic/text/bidi.h"
37#include "graphic/text/font_set.h"37#include "graphic/text/font_set.h"
38#include "graphic/text_layout.h"38#include "graphic/text_layout.h"
39#include "helper.h"
40#include "io/filesystem/disk_filesystem.h"39#include "io/filesystem/disk_filesystem.h"
41#include "io/filesystem/layered_filesystem.h"40#include "io/filesystem/layered_filesystem.h"
42#include "logic/filesystem_constants.h"41#include "logic/filesystem_constants.h"
4342
=== modified file 'src/wui/CMakeLists.txt'
--- src/wui/CMakeLists.txt 2019-08-27 19:00:30 +0000
+++ src/wui/CMakeLists.txt 2019-08-31 15:58:01 +0000
@@ -88,7 +88,6 @@
88 base_i18n88 base_i18n
89 base_log89 base_log
90 base_time_string90 base_time_string
91 helper
92 game_io91 game_io
93 graphic92 graphic
94 graphic_fonthandler93 graphic_fonthandler
9594
=== modified file 'src/wui/load_or_save_game.cc'
--- src/wui/load_or_save_game.cc 2019-05-27 14:28:34 +0000
+++ src/wui/load_or_save_game.cc 2019-08-31 15:58:01 +0000
@@ -31,8 +31,6 @@
31#include "game_io/game_loader.h"31#include "game_io/game_loader.h"
32#include "game_io/game_preload_packet.h"32#include "game_io/game_preload_packet.h"
33#include "graphic/font_handler.h"33#include "graphic/font_handler.h"
34#include "graphic/text_layout.h"
35#include "helper.h"
36#include "io/filesystem/filesystem_exceptions.h"34#include "io/filesystem/filesystem_exceptions.h"
37#include "io/filesystem/layered_filesystem.h"35#include "io/filesystem/layered_filesystem.h"
38#include "logic/filesystem_constants.h"36#include "logic/filesystem_constants.h"

Subscribers

People subscribed via source and target branches

to status/vote changes: