Merge lp:~widelands-dev/widelands/unify-program-parsers into lp:widelands
- unify-program-parsers
- Merge into trunk
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 |
Related bugs: |
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:
- 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.
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-directorie
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.
Notabilis (notabilis27) wrote : | # |
Looking good now, thanks.
GunChleoc (gunchleoc) wrote : | # |
Thanks for reviewing again :)
@bunnybot merge
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:/
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 5242. State: errored. Details: https:/
Appveyor build 5021. State: success. Details: https:/
Toni Förster (stonerl) wrote : | # |
Is it okay to force merge this branch?
test/maps/
Running Widelands ...
Seems to time out in some builds.
GunChleoc (gunchleoc) wrote : | # |
No, I still need to find the bug that occurs with some compilers.
GunChleoc (gunchleoc) wrote : | # |
I can reproduce the problem now in an Ubuntu trusty VM.
Preview Diff
1 | === modified file 'data/tribes/immovables/shipconstruction_atlanteans/init.lua' | |||
2 | --- data/tribes/immovables/shipconstruction_atlanteans/init.lua 2019-05-28 19:37:11 +0000 | |||
3 | +++ data/tribes/immovables/shipconstruction_atlanteans/init.lua 2019-08-31 15:58:01 +0000 | |||
4 | @@ -12,7 +12,7 @@ | |||
5 | 12 | programs = { | 12 | programs = { |
6 | 13 | program = { | 13 | program = { |
7 | 14 | "construct=idle 5000 210000", | 14 | "construct=idle 5000 210000", |
9 | 15 | "transform=bob tribe:atlanteans_ship", | 15 | "transform=bob atlanteans_ship", |
10 | 16 | } | 16 | } |
11 | 17 | }, | 17 | }, |
12 | 18 | buildcost = { | 18 | buildcost = { |
13 | 19 | 19 | ||
14 | === modified file 'data/tribes/immovables/shipconstruction_barbarians/init.lua' | |||
15 | --- data/tribes/immovables/shipconstruction_barbarians/init.lua 2019-05-28 19:37:11 +0000 | |||
16 | +++ data/tribes/immovables/shipconstruction_barbarians/init.lua 2019-08-31 15:58:01 +0000 | |||
17 | @@ -12,7 +12,7 @@ | |||
18 | 12 | programs = { | 12 | programs = { |
19 | 13 | program = { | 13 | program = { |
20 | 14 | "construct=idle 5000 210000", | 14 | "construct=idle 5000 210000", |
22 | 15 | "transform=bob tribe:barbarians_ship", | 15 | "transform=bob barbarians_ship", |
23 | 16 | } | 16 | } |
24 | 17 | }, | 17 | }, |
25 | 18 | buildcost = { | 18 | buildcost = { |
26 | 19 | 19 | ||
27 | === modified file 'data/tribes/immovables/shipconstruction_empire/init.lua' | |||
28 | --- data/tribes/immovables/shipconstruction_empire/init.lua 2019-05-28 19:37:11 +0000 | |||
29 | +++ data/tribes/immovables/shipconstruction_empire/init.lua 2019-08-31 15:58:01 +0000 | |||
30 | @@ -12,7 +12,7 @@ | |||
31 | 12 | programs = { | 12 | programs = { |
32 | 13 | program = { | 13 | program = { |
33 | 14 | "construct=idle 5000 210000", | 14 | "construct=idle 5000 210000", |
35 | 15 | "transform=bob tribe:empire_ship", | 15 | "transform=bob empire_ship", |
36 | 16 | } | 16 | } |
37 | 17 | }, | 17 | }, |
38 | 18 | buildcost = { | 18 | buildcost = { |
39 | 19 | 19 | ||
40 | === modified file 'data/tribes/immovables/shipconstruction_frisians/init.lua' | |||
41 | --- data/tribes/immovables/shipconstruction_frisians/init.lua 2019-05-28 19:37:11 +0000 | |||
42 | +++ data/tribes/immovables/shipconstruction_frisians/init.lua 2019-08-31 15:58:01 +0000 | |||
43 | @@ -12,7 +12,7 @@ | |||
44 | 12 | programs = { | 12 | programs = { |
45 | 13 | program = { | 13 | program = { |
46 | 14 | "construct=idle 5000 210000", | 14 | "construct=idle 5000 210000", |
48 | 15 | "transform=bob tribe:frisians_ship", | 15 | "transform=bob frisians_ship", |
49 | 16 | } | 16 | } |
50 | 17 | }, | 17 | }, |
51 | 18 | buildcost = { | 18 | buildcost = { |
52 | 19 | 19 | ||
53 | === modified file 'data/tribes/workers/empire/stonemason/init.lua' | |||
54 | --- data/tribes/workers/empire/stonemason/init.lua 2019-06-02 14:45:28 +0000 | |||
55 | +++ data/tribes/workers/empire/stonemason/init.lua 2019-08-31 15:58:01 +0000 | |||
56 | @@ -41,7 +41,7 @@ | |||
57 | 41 | "return" | 41 | "return" |
58 | 42 | }, | 42 | }, |
59 | 43 | cut_marble = { | 43 | cut_marble = { |
61 | 44 | "findobject= attrib:rocks radius:6", | 44 | "findobject=attrib:rocks radius:6", |
62 | 45 | "walk=object", | 45 | "walk=object", |
63 | 46 | "playsound=sound/stonecutting/stonecutter 220", | 46 | "playsound=sound/stonecutting/stonecutter 220", |
64 | 47 | "animate=hacking 17500", | 47 | "animate=hacking 17500", |
65 | 48 | 48 | ||
66 | === modified file 'src/CMakeLists.txt' | |||
67 | --- src/CMakeLists.txt 2019-07-20 14:32:57 +0000 | |||
68 | +++ src/CMakeLists.txt 2019-08-31 15:58:01 +0000 | |||
69 | @@ -132,16 +132,6 @@ | |||
70 | 132 | wui | 132 | wui |
71 | 133 | ) | 133 | ) |
72 | 134 | 134 | ||
73 | 135 | # TODO(sirver): Split into libs with useful names. | ||
74 | 136 | wl_library(helper | ||
75 | 137 | SRCS | ||
76 | 138 | helper.cc | ||
77 | 139 | helper.h | ||
78 | 140 | USES_SDL2 | ||
79 | 141 | DEPENDS | ||
80 | 142 | base_exceptions | ||
81 | 143 | ) | ||
82 | 144 | |||
83 | 145 | if (CMAKE_SYSTEM_NAME MATCHES "FreeBSD" OR CMAKE_SYSTEM_NAME MATCHES "OpenBSD") | 135 | if (CMAKE_SYSTEM_NAME MATCHES "FreeBSD" OR CMAKE_SYSTEM_NAME MATCHES "OpenBSD") |
84 | 146 | target_link_libraries(widelands_ball_of_mud ${EXECINFO_LIBRARY}) | 136 | target_link_libraries(widelands_ball_of_mud ${EXECINFO_LIBRARY}) |
85 | 147 | endif (CMAKE_SYSTEM_NAME MATCHES "FreeBSD" OR CMAKE_SYSTEM_NAME MATCHES "OpenBSD") | 137 | endif (CMAKE_SYSTEM_NAME MATCHES "FreeBSD" OR CMAKE_SYSTEM_NAME MATCHES "OpenBSD") |
86 | 148 | 138 | ||
87 | === modified file 'src/editor/editorinteractive.cc' | |||
88 | --- src/editor/editorinteractive.cc 2019-08-28 06:12:07 +0000 | |||
89 | +++ src/editor/editorinteractive.cc 2019-08-31 15:58:01 +0000 | |||
90 | @@ -474,8 +474,7 @@ | |||
91 | 474 | } | 474 | } |
92 | 475 | 475 | ||
93 | 476 | ml->load_map_complete(egbase(), Widelands::MapLoader::LoadType::kEditor); | 476 | ml->load_map_complete(egbase(), Widelands::MapLoader::LoadType::kEditor); |
96 | 477 | egbase().postload(); | 477 | egbase().create_tempfile_and_save_mapdata(FileSystem::ZIP); |
95 | 478 | egbase().load_graphics(loader_ui); | ||
97 | 479 | map_changed(MapWas::kReplaced); | 478 | map_changed(MapWas::kReplaced); |
98 | 480 | } | 479 | } |
99 | 481 | 480 | ||
100 | 482 | 481 | ||
101 | === modified file 'src/editor/ui_menus/main_menu_new_map.cc' | |||
102 | --- src/editor/ui_menus/main_menu_new_map.cc 2019-08-28 06:12:07 +0000 | |||
103 | +++ src/editor/ui_menus/main_menu_new_map.cc 2019-08-31 15:58:01 +0000 | |||
104 | @@ -110,8 +110,7 @@ | |||
105 | 110 | map_size_box_.selected_height(), list_.get_selected(), _("No Name"), | 110 | map_size_box_.selected_height(), list_.get_selected(), _("No Name"), |
106 | 111 | get_config_string("realname", pgettext("author_name", "Unknown"))); | 111 | get_config_string("realname", pgettext("author_name", "Unknown"))); |
107 | 112 | 112 | ||
110 | 113 | egbase.postload(); | 113 | egbase.create_tempfile_and_save_mapdata(FileSystem::ZIP); |
109 | 114 | egbase.load_graphics(loader_ui); | ||
111 | 115 | 114 | ||
112 | 116 | map->recalc_whole_map(egbase.world()); | 115 | map->recalc_whole_map(egbase.world()); |
113 | 117 | parent.map_changed(EditorInteractive::MapWas::kReplaced); | 116 | parent.map_changed(EditorInteractive::MapWas::kReplaced); |
114 | 118 | 117 | ||
115 | === modified file 'src/editor/ui_menus/main_menu_random_map.cc' | |||
116 | --- src/editor/ui_menus/main_menu_random_map.cc 2019-08-28 06:12:07 +0000 | |||
117 | +++ src/editor/ui_menus/main_menu_random_map.cc 2019-08-31 15:58:01 +0000 | |||
118 | @@ -516,8 +516,7 @@ | |||
119 | 516 | 516 | ||
120 | 517 | gen.create_random_map(); | 517 | gen.create_random_map(); |
121 | 518 | 518 | ||
124 | 519 | egbase.postload(); | 519 | egbase.create_tempfile_and_save_mapdata(FileSystem::ZIP); |
123 | 520 | egbase.load_graphics(loader_ui); | ||
125 | 521 | 520 | ||
126 | 522 | map->recalc_whole_map(egbase.world()); | 521 | map->recalc_whole_map(egbase.world()); |
127 | 523 | eia.map_changed(EditorInteractive::MapWas::kReplaced); | 522 | eia.map_changed(EditorInteractive::MapWas::kReplaced); |
128 | 524 | 523 | ||
129 | === removed file 'src/helper.cc' | |||
130 | --- src/helper.cc 2019-02-23 11:00:49 +0000 | |||
131 | +++ src/helper.cc 1970-01-01 00:00:00 +0000 | |||
132 | @@ -1,55 +0,0 @@ | |||
133 | 1 | /* | ||
134 | 2 | * Copyright (C) 2002-2019 by the Widelands Development Team | ||
135 | 3 | * | ||
136 | 4 | * This program is free software; you can redistribute it and/or | ||
137 | 5 | * modify it under the terms of the GNU General Public License | ||
138 | 6 | * as published by the Free Software Foundation; either version 2 | ||
139 | 7 | * of the License, or (at your option) any later version. | ||
140 | 8 | * | ||
141 | 9 | * This program is distributed in the hope that it will be useful, | ||
142 | 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
143 | 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
144 | 12 | * GNU General Public License for more details. | ||
145 | 13 | * | ||
146 | 14 | * You should have received a copy of the GNU General Public License | ||
147 | 15 | * along with this program; if not, write to the Free Software | ||
148 | 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
149 | 17 | * | ||
150 | 18 | */ | ||
151 | 19 | |||
152 | 20 | #include "helper.h" | ||
153 | 21 | |||
154 | 22 | #include <cstdarg> | ||
155 | 23 | #include <memory> | ||
156 | 24 | #include <string> | ||
157 | 25 | |||
158 | 26 | #include <boost/algorithm/string/replace.hpp> | ||
159 | 27 | #include <boost/format.hpp> | ||
160 | 28 | #include <boost/lexical_cast.hpp> | ||
161 | 29 | |||
162 | 30 | std::vector<std::string> split_string(const std::string& s, const char* const separators) { | ||
163 | 31 | std::vector<std::string> result; | ||
164 | 32 | for (std::string::size_type pos = 0, endpos; | ||
165 | 33 | (pos = s.find_first_not_of(separators, pos)) != std::string::npos; pos = endpos) { | ||
166 | 34 | endpos = s.find_first_of(separators, pos); | ||
167 | 35 | result.push_back(s.substr(pos, endpos - pos)); | ||
168 | 36 | } | ||
169 | 37 | return result; | ||
170 | 38 | } | ||
171 | 39 | |||
172 | 40 | char* next_word(char*& p, bool& reached_end, char const terminator) { | ||
173 | 41 | assert(terminator); | ||
174 | 42 | char* const result = p; | ||
175 | 43 | for (; *p != terminator; ++p) | ||
176 | 44 | if (*p == '\0') { | ||
177 | 45 | reached_end = true; | ||
178 | 46 | goto end; | ||
179 | 47 | } | ||
180 | 48 | reached_end = false; | ||
181 | 49 | *p = '\0'; // terminate the word | ||
182 | 50 | ++p; // move past the terminator | ||
183 | 51 | end: | ||
184 | 52 | if (result < p) | ||
185 | 53 | return result; | ||
186 | 54 | throw wexception("expected word"); | ||
187 | 55 | } | ||
188 | 56 | 0 | ||
189 | === removed file 'src/helper.h' | |||
190 | --- src/helper.h 2019-03-27 07:20:26 +0000 | |||
191 | +++ src/helper.h 1970-01-01 00:00:00 +0000 | |||
192 | @@ -1,44 +0,0 @@ | |||
193 | 1 | /* | ||
194 | 2 | * Copyright (C) 2006-2019 by the Widelands Development Team | ||
195 | 3 | * | ||
196 | 4 | * This program is free software; you can redistribute it and/or | ||
197 | 5 | * modify it under the terms of the GNU General Public License | ||
198 | 6 | * as published by the Free Software Foundation; either version 2 | ||
199 | 7 | * of the License, or (at your option) any later version. | ||
200 | 8 | * | ||
201 | 9 | * This program is distributed in the hope that it will be useful, | ||
202 | 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
203 | 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
204 | 12 | * GNU General Public License for more details. | ||
205 | 13 | * | ||
206 | 14 | * You should have received a copy of the GNU General Public License | ||
207 | 15 | * along with this program; if not, write to the Free Software | ||
208 | 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
209 | 17 | * | ||
210 | 18 | */ | ||
211 | 19 | |||
212 | 20 | #ifndef WL_HELPER_H | ||
213 | 21 | #define WL_HELPER_H | ||
214 | 22 | |||
215 | 23 | #include <cassert> | ||
216 | 24 | #include <cstring> | ||
217 | 25 | #include <string> | ||
218 | 26 | #include <vector> | ||
219 | 27 | |||
220 | 28 | #include <SDL_keyboard.h> | ||
221 | 29 | #include <boost/utility.hpp> | ||
222 | 30 | |||
223 | 31 | #include "base/wexception.h" | ||
224 | 32 | |||
225 | 33 | /// Returns the word starting at the character that p points to and ending | ||
226 | 34 | /// before the first terminator character. Replaces the terminator with null. | ||
227 | 35 | // TODO(sirver): move into a logic/strings lib or so. | ||
228 | 36 | char* next_word(char*& p, bool& reached_end, char terminator = ' '); | ||
229 | 37 | |||
230 | 38 | /// Split a string by separators. | ||
231 | 39 | /// \note This ignores empty elements, so do not use this for example to split | ||
232 | 40 | /// a string with newline characters into lines, because it would ignore empty | ||
233 | 41 | /// lines. | ||
234 | 42 | std::vector<std::string> split_string(const std::string&, char const* separators); | ||
235 | 43 | |||
236 | 44 | #endif // end of include guard: WL_HELPER_H | ||
237 | 45 | 0 | ||
238 | === modified file 'src/logic/editor_game_base.cc' | |||
239 | --- src/logic/editor_game_base.cc 2019-06-23 11:41:17 +0000 | |||
240 | +++ src/logic/editor_game_base.cc 2019-08-31 15:58:01 +0000 | |||
241 | @@ -114,50 +114,53 @@ | |||
242 | 114 | * throws an exception if something goes wrong | 114 | * throws an exception if something goes wrong |
243 | 115 | */ | 115 | */ |
244 | 116 | void EditorGameBase::create_tempfile_and_save_mapdata(FileSystem::Type const type) { | 116 | void EditorGameBase::create_tempfile_and_save_mapdata(FileSystem::Type const type) { |
265 | 117 | // should only be called when a map was already loaded | 117 | // save map data to temporary file and reassign map fs |
266 | 118 | assert(map_.filesystem()); | 118 | try { |
267 | 119 | 119 | g_fs->ensure_directory_exists(kTempFileDir); | |
268 | 120 | g_fs->ensure_directory_exists(kTempFileDir); | 120 | |
269 | 121 | 121 | std::string filename = kTempFileDir + g_fs->file_separator() + timestring() + "_mapdata"; | |
270 | 122 | std::string filename = kTempFileDir + g_fs->file_separator() + timestring() + "_mapdata"; | 122 | std::string complete_filename = filename + kTempFileExtension; |
271 | 123 | std::string complete_filename = filename + kTempFileExtension; | 123 | |
272 | 124 | 124 | // if a file with that name already exists, then try a few name modifications | |
273 | 125 | // if a file with that name already exists, then try a few name modifications | 125 | if (g_fs->file_exists(complete_filename)) { |
274 | 126 | if (g_fs->file_exists(complete_filename)) { | 126 | int suffix; |
275 | 127 | int suffix; | 127 | for (suffix = 0; suffix <= 9; suffix++) { |
276 | 128 | for (suffix = 0; suffix <= 9; suffix++) { | 128 | complete_filename = filename + "-" + std::to_string(suffix) + kTempFileExtension; |
277 | 129 | complete_filename = filename + "-" + std::to_string(suffix) + kTempFileExtension; | 129 | if (!g_fs->file_exists(complete_filename)) |
278 | 130 | if (!g_fs->file_exists(complete_filename)) | 130 | break; |
279 | 131 | break; | 131 | } |
280 | 132 | } | 132 | if (suffix > 9) { |
281 | 133 | if (suffix > 9) { | 133 | throw wexception("EditorGameBase::create_tempfile_and_save_mapdata(): for all considered " |
282 | 134 | throw wexception("EditorGameBase::create_tempfile_and_save_mapdata(): for all considered " | 134 | "filenames a file already existed"); |
283 | 135 | "filenames a file already existed"); | 135 | } |
284 | 136 | } | 136 | } |
285 | 137 | |||
286 | 138 | // create tmp_fs_ | ||
287 | 139 | tmp_fs_.reset(g_fs->create_sub_file_system(complete_filename, type)); | ||
288 | 140 | |||
289 | 141 | // save necessary map data (we actually save the whole map) | ||
290 | 142 | std::unique_ptr<Widelands::MapSaver> wms(new Widelands::MapSaver(*tmp_fs_, *this)); | ||
291 | 143 | wms->save(); | ||
292 | 144 | |||
293 | 145 | // swap map fs | ||
294 | 146 | std::unique_ptr<FileSystem> mapfs(tmp_fs_->make_sub_file_system(".")); | ||
295 | 147 | map_.swap_filesystem(mapfs); | ||
296 | 148 | mapfs.reset(); | ||
297 | 149 | |||
298 | 150 | // This is just a convenience hack: | ||
299 | 151 | // If tmp_fs_ is a zip filesystem then - because of the way zip filesystems are currently | ||
300 | 152 | // implemented - | ||
301 | 153 | // the file is still in zip mode right now, which means that the file isn't finalized yet, i.e., | ||
302 | 154 | // not even a valid zip file until zip mode ends. To force ending the zip mode (thus finalizing | ||
303 | 155 | // the file) | ||
304 | 156 | // we simply perform a (otherwise useless) filesystem request. | ||
305 | 157 | // It's not strictly necessary, but this way we get a valid zip file immediately istead of | ||
306 | 158 | // at some unkown later point (when an unzip operation happens or a filesystem object destructs). | ||
307 | 159 | tmp_fs_->file_exists("binary"); | ||
308 | 160 | } catch (const WException& e) { | ||
309 | 161 | log("EditorGameBase: saving map to temporary file failed: %s", e.what()); | ||
310 | 162 | throw; | ||
311 | 137 | } | 163 | } |
312 | 138 | |||
313 | 139 | // create tmp_fs_ | ||
314 | 140 | tmp_fs_.reset(g_fs->create_sub_file_system(complete_filename, type)); | ||
315 | 141 | |||
316 | 142 | // save necessary map data (we actually save the whole map) | ||
317 | 143 | std::unique_ptr<Widelands::MapSaver> wms(new Widelands::MapSaver(*tmp_fs_, *this)); | ||
318 | 144 | wms->save(); | ||
319 | 145 | |||
320 | 146 | // swap map fs | ||
321 | 147 | std::unique_ptr<FileSystem> mapfs(tmp_fs_->make_sub_file_system(".")); | ||
322 | 148 | map_.swap_filesystem(mapfs); | ||
323 | 149 | mapfs.reset(); | ||
324 | 150 | |||
325 | 151 | // This is just a convenience hack: | ||
326 | 152 | // If tmp_fs_ is a zip filesystem then - because of the way zip filesystems are currently | ||
327 | 153 | // implemented - | ||
328 | 154 | // the file is still in zip mode right now, which means that the file isn't finalized yet, i.e., | ||
329 | 155 | // not even a valid zip file until zip mode ends. To force ending the zip mode (thus finalizing | ||
330 | 156 | // the file) | ||
331 | 157 | // we simply perform a (otherwise useless) filesystem request. | ||
332 | 158 | // It's not strictly necessary, but this way we get a valid zip file immediately istead of | ||
333 | 159 | // at some unkown later point (when an unzip operation happens or a filesystem object destructs). | ||
334 | 160 | tmp_fs_->file_exists("binary"); | ||
335 | 161 | } | 164 | } |
336 | 162 | 165 | ||
337 | 163 | void EditorGameBase::think() { | 166 | void EditorGameBase::think() { |
338 | @@ -276,20 +279,11 @@ | |||
339 | 276 | } | 279 | } |
340 | 277 | 280 | ||
341 | 278 | /** | 281 | /** |
345 | 279 | * Load and prepare detailed game data. | 282 | * Load and prepare detailed game and map data. |
346 | 280 | * This happens once just after the host has started the game and before the | 283 | * This happens once just after the host has started the game / the editor has started and before the graphics are loaded. |
344 | 281 | * graphics are loaded. | ||
347 | 282 | */ | 284 | */ |
348 | 283 | void EditorGameBase::postload() { | 285 | void EditorGameBase::postload() { |
358 | 284 | if (map_.filesystem()) { | 286 | create_tempfile_and_save_mapdata(FileSystem::ZIP); |
350 | 285 | // save map data to temporary file and reassign map fs | ||
351 | 286 | try { | ||
352 | 287 | create_tempfile_and_save_mapdata(FileSystem::ZIP); | ||
353 | 288 | } catch (const WException& e) { | ||
354 | 289 | log("EditorGameBase::postload: saving map to temporary file failed: %s", e.what()); | ||
355 | 290 | throw; | ||
356 | 291 | } | ||
357 | 292 | } | ||
359 | 293 | 287 | ||
360 | 294 | // Postload tribes | 288 | // Postload tribes |
361 | 295 | assert(tribes_); | 289 | assert(tribes_); |
362 | 296 | 290 | ||
363 | === modified file 'src/logic/editor_game_base.h' | |||
364 | --- src/logic/editor_game_base.h 2019-05-14 16:25:57 +0000 | |||
365 | +++ src/logic/editor_game_base.h 2019-08-31 15:58:01 +0000 | |||
366 | @@ -200,6 +200,8 @@ | |||
367 | 200 | // Returns the mutable tribes. Prefer tribes() whenever possible. | 200 | // Returns the mutable tribes. Prefer tribes() whenever possible. |
368 | 201 | Tribes* mutable_tribes(); | 201 | Tribes* mutable_tribes(); |
369 | 202 | 202 | ||
370 | 203 | void create_tempfile_and_save_mapdata(FileSystem::Type type); | ||
371 | 204 | |||
372 | 203 | private: | 205 | private: |
373 | 204 | /// Common function for create_critter and create_ship. | 206 | /// Common function for create_critter and create_ship. |
374 | 205 | Bob& create_bob(Coords, const BobDescr&, Player* owner = nullptr); | 207 | Bob& create_bob(Coords, const BobDescr&, Player* owner = nullptr); |
375 | @@ -264,7 +266,6 @@ | |||
376 | 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. |
377 | 265 | std::unique_ptr<FileSystem> tmp_fs_; | 267 | std::unique_ptr<FileSystem> tmp_fs_; |
378 | 266 | void delete_tempfile(); | 268 | void delete_tempfile(); |
379 | 267 | void create_tempfile_and_save_mapdata(FileSystem::Type type); | ||
380 | 268 | 269 | ||
381 | 269 | DISALLOW_COPY_AND_ASSIGN(EditorGameBase); | 270 | DISALLOW_COPY_AND_ASSIGN(EditorGameBase); |
382 | 270 | }; | 271 | }; |
383 | 271 | 272 | ||
384 | === modified file 'src/logic/map_objects/CMakeLists.txt' | |||
385 | --- src/logic/map_objects/CMakeLists.txt 2019-08-09 17:29:40 +0000 | |||
386 | +++ src/logic/map_objects/CMakeLists.txt 2019-08-31 15:58:01 +0000 | |||
387 | @@ -39,8 +39,11 @@ | |||
388 | 39 | immovable.cc | 39 | immovable.cc |
389 | 40 | immovable.h | 40 | immovable.h |
390 | 41 | immovable_program.h | 41 | immovable_program.h |
391 | 42 | immovable_program.cc | ||
392 | 42 | map_object.cc | 43 | map_object.cc |
393 | 43 | map_object.h | 44 | map_object.h |
394 | 45 | map_object_program.cc | ||
395 | 46 | map_object_program.h | ||
396 | 44 | terrain_affinity.cc | 47 | terrain_affinity.cc |
397 | 45 | terrain_affinity.h | 48 | terrain_affinity.h |
398 | 46 | tribes/attack_target.h | 49 | tribes/attack_target.h |
399 | @@ -129,7 +132,6 @@ | |||
400 | 129 | graphic_surface | 132 | graphic_surface |
401 | 130 | graphic_text_layout | 133 | graphic_text_layout |
402 | 131 | graphic_toolbar_imageset | 134 | graphic_toolbar_imageset |
403 | 132 | helper | ||
404 | 133 | io_fileread | 135 | io_fileread |
405 | 134 | io_filesystem | 136 | io_filesystem |
406 | 135 | logic # TODO(GunChleoc): Circular dependency | 137 | logic # TODO(GunChleoc): Circular dependency |
407 | 136 | 138 | ||
408 | === modified file 'src/logic/map_objects/bob.cc' | |||
409 | --- src/logic/map_objects/bob.cc 2019-05-11 13:48:12 +0000 | |||
410 | +++ src/logic/map_objects/bob.cc 2019-08-31 15:58:01 +0000 | |||
411 | @@ -1092,8 +1092,8 @@ | |||
412 | 1092 | throw GameDataError("unknown bob task '%s'", name.c_str()); | 1092 | throw GameDataError("unknown bob task '%s'", name.c_str()); |
413 | 1093 | } | 1093 | } |
414 | 1094 | 1094 | ||
417 | 1095 | const BobProgramBase* Bob::Loader::get_program(const std::string& name) { | 1095 | const MapObjectProgram* Bob::Loader::get_program(const std::string& name) { |
418 | 1096 | throw GameDataError("unknown bob program '%s'", name.c_str()); | 1096 | throw GameDataError("unknown map object program '%s'", name.c_str()); |
419 | 1097 | } | 1097 | } |
420 | 1098 | 1098 | ||
421 | 1099 | void Bob::save(EditorGameBase& eg, MapObjectSaver& mos, FileWrite& fw) { | 1099 | void Bob::save(EditorGameBase& eg, MapObjectSaver& mos, FileWrite& fw) { |
422 | @@ -1156,7 +1156,7 @@ | |||
423 | 1156 | fw.unsigned_8(0); | 1156 | fw.unsigned_8(0); |
424 | 1157 | } | 1157 | } |
425 | 1158 | 1158 | ||
427 | 1159 | fw.c_string(state.program ? state.program->get_name() : ""); | 1159 | fw.c_string(state.program ? state.program->name() : ""); |
428 | 1160 | } | 1160 | } |
429 | 1161 | } | 1161 | } |
430 | 1162 | } // namespace Widelands | 1162 | } // namespace Widelands |
431 | 1163 | 1163 | ||
432 | === modified file 'src/logic/map_objects/bob.h' | |||
433 | --- src/logic/map_objects/bob.h 2019-04-24 06:01:37 +0000 | |||
434 | +++ src/logic/map_objects/bob.h 2019-08-31 15:58:01 +0000 | |||
435 | @@ -27,29 +27,19 @@ | |||
436 | 27 | #include "graphic/diranimations.h" | 27 | #include "graphic/diranimations.h" |
437 | 28 | #include "logic/map_objects/draw_text.h" | 28 | #include "logic/map_objects/draw_text.h" |
438 | 29 | #include "logic/map_objects/map_object.h" | 29 | #include "logic/map_objects/map_object.h" |
439 | 30 | #include "logic/map_objects/map_object_program.h" | ||
440 | 30 | #include "logic/map_objects/walkingdir.h" | 31 | #include "logic/map_objects/walkingdir.h" |
441 | 31 | #include "logic/widelands_geometry.h" | 32 | #include "logic/widelands_geometry.h" |
442 | 32 | 33 | ||
443 | 33 | namespace Widelands { | 34 | namespace Widelands { |
444 | 35 | |||
445 | 36 | class Bob; | ||
446 | 34 | class Map; | 37 | class Map; |
447 | 35 | struct Route; | 38 | struct Route; |
448 | 36 | struct Transfer; | 39 | struct Transfer; |
449 | 37 | class TribeDescr; | 40 | class TribeDescr; |
450 | 38 | 41 | ||
451 | 39 | /** | 42 | /** |
452 | 40 | * BobProgramBase is only used that | ||
453 | 41 | * get_name always works | ||
454 | 42 | */ | ||
455 | 43 | |||
456 | 44 | struct BobProgramBase { | ||
457 | 45 | virtual ~BobProgramBase() { | ||
458 | 46 | } | ||
459 | 47 | virtual std::string get_name() const = 0; | ||
460 | 48 | }; | ||
461 | 49 | |||
462 | 50 | class Bob; | ||
463 | 51 | |||
464 | 52 | /** | ||
465 | 53 | * Implement MapObjectDescr for the following \ref Bob class. | 43 | * Implement MapObjectDescr for the following \ref Bob class. |
466 | 54 | */ | 44 | */ |
467 | 55 | class BobDescr : public MapObjectDescr { | 45 | class BobDescr : public MapObjectDescr { |
468 | @@ -223,7 +213,7 @@ | |||
469 | 223 | DirAnimations diranims; | 213 | DirAnimations diranims; |
470 | 224 | Path* path; | 214 | Path* path; |
471 | 225 | Route* route; | 215 | Route* route; |
473 | 226 | const BobProgramBase* program; ///< pointer to current program | 216 | const MapObjectProgram* program; ///< pointer to current program |
474 | 227 | }; | 217 | }; |
475 | 228 | 218 | ||
476 | 229 | MO_DESCR(BobDescr) | 219 | MO_DESCR(BobDescr) |
477 | @@ -419,7 +409,7 @@ | |||
478 | 419 | 409 | ||
479 | 420 | protected: | 410 | protected: |
480 | 421 | virtual const Task* get_task(const std::string& name); | 411 | virtual const Task* get_task(const std::string& name); |
482 | 422 | virtual const BobProgramBase* get_program(const std::string& name); | 412 | virtual const MapObjectProgram* get_program(const std::string& name); |
483 | 423 | 413 | ||
484 | 424 | private: | 414 | private: |
485 | 425 | struct LoadState { | 415 | struct LoadState { |
486 | 426 | 416 | ||
487 | === modified file 'src/logic/map_objects/immovable.cc' | |||
488 | --- src/logic/map_objects/immovable.cc 2019-05-28 17:01:30 +0000 | |||
489 | +++ src/logic/map_objects/immovable.cc 2019-08-31 15:58:01 +0000 | |||
490 | @@ -19,41 +19,17 @@ | |||
491 | 19 | 19 | ||
492 | 20 | #include "logic/map_objects/immovable.h" | 20 | #include "logic/map_objects/immovable.h" |
493 | 21 | 21 | ||
494 | 22 | #include <cstdio> | ||
495 | 23 | #include <cstring> | ||
496 | 24 | #include <memory> | 22 | #include <memory> |
497 | 25 | 23 | ||
498 | 26 | #include <boost/algorithm/string.hpp> | ||
499 | 27 | #include <boost/format.hpp> | ||
500 | 28 | |||
501 | 29 | #include "base/log.h" | ||
502 | 30 | #include "base/macros.h" | ||
503 | 31 | #include "base/wexception.h" | ||
504 | 32 | #include "config.h" | ||
505 | 33 | #include "graphic/graphic.h" | ||
506 | 34 | #include "graphic/rendertarget.h" | ||
507 | 35 | #include "helper.h" | ||
508 | 36 | #include "io/fileread.h" | 24 | #include "io/fileread.h" |
509 | 37 | #include "io/filewrite.h" | 25 | #include "io/filewrite.h" |
510 | 38 | #include "logic/editor_game_base.h" | ||
511 | 39 | #include "logic/field.h" | ||
512 | 40 | #include "logic/game.h" | ||
513 | 41 | #include "logic/game_data_error.h" | 26 | #include "logic/game_data_error.h" |
514 | 42 | #include "logic/map.h" | ||
515 | 43 | #include "logic/map_objects/immovable_program.h" | 27 | #include "logic/map_objects/immovable_program.h" |
516 | 44 | #include "logic/map_objects/terrain_affinity.h" | 28 | #include "logic/map_objects/terrain_affinity.h" |
517 | 45 | #include "logic/map_objects/tribes/tribe_descr.h" | ||
518 | 46 | #include "logic/map_objects/tribes/worker.h" | ||
519 | 47 | #include "logic/map_objects/world/world.h" | 29 | #include "logic/map_objects/world/world.h" |
520 | 48 | #include "logic/mapfringeregion.h" | ||
521 | 49 | #include "logic/player.h" | 30 | #include "logic/player.h" |
522 | 50 | #include "logic/widelands_geometry_io.h" | 31 | #include "logic/widelands_geometry_io.h" |
523 | 51 | #include "map_io/tribes_legacy_lookup_table.h" | ||
524 | 52 | #include "map_io/world_legacy_lookup_table.h" | 32 | #include "map_io/world_legacy_lookup_table.h" |
525 | 53 | #include "notifications/notifications.h" | ||
526 | 54 | #include "scripting/lua_table.h" | ||
527 | 55 | #include "sound/note_sound.h" | ||
528 | 56 | #include "sound/sound_handler.h" | ||
529 | 57 | 33 | ||
530 | 58 | namespace Widelands { | 34 | namespace Widelands { |
531 | 59 | 35 | ||
532 | @@ -140,52 +116,6 @@ | |||
533 | 140 | /* | 116 | /* |
534 | 141 | ============================================================================== | 117 | ============================================================================== |
535 | 142 | 118 | ||
536 | 143 | ImmovableProgram IMPLEMENTATION | ||
537 | 144 | |||
538 | 145 | ============================================================================== | ||
539 | 146 | */ | ||
540 | 147 | |||
541 | 148 | ImmovableProgram::ImmovableProgram(const std::string& init_name, | ||
542 | 149 | const std::vector<std::string>& lines, | ||
543 | 150 | ImmovableDescr* immovable) | ||
544 | 151 | : name_(init_name) { | ||
545 | 152 | for (const std::string& line : lines) { | ||
546 | 153 | std::vector<std::string> parts; | ||
547 | 154 | boost::split(parts, line, boost::is_any_of("=")); | ||
548 | 155 | if (parts.size() != 2) { | ||
549 | 156 | throw GameDataError("invalid line: %s.", line.c_str()); | ||
550 | 157 | } | ||
551 | 158 | std::unique_ptr<char[]> arguments(new char[parts[1].size() + 1]); | ||
552 | 159 | strncpy(arguments.get(), parts[1].c_str(), parts[1].size() + 1); | ||
553 | 160 | |||
554 | 161 | Action* action; | ||
555 | 162 | if (parts[0] == "animate") { | ||
556 | 163 | action = new ActAnimate(arguments.get(), *immovable); | ||
557 | 164 | } else if (parts[0] == "transform") { | ||
558 | 165 | action = new ActTransform(arguments.get(), *immovable); | ||
559 | 166 | } else if (parts[0] == "grow") { | ||
560 | 167 | action = new ActGrow(arguments.get(), *immovable); | ||
561 | 168 | } else if (parts[0] == "remove") { | ||
562 | 169 | action = new ActRemove(arguments.get(), *immovable); | ||
563 | 170 | } else if (parts[0] == "seed") { | ||
564 | 171 | action = new ActSeed(arguments.get(), *immovable); | ||
565 | 172 | } else if (parts[0] == "playsound") { | ||
566 | 173 | action = new ActPlaySound(arguments.get(), *immovable); | ||
567 | 174 | } else if (parts[0] == "construct") { | ||
568 | 175 | action = new ActConstruct(arguments.get(), *immovable); | ||
569 | 176 | } else { | ||
570 | 177 | throw GameDataError("unknown command type \"%s\" in immovable \"%s\"", parts[0].c_str(), | ||
571 | 178 | immovable->name().c_str()); | ||
572 | 179 | } | ||
573 | 180 | actions_.push_back(action); | ||
574 | 181 | } | ||
575 | 182 | if (actions_.empty()) | ||
576 | 183 | throw GameDataError("no actions"); | ||
577 | 184 | } | ||
578 | 185 | |||
579 | 186 | /* | ||
580 | 187 | ============================================================================== | ||
581 | 188 | |||
582 | 189 | ImmovableDescr IMPLEMENTATION | 119 | ImmovableDescr IMPLEMENTATION |
583 | 190 | 120 | ||
584 | 191 | ============================================================================== | 121 | ============================================================================== |
585 | @@ -249,12 +179,16 @@ | |||
586 | 249 | } | 179 | } |
587 | 250 | 180 | ||
588 | 251 | std::unique_ptr<LuaTable> programs = table.get_table("programs"); | 181 | std::unique_ptr<LuaTable> programs = table.get_table("programs"); |
590 | 252 | for (const std::string& program_name : programs->keys<std::string>()) { | 182 | for (std::string program_name : programs->keys<std::string>()) { |
591 | 183 | std::transform(program_name.begin(), program_name.end(), program_name.begin(), tolower); | ||
592 | 184 | if (programs_.count(program_name)) { | ||
593 | 185 | throw GameDataError("Program '%s' has already been declared for immovable '%s'", program_name.c_str(), name().c_str()); | ||
594 | 186 | } | ||
595 | 253 | try { | 187 | try { |
596 | 254 | programs_[program_name] = new ImmovableProgram( | 188 | programs_[program_name] = new ImmovableProgram( |
598 | 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); |
599 | 256 | } catch (const std::exception& e) { | 190 | } catch (const std::exception& e) { |
601 | 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()); |
602 | 258 | } | 192 | } |
603 | 259 | } | 193 | } |
604 | 260 | 194 | ||
605 | @@ -308,9 +242,9 @@ | |||
606 | 308 | void ImmovableDescr::make_sure_default_program_is_there() { | 242 | void ImmovableDescr::make_sure_default_program_is_there() { |
607 | 309 | if (!programs_.count("program")) { // default program | 243 | if (!programs_.count("program")) { // default program |
608 | 310 | assert(is_animation_known("idle")); | 244 | assert(is_animation_known("idle")); |
610 | 311 | char parameters[] = "idle"; | 245 | std::vector<std::string> arguments{"idle"}; |
611 | 312 | programs_["program"] = | 246 | programs_["program"] = |
613 | 313 | new ImmovableProgram("program", new ImmovableProgram::ActAnimate(parameters, *this)); | 247 | new ImmovableProgram("program", std::unique_ptr<ImmovableProgram::Action>(new ImmovableProgram::ActAnimate(arguments, *this))); |
614 | 314 | } | 248 | } |
615 | 315 | } | 249 | } |
616 | 316 | 250 | ||
617 | @@ -418,10 +352,12 @@ | |||
618 | 418 | ImmovableProgram const* prog = program_; | 352 | ImmovableProgram const* prog = program_; |
619 | 419 | if (!prog) { | 353 | if (!prog) { |
620 | 420 | prog = descr().get_program("program"); | 354 | prog = descr().get_program("program"); |
625 | 421 | assert(prog != nullptr); | 355 | } |
626 | 422 | } | 356 | assert(prog != nullptr); |
627 | 423 | if (upcast(ImmovableProgram::ActAnimate const, act_animate, &(*prog)[program_ptr_])) | 357 | |
628 | 424 | start_animation(egbase, descr().get_animation(act_animate->animation(), this)); | 358 | if (upcast(ImmovableProgram::ActAnimate const, act_animate, &(*prog)[program_ptr_])) { |
629 | 359 | start_animation(egbase, act_animate->animation()); | ||
630 | 360 | } | ||
631 | 425 | 361 | ||
632 | 426 | if (upcast(Game, game, &egbase)) { | 362 | if (upcast(Game, game, &egbase)) { |
633 | 427 | switch_program(*game, "program"); | 363 | switch_program(*game, "program"); |
634 | @@ -761,391 +697,6 @@ | |||
635 | 761 | return loader.release(); | 697 | return loader.release(); |
636 | 762 | } | 698 | } |
637 | 763 | 699 | ||
638 | 764 | ImmovableProgram::Action::~Action() { | ||
639 | 765 | } | ||
640 | 766 | |||
641 | 767 | ImmovableProgram::ActAnimate::ActAnimate(char* parameters, ImmovableDescr& descr) { | ||
642 | 768 | try { | ||
643 | 769 | bool reached_end; | ||
644 | 770 | animation_name_ = std::string(next_word(parameters, reached_end)); | ||
645 | 771 | if (!descr.is_animation_known(animation_name_)) { | ||
646 | 772 | throw GameDataError("Unknown animation: %s.", animation_name_.c_str()); | ||
647 | 773 | } | ||
648 | 774 | |||
649 | 775 | if (!reached_end) { // The next parameter is the duration. | ||
650 | 776 | char* endp; | ||
651 | 777 | long int const value = strtol(parameters, &endp, 0); | ||
652 | 778 | if (*endp || value <= 0) | ||
653 | 779 | throw GameDataError("expected %s but found \"%s\"", "duration in ms", parameters); | ||
654 | 780 | duration_ = value; | ||
655 | 781 | } else { | ||
656 | 782 | duration_ = 0; // forever | ||
657 | 783 | } | ||
658 | 784 | } catch (const WException& e) { | ||
659 | 785 | throw GameDataError("animate: %s", e.what()); | ||
660 | 786 | } | ||
661 | 787 | } | ||
662 | 788 | |||
663 | 789 | /// Use convolutuion to make the animation time a random variable with binomial | ||
664 | 790 | /// distribution and the configured time as the expected value. | ||
665 | 791 | void ImmovableProgram::ActAnimate::execute(Game& game, Immovable& immovable) const { | ||
666 | 792 | immovable.start_animation(game, immovable.descr().get_animation(animation_name_, &immovable)); | ||
667 | 793 | immovable.program_step( | ||
668 | 794 | game, duration_ ? 1 + game.logic_rand() % duration_ + game.logic_rand() % duration_ : 0); | ||
669 | 795 | } | ||
670 | 796 | |||
671 | 797 | ImmovableProgram::ActPlaySound::ActPlaySound(char* parameters, const ImmovableDescr&) { | ||
672 | 798 | try { | ||
673 | 799 | bool reached_end; | ||
674 | 800 | std::string name = next_word(parameters, reached_end); | ||
675 | 801 | |||
676 | 802 | if (!reached_end) { | ||
677 | 803 | char* endp; | ||
678 | 804 | unsigned long long int const value = strtoull(parameters, &endp, 0); | ||
679 | 805 | priority = value; | ||
680 | 806 | if (*endp || priority != value) | ||
681 | 807 | throw GameDataError("expected %s but found \"%s\"", "priority", parameters); | ||
682 | 808 | } else | ||
683 | 809 | priority = 127; | ||
684 | 810 | |||
685 | 811 | fx = g_sh->register_fx(SoundType::kAmbient, name); | ||
686 | 812 | } catch (const WException& e) { | ||
687 | 813 | throw GameDataError("playsound: %s", e.what()); | ||
688 | 814 | } | ||
689 | 815 | } | ||
690 | 816 | |||
691 | 817 | /** Demand from the g_sound_handler to play a certain sound effect. | ||
692 | 818 | * Whether the effect actually gets played | ||
693 | 819 | * is decided only by the sound server*/ | ||
694 | 820 | void ImmovableProgram::ActPlaySound::execute(Game& game, Immovable& immovable) const { | ||
695 | 821 | Notifications::publish(NoteSound(SoundType::kAmbient, fx, immovable.get_position(), priority)); | ||
696 | 822 | immovable.program_step(game); | ||
697 | 823 | } | ||
698 | 824 | |||
699 | 825 | ImmovableProgram::ActTransform::ActTransform(char* parameters, ImmovableDescr& descr) { | ||
700 | 826 | try { | ||
701 | 827 | tribe = true; | ||
702 | 828 | bob = false; | ||
703 | 829 | probability = 0; | ||
704 | 830 | |||
705 | 831 | std::vector<std::string> params = split_string(parameters, " "); | ||
706 | 832 | for (uint32_t i = 0; i < params.size(); ++i) { | ||
707 | 833 | if (params[i] == "bob") | ||
708 | 834 | bob = true; | ||
709 | 835 | else if (params[i] == "immovable") | ||
710 | 836 | bob = false; | ||
711 | 837 | else if (params[i][0] >= '0' && params[i][0] <= '9') { | ||
712 | 838 | long int const value = atoi(params[i].c_str()); | ||
713 | 839 | if (value < 1 || 254 < value) | ||
714 | 840 | throw GameDataError("expected %s but found \"%s\"", "probability in range [1, 254]", | ||
715 | 841 | params[i].c_str()); | ||
716 | 842 | probability = value; | ||
717 | 843 | } else { | ||
718 | 844 | std::vector<std::string> segments = split_string(params[i], ":"); | ||
719 | 845 | |||
720 | 846 | if (segments.size() > 2) | ||
721 | 847 | throw GameDataError("object type has more than 2 segments"); | ||
722 | 848 | if (segments.size() == 2) { | ||
723 | 849 | if (segments[0] == "world") | ||
724 | 850 | tribe = false; | ||
725 | 851 | else if (segments[0] == "tribe") { | ||
726 | 852 | if (descr.owner_type() != MapObjectDescr::OwnerType::kTribe) | ||
727 | 853 | throw GameDataError("scope \"tribe\" does not match the immovable type"); | ||
728 | 854 | tribe = true; | ||
729 | 855 | } else | ||
730 | 856 | throw GameDataError("unknown scope \"%s\" given for target type (must be " | ||
731 | 857 | "\"world\" or \"tribe\")", | ||
732 | 858 | parameters); | ||
733 | 859 | |||
734 | 860 | type_name = segments[1]; | ||
735 | 861 | } else { | ||
736 | 862 | type_name = segments[0]; | ||
737 | 863 | } | ||
738 | 864 | } | ||
739 | 865 | } | ||
740 | 866 | if (type_name == descr.name()) | ||
741 | 867 | throw GameDataError("illegal transformation to the same type"); | ||
742 | 868 | } catch (const WException& e) { | ||
743 | 869 | throw GameDataError("transform: %s", e.what()); | ||
744 | 870 | } | ||
745 | 871 | } | ||
746 | 872 | |||
747 | 873 | void ImmovableProgram::ActTransform::execute(Game& game, Immovable& immovable) const { | ||
748 | 874 | if (probability == 0 || game.logic_rand() % 256 < probability) { | ||
749 | 875 | Player* player = immovable.get_owner(); | ||
750 | 876 | Coords const c = immovable.get_position(); | ||
751 | 877 | MapObjectDescr::OwnerType owner_type = immovable.descr().owner_type(); | ||
752 | 878 | immovable.remove(game); // Now immovable is a dangling reference! | ||
753 | 879 | |||
754 | 880 | if (bob) { | ||
755 | 881 | game.create_ship(c, type_name, player); | ||
756 | 882 | } else { | ||
757 | 883 | game.create_immovable_with_name( | ||
758 | 884 | c, type_name, owner_type, player, nullptr /* former_building_descr */); | ||
759 | 885 | } | ||
760 | 886 | } else | ||
761 | 887 | immovable.program_step(game); | ||
762 | 888 | } | ||
763 | 889 | |||
764 | 890 | ImmovableProgram::ActGrow::ActGrow(char* parameters, ImmovableDescr& descr) { | ||
765 | 891 | if (!descr.has_terrain_affinity()) { | ||
766 | 892 | throw GameDataError( | ||
767 | 893 | "Immovable %s can 'grow', but has no terrain_affinity entry.", descr.name().c_str()); | ||
768 | 894 | } | ||
769 | 895 | |||
770 | 896 | try { | ||
771 | 897 | tribe = true; | ||
772 | 898 | for (char* p = parameters;;) | ||
773 | 899 | switch (*p) { | ||
774 | 900 | case ':': { | ||
775 | 901 | *p = '\0'; | ||
776 | 902 | ++p; | ||
777 | 903 | if (descr.owner_type() != MapObjectDescr::OwnerType::kTribe) | ||
778 | 904 | throw GameDataError("immovable type not in tribes but target type has scope " | ||
779 | 905 | "(\"%s\")", | ||
780 | 906 | parameters); | ||
781 | 907 | else if (strcmp(parameters, "world")) | ||
782 | 908 | throw GameDataError("scope \"%s\" given for target type (must be " | ||
783 | 909 | "\"world\")", | ||
784 | 910 | parameters); | ||
785 | 911 | tribe = false; | ||
786 | 912 | parameters = p; | ||
787 | 913 | break; | ||
788 | 914 | } | ||
789 | 915 | case '\0': | ||
790 | 916 | goto end; | ||
791 | 917 | default: | ||
792 | 918 | ++p; | ||
793 | 919 | break; | ||
794 | 920 | } | ||
795 | 921 | end: | ||
796 | 922 | type_name = parameters; | ||
797 | 923 | } catch (const WException& e) { | ||
798 | 924 | throw GameDataError("grow: %s", e.what()); | ||
799 | 925 | } | ||
800 | 926 | } | ||
801 | 927 | |||
802 | 928 | void ImmovableProgram::ActGrow::execute(Game& game, Immovable& immovable) const { | ||
803 | 929 | const Map& map = game.map(); | ||
804 | 930 | FCoords const f = map.get_fcoords(immovable.get_position()); | ||
805 | 931 | const ImmovableDescr& descr = immovable.descr(); | ||
806 | 932 | |||
807 | 933 | if ((game.logic_rand() % TerrainAffinity::kPrecisionFactor) < | ||
808 | 934 | probability_to_grow(descr.terrain_affinity(), f, map, game.world().terrains())) { | ||
809 | 935 | MapObjectDescr::OwnerType owner_type = descr.owner_type(); | ||
810 | 936 | Player* owner = immovable.get_owner(); | ||
811 | 937 | immovable.remove(game); // Now immovable is a dangling reference! | ||
812 | 938 | game.create_immovable_with_name( | ||
813 | 939 | f, type_name, owner_type, owner, nullptr /* former_building_descr */); | ||
814 | 940 | } else { | ||
815 | 941 | immovable.program_step(game); | ||
816 | 942 | } | ||
817 | 943 | } | ||
818 | 944 | |||
819 | 945 | /** | ||
820 | 946 | * remove | ||
821 | 947 | */ | ||
822 | 948 | ImmovableProgram::ActRemove::ActRemove(char* parameters, ImmovableDescr&) { | ||
823 | 949 | try { | ||
824 | 950 | if (*parameters) { | ||
825 | 951 | char* endp; | ||
826 | 952 | long int const value = strtol(parameters, &endp, 0); | ||
827 | 953 | if (*endp || value < 1 || 254 < value) | ||
828 | 954 | throw GameDataError( | ||
829 | 955 | "expected %s but found \"%s\"", "probability in range [1, 254]", parameters); | ||
830 | 956 | probability = value; | ||
831 | 957 | } else | ||
832 | 958 | probability = 0; | ||
833 | 959 | } catch (const WException& e) { | ||
834 | 960 | throw GameDataError("remove: %s", e.what()); | ||
835 | 961 | } | ||
836 | 962 | } | ||
837 | 963 | |||
838 | 964 | void ImmovableProgram::ActRemove::execute(Game& game, Immovable& immovable) const { | ||
839 | 965 | if (probability == 0 || game.logic_rand() % 256 < probability) | ||
840 | 966 | immovable.remove(game); // Now immovable is a dangling reference! | ||
841 | 967 | else | ||
842 | 968 | immovable.program_step(game); | ||
843 | 969 | } | ||
844 | 970 | |||
845 | 971 | ImmovableProgram::ActSeed::ActSeed(char* parameters, ImmovableDescr& descr) { | ||
846 | 972 | try { | ||
847 | 973 | probability = 0; | ||
848 | 974 | for (char* p = parameters;;) | ||
849 | 975 | switch (*p) { | ||
850 | 976 | case ':': { | ||
851 | 977 | *p = '\0'; | ||
852 | 978 | ++p; | ||
853 | 979 | if (descr.owner_type() != MapObjectDescr::OwnerType::kTribe) | ||
854 | 980 | throw GameDataError("immovable type not in tribes but target type has scope " | ||
855 | 981 | "(\"%s\")", | ||
856 | 982 | parameters); | ||
857 | 983 | else if (strcmp(parameters, "world")) | ||
858 | 984 | throw GameDataError("scope \"%s\" given for target type (must be " | ||
859 | 985 | "\"world\")", | ||
860 | 986 | parameters); | ||
861 | 987 | parameters = p; | ||
862 | 988 | break; | ||
863 | 989 | } | ||
864 | 990 | case ' ': { | ||
865 | 991 | *p = '\0'; | ||
866 | 992 | ++p; | ||
867 | 993 | char* endp; | ||
868 | 994 | long int const value = strtol(p, &endp, 0); | ||
869 | 995 | if (*endp || value < 1 || 254 < value) | ||
870 | 996 | throw GameDataError( | ||
871 | 997 | "expected %s but found \"%s\"", "probability in range [1, 254]", p); | ||
872 | 998 | probability = value; | ||
873 | 999 | // fallthrough | ||
874 | 1000 | } | ||
875 | 1001 | FALLS_THROUGH; | ||
876 | 1002 | case '\0': | ||
877 | 1003 | goto end; | ||
878 | 1004 | default: | ||
879 | 1005 | ++p; | ||
880 | 1006 | break; | ||
881 | 1007 | } | ||
882 | 1008 | end: | ||
883 | 1009 | type_name = parameters; | ||
884 | 1010 | } catch (const WException& e) { | ||
885 | 1011 | throw GameDataError("seed: %s", e.what()); | ||
886 | 1012 | } | ||
887 | 1013 | } | ||
888 | 1014 | |||
889 | 1015 | void ImmovableProgram::ActSeed::execute(Game& game, Immovable& immovable) const { | ||
890 | 1016 | const Map& map = game.map(); | ||
891 | 1017 | FCoords const f = map.get_fcoords(immovable.get_position()); | ||
892 | 1018 | const ImmovableDescr& descr = immovable.descr(); | ||
893 | 1019 | |||
894 | 1020 | if ((game.logic_rand() % TerrainAffinity::kPrecisionFactor) < | ||
895 | 1021 | probability_to_grow(descr.terrain_affinity(), f, map, game.world().terrains())) { | ||
896 | 1022 | // Seed a new tree. | ||
897 | 1023 | MapFringeRegion<> mr(map, Area<>(f, 0)); | ||
898 | 1024 | uint32_t fringe_size = 0; | ||
899 | 1025 | do { | ||
900 | 1026 | mr.extend(map); | ||
901 | 1027 | fringe_size += 6; | ||
902 | 1028 | } while (game.logic_rand() % std::numeric_limits<uint8_t>::max() < probability); | ||
903 | 1029 | |||
904 | 1030 | for (uint32_t n = game.logic_rand() % fringe_size; n; --n) { | ||
905 | 1031 | mr.advance(map); | ||
906 | 1032 | } | ||
907 | 1033 | |||
908 | 1034 | const FCoords new_location = map.get_fcoords(mr.location()); | ||
909 | 1035 | if (!new_location.field->get_immovable() && | ||
910 | 1036 | (new_location.field->nodecaps() & MOVECAPS_WALK) && | ||
911 | 1037 | (game.logic_rand() % TerrainAffinity::kPrecisionFactor) < | ||
912 | 1038 | probability_to_grow( | ||
913 | 1039 | descr.terrain_affinity(), new_location, map, game.world().terrains())) { | ||
914 | 1040 | game.create_immovable_with_name(mr.location(), type_name, descr.owner_type(), | ||
915 | 1041 | nullptr /* owner */, nullptr /* former_building_descr */); | ||
916 | 1042 | } | ||
917 | 1043 | } | ||
918 | 1044 | |||
919 | 1045 | immovable.program_step(game); | ||
920 | 1046 | } | ||
921 | 1047 | |||
922 | 1048 | ImmovableProgram::ActConstruct::ActConstruct(char* parameters, ImmovableDescr& descr) { | ||
923 | 1049 | try { | ||
924 | 1050 | if (descr.owner_type() != MapObjectDescr::OwnerType::kTribe) | ||
925 | 1051 | throw GameDataError("only usable for tribe immovable"); | ||
926 | 1052 | |||
927 | 1053 | std::vector<std::string> params = split_string(parameters, " "); | ||
928 | 1054 | |||
929 | 1055 | if (params.size() != 3) | ||
930 | 1056 | throw GameDataError("usage: animation-name buildtime decaytime"); | ||
931 | 1057 | |||
932 | 1058 | buildtime_ = atoi(params[1].c_str()); | ||
933 | 1059 | decaytime_ = atoi(params[2].c_str()); | ||
934 | 1060 | |||
935 | 1061 | animation_name_ = params[0]; | ||
936 | 1062 | if (!descr.is_animation_known(animation_name_)) { | ||
937 | 1063 | throw GameDataError("unknown animation \"%s\" in immovable program for immovable \"%s\"", | ||
938 | 1064 | animation_name_.c_str(), descr.name().c_str()); | ||
939 | 1065 | } | ||
940 | 1066 | } catch (const WException& e) { | ||
941 | 1067 | throw GameDataError("construct: %s", e.what()); | ||
942 | 1068 | } | ||
943 | 1069 | } | ||
944 | 1070 | |||
945 | 1071 | constexpr uint8_t kCurrentPacketVersionConstructionData = 1; | ||
946 | 1072 | |||
947 | 1073 | struct ActConstructData : ImmovableActionData { | ||
948 | 1074 | const char* name() const override { | ||
949 | 1075 | return "construct"; | ||
950 | 1076 | } | ||
951 | 1077 | void save(FileWrite& fw, Immovable& imm) override { | ||
952 | 1078 | fw.unsigned_8(kCurrentPacketVersionConstructionData); | ||
953 | 1079 | delivered.save(fw, imm.get_owner()->tribe()); | ||
954 | 1080 | } | ||
955 | 1081 | |||
956 | 1082 | static ActConstructData* load(FileRead& fr, Immovable& imm) { | ||
957 | 1083 | ActConstructData* d = new ActConstructData; | ||
958 | 1084 | |||
959 | 1085 | try { | ||
960 | 1086 | uint8_t packet_version = fr.unsigned_8(); | ||
961 | 1087 | if (packet_version == kCurrentPacketVersionConstructionData) { | ||
962 | 1088 | d->delivered.load(fr, imm.get_owner()->tribe()); | ||
963 | 1089 | } else { | ||
964 | 1090 | throw UnhandledVersionError( | ||
965 | 1091 | "ActConstructData", packet_version, kCurrentPacketVersionConstructionData); | ||
966 | 1092 | } | ||
967 | 1093 | } catch (const WException& e) { | ||
968 | 1094 | delete d; | ||
969 | 1095 | d = nullptr; | ||
970 | 1096 | throw GameDataError("ActConstructData: %s", e.what()); | ||
971 | 1097 | } | ||
972 | 1098 | |||
973 | 1099 | return d; | ||
974 | 1100 | } | ||
975 | 1101 | |||
976 | 1102 | Buildcost delivered; | ||
977 | 1103 | }; | ||
978 | 1104 | |||
979 | 1105 | void ImmovableProgram::ActConstruct::execute(Game& g, Immovable& imm) const { | ||
980 | 1106 | ActConstructData* d = imm.get_action_data<ActConstructData>(); | ||
981 | 1107 | if (!d) { | ||
982 | 1108 | // First execution | ||
983 | 1109 | d = new ActConstructData; | ||
984 | 1110 | imm.set_action_data(d); | ||
985 | 1111 | |||
986 | 1112 | imm.start_animation(g, imm.descr().get_animation(animation_name_, &imm)); | ||
987 | 1113 | imm.anim_construction_total_ = imm.descr().buildcost().total(); | ||
988 | 1114 | } else { | ||
989 | 1115 | // Perhaps we are called due to the construction timeout of the last construction step | ||
990 | 1116 | Buildcost remaining; | ||
991 | 1117 | imm.construct_remaining_buildcost(g, &remaining); | ||
992 | 1118 | if (remaining.empty()) { | ||
993 | 1119 | imm.program_step(g); | ||
994 | 1120 | return; | ||
995 | 1121 | } | ||
996 | 1122 | |||
997 | 1123 | // Otherwise, this is a decay timeout | ||
998 | 1124 | uint32_t totaldelivered = 0; | ||
999 | 1125 | for (Buildcost::const_iterator it = d->delivered.begin(); it != d->delivered.end(); ++it) | ||
1000 | 1126 | totaldelivered += it->second; | ||
1001 | 1127 | |||
1002 | 1128 | if (!totaldelivered) { | ||
1003 | 1129 | imm.remove(g); | ||
1004 | 1130 | return; | ||
1005 | 1131 | } | ||
1006 | 1132 | |||
1007 | 1133 | uint32_t randdecay = g.logic_rand() % totaldelivered; | ||
1008 | 1134 | for (Buildcost::iterator it = d->delivered.begin(); it != d->delivered.end(); ++it) { | ||
1009 | 1135 | if (randdecay < it->second) { | ||
1010 | 1136 | it->second--; | ||
1011 | 1137 | break; | ||
1012 | 1138 | } | ||
1013 | 1139 | |||
1014 | 1140 | randdecay -= it->second; | ||
1015 | 1141 | } | ||
1016 | 1142 | |||
1017 | 1143 | imm.anim_construction_done_ = d->delivered.total(); | ||
1018 | 1144 | } | ||
1019 | 1145 | |||
1020 | 1146 | imm.program_step_ = imm.schedule_act(g, decaytime_); | ||
1021 | 1147 | } | ||
1022 | 1148 | |||
1023 | 1149 | /** | 700 | /** |
1024 | 1150 | * For an immovable that is currently in construction mode, return \c true and | 701 | * For an immovable that is currently in construction mode, return \c true and |
1025 | 1151 | * compute the remaining buildcost. | 702 | * compute the remaining buildcost. |
1026 | @@ -1208,16 +759,6 @@ | |||
1027 | 1208 | return true; | 759 | return true; |
1028 | 1209 | } | 760 | } |
1029 | 1210 | 761 | ||
1030 | 1211 | ImmovableActionData* | ||
1031 | 1212 | ImmovableActionData::load(FileRead& fr, Immovable& imm, const std::string& name) { | ||
1032 | 1213 | // TODO(GunChleoc): Use "construct" only after Build 20 | ||
1033 | 1214 | if (name == "construction" || name == "construct") | ||
1034 | 1215 | return ActConstructData::load(fr, imm); | ||
1035 | 1216 | else { | ||
1036 | 1217 | log("ImmovableActionData::load: type %s not known", name.c_str()); | ||
1037 | 1218 | return nullptr; | ||
1038 | 1219 | } | ||
1039 | 1220 | } | ||
1040 | 1221 | 762 | ||
1041 | 1222 | /* | 763 | /* |
1042 | 1223 | ============================================================================== | 764 | ============================================================================== |
1043 | 1224 | 765 | ||
1044 | === added file 'src/logic/map_objects/immovable_program.cc' | |||
1045 | --- src/logic/map_objects/immovable_program.cc 1970-01-01 00:00:00 +0000 | |||
1046 | +++ src/logic/map_objects/immovable_program.cc 2019-08-31 15:58:01 +0000 | |||
1047 | @@ -0,0 +1,344 @@ | |||
1048 | 1 | /* | ||
1049 | 2 | * Copyright (C) 2002-2019 by the Widelands Development Team | ||
1050 | 3 | * | ||
1051 | 4 | * This program is free software; you can redistribute it and/or | ||
1052 | 5 | * modify it under the terms of the GNU General Public License | ||
1053 | 6 | * as published by the Free Software Foundation; either version 2 | ||
1054 | 7 | * of the License, or (at your option) any later version. | ||
1055 | 8 | * | ||
1056 | 9 | * This program is distributed in the hope that it will be useful, | ||
1057 | 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1058 | 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1059 | 12 | * GNU General Public License for more details. | ||
1060 | 13 | * | ||
1061 | 14 | * You should have received a copy of the GNU General Public License | ||
1062 | 15 | * along with this program; if not, write to the Free Software | ||
1063 | 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
1064 | 17 | * | ||
1065 | 18 | */ | ||
1066 | 19 | |||
1067 | 20 | #include "logic/map_objects/immovable_program.h" | ||
1068 | 21 | |||
1069 | 22 | #include <memory> | ||
1070 | 23 | |||
1071 | 24 | #include "logic/game.h" | ||
1072 | 25 | #include "logic/game_data_error.h" | ||
1073 | 26 | #include "logic/map_objects/terrain_affinity.h" | ||
1074 | 27 | #include "logic/map_objects/world/world.h" | ||
1075 | 28 | #include "logic/mapfringeregion.h" | ||
1076 | 29 | #include "logic/player.h" | ||
1077 | 30 | #include "sound/note_sound.h" | ||
1078 | 31 | |||
1079 | 32 | namespace Widelands { | ||
1080 | 33 | |||
1081 | 34 | ImmovableProgram::ImmovableProgram(const std::string& init_name, std::unique_ptr<Action> action) : MapObjectProgram(init_name) { | ||
1082 | 35 | actions_.push_back(std::move(action)); | ||
1083 | 36 | } | ||
1084 | 37 | |||
1085 | 38 | ImmovableProgram::ImmovableProgram(const std::string& init_name, | ||
1086 | 39 | const std::vector<std::string>& lines, | ||
1087 | 40 | const ImmovableDescr& immovable) | ||
1088 | 41 | : MapObjectProgram(init_name) { | ||
1089 | 42 | for (const std::string& line : lines) { | ||
1090 | 43 | if (line.empty()) { | ||
1091 | 44 | throw GameDataError("Empty line"); | ||
1092 | 45 | } | ||
1093 | 46 | try { | ||
1094 | 47 | ProgramParseInput parseinput = parse_program_string(line); | ||
1095 | 48 | |||
1096 | 49 | if (parseinput.name == "animate") { | ||
1097 | 50 | actions_.push_back(std::unique_ptr<Action>(new ActAnimate(parseinput.arguments, immovable))); | ||
1098 | 51 | } else if (parseinput.name == "transform") { | ||
1099 | 52 | actions_.push_back(std::unique_ptr<Action>(new ActTransform(parseinput.arguments, immovable))); | ||
1100 | 53 | } else if (parseinput.name == "grow") { | ||
1101 | 54 | actions_.push_back(std::unique_ptr<Action>(new ActGrow(parseinput.arguments, immovable))); | ||
1102 | 55 | } else if (parseinput.name == "remove") { | ||
1103 | 56 | actions_.push_back(std::unique_ptr<Action>(new ActRemove(parseinput.arguments))); | ||
1104 | 57 | } else if (parseinput.name == "seed") { | ||
1105 | 58 | actions_.push_back(std::unique_ptr<Action>(new ActSeed(parseinput.arguments, immovable))); | ||
1106 | 59 | } else if (parseinput.name == "playsound") { | ||
1107 | 60 | actions_.push_back(std::unique_ptr<Action>(new ActPlaySound(parseinput.arguments))); | ||
1108 | 61 | } else if (parseinput.name == "construct") { | ||
1109 | 62 | actions_.push_back(std::unique_ptr<Action>(new ActConstruct(parseinput.arguments, immovable))); | ||
1110 | 63 | } else { | ||
1111 | 64 | throw GameDataError("Unknown command '%s' in line '%s'", parseinput.name.c_str(), line.c_str()); | ||
1112 | 65 | } | ||
1113 | 66 | } catch (const GameDataError& e) { | ||
1114 | 67 | throw GameDataError("Error reading line '%s': %s", line.c_str(), e.what()); | ||
1115 | 68 | } | ||
1116 | 69 | } | ||
1117 | 70 | if (actions_.empty()) { | ||
1118 | 71 | throw GameDataError("No actions found"); | ||
1119 | 72 | } | ||
1120 | 73 | } | ||
1121 | 74 | |||
1122 | 75 | ImmovableProgram::Action::~Action() { | ||
1123 | 76 | } | ||
1124 | 77 | |||
1125 | 78 | ImmovableProgram::ActAnimate::ActAnimate(const std::vector<std::string>& arguments, const ImmovableDescr& descr) { | ||
1126 | 79 | parameters = MapObjectProgram::parse_act_animate(arguments, descr, true); | ||
1127 | 80 | } | ||
1128 | 81 | |||
1129 | 82 | /// Use convolution to make the animation time a random variable with binomial | ||
1130 | 83 | /// distribution and the configured time as the expected value. | ||
1131 | 84 | void ImmovableProgram::ActAnimate::execute(Game& game, Immovable& immovable) const { | ||
1132 | 85 | immovable.start_animation(game, parameters.animation); | ||
1133 | 86 | immovable.program_step( | ||
1134 | 87 | game, parameters.duration ? 1 + game.logic_rand() % parameters.duration + game.logic_rand() % parameters.duration : 0); | ||
1135 | 88 | } | ||
1136 | 89 | |||
1137 | 90 | ImmovableProgram::ActPlaySound::ActPlaySound(const std::vector<std::string>& arguments) { | ||
1138 | 91 | parameters = MapObjectProgram::parse_act_play_sound(arguments, kFxPriorityAllowMultiple - 1); | ||
1139 | 92 | } | ||
1140 | 93 | |||
1141 | 94 | /** | ||
1142 | 95 | * Send request to the g_sound_handler to play a certain sound effect. | ||
1143 | 96 | * Whether the effect actually gets played is decided by the sound server itself. | ||
1144 | 97 | */ | ||
1145 | 98 | void ImmovableProgram::ActPlaySound::execute(Game& game, Immovable& immovable) const { | ||
1146 | 99 | Notifications::publish(NoteSound(SoundType::kAmbient, parameters.fx, immovable.get_position(), parameters.priority)); | ||
1147 | 100 | immovable.program_step(game); | ||
1148 | 101 | } | ||
1149 | 102 | |||
1150 | 103 | ImmovableProgram::ActTransform::ActTransform(std::vector<std::string>& arguments, const ImmovableDescr& descr) { | ||
1151 | 104 | if (arguments.empty()) { | ||
1152 | 105 | throw GameDataError("Usage: transform=[bob] <name> [<probability>]"); | ||
1153 | 106 | } | ||
1154 | 107 | try { | ||
1155 | 108 | bob = false; | ||
1156 | 109 | probability = 0; | ||
1157 | 110 | |||
1158 | 111 | for (const std::string& argument : arguments) { | ||
1159 | 112 | if (argument == "bob") { | ||
1160 | 113 | bob = true; | ||
1161 | 114 | } else if (argument[0] >= '0' && argument[0] <= '9') { | ||
1162 | 115 | probability = read_positive(argument, 254); | ||
1163 | 116 | } else { | ||
1164 | 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. | ||
1165 | 118 | type_name = argument; | ||
1166 | 119 | } | ||
1167 | 120 | } | ||
1168 | 121 | if (type_name == descr.name()) { | ||
1169 | 122 | throw GameDataError("illegal transformation to the same type"); | ||
1170 | 123 | } | ||
1171 | 124 | } catch (const WException& e) { | ||
1172 | 125 | throw GameDataError("transform: %s", e.what()); | ||
1173 | 126 | } | ||
1174 | 127 | } | ||
1175 | 128 | |||
1176 | 129 | void ImmovableProgram::ActTransform::execute(Game& game, Immovable& immovable) const { | ||
1177 | 130 | if (probability == 0 || game.logic_rand() % 256 < probability) { | ||
1178 | 131 | Player* player = immovable.get_owner(); | ||
1179 | 132 | Coords const c = immovable.get_position(); | ||
1180 | 133 | MapObjectDescr::OwnerType owner_type = immovable.descr().owner_type(); | ||
1181 | 134 | immovable.remove(game); // Now immovable is a dangling reference! | ||
1182 | 135 | |||
1183 | 136 | if (bob) { | ||
1184 | 137 | game.create_ship(c, type_name, player); | ||
1185 | 138 | } else { | ||
1186 | 139 | game.create_immovable_with_name( | ||
1187 | 140 | c, type_name, owner_type, player, nullptr /* former_building_descr */); | ||
1188 | 141 | } | ||
1189 | 142 | } else | ||
1190 | 143 | immovable.program_step(game); | ||
1191 | 144 | } | ||
1192 | 145 | |||
1193 | 146 | ImmovableProgram::ActGrow::ActGrow(std::vector<std::string>& arguments, const ImmovableDescr& descr) { | ||
1194 | 147 | if (arguments.size() != 1) { | ||
1195 | 148 | throw GameDataError("Usage: grow=<immovable name>"); | ||
1196 | 149 | } | ||
1197 | 150 | if (!descr.has_terrain_affinity()) { | ||
1198 | 151 | throw GameDataError( | ||
1199 | 152 | "Immovable %s can 'grow', but has no terrain_affinity entry.", descr.name().c_str()); | ||
1200 | 153 | } | ||
1201 | 154 | |||
1202 | 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. | ||
1203 | 156 | type_name = arguments.front(); | ||
1204 | 157 | } | ||
1205 | 158 | |||
1206 | 159 | void ImmovableProgram::ActGrow::execute(Game& game, Immovable& immovable) const { | ||
1207 | 160 | const Map& map = game.map(); | ||
1208 | 161 | FCoords const f = map.get_fcoords(immovable.get_position()); | ||
1209 | 162 | const ImmovableDescr& descr = immovable.descr(); | ||
1210 | 163 | |||
1211 | 164 | if ((game.logic_rand() % TerrainAffinity::kPrecisionFactor) < | ||
1212 | 165 | probability_to_grow(descr.terrain_affinity(), f, map, game.world().terrains())) { | ||
1213 | 166 | MapObjectDescr::OwnerType owner_type = descr.owner_type(); | ||
1214 | 167 | Player* owner = immovable.get_owner(); | ||
1215 | 168 | immovable.remove(game); // Now immovable is a dangling reference! | ||
1216 | 169 | game.create_immovable_with_name( | ||
1217 | 170 | f, type_name, owner_type, owner, nullptr /* former_building_descr */); | ||
1218 | 171 | } else { | ||
1219 | 172 | immovable.program_step(game); | ||
1220 | 173 | } | ||
1221 | 174 | } | ||
1222 | 175 | |||
1223 | 176 | /** | ||
1224 | 177 | * remove | ||
1225 | 178 | */ | ||
1226 | 179 | ImmovableProgram::ActRemove::ActRemove(std::vector<std::string>& arguments) { | ||
1227 | 180 | if (arguments.size() > 1) { | ||
1228 | 181 | throw GameDataError("Usage: remove=[<probability>]"); | ||
1229 | 182 | } | ||
1230 | 183 | probability = arguments.empty() ? 0 : read_positive(arguments.front(), 254); | ||
1231 | 184 | } | ||
1232 | 185 | |||
1233 | 186 | void ImmovableProgram::ActRemove::execute(Game& game, Immovable& immovable) const { | ||
1234 | 187 | if (probability == 0 || game.logic_rand() % 256 < probability) { | ||
1235 | 188 | immovable.remove(game); // Now immovable is a dangling reference! | ||
1236 | 189 | } else { | ||
1237 | 190 | immovable.program_step(game); | ||
1238 | 191 | } | ||
1239 | 192 | } | ||
1240 | 193 | |||
1241 | 194 | ImmovableProgram::ActSeed::ActSeed(std::vector<std::string>& arguments, const ImmovableDescr& descr) { | ||
1242 | 195 | if (arguments.size() != 1) { | ||
1243 | 196 | throw GameDataError("Usage: seed=<immovable name>"); | ||
1244 | 197 | } | ||
1245 | 198 | if (!descr.has_terrain_affinity()) { | ||
1246 | 199 | throw GameDataError( | ||
1247 | 200 | "Immovable %s can 'seed', but has no terrain_affinity entry.", descr.name().c_str()); | ||
1248 | 201 | } | ||
1249 | 202 | |||
1250 | 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. | ||
1251 | 204 | type_name = arguments.front(); | ||
1252 | 205 | } | ||
1253 | 206 | |||
1254 | 207 | void ImmovableProgram::ActSeed::execute(Game& game, Immovable& immovable) const { | ||
1255 | 208 | const Map& map = game.map(); | ||
1256 | 209 | FCoords const f = map.get_fcoords(immovable.get_position()); | ||
1257 | 210 | const ImmovableDescr& descr = immovable.descr(); | ||
1258 | 211 | |||
1259 | 212 | if ((game.logic_rand() % TerrainAffinity::kPrecisionFactor) < | ||
1260 | 213 | probability_to_grow(descr.terrain_affinity(), f, map, game.world().terrains())) { | ||
1261 | 214 | // Seed a new tree. | ||
1262 | 215 | MapFringeRegion<> mr(map, Area<>(f, 0)); | ||
1263 | 216 | uint32_t fringe_size = 0; | ||
1264 | 217 | do { | ||
1265 | 218 | mr.extend(map); | ||
1266 | 219 | fringe_size += 6; | ||
1267 | 220 | } while (game.logic_rand() % std::numeric_limits<uint8_t>::max() < probability); | ||
1268 | 221 | |||
1269 | 222 | for (uint32_t n = game.logic_rand() % fringe_size; n; --n) { | ||
1270 | 223 | mr.advance(map); | ||
1271 | 224 | } | ||
1272 | 225 | |||
1273 | 226 | const FCoords new_location = map.get_fcoords(mr.location()); | ||
1274 | 227 | if (!new_location.field->get_immovable() && | ||
1275 | 228 | (new_location.field->nodecaps() & MOVECAPS_WALK) && | ||
1276 | 229 | (game.logic_rand() % TerrainAffinity::kPrecisionFactor) < | ||
1277 | 230 | probability_to_grow( | ||
1278 | 231 | descr.terrain_affinity(), new_location, map, game.world().terrains())) { | ||
1279 | 232 | game.create_immovable_with_name(mr.location(), type_name, descr.owner_type(), | ||
1280 | 233 | nullptr /* owner */, nullptr /* former_building_descr */); | ||
1281 | 234 | } | ||
1282 | 235 | } | ||
1283 | 236 | |||
1284 | 237 | immovable.program_step(game); | ||
1285 | 238 | } | ||
1286 | 239 | |||
1287 | 240 | ImmovableProgram::ActConstruct::ActConstruct(std::vector<std::string>& arguments, const ImmovableDescr& descr) { | ||
1288 | 241 | if (arguments.size() != 3) { | ||
1289 | 242 | throw GameDataError("Usage: construct=<animation> <build duration> <decay duration>"); | ||
1290 | 243 | } | ||
1291 | 244 | try { | ||
1292 | 245 | animation_name_ = arguments[0]; | ||
1293 | 246 | if (!descr.is_animation_known(animation_name_)) { | ||
1294 | 247 | throw GameDataError("Unknown animation '%s' in immovable program for immovable '%s'", | ||
1295 | 248 | animation_name_.c_str(), descr.name().c_str()); | ||
1296 | 249 | } | ||
1297 | 250 | |||
1298 | 251 | buildtime_ = read_positive(arguments[1]); | ||
1299 | 252 | decaytime_ = read_positive(arguments[2]); | ||
1300 | 253 | } catch (const WException& e) { | ||
1301 | 254 | throw GameDataError("construct: %s", e.what()); | ||
1302 | 255 | } | ||
1303 | 256 | } | ||
1304 | 257 | |||
1305 | 258 | constexpr uint8_t kCurrentPacketVersionConstructionData = 1; | ||
1306 | 259 | |||
1307 | 260 | |||
1308 | 261 | const char* ActConstructData::name() const { | ||
1309 | 262 | return "construct"; | ||
1310 | 263 | } | ||
1311 | 264 | void ActConstructData::save(FileWrite& fw, Immovable& imm) const { | ||
1312 | 265 | fw.unsigned_8(kCurrentPacketVersionConstructionData); | ||
1313 | 266 | delivered.save(fw, imm.get_owner()->tribe()); | ||
1314 | 267 | } | ||
1315 | 268 | |||
1316 | 269 | ActConstructData* ActConstructData::load(FileRead& fr, Immovable& imm) { | ||
1317 | 270 | ActConstructData* d = new ActConstructData; | ||
1318 | 271 | |||
1319 | 272 | try { | ||
1320 | 273 | uint8_t packet_version = fr.unsigned_8(); | ||
1321 | 274 | if (packet_version == kCurrentPacketVersionConstructionData) { | ||
1322 | 275 | d->delivered.load(fr, imm.get_owner()->tribe()); | ||
1323 | 276 | } else { | ||
1324 | 277 | throw UnhandledVersionError( | ||
1325 | 278 | "ActConstructData", packet_version, kCurrentPacketVersionConstructionData); | ||
1326 | 279 | } | ||
1327 | 280 | } catch (const WException& e) { | ||
1328 | 281 | delete d; | ||
1329 | 282 | d = nullptr; | ||
1330 | 283 | throw GameDataError("ActConstructData: %s", e.what()); | ||
1331 | 284 | } | ||
1332 | 285 | |||
1333 | 286 | return d; | ||
1334 | 287 | } | ||
1335 | 288 | |||
1336 | 289 | |||
1337 | 290 | void ImmovableProgram::ActConstruct::execute(Game& g, Immovable& imm) const { | ||
1338 | 291 | ActConstructData* d = imm.get_action_data<ActConstructData>(); | ||
1339 | 292 | if (!d) { | ||
1340 | 293 | // First execution | ||
1341 | 294 | d = new ActConstructData; | ||
1342 | 295 | imm.set_action_data(d); | ||
1343 | 296 | |||
1344 | 297 | imm.start_animation(g, imm.descr().get_animation(animation_name_, &imm)); | ||
1345 | 298 | imm.anim_construction_total_ = imm.descr().buildcost().total(); | ||
1346 | 299 | } else { | ||
1347 | 300 | // Perhaps we are called due to the construction timeout of the last construction step | ||
1348 | 301 | Buildcost remaining; | ||
1349 | 302 | imm.construct_remaining_buildcost(g, &remaining); | ||
1350 | 303 | if (remaining.empty()) { | ||
1351 | 304 | imm.program_step(g); | ||
1352 | 305 | return; | ||
1353 | 306 | } | ||
1354 | 307 | |||
1355 | 308 | // Otherwise, this is a decay timeout | ||
1356 | 309 | uint32_t totaldelivered = 0; | ||
1357 | 310 | for (const auto& addme : d->delivered) { | ||
1358 | 311 | totaldelivered += addme.second; | ||
1359 | 312 | } | ||
1360 | 313 | |||
1361 | 314 | if (!totaldelivered) { | ||
1362 | 315 | imm.remove(g); | ||
1363 | 316 | return; | ||
1364 | 317 | } | ||
1365 | 318 | |||
1366 | 319 | uint32_t randdecay = g.logic_rand() % totaldelivered; | ||
1367 | 320 | for (Buildcost::iterator it = d->delivered.begin(); it != d->delivered.end(); ++it) { | ||
1368 | 321 | if (randdecay < it->second) { | ||
1369 | 322 | it->second--; | ||
1370 | 323 | break; | ||
1371 | 324 | } | ||
1372 | 325 | |||
1373 | 326 | randdecay -= it->second; | ||
1374 | 327 | } | ||
1375 | 328 | |||
1376 | 329 | imm.anim_construction_done_ = d->delivered.total(); | ||
1377 | 330 | } | ||
1378 | 331 | |||
1379 | 332 | imm.program_step_ = imm.schedule_act(g, decaytime_); | ||
1380 | 333 | } | ||
1381 | 334 | |||
1382 | 335 | ImmovableActionData* | ||
1383 | 336 | ImmovableActionData::load(FileRead& fr, Immovable& imm, const std::string& name) { | ||
1384 | 337 | if (name == "construct") { | ||
1385 | 338 | return ActConstructData::load(fr, imm); | ||
1386 | 339 | } else { | ||
1387 | 340 | log("ImmovableActionData::load: type %s not known", name.c_str()); | ||
1388 | 341 | return nullptr; | ||
1389 | 342 | } | ||
1390 | 343 | } | ||
1391 | 344 | } // namespace Widelands | ||
1392 | 0 | 345 | ||
1393 | === modified file 'src/logic/map_objects/immovable_program.h' | |||
1394 | --- src/logic/map_objects/immovable_program.h 2019-04-26 12:46:40 +0000 | |||
1395 | +++ src/logic/map_objects/immovable_program.h 2019-08-31 15:58:01 +0000 | |||
1396 | @@ -21,21 +21,19 @@ | |||
1397 | 21 | #define WL_LOGIC_MAP_OBJECTS_IMMOVABLE_PROGRAM_H | 21 | #define WL_LOGIC_MAP_OBJECTS_IMMOVABLE_PROGRAM_H |
1398 | 22 | 22 | ||
1399 | 23 | #include <cstring> | 23 | #include <cstring> |
1400 | 24 | #include <memory> | ||
1401 | 24 | #include <string> | 25 | #include <string> |
1402 | 25 | 26 | ||
1403 | 26 | #include "base/macros.h" | 27 | #include "base/macros.h" |
1404 | 27 | 28 | ||
1405 | 28 | /* | ||
1406 | 29 | * Implementation is in immovable.cc | ||
1407 | 30 | */ | ||
1408 | 31 | |||
1409 | 32 | #include "logic/map_objects/buildcost.h" | 29 | #include "logic/map_objects/buildcost.h" |
1410 | 33 | #include "logic/map_objects/immovable.h" | 30 | #include "logic/map_objects/immovable.h" |
1411 | 31 | #include "logic/map_objects/map_object_program.h" | ||
1412 | 34 | 32 | ||
1413 | 35 | namespace Widelands { | 33 | namespace Widelands { |
1414 | 36 | 34 | ||
1415 | 37 | /// Ordered sequence of actions (at least 1). Has a name. | 35 | /// Ordered sequence of actions (at least 1). Has a name. |
1417 | 38 | struct ImmovableProgram { | 36 | struct ImmovableProgram : public MapObjectProgram { |
1418 | 39 | 37 | ||
1419 | 40 | /// Can be executed on an Immovable. | 38 | /// Can be executed on an Immovable. |
1420 | 41 | struct Action { | 39 | struct Action { |
1421 | @@ -63,15 +61,14 @@ | |||
1422 | 63 | /// will not be stopped by this command. It will run until another animation | 61 | /// will not be stopped by this command. It will run until another animation |
1423 | 64 | /// is started.) | 62 | /// is started.) |
1424 | 65 | struct ActAnimate : public Action { | 63 | struct ActAnimate : public Action { |
1426 | 66 | ActAnimate(char* parameters, ImmovableDescr&); | 64 | ActAnimate(const std::vector<std::string>& arguments, const ImmovableDescr&); |
1427 | 67 | void execute(Game&, Immovable&) const override; | 65 | void execute(Game&, Immovable&) const override; |
1430 | 68 | const std::string& animation() const { | 66 | uint32_t animation() const { |
1431 | 69 | return animation_name_; | 67 | return parameters.animation; |
1432 | 70 | } | 68 | } |
1433 | 71 | 69 | ||
1434 | 72 | private: | 70 | private: |
1437 | 73 | std::string animation_name_; | 71 | AnimationParameters parameters; |
1436 | 74 | Duration duration_; | ||
1438 | 75 | }; | 72 | }; |
1439 | 76 | 73 | ||
1440 | 77 | /// Transforms the immovable into another immovable or into a bob | 74 | /// Transforms the immovable into another immovable or into a bob |
1441 | @@ -91,28 +88,26 @@ | |||
1442 | 91 | /// name: | 88 | /// name: |
1443 | 92 | /// name of the replacement object | 89 | /// name of the replacement object |
1444 | 93 | struct ActTransform : public Action { | 90 | struct ActTransform : public Action { |
1446 | 94 | ActTransform(char* parameters, ImmovableDescr&); | 91 | ActTransform(std::vector<std::string>& arguments, const ImmovableDescr&); |
1447 | 95 | void execute(Game&, Immovable&) const override; | 92 | void execute(Game&, Immovable&) const override; |
1448 | 96 | 93 | ||
1449 | 97 | private: | 94 | private: |
1450 | 98 | std::string type_name; | 95 | std::string type_name; |
1451 | 99 | bool bob; | 96 | bool bob; |
1452 | 100 | bool tribe; | ||
1453 | 101 | uint8_t probability; | 97 | uint8_t probability; |
1454 | 102 | }; | 98 | }; |
1455 | 103 | 99 | ||
1456 | 104 | /// Like ActTransform but the probability is determined by the suitability. | 100 | /// Like ActTransform but the probability is determined by the suitability. |
1457 | 105 | struct ActGrow : public Action { | 101 | struct ActGrow : public Action { |
1459 | 106 | ActGrow(char* parameters, ImmovableDescr&); | 102 | ActGrow(std::vector<std::string>& arguments, const ImmovableDescr&); |
1460 | 107 | void execute(Game&, Immovable&) const override; | 103 | void execute(Game&, Immovable&) const override; |
1461 | 108 | 104 | ||
1462 | 109 | private: | 105 | private: |
1463 | 110 | std::string type_name; | 106 | std::string type_name; |
1464 | 111 | bool tribe; | ||
1465 | 112 | }; | 107 | }; |
1466 | 113 | 108 | ||
1467 | 114 | struct ActRemove : public Action { | 109 | struct ActRemove : public Action { |
1469 | 115 | ActRemove(char* parameters, ImmovableDescr&); | 110 | ActRemove(std::vector<std::string>& arguments); |
1470 | 116 | void execute(Game&, Immovable&) const override; | 111 | void execute(Game&, Immovable&) const override; |
1471 | 117 | 112 | ||
1472 | 118 | private: | 113 | private: |
1473 | @@ -120,7 +115,7 @@ | |||
1474 | 120 | }; | 115 | }; |
1475 | 121 | 116 | ||
1476 | 122 | struct ActSeed : public Action { | 117 | struct ActSeed : public Action { |
1478 | 123 | ActSeed(char* parameters, ImmovableDescr&); | 118 | ActSeed(std::vector<std::string>& arguments, const ImmovableDescr&); |
1479 | 124 | void execute(Game&, Immovable&) const override; | 119 | void execute(Game&, Immovable&) const override; |
1480 | 125 | 120 | ||
1481 | 126 | private: | 121 | private: |
1482 | @@ -142,12 +137,11 @@ | |||
1483 | 142 | /// Plays the specified sound effect with the specified priority. Whether the | 137 | /// Plays the specified sound effect with the specified priority. Whether the |
1484 | 143 | /// sound effect is actually played is determined by the sound handler. | 138 | /// sound effect is actually played is determined by the sound handler. |
1485 | 144 | struct ActPlaySound : public Action { | 139 | struct ActPlaySound : public Action { |
1487 | 145 | ActPlaySound(char* parameters, const ImmovableDescr&); | 140 | ActPlaySound(const std::vector<std::string>& arguments); |
1488 | 146 | void execute(Game&, Immovable&) const override; | 141 | void execute(Game&, Immovable&) const override; |
1489 | 147 | 142 | ||
1490 | 148 | private: | 143 | private: |
1493 | 149 | FxId fx; | 144 | PlaySoundParameters parameters; |
1492 | 150 | uint8_t priority; | ||
1494 | 151 | }; | 145 | }; |
1495 | 152 | 146 | ||
1496 | 153 | /** | 147 | /** |
1497 | @@ -164,7 +158,7 @@ | |||
1498 | 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. |
1499 | 165 | */ | 159 | */ |
1500 | 166 | struct ActConstruct : public Action { | 160 | struct ActConstruct : public Action { |
1502 | 167 | ActConstruct(char* parameters, ImmovableDescr&); | 161 | ActConstruct(std::vector<std::string>& arguments, const ImmovableDescr&); |
1503 | 168 | void execute(Game&, Immovable&) const override; | 162 | void execute(Game&, Immovable&) const override; |
1504 | 169 | 163 | ||
1505 | 170 | Duration buildtime() const { | 164 | Duration buildtime() const { |
1506 | @@ -181,24 +175,16 @@ | |||
1507 | 181 | }; | 175 | }; |
1508 | 182 | 176 | ||
1509 | 183 | /// Create a program with a single action. | 177 | /// Create a program with a single action. |
1513 | 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); |
1511 | 185 | actions_.push_back(action); | ||
1512 | 186 | } | ||
1514 | 187 | 179 | ||
1516 | 188 | // Create an immovable program from a number of lines. | 180 | /// Create an immovable program from a number of lines. |
1517 | 189 | ImmovableProgram(const std::string& init_name, | 181 | ImmovableProgram(const std::string& init_name, |
1518 | 190 | const std::vector<std::string>& lines, | 182 | const std::vector<std::string>& lines, |
1520 | 191 | ImmovableDescr* immovable); | 183 | const ImmovableDescr& immovable); |
1521 | 192 | 184 | ||
1522 | 193 | ~ImmovableProgram() { | 185 | ~ImmovableProgram() { |
1523 | 194 | for (Action* action : actions_) { | ||
1524 | 195 | delete action; | ||
1525 | 196 | } | ||
1526 | 197 | } | 186 | } |
1527 | 198 | 187 | ||
1528 | 199 | const std::string& name() const { | ||
1529 | 200 | return name_; | ||
1530 | 201 | } | ||
1531 | 202 | size_t size() const { | 188 | size_t size() const { |
1532 | 203 | return actions_.size(); | 189 | return actions_.size(); |
1533 | 204 | } | 190 | } |
1534 | @@ -207,14 +193,8 @@ | |||
1535 | 207 | return *actions_[idx]; | 193 | return *actions_[idx]; |
1536 | 208 | } | 194 | } |
1537 | 209 | 195 | ||
1538 | 210 | using Actions = std::vector<Action*>; | ||
1539 | 211 | const Actions& actions() const { | ||
1540 | 212 | return actions_; | ||
1541 | 213 | } | ||
1542 | 214 | |||
1543 | 215 | private: | 196 | private: |
1546 | 216 | std::string name_; | 197 | std::vector<std::unique_ptr<Action>> actions_; |
1545 | 217 | Actions actions_; | ||
1547 | 218 | }; | 198 | }; |
1548 | 219 | 199 | ||
1549 | 220 | struct ImmovableActionData { | 200 | struct ImmovableActionData { |
1550 | @@ -224,10 +204,18 @@ | |||
1551 | 224 | } | 204 | } |
1552 | 225 | 205 | ||
1553 | 226 | virtual const char* name() const = 0; | 206 | virtual const char* name() const = 0; |
1555 | 227 | virtual void save(FileWrite& fw, Immovable& imm) = 0; | 207 | virtual void save(FileWrite& fw, Immovable& imm) const = 0; |
1556 | 228 | 208 | ||
1557 | 229 | static ImmovableActionData* load(FileRead& fr, Immovable& imm, const std::string& name); | 209 | static ImmovableActionData* load(FileRead& fr, Immovable& imm, const std::string& name); |
1558 | 230 | }; | 210 | }; |
1559 | 211 | |||
1560 | 212 | struct ActConstructData : ImmovableActionData { | ||
1561 | 213 | const char* name() const override; | ||
1562 | 214 | void save(FileWrite& fw, Immovable& imm) const override; | ||
1563 | 215 | static ActConstructData* load(FileRead& fr, Immovable& imm); | ||
1564 | 216 | |||
1565 | 217 | Buildcost delivered; | ||
1566 | 218 | }; | ||
1567 | 231 | } // namespace Widelands | 219 | } // namespace Widelands |
1568 | 232 | 220 | ||
1569 | 233 | #endif // end of include guard: WL_LOGIC_MAP_OBJECTS_IMMOVABLE_PROGRAM_H | 221 | #endif // end of include guard: WL_LOGIC_MAP_OBJECTS_IMMOVABLE_PROGRAM_H |
1570 | 234 | 222 | ||
1571 | === modified file 'src/logic/map_objects/map_object.h' | |||
1572 | --- src/logic/map_objects/map_object.h 2019-05-25 08:51:42 +0000 | |||
1573 | +++ src/logic/map_objects/map_object.h 2019-08-31 15:58:01 +0000 | |||
1574 | @@ -125,6 +125,7 @@ | |||
1575 | 125 | } | 125 | } |
1576 | 126 | 126 | ||
1577 | 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; |
1578 | 128 | |||
1579 | 128 | uint32_t main_animation() const; | 129 | uint32_t main_animation() const; |
1580 | 129 | std::string get_animation_name(uint32_t) const; ///< needed for save, debug | 130 | std::string get_animation_name(uint32_t) const; ///< needed for save, debug |
1581 | 130 | 131 | ||
1582 | 131 | 132 | ||
1583 | === added file 'src/logic/map_objects/map_object_program.cc' | |||
1584 | --- src/logic/map_objects/map_object_program.cc 1970-01-01 00:00:00 +0000 | |||
1585 | +++ src/logic/map_objects/map_object_program.cc 2019-08-31 15:58:01 +0000 | |||
1586 | @@ -0,0 +1,125 @@ | |||
1587 | 1 | /* | ||
1588 | 2 | * Copyright (C) 2002-2019 by the Widelands Development Team | ||
1589 | 3 | * | ||
1590 | 4 | * This program is free software; you can redistribute it and/or | ||
1591 | 5 | * modify it under the terms of the GNU General Public License | ||
1592 | 6 | * as published by the Free Software Foundation; either version 2 | ||
1593 | 7 | * of the License, or (at your option) any later version. | ||
1594 | 8 | * | ||
1595 | 9 | * This program is distributed in the hope that it will be useful, | ||
1596 | 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1597 | 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1598 | 12 | * GNU General Public License for more details. | ||
1599 | 13 | * | ||
1600 | 14 | * You should have received a copy of the GNU General Public License | ||
1601 | 15 | * along with this program; if not, write to the Free Software | ||
1602 | 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
1603 | 17 | * | ||
1604 | 18 | */ | ||
1605 | 19 | |||
1606 | 20 | #include "logic/map_objects/map_object_program.h" | ||
1607 | 21 | |||
1608 | 22 | #include "io/filesystem/layered_filesystem.h" | ||
1609 | 23 | #include "logic/game_data_error.h" | ||
1610 | 24 | #include "logic/map_objects/map_object.h" | ||
1611 | 25 | #include "sound/sound_handler.h" | ||
1612 | 26 | |||
1613 | 27 | namespace Widelands { | ||
1614 | 28 | |||
1615 | 29 | MapObjectProgram::MapObjectProgram(const std::string& init_name) : name_ (init_name) {} | ||
1616 | 30 | |||
1617 | 31 | const std::string& MapObjectProgram::name() const { | ||
1618 | 32 | return name_; | ||
1619 | 33 | } | ||
1620 | 34 | |||
1621 | 35 | std::vector<std::string> MapObjectProgram::split_string(const std::string& s, const char* const separators) { | ||
1622 | 36 | std::vector<std::string> result; | ||
1623 | 37 | for (std::string::size_type pos = 0, endpos; | ||
1624 | 38 | (pos = s.find_first_not_of(separators, pos)) != std::string::npos; pos = endpos) { | ||
1625 | 39 | endpos = s.find_first_of(separators, pos); | ||
1626 | 40 | result.push_back(s.substr(pos, endpos - pos)); | ||
1627 | 41 | } | ||
1628 | 42 | return result; | ||
1629 | 43 | } | ||
1630 | 44 | |||
1631 | 45 | |||
1632 | 46 | unsigned int MapObjectProgram::read_int(const std::string& input, int min_value, int max_value) { | ||
1633 | 47 | unsigned int result = 0U; | ||
1634 | 48 | char* endp; | ||
1635 | 49 | long int const value = strtol(input.c_str(), &endp, 0); | ||
1636 | 50 | result = value; | ||
1637 | 51 | if (*endp || result != value) { | ||
1638 | 52 | throw GameDataError("Expected a number but found \"%s\"", input.c_str()); | ||
1639 | 53 | } | ||
1640 | 54 | if (value < min_value) { | ||
1641 | 55 | throw GameDataError("Expected a number >= %d but found \"%s\"", min_value, input.c_str()); | ||
1642 | 56 | } | ||
1643 | 57 | if (value > max_value) { | ||
1644 | 58 | throw GameDataError("Expected a number <= %d but found \"%s\"", max_value, input.c_str()); | ||
1645 | 59 | } | ||
1646 | 60 | return result; | ||
1647 | 61 | } | ||
1648 | 62 | |||
1649 | 63 | unsigned int MapObjectProgram::read_positive(const std::string& input, int max_value) { | ||
1650 | 64 | return read_int(input, 1, max_value); | ||
1651 | 65 | } | ||
1652 | 66 | |||
1653 | 67 | MapObjectProgram::ProgramParseInput MapObjectProgram::parse_program_string(const std::string& line) { | ||
1654 | 68 | const std::pair<std::string, std::string> key_values = MapObjectProgram::read_key_value_pair(line, '='); | ||
1655 | 69 | return ProgramParseInput{key_values.first, split_string(key_values.second, " \t\n")}; | ||
1656 | 70 | } | ||
1657 | 71 | |||
1658 | 72 | const 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) { | ||
1659 | 73 | const size_t idx = input.find(separator); | ||
1660 | 74 | const std::string key = input.substr(0, idx); | ||
1661 | 75 | |||
1662 | 76 | if (!expected_key.empty()) { | ||
1663 | 77 | if (idx == input.npos) { | ||
1664 | 78 | throw GameDataError("Empty value in '%s' for separator '%c'\n", input.c_str(), separator); | ||
1665 | 79 | } | ||
1666 | 80 | if (key != expected_key) { | ||
1667 | 81 | throw GameDataError("Expected key '%s' but found '%s' in '%s'\n", expected_key.c_str(), key.c_str(), input.c_str()); | ||
1668 | 82 | } | ||
1669 | 83 | } | ||
1670 | 84 | |||
1671 | 85 | return std::make_pair(key, idx == input.npos ? default_value : input.substr(idx + 1)); | ||
1672 | 86 | } | ||
1673 | 87 | |||
1674 | 88 | MapObjectProgram::AnimationParameters MapObjectProgram::parse_act_animate(const std::vector<std::string>& arguments, const MapObjectDescr& descr, bool is_idle_allowed) { | ||
1675 | 89 | if (arguments.size() < 1 || arguments.size() > 2) { | ||
1676 | 90 | throw GameDataError("Usage: animate=<name> [<duration>]"); | ||
1677 | 91 | } | ||
1678 | 92 | |||
1679 | 93 | AnimationParameters result; | ||
1680 | 94 | const std::string& animation_name = arguments.at(0); | ||
1681 | 95 | |||
1682 | 96 | if (!is_idle_allowed && animation_name == "idle") { | ||
1683 | 97 | throw GameDataError("'idle' animation is default; calling is not allowed"); | ||
1684 | 98 | } | ||
1685 | 99 | if (!descr.is_animation_known(animation_name)) { | ||
1686 | 100 | throw GameDataError("Unknown animation '%s'", animation_name.c_str()); | ||
1687 | 101 | } | ||
1688 | 102 | result.animation = descr.get_animation(animation_name, nullptr); | ||
1689 | 103 | |||
1690 | 104 | if (arguments.size() == 2) { | ||
1691 | 105 | result.duration = read_positive(arguments.at(1)); | ||
1692 | 106 | } | ||
1693 | 107 | return result; | ||
1694 | 108 | } | ||
1695 | 109 | |||
1696 | 110 | MapObjectProgram::PlaySoundParameters MapObjectProgram::parse_act_play_sound(const std::vector<std::string>& arguments, uint8_t default_priority) { | ||
1697 | 111 | if (arguments.size() < 1 || arguments.size() > 2) { | ||
1698 | 112 | throw GameDataError("Usage: playsound=<sound_dir/sound_name> [priority]"); | ||
1699 | 113 | } | ||
1700 | 114 | PlaySoundParameters result; | ||
1701 | 115 | result.fx = SoundHandler::register_fx(SoundType::kAmbient, arguments.at(0)); | ||
1702 | 116 | |||
1703 | 117 | result.priority = arguments.size() == 2 ? read_positive(arguments.at(1)) : default_priority; | ||
1704 | 118 | if (result.priority < kFxPriorityLowest) { | ||
1705 | 119 | throw GameDataError("Minmum priority for sounds is %d, but only %d was specified for %s", | ||
1706 | 120 | kFxPriorityLowest, result.priority, arguments.at(0).c_str()); | ||
1707 | 121 | } | ||
1708 | 122 | return result; | ||
1709 | 123 | } | ||
1710 | 124 | |||
1711 | 125 | } // namespace Widelands | ||
1712 | 0 | 126 | ||
1713 | === added file 'src/logic/map_objects/map_object_program.h' | |||
1714 | --- src/logic/map_objects/map_object_program.h 1970-01-01 00:00:00 +0000 | |||
1715 | +++ src/logic/map_objects/map_object_program.h 2019-08-31 15:58:01 +0000 | |||
1716 | @@ -0,0 +1,98 @@ | |||
1717 | 1 | /* | ||
1718 | 2 | * Copyright (C) 2002-2019 by the Widelands Development Team | ||
1719 | 3 | * | ||
1720 | 4 | * This program is free software; you can redistribute it and/or | ||
1721 | 5 | * modify it under the terms of the GNU General Public License | ||
1722 | 6 | * as published by the Free Software Foundation; either version 2 | ||
1723 | 7 | * of the License, or (at your option) any later version. | ||
1724 | 8 | * | ||
1725 | 9 | * This program is distributed in the hope that it will be useful, | ||
1726 | 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1727 | 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1728 | 12 | * GNU General Public License for more details. | ||
1729 | 13 | * | ||
1730 | 14 | * You should have received a copy of the GNU General Public License | ||
1731 | 15 | * along with this program; if not, write to the Free Software | ||
1732 | 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
1733 | 17 | * | ||
1734 | 18 | */ | ||
1735 | 19 | |||
1736 | 20 | #ifndef WL_LOGIC_MAP_OBJECTS_MAP_OBJECT_PROGRAM_H | ||
1737 | 21 | #define WL_LOGIC_MAP_OBJECTS_MAP_OBJECT_PROGRAM_H | ||
1738 | 22 | |||
1739 | 23 | #include <limits> | ||
1740 | 24 | #include <string> | ||
1741 | 25 | #include <vector> | ||
1742 | 26 | |||
1743 | 27 | #include "logic/widelands.h" | ||
1744 | 28 | #include "sound/constants.h" | ||
1745 | 29 | |||
1746 | 30 | namespace Widelands { | ||
1747 | 31 | |||
1748 | 32 | struct MapObjectDescr; | ||
1749 | 33 | |||
1750 | 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. | ||
1751 | 35 | struct MapObjectProgram { | ||
1752 | 36 | const std::string& name() const; | ||
1753 | 37 | |||
1754 | 38 | explicit MapObjectProgram(const std::string& init_name); | ||
1755 | 39 | virtual ~MapObjectProgram() = default; | ||
1756 | 40 | |||
1757 | 41 | protected: | ||
1758 | 42 | /// Splits a string by separators. | ||
1759 | 43 | /// \note This ignores empty elements, so do not use this for example to split | ||
1760 | 44 | /// a string with newline characters into lines, because it would ignore empty | ||
1761 | 45 | /// lines. | ||
1762 | 46 | static std::vector<std::string> split_string(const std::string&, char const* separators); | ||
1763 | 47 | |||
1764 | 48 | /// Reads an int value from a string. Throws a GameDataError if 'min_value' or 'max_value' are exceeded | ||
1765 | 49 | static unsigned int read_int(const std::string& input, int min_value, int max_value = std::numeric_limits<int32_t>::max()); | ||
1766 | 50 | /// Same as 'read_int', with 'min_value' == 1 | ||
1767 | 51 | static unsigned int read_positive(const std::string& input, int max_value = std::numeric_limits<int32_t>::max()); | ||
1768 | 52 | |||
1769 | 53 | /** | ||
1770 | 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" | ||
1771 | 55 | * @param input The string to parse | ||
1772 | 56 | * @param separator The separator for splitting the string, e.g. ':' or '=' | ||
1773 | 57 | * @param default_value A default to assign to the right-hand value if the separator is not found | ||
1774 | 58 | * @param expected_key If this is not empty, the left-hand key must match this string | ||
1775 | 59 | * @return A key, value pair | ||
1776 | 60 | */ | ||
1777 | 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 = ""); | ||
1778 | 62 | |||
1779 | 63 | /// Left-hand and right-hand elements of a line in a program, e.g. parsed from "return=skipped unless economy needs meal" | ||
1780 | 64 | struct ProgramParseInput { | ||
1781 | 65 | /// Program name, e.g. "return" | ||
1782 | 66 | std::string name; | ||
1783 | 67 | /// Program arguments, e.g. { "skipped", "unless", "economy", "needs", "meal" } | ||
1784 | 68 | std::vector<std::string> arguments; | ||
1785 | 69 | }; | ||
1786 | 70 | /// Reads the program name and arguments from a string | ||
1787 | 71 | static ProgramParseInput parse_program_string(const std::string& line); | ||
1788 | 72 | |||
1789 | 73 | /// Animation information | ||
1790 | 74 | struct AnimationParameters { | ||
1791 | 75 | /// Animation ID | ||
1792 | 76 | uint32_t animation = 0; | ||
1793 | 77 | /// Animation duration. 0 will play the animation forever. | ||
1794 | 78 | Duration duration = 0; | ||
1795 | 79 | }; | ||
1796 | 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". | ||
1797 | 81 | static AnimationParameters parse_act_animate(const std::vector<std::string>& arguments, const MapObjectDescr& descr, bool is_idle_allowed); | ||
1798 | 82 | |||
1799 | 83 | /// Sound effect information | ||
1800 | 84 | struct PlaySoundParameters { | ||
1801 | 85 | /// Sound effect ID | ||
1802 | 86 | FxId fx; | ||
1803 | 87 | /// Sound effect priority | ||
1804 | 88 | uint8_t priority = 0; | ||
1805 | 89 | }; | ||
1806 | 90 | /// Parses the arguments for a play_sound action, e.g. { "sound/smiths/sharpening", "120" } | ||
1807 | 91 | static PlaySoundParameters parse_act_play_sound(const std::vector<std::string>& arguments, uint8_t default_priority); | ||
1808 | 92 | |||
1809 | 93 | private: | ||
1810 | 94 | const std::string name_; | ||
1811 | 95 | }; | ||
1812 | 96 | } // namespace Widelands | ||
1813 | 97 | |||
1814 | 98 | #endif // end of include guard: WL_LOGIC_MAP_OBJECTS_MAP_OBJECT_PROGRAM_H | ||
1815 | 0 | 99 | ||
1816 | === modified file 'src/logic/map_objects/tribes/production_program.cc' | |||
1817 | --- src/logic/map_objects/tribes/production_program.cc 2019-05-18 11:58:43 +0000 | |||
1818 | +++ src/logic/map_objects/tribes/production_program.cc 2019-08-31 15:58:01 +0000 | |||
1819 | @@ -20,10 +20,6 @@ | |||
1820 | 20 | #include "logic/map_objects/tribes/production_program.h" | 20 | #include "logic/map_objects/tribes/production_program.h" |
1821 | 21 | 21 | ||
1822 | 22 | #include <memory> | 22 | #include <memory> |
1823 | 23 | #include <sstream> | ||
1824 | 24 | |||
1825 | 25 | #include <boost/algorithm/string.hpp> | ||
1826 | 26 | #include <boost/format.hpp> | ||
1827 | 27 | 23 | ||
1828 | 28 | #include "base/i18n.h" | 24 | #include "base/i18n.h" |
1829 | 29 | #include "base/macros.h" | 25 | #include "base/macros.h" |
1830 | @@ -32,7 +28,6 @@ | |||
1831 | 32 | #include "economy/economy.h" | 28 | #include "economy/economy.h" |
1832 | 33 | #include "economy/flag.h" | 29 | #include "economy/flag.h" |
1833 | 34 | #include "economy/input_queue.h" | 30 | #include "economy/input_queue.h" |
1834 | 35 | #include "helper.h" | ||
1835 | 36 | #include "io/filesystem/layered_filesystem.h" | 31 | #include "io/filesystem/layered_filesystem.h" |
1836 | 37 | #include "logic/game.h" | 32 | #include "logic/game.h" |
1837 | 38 | #include "logic/game_data_error.h" | 33 | #include "logic/game_data_error.h" |
1838 | @@ -56,135 +51,42 @@ | |||
1839 | 56 | namespace Widelands { | 51 | namespace Widelands { |
1840 | 57 | 52 | ||
1841 | 58 | namespace { | 53 | namespace { |
1971 | 59 | 54 | /// If the iterator contents match the string, increment the iterator. Returns whether it matched. | |
1972 | 60 | /// Matches the string that candidate points to against the string that | 55 | bool match_and_skip(const std::vector<std::string>& args, std::vector<std::string>::const_iterator& it, const std::string& matchme) { |
1973 | 61 | /// template points to. Stops at when reaching a null character or the | 56 | const bool result = (it != args.end()) && (*it == matchme); |
1974 | 62 | /// character terminator. If a match is found, candidate is moved beyond the | 57 | if (result) { |
1975 | 63 | /// matched part. | 58 | ++it; |
1976 | 64 | /// | 59 | } |
1977 | 65 | /// example: | 60 | return result; |
1978 | 66 | /// char const * candidate = "return 75"; | 61 | } |
1979 | 67 | /// bool const result = match(candidate, "return"); | 62 | |
1980 | 68 | /// now candidate points to " 75" and result is true | 63 | ProductionProgram::ActReturn::Condition* create_economy_condition(const std::string& item, const ProductionSiteDescr& descr, const Tribes& tribes) { |
1981 | 69 | bool match(char*& candidate, const char* pattern) { | 64 | DescriptionIndex index = tribes.ware_index(item); |
1982 | 70 | for (char* p = candidate;; ++p, ++pattern) | 65 | if (tribes.ware_exists(index)) { |
1983 | 71 | if (!*pattern) { | 66 | descr.ware_demand_checks()->insert(index); |
1984 | 72 | candidate = p; | 67 | return new ProductionProgram::ActReturn::EconomyNeedsWare(index); |
1985 | 73 | return true; | 68 | } else if (tribes.worker_exists(tribes.worker_index(item))) { |
1986 | 74 | } else if (*p != *pattern) | 69 | index = tribes.worker_index(item); |
1987 | 75 | break; | 70 | descr.worker_demand_checks()->insert(index); |
1988 | 76 | return false; | 71 | return new ProductionProgram::ActReturn::EconomyNeedsWorker(index); |
1989 | 77 | } | 72 | } else { |
1990 | 78 | 73 | throw GameDataError("Expected ware or worker type but found '%s'", item.c_str()); | |
1991 | 79 | /// Skips a sequence of consecutive characters with the value c, starting at p. | 74 | } |
1992 | 80 | /// Throws WException if no characters were skipped. | 75 | } |
1993 | 81 | void force_skip(char*& p, char const c = ' ') { | 76 | |
1994 | 82 | char* t = p; | 77 | TrainingAttribute parse_training_attribute(const std::string& argument) { |
1995 | 83 | while (*t == c) | 78 | if (argument == "health") { |
1996 | 84 | ++t; | 79 | return TrainingAttribute::kHealth; |
1997 | 85 | if (p < t) | 80 | } else if (argument == "attack") { |
1998 | 86 | p = t; | 81 | return TrainingAttribute::kAttack; |
1999 | 87 | else | 82 | } else if (argument == "defense") { |
2000 | 88 | throw wexception("expected '%c' but found \"%s\"", c, p); | 83 | return TrainingAttribute::kDefense; |
2001 | 89 | } | 84 | } else if (argument == "evade") { |
2002 | 90 | 85 | return TrainingAttribute::kEvade; | |
2003 | 91 | /// Skips a sequence of consecutive characters with the value c, starting at p. | 86 | } else { |
2004 | 92 | /// Returns whether any characters were skipped. | 87 | throw GameDataError("Expected health|attack|defense|evade after 'soldier' but found '%s'", argument.c_str()); |
2005 | 93 | bool skip(char*& p, char const c = ' ') { | 88 | } |
2006 | 94 | char* t = p; | 89 | } |
1878 | 95 | while (*t == c) | ||
1879 | 96 | ++t; | ||
1880 | 97 | if (p < t) { | ||
1881 | 98 | p = t; | ||
1882 | 99 | return true; | ||
1883 | 100 | } else | ||
1884 | 101 | return false; | ||
1885 | 102 | } | ||
1886 | 103 | |||
1887 | 104 | /// Combines match and force_skip. | ||
1888 | 105 | /// | ||
1889 | 106 | /// example: | ||
1890 | 107 | /// char const * candidate = "return 75"; | ||
1891 | 108 | /// bool const result = match_force_skip(candidate, "return"); | ||
1892 | 109 | /// now candidate points to "75" and result is true | ||
1893 | 110 | /// | ||
1894 | 111 | /// example: | ||
1895 | 112 | /// char const * candidate = "return75"; | ||
1896 | 113 | /// bool const result = match_force_skip(candidate, "return"); | ||
1897 | 114 | /// throws WException | ||
1898 | 115 | bool match_force_skip(char*& candidate, const char* pattern) { | ||
1899 | 116 | for (char* p = candidate;; ++p, ++pattern) | ||
1900 | 117 | if (!*pattern) { | ||
1901 | 118 | force_skip(p); | ||
1902 | 119 | candidate = p; | ||
1903 | 120 | return true; | ||
1904 | 121 | } else if (*p != *pattern) | ||
1905 | 122 | return false; | ||
1906 | 123 | |||
1907 | 124 | NEVER_HERE(); | ||
1908 | 125 | } | ||
1909 | 126 | |||
1910 | 127 | ProductionProgram::ActReturn::Condition* create_economy_condition(char*& parameters, | ||
1911 | 128 | const Tribes& tribes) { | ||
1912 | 129 | try { | ||
1913 | 130 | if (match_force_skip(parameters, "needs")) | ||
1914 | 131 | try { | ||
1915 | 132 | bool reached_end; | ||
1916 | 133 | char const* const type_name = next_word(parameters, reached_end); | ||
1917 | 134 | const DescriptionIndex& wareindex = tribes.ware_index(type_name); | ||
1918 | 135 | if (tribes.ware_exists(wareindex)) { | ||
1919 | 136 | for (size_t i = 0; i < tribes.nrtribes(); ++i) { | ||
1920 | 137 | const TribeDescr& tribe_descr = *tribes.get_tribe_descr(i); | ||
1921 | 138 | if (tribe_descr.has_ware(wareindex)) { | ||
1922 | 139 | tribes.set_ware_type_has_demand_check(wareindex, tribe_descr.name()); | ||
1923 | 140 | } | ||
1924 | 141 | } | ||
1925 | 142 | return new ProductionProgram::ActReturn::EconomyNeedsWare(wareindex); | ||
1926 | 143 | } else if (tribes.worker_exists(tribes.worker_index(type_name))) { | ||
1927 | 144 | const DescriptionIndex& workerindex = tribes.worker_index(type_name); | ||
1928 | 145 | for (size_t i = 0; i < tribes.nrtribes(); ++i) { | ||
1929 | 146 | const TribeDescr* tribe_descr = tribes.get_tribe_descr(i); | ||
1930 | 147 | if (tribe_descr->has_worker(workerindex)) { | ||
1931 | 148 | tribes.set_worker_type_has_demand_check(workerindex); | ||
1932 | 149 | } | ||
1933 | 150 | } | ||
1934 | 151 | return new ProductionProgram::ActReturn::EconomyNeedsWorker(workerindex); | ||
1935 | 152 | } else | ||
1936 | 153 | throw GameDataError( | ||
1937 | 154 | "expected %s but found \"%s\"", "ware type or worker type", type_name); | ||
1938 | 155 | } catch (const WException& e) { | ||
1939 | 156 | throw GameDataError("needs: %s", e.what()); | ||
1940 | 157 | } | ||
1941 | 158 | else | ||
1942 | 159 | throw GameDataError("expected %s but found \"%s\"", "\"needs\"", parameters); | ||
1943 | 160 | } catch (const WException& e) { | ||
1944 | 161 | throw GameDataError("economy: %s", e.what()); | ||
1945 | 162 | } | ||
1946 | 163 | } | ||
1947 | 164 | |||
1948 | 165 | ProductionProgram::ActReturn::Condition* | ||
1949 | 166 | create_site_condition(char*& parameters, const ProductionSiteDescr& descr, const Tribes& tribes) { | ||
1950 | 167 | try { | ||
1951 | 168 | if (match_force_skip(parameters, "has")) | ||
1952 | 169 | return new ProductionProgram::ActReturn::SiteHas(parameters, descr, tribes); | ||
1953 | 170 | else | ||
1954 | 171 | throw GameDataError("expected %s but found \"%s\"", "\"has\"", parameters); | ||
1955 | 172 | } catch (const WException& e) { | ||
1956 | 173 | throw GameDataError("site: %s", e.what()); | ||
1957 | 174 | } | ||
1958 | 175 | } | ||
1959 | 176 | |||
1960 | 177 | ProductionProgram::ActReturn::Condition* create_workers_condition(char*& parameters) { | ||
1961 | 178 | try { | ||
1962 | 179 | if (match(parameters, "need experience")) | ||
1963 | 180 | return new ProductionProgram::ActReturn::WorkersNeedExperience; | ||
1964 | 181 | else | ||
1965 | 182 | throw GameDataError("expected %s but found \"%s\"", "\"need experience\"", parameters); | ||
1966 | 183 | } catch (const WException& e) { | ||
1967 | 184 | throw GameDataError("workers: %s", e.what()); | ||
1968 | 185 | } | ||
1969 | 186 | } | ||
1970 | 187 | |||
2007 | 188 | } // namespace | 90 | } // namespace |
2008 | 189 | 91 | ||
2009 | 190 | ProductionProgram::Action::~Action() { | 92 | ProductionProgram::Action::~Action() { |
2010 | @@ -197,90 +99,99 @@ | |||
2011 | 197 | void ProductionProgram::Action::building_work_failed(Game&, ProductionSite&, Worker&) const { | 99 | void ProductionProgram::Action::building_work_failed(Game&, ProductionSite&, Worker&) const { |
2012 | 198 | } | 100 | } |
2013 | 199 | 101 | ||
2077 | 200 | void ProductionProgram::parse_ware_type_group(char*& parameters, | 102 | ProductionProgram::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) { |
2078 | 201 | WareTypeGroup& group, | 103 | ProductionProgram::Groups result; |
2079 | 202 | const Tribes& tribes, | 104 | |
2080 | 203 | const BillOfMaterials& input_wares, | 105 | for (auto& it = begin; it != end; ++it) { |
2081 | 204 | const BillOfMaterials& input_workers) { | 106 | const std::pair<std::string, std::string> names_to_amount = read_key_value_pair(*it, ':', "1"); |
2082 | 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); |
2083 | 206 | uint8_t count = 1; | 108 | uint8_t max_amount = 0; |
2084 | 207 | uint8_t count_max = 0; | 109 | std::set<std::pair<DescriptionIndex, WareWorker>> ware_worker_names; |
2085 | 208 | for (;;) { | 110 | for (const std::string& item_name : split_string(names_to_amount.first, ",")) { |
2086 | 209 | char const* ware = parameters; | 111 | // Try as ware |
2087 | 210 | while (*parameters && *parameters != ',' && *parameters != ':' && *parameters != ' ') | 112 | WareWorker type = wwWARE; |
2088 | 211 | ++parameters; | 113 | DescriptionIndex item_index = tribes.ware_index(item_name); |
2089 | 212 | char const terminator = *parameters; | 114 | if (!tribes.ware_exists(item_index)) { |
2090 | 213 | *parameters = '\0'; | 115 | item_index = tribes.worker_index(item_name); |
2091 | 214 | 116 | if (tribes.worker_exists(item_index)) { | |
2092 | 215 | // Try as ware | 117 | // It is a worker |
2093 | 216 | WareWorker type = wwWARE; | 118 | type = wwWORKER; |
2094 | 217 | const BillOfMaterials* input_list = &input_wares; | 119 | } else { |
2095 | 218 | DescriptionIndex ware_index = tribes.ware_index(ware); | 120 | throw GameDataError("Expected ware or worker type but found '%s'", item_name.c_str()); |
2096 | 219 | if (!tribes.ware_exists(ware_index)) { | 121 | } |
2097 | 220 | ware_index = tribes.worker_index(ware); | 122 | } |
2098 | 221 | if (tribes.worker_exists(ware_index)) { | 123 | |
2099 | 222 | // It is a worker | 124 | // Sanity checks |
2100 | 223 | type = wwWORKER; | 125 | bool found = false; |
2101 | 224 | input_list = &input_workers; | 126 | const BillOfMaterials& inputs = (type == wwWARE) ? descr.input_wares() : descr.input_workers(); |
2102 | 225 | } else { | 127 | for (const WareAmount& input : inputs) { |
2103 | 226 | throw GameDataError("Unknown ware or worker type \"%s\"", ware); | 128 | if (input.first == item_index) { |
2104 | 227 | } | 129 | max_amount += input.second; |
2105 | 228 | } | 130 | found = true; |
2106 | 229 | 131 | break; | |
2107 | 230 | bool found = false; | 132 | } |
2108 | 231 | for (const WareAmount& input : *input_list) { | 133 | } |
2109 | 232 | if (input.first == ware_index) { | 134 | if (!found) { |
2110 | 233 | count_max += input.second; | 135 | throw GameDataError("%s was not declared in the building's 'inputs' table", item_name.c_str()); |
2111 | 234 | found = true; | 136 | } |
2112 | 235 | break; | 137 | |
2113 | 236 | } | 138 | if (max_amount < amount) { |
2114 | 237 | } | 139 | throw GameDataError("Ware/worker count is %u but (total) input storage capacity of " |
2115 | 238 | if (!found) { | 140 | "the specified ware type(s) is only %u, so the ware/worker requirement can " |
2053 | 239 | throw GameDataError("%s is not declared as an input (\"%s=<count>\" was not " | ||
2054 | 240 | "found in the [inputs] section)", | ||
2055 | 241 | ware, ware); | ||
2056 | 242 | } | ||
2057 | 243 | |||
2058 | 244 | if (group.first.size() && ware_index <= group.first.begin()->first) | ||
2059 | 245 | throw GameDataError("wrong order of ware types within group: ware type %s appears " | ||
2060 | 246 | "after ware type %s (fix order!)", | ||
2061 | 247 | ware, | ||
2062 | 248 | tribes.get_ware_descr(group.first.begin()->first)->name().c_str()); | ||
2063 | 249 | last_insert_pos = group.first.insert(last_insert_pos, std::make_pair(ware_index, type)); | ||
2064 | 250 | *parameters = terminator; | ||
2065 | 251 | switch (terminator) { | ||
2066 | 252 | case ':': { | ||
2067 | 253 | ++parameters; | ||
2068 | 254 | char* endp; | ||
2069 | 255 | unsigned long long int const value = strtoull(parameters, &endp, 0); | ||
2070 | 256 | count = value; | ||
2071 | 257 | if ((*endp && *endp != ' ') || value < 1 || count != value) | ||
2072 | 258 | throw GameDataError("expected %s but found \"%s\"", "count", parameters); | ||
2073 | 259 | parameters = endp; | ||
2074 | 260 | if (count_max < count) | ||
2075 | 261 | throw GameDataError("group count is %u but (total) input storage capacity of " | ||
2076 | 262 | "the specified ware type(s) is only %u, so the group can " | ||
2116 | 263 | "never be fulfilled by the site", | 141 | "never be fulfilled by the site", |
2132 | 264 | count, count_max); | 142 | static_cast<unsigned int>(amount), static_cast<unsigned int>(max_amount)); |
2133 | 265 | } | 143 | } |
2134 | 266 | FALLS_THROUGH; | 144 | // Add item |
2135 | 267 | case '\0': | 145 | ware_worker_names.insert(std::make_pair(item_index, type)); |
2136 | 268 | case ' ': | 146 | } |
2137 | 269 | group.second = count; | 147 | // Add set |
2138 | 270 | return; | 148 | result.push_back(std::make_pair(ware_worker_names, amount)); |
2139 | 271 | case ',': | 149 | } |
2140 | 272 | ++parameters; | 150 | if (result.empty()) { |
2141 | 273 | break; | 151 | throw GameDataError("No wares or workers found"); |
2142 | 274 | default: | 152 | } |
2143 | 275 | // scan for terminator should ensure that this cannot happen | 153 | return result; |
2144 | 276 | NEVER_HERE(); | 154 | } |
2145 | 277 | } | 155 | |
2146 | 278 | } | 156 | |
2147 | 157 | BillOfMaterials ProductionProgram::parse_bill_of_materials(const std::vector<std::string>& arguments, | ||
2148 | 158 | WareWorker ww, | ||
2149 | 159 | const ProductionSiteDescr& descr, | ||
2150 | 160 | const Tribes& tribes) { | ||
2151 | 161 | BillOfMaterials result; | ||
2152 | 162 | for (const std::string& argument : arguments) { | ||
2153 | 163 | const std::pair<std::string, std::string> produceme = read_key_value_pair(argument, ':', "1"); | ||
2154 | 164 | |||
2155 | 165 | const DescriptionIndex index = ww == WareWorker::wwWARE ? | ||
2156 | 166 | tribes.safe_ware_index(produceme.first) : | ||
2157 | 167 | tribes.safe_worker_index(produceme.first); | ||
2158 | 168 | |||
2159 | 169 | // Verify the building outputs | ||
2160 | 170 | switch (ww) { | ||
2161 | 171 | case WareWorker::wwWARE: | ||
2162 | 172 | if (!descr.is_output_ware_type(index)) { | ||
2163 | 173 | throw GameDataError("Ware '%s' is not listed in the building's 'outputs' table", | ||
2164 | 174 | produceme.first.c_str()); | ||
2165 | 175 | } break; | ||
2166 | 176 | case WareWorker::wwWORKER: | ||
2167 | 177 | if (!descr.is_output_worker_type(index)) { | ||
2168 | 178 | throw GameDataError("Worker '%s' is not listed in the building's 'outputs' table", | ||
2169 | 179 | produceme.first.c_str()); | ||
2170 | 180 | } break; | ||
2171 | 181 | } | ||
2172 | 182 | |||
2173 | 183 | result.push_back(std::make_pair(index, read_positive(produceme.second))); | ||
2174 | 184 | } | ||
2175 | 185 | return result; | ||
2176 | 279 | } | 186 | } |
2177 | 280 | 187 | ||
2178 | 281 | ProductionProgram::ActReturn::Condition::~Condition() { | 188 | ProductionProgram::ActReturn::Condition::~Condition() { |
2179 | 282 | } | 189 | } |
2180 | 283 | 190 | ||
2181 | 191 | ProductionProgram::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) | ||
2182 | 192 | : operand(create_condition(arguments, begin, end, descr, tribes)) { | ||
2183 | 193 | } | ||
2184 | 194 | |||
2185 | 284 | ProductionProgram::ActReturn::Negation::~Negation() { | 195 | ProductionProgram::ActReturn::Negation::~Negation() { |
2186 | 285 | delete operand; | 196 | delete operand; |
2187 | 286 | } | 197 | } |
2188 | @@ -343,13 +254,14 @@ | |||
2189 | 343 | return result; | 254 | return result; |
2190 | 344 | } | 255 | } |
2191 | 345 | 256 | ||
2193 | 346 | ProductionProgram::ActReturn::SiteHas::SiteHas(char*& parameters, | 257 | ProductionProgram::ActReturn::SiteHas::SiteHas(std::vector<std::string>::const_iterator begin, |
2194 | 258 | std::vector<std::string>::const_iterator end, | ||
2195 | 347 | const ProductionSiteDescr& descr, | 259 | const ProductionSiteDescr& descr, |
2196 | 348 | const Tribes& tribes) { | 260 | const Tribes& tribes) { |
2197 | 349 | try { | 261 | try { |
2201 | 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(); |
2202 | 351 | } catch (const WException& e) { | 263 | } catch (const GameDataError& e) { |
2203 | 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()); |
2204 | 353 | } | 265 | } |
2205 | 354 | } | 266 | } |
2206 | 355 | bool ProductionProgram::ActReturn::SiteHas::evaluate(const ProductionSite& ps) const { | 267 | bool ProductionProgram::ActReturn::SiteHas::evaluate(const ProductionSite& ps) const { |
2207 | @@ -438,76 +350,89 @@ | |||
2208 | 438 | return _("the workers need no experience"); | 350 | return _("the workers need no experience"); |
2209 | 439 | } | 351 | } |
2210 | 440 | 352 | ||
2213 | 441 | ProductionProgram::ActReturn::Condition* ProductionProgram::ActReturn::create_condition( | 353 | ProductionProgram::ActReturn::Condition* ProductionProgram::ActReturn::create_condition(const std::vector<std::string>& arguments, |
2214 | 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) { |
2215 | 355 | if (begin == end) { | ||
2216 | 356 | throw GameDataError("Expected a condition after '%s'", (begin - 1)->c_str()); | ||
2217 | 357 | } | ||
2218 | 443 | try { | 358 | try { |
2228 | 444 | if (match_force_skip(parameters, "not")) | 359 | if (match_and_skip(arguments, begin, "not")) { |
2229 | 445 | return new ActReturn::Negation(parameters, descr, tribes); | 360 | return new ActReturn::Negation(arguments, begin, end, descr, tribes); |
2230 | 446 | else if (match_force_skip(parameters, "economy")) | 361 | } else if (match_and_skip(arguments, begin, "economy")) { |
2231 | 447 | return create_economy_condition(parameters, tribes); | 362 | if (!match_and_skip(arguments, begin, "needs")) { |
2232 | 448 | else if (match_force_skip(parameters, "site")) | 363 | throw GameDataError("Expected 'needs' after 'economy' but found '%s'", begin->c_str()); |
2233 | 449 | return create_site_condition(parameters, descr, tribes); | 364 | } |
2234 | 450 | else if (match_force_skip(parameters, "workers")) | 365 | return create_economy_condition(*begin, descr, tribes); |
2235 | 451 | return create_workers_condition(parameters); | 366 | } else if (match_and_skip(arguments, begin, "site")) { |
2236 | 452 | else | 367 | if (!match_and_skip(arguments, begin, "has")) { |
2237 | 368 | throw GameDataError("Expected 'has' after 'site' but found '%s'", begin->c_str()); | ||
2238 | 369 | } | ||
2239 | 370 | return new ProductionProgram::ActReturn::SiteHas(begin, end, descr, tribes); | ||
2240 | 371 | } else if (match_and_skip(arguments, begin, "workers")) { | ||
2241 | 372 | if (!match_and_skip(arguments, begin, "need")) { | ||
2242 | 373 | throw GameDataError("Expected 'need experience' after 'workers' but found '%s'", begin->c_str()); | ||
2243 | 374 | } | ||
2244 | 375 | if (!match_and_skip(arguments, begin, "experience")) { | ||
2245 | 376 | throw GameDataError("Expected 'experience' after 'workers need' but found '%s'", begin->c_str()); | ||
2246 | 377 | } | ||
2247 | 378 | return new ProductionProgram::ActReturn::WorkersNeedExperience(); | ||
2248 | 379 | } else { | ||
2249 | 453 | throw GameDataError( | 380 | throw GameDataError( |
2251 | 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()); |
2252 | 382 | } | ||
2253 | 455 | } catch (const WException& e) { | 383 | } catch (const WException& e) { |
2255 | 456 | throw GameDataError("invalid condition: %s", e.what()); | 384 | throw GameDataError("Invalid condition. %s", e.what()); |
2256 | 457 | } | 385 | } |
2257 | 458 | } | 386 | } |
2258 | 459 | 387 | ||
2260 | 460 | ProductionProgram::ActReturn::ActReturn(char* parameters, | 388 | ProductionProgram::ActReturn::ActReturn(const std::vector<std::string>& arguments, |
2261 | 461 | const ProductionSiteDescr& descr, | 389 | const ProductionSiteDescr& descr, |
2262 | 462 | const Tribes& tribes) { | 390 | const Tribes& tribes) { |
2311 | 463 | try { | 391 | if (arguments.empty()) { |
2312 | 464 | if (match(parameters, "failed")) | 392 | throw GameDataError("Usage: return=failed|completed|skipped|no_stats [when|unless <conditions>]"); |
2313 | 465 | result_ = ProgramResult::kFailed; | 393 | } |
2314 | 466 | else if (match(parameters, "completed")) | 394 | auto begin = arguments.begin(); |
2315 | 467 | result_ = ProgramResult::kCompleted; | 395 | |
2316 | 468 | else if (match(parameters, "skipped")) | 396 | if (match_and_skip(arguments, begin, "failed")) { |
2317 | 469 | result_ = ProgramResult::kSkipped; | 397 | result_ = ProgramResult::kFailed; |
2318 | 470 | else if (match(parameters, "no_stats")) | 398 | } else if (match_and_skip(arguments, begin, "completed")) { |
2319 | 471 | result_ = ProgramResult::kNone; | 399 | result_ = ProgramResult::kCompleted; |
2320 | 472 | else | 400 | } else if (match_and_skip(arguments, begin, "skipped")) { |
2321 | 473 | throw GameDataError("expected %s but found \"%s\"", | 401 | result_ = ProgramResult::kSkipped; |
2322 | 474 | "{\"failed\"|\"completed\"|\"skipped\"|\"no_stats\"}", parameters); | 402 | } else if (match_and_skip(arguments, begin, "no_stats")) { |
2323 | 475 | 403 | result_ = ProgramResult::kNone; | |
2324 | 476 | if (skip(parameters)) { | 404 | } else { |
2325 | 477 | if (match_force_skip(parameters, "when")) { | 405 | throw GameDataError("Usage: return=failed|completed|skipped|no_stats [when|unless <conditions>]"); |
2326 | 478 | is_when_ = true; | 406 | } |
2327 | 479 | for (;;) { | 407 | |
2328 | 480 | conditions_.push_back(create_condition(parameters, descr, tribes)); | 408 | |
2329 | 481 | if (*parameters) { | 409 | // Parse all arguments starting from the given iterator into our 'conditions_', splitting individual conditions by the given 'separator' |
2330 | 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) { |
2331 | 483 | if (!match_force_skip(parameters, "and")) | 411 | while (it != args.end()) { |
2332 | 484 | throw GameDataError("expected \"%s\" or end of input", "and"); | 412 | auto end = it + 1; |
2333 | 485 | } else | 413 | while (end != args.end() && *end != separator) { |
2334 | 486 | break; | 414 | ++end; |
2335 | 487 | } | 415 | } |
2336 | 488 | } else if (match_force_skip(parameters, "unless")) { | 416 | if (it == end) { |
2337 | 489 | is_when_ = false; | 417 | throw GameDataError("Expected: [%s] <condition> after '%s'", separator.c_str(), (it - 1)->c_str()); |
2338 | 490 | for (;;) { | 418 | } |
2339 | 491 | if (!*parameters) | 419 | |
2340 | 492 | throw GameDataError("expected condition at end of input"); | 420 | conditions_.push_back(create_condition(args, it, end, descr, tribes)); |
2341 | 493 | conditions_.push_back(create_condition(parameters, descr, tribes)); | 421 | match_and_skip(args, end, separator); |
2342 | 494 | if (*parameters) { | 422 | it = end; |
2343 | 495 | skip(parameters); | 423 | } |
2344 | 496 | if (!match_force_skip(parameters, "or")) | 424 | }; |
2345 | 497 | throw GameDataError("expected \"%s\" or end of input", "or"); | 425 | |
2346 | 498 | } else | 426 | is_when_ = true; |
2347 | 499 | break; | 427 | if (begin != arguments.end()) { |
2348 | 500 | } | 428 | if (match_and_skip(arguments, begin, "when")) { |
2349 | 501 | } else | 429 | parse_conditions(arguments, begin, "and"); |
2350 | 502 | throw GameDataError( | 430 | } else if (match_and_skip(arguments, begin, "unless")) { |
2351 | 503 | "expected %s but found \"%s\"", "{\"when\"|\"unless\"}", parameters); | 431 | is_when_ = false; |
2352 | 504 | } else if (*parameters) | 432 | parse_conditions(arguments, begin, "or"); |
2353 | 505 | throw GameDataError("expected %s but found \"%s\"", ("space or end of input"), parameters); | 433 | } else { |
2354 | 506 | else | 434 | throw GameDataError("Expected when|unless but found '%s'", begin->c_str()); |
2355 | 507 | is_when_ = true; | 435 | } |
2308 | 508 | |||
2309 | 509 | } catch (const WException& e) { | ||
2310 | 510 | throw GameDataError("return: %s", e.what()); | ||
2356 | 511 | } | 436 | } |
2357 | 512 | } | 437 | } |
2358 | 513 | 438 | ||
2359 | @@ -573,7 +498,11 @@ | |||
2360 | 573 | return ps.program_end(game, result_); | 498 | return ps.program_end(game, result_); |
2361 | 574 | } | 499 | } |
2362 | 575 | 500 | ||
2364 | 576 | ProductionProgram::ActCall::ActCall(char* parameters, const ProductionSiteDescr& descr) { | 501 | ProductionProgram::ActCall::ActCall(const std::vector<std::string>& arguments, const ProductionSiteDescr& descr) { |
2365 | 502 | if (arguments.size() < 1 || arguments.size() > 4) { | ||
2366 | 503 | throw GameDataError("Usage: call=<program name> [on failure|completion|skip fail|complete|skip|repeat]"); | ||
2367 | 504 | } | ||
2368 | 505 | |||
2369 | 577 | // Initialize with default handling methods. | 506 | // Initialize with default handling methods. |
2370 | 578 | handling_methods_[program_result_index(ProgramResult::kFailed)] = | 507 | handling_methods_[program_result_index(ProgramResult::kFailed)] = |
2371 | 579 | ProgramResultHandlingMethod::kContinue; | 508 | ProgramResultHandlingMethod::kContinue; |
2372 | @@ -582,61 +511,60 @@ | |||
2373 | 582 | handling_methods_[program_result_index(ProgramResult::kSkipped)] = | 511 | handling_methods_[program_result_index(ProgramResult::kSkipped)] = |
2374 | 583 | ProgramResultHandlingMethod::kContinue; | 512 | ProgramResultHandlingMethod::kContinue; |
2375 | 584 | 513 | ||
2431 | 585 | try { | 514 | // Fetch program to call |
2432 | 586 | bool reached_end; | 515 | const std::string& program_name = arguments.front(); |
2433 | 587 | { | 516 | const ProductionSiteDescr::Programs& programs = descr.programs(); |
2434 | 588 | char const* const program_name = next_word(parameters, reached_end); | 517 | ProductionSiteDescr::Programs::const_iterator const it = programs.find(program_name); |
2435 | 589 | const ProductionSiteDescr::Programs& programs = descr.programs(); | 518 | if (it == programs.end()) { |
2436 | 590 | ProductionSiteDescr::Programs::const_iterator const it = programs.find(program_name); | 519 | throw GameDataError("The program '%s' has not (yet) been declared in %s " |
2437 | 591 | if (it == programs.end()) | 520 | "(wrong declaration order?)", |
2438 | 592 | throw GameDataError("the program \"%s\" has not (yet) been declared in %s " | 521 | program_name.c_str(), descr.name().c_str()); |
2439 | 593 | "(wrong declaration order?)", | 522 | } |
2440 | 594 | program_name, descr.name().c_str()); | 523 | program_ = it->second.get(); |
2441 | 595 | program_ = it->second.get(); | 524 | |
2442 | 596 | } | 525 | // Override with specified handling methods. |
2443 | 597 | 526 | if (arguments.size() > 1) { | |
2444 | 598 | // Override with specified handling methods. | 527 | if (arguments.at(1) != "on") { |
2445 | 599 | while (!reached_end) { | 528 | throw GameDataError("Expected 'on' keyword in second position"); |
2446 | 600 | skip(parameters); | 529 | } |
2447 | 601 | match_force_skip(parameters, "on"); | 530 | |
2448 | 602 | 531 | ProgramResult result_to_set_method_for; | |
2449 | 603 | ProgramResult result_to_set_method_for; | 532 | if (arguments.at(2) == "failure") { |
2450 | 604 | if (match_force_skip(parameters, "failure")) { | 533 | if (handling_methods_[program_result_index(ProgramResult::kFailed)] != |
2451 | 605 | if (handling_methods_[program_result_index(ProgramResult::kFailed)] != | 534 | ProgramResultHandlingMethod::kContinue) { |
2452 | 606 | ProgramResultHandlingMethod::kContinue) | 535 | throw GameDataError("%s handling method already defined", "failure"); |
2453 | 607 | throw GameDataError("%s handling method already defined", "failure"); | 536 | } |
2454 | 608 | result_to_set_method_for = ProgramResult::kFailed; | 537 | result_to_set_method_for = ProgramResult::kFailed; |
2455 | 609 | } else if (match_force_skip(parameters, "completion")) { | 538 | } else if (arguments.at(2) == "completion") { |
2456 | 610 | if (handling_methods_[program_result_index(ProgramResult::kCompleted)] != | 539 | if (handling_methods_[program_result_index(ProgramResult::kCompleted)] != |
2457 | 611 | ProgramResultHandlingMethod::kContinue) | 540 | ProgramResultHandlingMethod::kContinue) { |
2458 | 612 | throw GameDataError("%s handling method already defined", "completion"); | 541 | throw GameDataError("%s handling method already defined", "completion"); |
2459 | 613 | result_to_set_method_for = ProgramResult::kCompleted; | 542 | } |
2460 | 614 | } else if (match_force_skip(parameters, "skip")) { | 543 | result_to_set_method_for = ProgramResult::kCompleted; |
2461 | 615 | if (handling_methods_[program_result_index(ProgramResult::kSkipped)] != | 544 | } else if (arguments.at(2) == "skip") { |
2462 | 616 | ProgramResultHandlingMethod::kContinue) | 545 | if (handling_methods_[program_result_index(ProgramResult::kSkipped)] != |
2463 | 617 | throw GameDataError("%s handling method already defined", "skip"); | 546 | ProgramResultHandlingMethod::kContinue) { |
2464 | 618 | result_to_set_method_for = ProgramResult::kSkipped; | 547 | throw GameDataError("%s handling method already defined", "skip"); |
2465 | 619 | } else | 548 | } |
2466 | 620 | throw GameDataError( | 549 | result_to_set_method_for = ProgramResult::kSkipped; |
2467 | 621 | "expected %s but found \"%s\"", "{\"failure\"|\"completion\"|\"skip\"}", parameters); | 550 | } else { |
2468 | 622 | 551 | throw GameDataError( | |
2469 | 623 | ProgramResultHandlingMethod handling_method; | 552 | "Expected failure|completion|skip after 'on' but found '%s'", arguments.at(2).c_str()); |
2470 | 624 | if (match(parameters, "fail")) | 553 | } |
2471 | 625 | handling_method = ProgramResultHandlingMethod::kFail; | 554 | |
2472 | 626 | else if (match(parameters, "complete")) | 555 | ProgramResultHandlingMethod handling_method; |
2473 | 627 | handling_method = ProgramResultHandlingMethod::kComplete; | 556 | if (arguments.at(3) == "fail") { |
2474 | 628 | else if (match(parameters, "skip")) | 557 | handling_method = ProgramResultHandlingMethod::kFail; |
2475 | 629 | handling_method = ProgramResultHandlingMethod::kSkip; | 558 | } else if (arguments.at(3) == "complete") { |
2476 | 630 | else if (match(parameters, "repeat")) | 559 | handling_method = ProgramResultHandlingMethod::kComplete; |
2477 | 631 | handling_method = ProgramResultHandlingMethod::kRepeat; | 560 | } else if (arguments.at(3) == "skip") { |
2478 | 632 | else | 561 | handling_method = ProgramResultHandlingMethod::kSkip; |
2479 | 633 | throw GameDataError("expected %s but found \"%s\"", | 562 | } else if (arguments.at(3) == "repeat") { |
2480 | 634 | "{\"fail\"|\"complete\"|\"skip\"|\"repeat\"}", parameters); | 563 | handling_method = ProgramResultHandlingMethod::kRepeat; |
2481 | 635 | handling_methods_[program_result_index(result_to_set_method_for)] = handling_method; | 564 | } else { |
2482 | 636 | reached_end = !*parameters; | 565 | throw GameDataError("Expected fail|complete|skip|repeat in final position but found '%s'", arguments.at(3).c_str()); |
2483 | 637 | } | 566 | } |
2484 | 638 | } catch (const WException& e) { | 567 | handling_methods_[program_result_index(result_to_set_method_for)] = handling_method; |
2430 | 639 | throw GameDataError("call: %s", e.what()); | ||
2485 | 640 | } | 568 | } |
2486 | 641 | } | 569 | } |
2487 | 642 | 570 | ||
2488 | @@ -662,38 +590,38 @@ | |||
2489 | 662 | } | 590 | } |
2490 | 663 | } | 591 | } |
2491 | 664 | 592 | ||
2493 | 665 | ProductionProgram::ActCallWorker::ActCallWorker(char* parameters, | 593 | ProductionProgram::ActCallWorker::ActCallWorker(const std::vector<std::string>& arguments, |
2494 | 666 | const std::string& production_program_name, | 594 | const std::string& production_program_name, |
2495 | 667 | ProductionSiteDescr* descr, | 595 | ProductionSiteDescr* descr, |
2496 | 668 | const Tribes& tribes) { | 596 | const Tribes& tribes) { |
2522 | 669 | try { | 597 | if (arguments.size() != 1) { |
2523 | 670 | program_ = parameters; | 598 | throw GameDataError("Usage: callworker=<worker program name>"); |
2524 | 671 | 599 | } | |
2525 | 672 | // Quote form "void ProductionSite::program_act(Game &)": | 600 | |
2526 | 673 | // "Always main worker is doing stuff" | 601 | program_ = arguments.front(); |
2527 | 674 | const WorkerDescr& main_worker_descr = | 602 | |
2528 | 675 | *tribes.get_worker_descr(descr->working_positions()[0].first); | 603 | // Quote from "void ProductionSite::program_act(Game &)": |
2529 | 676 | 604 | // "Always main worker is doing stuff" | |
2530 | 677 | // This will fail unless the main worker has a program with the given | 605 | const WorkerDescr& main_worker_descr = |
2531 | 678 | // name, so it also validates the parameter. | 606 | *tribes.get_worker_descr(descr->working_positions().front().first); |
2532 | 679 | const WorkareaInfo& worker_workarea_info = | 607 | |
2533 | 680 | main_worker_descr.get_program(program_)->get_workarea_info(); | 608 | // This will fail unless the main worker has a program with the given |
2534 | 681 | 609 | // name, so it also validates the parameter. | |
2535 | 682 | for (const auto& area_info : worker_workarea_info) { | 610 | const WorkareaInfo& worker_workarea_info = |
2536 | 683 | std::set<std::string>& building_radius_infos = descr->workarea_info_[area_info.first]; | 611 | main_worker_descr.get_program(program_)->get_workarea_info(); |
2537 | 684 | 612 | ||
2538 | 685 | for (const std::string& worker_name : area_info.second) { | 613 | for (const auto& area_info : worker_workarea_info) { |
2539 | 686 | std::string description = descr->name(); | 614 | std::set<std::string>& building_radius_infos = descr->workarea_info_[area_info.first]; |
2540 | 687 | description += ' '; | 615 | |
2541 | 688 | description += production_program_name; | 616 | for (const std::string& worker_name : area_info.second) { |
2542 | 689 | description += " worker "; | 617 | std::string description = descr->name(); |
2543 | 690 | description += main_worker_descr.name(); | 618 | description += ' '; |
2544 | 691 | description += worker_name; | 619 | description += production_program_name; |
2545 | 692 | building_radius_infos.insert(description); | 620 | description += " worker "; |
2546 | 693 | } | 621 | description += main_worker_descr.name(); |
2547 | 622 | description += worker_name; | ||
2548 | 623 | building_radius_infos.insert(description); | ||
2549 | 694 | } | 624 | } |
2550 | 695 | } catch (const WException& e) { | ||
2551 | 696 | throw GameDataError("worker: %s", e.what()); | ||
2552 | 697 | } | 625 | } |
2553 | 698 | } | 626 | } |
2554 | 699 | 627 | ||
2555 | @@ -722,42 +650,27 @@ | |||
2556 | 722 | psite.program_end(game, ProgramResult::kFailed); | 650 | psite.program_end(game, ProgramResult::kFailed); |
2557 | 723 | } | 651 | } |
2558 | 724 | 652 | ||
2571 | 725 | ProductionProgram::ActSleep::ActSleep(char* parameters) { | 653 | ProductionProgram::ActSleep::ActSleep(const std::vector<std::string>& arguments) { |
2572 | 726 | try { | 654 | if (arguments.size() != 1) { |
2573 | 727 | if (*parameters) { | 655 | throw GameDataError("Usage: sleep=<duration>"); |
2562 | 728 | char* endp; | ||
2563 | 729 | long long int const value = strtoll(parameters, &endp, 0); | ||
2564 | 730 | duration_ = value; | ||
2565 | 731 | if (*endp || value <= 0 || duration_ != value) | ||
2566 | 732 | throw GameDataError("expected %s but found \"%s\"", "duration in ms", parameters); | ||
2567 | 733 | } else | ||
2568 | 734 | duration_ = 0; // Get duration from the result of a previous action. | ||
2569 | 735 | } catch (const WException& e) { | ||
2570 | 736 | throw GameDataError("sleep: %s", e.what()); | ||
2574 | 737 | } | 656 | } |
2575 | 657 | duration_ = read_positive(arguments.front()); | ||
2576 | 738 | } | 658 | } |
2577 | 739 | 659 | ||
2578 | 740 | void ProductionProgram::ActSleep::execute(Game& game, ProductionSite& ps) const { | 660 | void ProductionProgram::ActSleep::execute(Game& game, ProductionSite& ps) const { |
2579 | 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); |
2580 | 742 | } | 662 | } |
2581 | 743 | 663 | ||
2593 | 744 | ProductionProgram::ActCheckMap::ActCheckMap(char* parameters) { | 664 | ProductionProgram::ActCheckMap::ActCheckMap(const std::vector<std::string>& arguments) { |
2594 | 745 | try { | 665 | if (arguments.size() != 1 || arguments.front() != "seafaring") { |
2595 | 746 | if (*parameters) { | 666 | throw GameDataError("Usage: checkmap=seafaring"); |
2585 | 747 | if (!strcmp(parameters, "seafaring")) | ||
2586 | 748 | feature_ = SEAFARING; | ||
2587 | 749 | else | ||
2588 | 750 | throw GameDataError("Unknown parameter \"%s\"", parameters); | ||
2589 | 751 | } else | ||
2590 | 752 | throw GameDataError("No parameter given!"); | ||
2591 | 753 | } catch (const WException& e) { | ||
2592 | 754 | throw GameDataError("sleep: %s", e.what()); | ||
2596 | 755 | } | 667 | } |
2597 | 668 | feature_ = Feature::kSeafaring; | ||
2598 | 756 | } | 669 | } |
2599 | 757 | 670 | ||
2600 | 758 | void ProductionProgram::ActCheckMap::execute(Game& game, ProductionSite& ps) const { | 671 | void ProductionProgram::ActCheckMap::execute(Game& game, ProductionSite& ps) const { |
2601 | 759 | switch (feature_) { | 672 | switch (feature_) { |
2603 | 760 | case SEAFARING: { | 673 | case Feature::kSeafaring: { |
2604 | 761 | if (game.map().allows_seafaring()) { | 674 | if (game.map().allows_seafaring()) { |
2605 | 762 | return ps.program_step(game, 0); | 675 | return ps.program_step(game, 0); |
2606 | 763 | } else { | 676 | } else { |
2607 | @@ -765,57 +678,26 @@ | |||
2608 | 765 | return ps.program_end(game, ProgramResult::kFailed); | 678 | return ps.program_end(game, ProgramResult::kFailed); |
2609 | 766 | } | 679 | } |
2610 | 767 | } | 680 | } |
2611 | 768 | default: | ||
2612 | 769 | NEVER_HERE(); | ||
2613 | 770 | } | 681 | } |
2614 | 682 | NEVER_HERE(); | ||
2615 | 771 | } | 683 | } |
2616 | 772 | 684 | ||
2638 | 773 | ProductionProgram::ActAnimate::ActAnimate(char* parameters, ProductionSiteDescr* descr) { | 685 | ProductionProgram::ActAnimate::ActAnimate(const std::vector<std::string>& arguments, ProductionSiteDescr* descr) { |
2639 | 774 | try { | 686 | parameters = MapObjectProgram::parse_act_animate(arguments, *descr, false); |
2619 | 775 | bool reached_end; | ||
2620 | 776 | animation_name_ = std::string(next_word(parameters, reached_end)); | ||
2621 | 777 | if (animation_name_ == "idle") { | ||
2622 | 778 | throw GameDataError("idle animation is default; calling is not allowed"); | ||
2623 | 779 | } | ||
2624 | 780 | if (!descr->is_animation_known(animation_name_)) { | ||
2625 | 781 | throw GameDataError("Unknown animation '%s'", animation_name_.c_str()); | ||
2626 | 782 | } | ||
2627 | 783 | if (!reached_end) { // The next parameter is the duration. | ||
2628 | 784 | char* endp; | ||
2629 | 785 | long long int const value = strtoll(parameters, &endp, 0); | ||
2630 | 786 | duration_ = value; | ||
2631 | 787 | if (*endp || value <= 0 || duration_ != value) | ||
2632 | 788 | throw GameDataError("expected %s but found \"%s\"", "duration in ms", parameters); | ||
2633 | 789 | } else | ||
2634 | 790 | duration_ = 0; // Get duration from the result of a previous action. | ||
2635 | 791 | } catch (const WException& e) { | ||
2636 | 792 | throw GameDataError("animate: %s", e.what()); | ||
2637 | 793 | } | ||
2640 | 794 | } | 687 | } |
2641 | 795 | 688 | ||
2642 | 796 | void ProductionProgram::ActAnimate::execute(Game& game, ProductionSite& ps) const { | 689 | void ProductionProgram::ActAnimate::execute(Game& game, ProductionSite& ps) const { |
2645 | 797 | ps.start_animation(game, ps.descr().get_animation(animation_name_, &ps)); | 690 | ps.start_animation(game, parameters.animation); |
2646 | 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); |
2647 | 799 | } | 692 | } |
2648 | 800 | 693 | ||
2650 | 801 | ProductionProgram::ActConsume::ActConsume(char* parameters, | 694 | ProductionProgram::ActConsume::ActConsume(const std::vector<std::string>& arguments, |
2651 | 802 | const ProductionSiteDescr& descr, | 695 | const ProductionSiteDescr& descr, |
2652 | 803 | const Tribes& tribes) { | 696 | const Tribes& tribes) { |
2667 | 804 | try { | 697 | if (arguments.empty()) { |
2668 | 805 | for (;;) { | 698 | throw GameDataError("Usage: consume=<ware or worker>[,<ware or worker>[,...]][:<amount>] ..."); |
2655 | 806 | consumed_wares_workers_.resize(consumed_wares_workers_.size() + 1); | ||
2656 | 807 | parse_ware_type_group(parameters, *consumed_wares_workers_.rbegin(), tribes, | ||
2657 | 808 | descr.input_wares(), descr.input_workers()); | ||
2658 | 809 | if (!*parameters) | ||
2659 | 810 | break; | ||
2660 | 811 | force_skip(parameters); | ||
2661 | 812 | } | ||
2662 | 813 | if (consumed_wares_workers_.empty()) { | ||
2663 | 814 | throw GameDataError("expected ware_type1[,ware_type2[,...]][:N] ..."); | ||
2664 | 815 | } | ||
2665 | 816 | } catch (const WException& e) { | ||
2666 | 817 | throw GameDataError("consume: %s", e.what()); | ||
2669 | 818 | } | 699 | } |
2670 | 700 | consumed_wares_workers_ = parse_ware_type_groups(arguments.begin(), arguments.end(), descr, tribes); | ||
2671 | 819 | } | 701 | } |
2672 | 820 | 702 | ||
2673 | 821 | void ProductionProgram::ActConsume::execute(Game& game, ProductionSite& ps) const { | 703 | void ProductionProgram::ActConsume::execute(Game& game, ProductionSite& ps) const { |
2674 | @@ -938,47 +820,13 @@ | |||
2675 | 938 | } | 820 | } |
2676 | 939 | } | 821 | } |
2677 | 940 | 822 | ||
2679 | 941 | ProductionProgram::ActProduce::ActProduce(char* parameters, | 823 | ProductionProgram::ActProduce::ActProduce(const std::vector<std::string>& arguments, |
2680 | 942 | const ProductionSiteDescr& descr, | 824 | const ProductionSiteDescr& descr, |
2681 | 943 | const Tribes& tribes) { | 825 | const Tribes& tribes) { |
2719 | 944 | try { | 826 | if (arguments.empty()) { |
2720 | 945 | for (bool more = true; more; ++parameters) { | 827 | throw GameDataError("Usage: produce=<ware name>[:<amount>] [<ware name>[:<amount>]...]"); |
2684 | 946 | produced_wares_.resize(produced_wares_.size() + 1); | ||
2685 | 947 | WareAmount& item = *produced_wares_.rbegin(); | ||
2686 | 948 | skip(parameters); | ||
2687 | 949 | char const* ware = parameters; | ||
2688 | 950 | for (;; ++parameters) { | ||
2689 | 951 | switch (*parameters) { | ||
2690 | 952 | default: | ||
2691 | 953 | break; | ||
2692 | 954 | case '\0': | ||
2693 | 955 | case ' ': | ||
2694 | 956 | item.second = 1; | ||
2695 | 957 | goto item_end; | ||
2696 | 958 | case ':': { | ||
2697 | 959 | *parameters = '\0'; | ||
2698 | 960 | ++parameters; | ||
2699 | 961 | char* endp; | ||
2700 | 962 | unsigned long long int const value = strtoull(parameters, &endp, 0); | ||
2701 | 963 | item.second = value; | ||
2702 | 964 | if ((*endp && *endp != ' ') || value < 1 || item.second != value) | ||
2703 | 965 | throw GameDataError("expected %s but found \"%s\"", "count", parameters); | ||
2704 | 966 | parameters = endp; | ||
2705 | 967 | goto item_end; | ||
2706 | 968 | } | ||
2707 | 969 | } | ||
2708 | 970 | } | ||
2709 | 971 | item_end: | ||
2710 | 972 | more = *parameters != '\0'; | ||
2711 | 973 | *parameters = '\0'; | ||
2712 | 974 | if (!descr.is_output_ware_type(item.first = tribes.safe_ware_index(ware))) | ||
2713 | 975 | throw GameDataError("%s is not declared as an output (\"%s\" was not " | ||
2714 | 976 | "found in the \"outputs\" table)", | ||
2715 | 977 | ware, ware); | ||
2716 | 978 | } | ||
2717 | 979 | } catch (const WException& e) { | ||
2718 | 980 | throw GameDataError("produce: %s", e.what()); | ||
2721 | 981 | } | 828 | } |
2722 | 829 | produced_wares_ = parse_bill_of_materials(arguments, WareWorker::wwWARE, descr, tribes); | ||
2723 | 982 | } | 830 | } |
2724 | 983 | 831 | ||
2725 | 984 | void ProductionProgram::ActProduce::execute(Game& game, ProductionSite& ps) const { | 832 | void ProductionProgram::ActProduce::execute(Game& game, ProductionSite& ps) const { |
2726 | @@ -1024,47 +872,13 @@ | |||
2727 | 1024 | return false; | 872 | return false; |
2728 | 1025 | } | 873 | } |
2729 | 1026 | 874 | ||
2731 | 1027 | ProductionProgram::ActRecruit::ActRecruit(char* parameters, | 875 | ProductionProgram::ActRecruit::ActRecruit(const std::vector<std::string>& arguments, |
2732 | 1028 | const ProductionSiteDescr& descr, | 876 | const ProductionSiteDescr& descr, |
2733 | 1029 | const Tribes& tribes) { | 877 | const Tribes& tribes) { |
2771 | 1030 | try { | 878 | if (arguments.empty()) { |
2772 | 1031 | for (bool more = true; more; ++parameters) { | 879 | throw GameDataError("Usage: recruit=<worker name>[:<amount>] [<worker name>[:<amount>]...]"); |
2736 | 1032 | recruited_workers_.resize(recruited_workers_.size() + 1); | ||
2737 | 1033 | WareAmount& item = *recruited_workers_.rbegin(); | ||
2738 | 1034 | skip(parameters); | ||
2739 | 1035 | char const* worker = parameters; | ||
2740 | 1036 | for (;; ++parameters) { | ||
2741 | 1037 | switch (*parameters) { | ||
2742 | 1038 | default: | ||
2743 | 1039 | break; | ||
2744 | 1040 | case '\0': | ||
2745 | 1041 | case ' ': | ||
2746 | 1042 | item.second = 1; | ||
2747 | 1043 | goto item_end; | ||
2748 | 1044 | case ':': { | ||
2749 | 1045 | *parameters = '\0'; | ||
2750 | 1046 | ++parameters; | ||
2751 | 1047 | char* endp; | ||
2752 | 1048 | unsigned long long int const value = strtoull(parameters, &endp, 0); | ||
2753 | 1049 | item.second = value; | ||
2754 | 1050 | if ((*endp && *endp != ' ') || value < 1 || item.second != value) | ||
2755 | 1051 | throw GameDataError("expected %s but found \"%s\"", "count", parameters); | ||
2756 | 1052 | parameters = endp; | ||
2757 | 1053 | goto item_end; | ||
2758 | 1054 | } | ||
2759 | 1055 | } | ||
2760 | 1056 | } | ||
2761 | 1057 | item_end: | ||
2762 | 1058 | more = *parameters != '\0'; | ||
2763 | 1059 | *parameters = '\0'; | ||
2764 | 1060 | if (!descr.is_output_worker_type(item.first = tribes.safe_worker_index(worker))) | ||
2765 | 1061 | throw GameDataError("%s is not declared as an output (\"%s\" was not " | ||
2766 | 1062 | "found in the \"outputs\" table)", | ||
2767 | 1063 | worker, worker); | ||
2768 | 1064 | } | ||
2769 | 1065 | } catch (const WException& e) { | ||
2770 | 1066 | throw GameDataError("recruit: %s", e.what()); | ||
2773 | 1067 | } | 880 | } |
2774 | 881 | recruited_workers_ = parse_bill_of_materials(arguments, WareWorker::wwWORKER, descr, tribes); | ||
2775 | 1068 | } | 882 | } |
2776 | 1069 | 883 | ||
2777 | 1070 | void ProductionProgram::ActRecruit::execute(Game& game, ProductionSite& ps) const { | 884 | void ProductionProgram::ActRecruit::execute(Game& game, ProductionSite& ps) const { |
2778 | @@ -1106,55 +920,23 @@ | |||
2779 | 1106 | return false; | 920 | return false; |
2780 | 1107 | } | 921 | } |
2781 | 1108 | 922 | ||
2783 | 1109 | ProductionProgram::ActMine::ActMine(char* parameters, | 923 | ProductionProgram::ActMine::ActMine(const std::vector<std::string>& arguments, |
2784 | 1110 | const World& world, | 924 | const World& world, |
2785 | 1111 | const std::string& production_program_name, | 925 | const std::string& production_program_name, |
2786 | 1112 | ProductionSiteDescr* descr) { | 926 | ProductionSiteDescr* descr) { |
2831 | 1113 | try { | 927 | if (arguments.size() != 5) { |
2832 | 1114 | bool reached_end; | 928 | throw GameDataError("Usage: mine=resource <workarea radius> <max> <chance> <worker experience gained>"); |
2789 | 1115 | resource_ = world.safe_resource_index(next_word(parameters, reached_end)); | ||
2790 | 1116 | |||
2791 | 1117 | { | ||
2792 | 1118 | char* endp; | ||
2793 | 1119 | unsigned long long int const value = strtoull(parameters, &endp, 0); | ||
2794 | 1120 | distance_ = value; | ||
2795 | 1121 | if (*endp != ' ' || distance_ != value) | ||
2796 | 1122 | throw GameDataError("expected %s but found \"%s\"", "distance", parameters); | ||
2797 | 1123 | parameters = endp; | ||
2798 | 1124 | } | ||
2799 | 1125 | |||
2800 | 1126 | { | ||
2801 | 1127 | char* endp; | ||
2802 | 1128 | unsigned long long int const value = strtoull(parameters, &endp, 0); | ||
2803 | 1129 | max_ = value; | ||
2804 | 1130 | if (*endp != ' ' || value < 1 || 100 < value) | ||
2805 | 1131 | throw GameDataError("expected %s but found \"%s\"", "percentage", parameters); | ||
2806 | 1132 | parameters = endp; | ||
2807 | 1133 | } | ||
2808 | 1134 | |||
2809 | 1135 | { | ||
2810 | 1136 | char* endp; | ||
2811 | 1137 | unsigned long long int const value = strtoull(parameters, &endp, 0); | ||
2812 | 1138 | chance_ = value; | ||
2813 | 1139 | if (*endp != ' ' || value < 1 || 100 < value) | ||
2814 | 1140 | throw GameDataError("expected %s but found \"%s\"", "percentage", parameters); | ||
2815 | 1141 | parameters = endp; | ||
2816 | 1142 | } | ||
2817 | 1143 | { | ||
2818 | 1144 | char* endp; | ||
2819 | 1145 | unsigned long long int const value = strtoull(parameters, &endp, 0); | ||
2820 | 1146 | training_ = value; | ||
2821 | 1147 | if (*endp || value < 1 || 100 < value) | ||
2822 | 1148 | throw GameDataError("expected %s but found \"%s\"", "percentage", parameters); | ||
2823 | 1149 | } | ||
2824 | 1150 | std::string description = (boost::format("%1$s %2$s mine %3$s") % descr->name() % | ||
2825 | 1151 | production_program_name % world.get_resource(resource_)->name()) | ||
2826 | 1152 | .str(); | ||
2827 | 1153 | |||
2828 | 1154 | descr->workarea_info_[distance_].insert(description); | ||
2829 | 1155 | } catch (const WException& e) { | ||
2830 | 1156 | throw GameDataError("mine: %s", e.what()); | ||
2833 | 1157 | } | 929 | } |
2834 | 930 | |||
2835 | 931 | resource_ = world.safe_resource_index(arguments.front().c_str()); | ||
2836 | 932 | distance_ = read_positive(arguments.at(1)); | ||
2837 | 933 | max_ = read_positive(arguments.at(2)); | ||
2838 | 934 | chance_ = read_positive(arguments.at(3)); | ||
2839 | 935 | training_ = read_positive(arguments.at(4)); | ||
2840 | 936 | |||
2841 | 937 | const std::string description = descr->name() + " " + | ||
2842 | 938 | production_program_name + " mine " + world.get_resource(resource_)->name(); | ||
2843 | 939 | descr->workarea_info_[distance_].insert(description); | ||
2844 | 1158 | } | 940 | } |
2845 | 1159 | 941 | ||
2846 | 1160 | void ProductionProgram::ActMine::execute(Game& game, ProductionSite& ps) const { | 942 | void ProductionProgram::ActMine::execute(Game& game, ProductionSite& ps) const { |
2847 | @@ -1268,32 +1050,16 @@ | |||
2848 | 1268 | return ps.program_step(game); | 1050 | return ps.program_step(game); |
2849 | 1269 | } | 1051 | } |
2850 | 1270 | 1052 | ||
2868 | 1271 | ProductionProgram::ActCheckSoldier::ActCheckSoldier(char* parameters) { | 1053 | ProductionProgram::ActCheckSoldier::ActCheckSoldier(const std::vector<std::string>& arguments) { |
2869 | 1272 | // TODO(unknown): This is currently hardcoded for "soldier", but should allow any | 1054 | if (arguments.size() != 3) { |
2870 | 1273 | // soldier type name. | 1055 | throw GameDataError("Usage: checksoldier=soldier <training attribute> <level>"); |
2871 | 1274 | if (!match_force_skip(parameters, "soldier")) | 1056 | } |
2855 | 1275 | throw GameDataError("expected %s but found \"%s\"", "soldier type", parameters); | ||
2856 | 1276 | try { | ||
2857 | 1277 | if (match_force_skip(parameters, "health")) | ||
2858 | 1278 | attribute = TrainingAttribute::kHealth; | ||
2859 | 1279 | else if (match_force_skip(parameters, "attack")) | ||
2860 | 1280 | attribute = TrainingAttribute::kAttack; | ||
2861 | 1281 | else if (match_force_skip(parameters, "defense")) | ||
2862 | 1282 | attribute = TrainingAttribute::kDefense; | ||
2863 | 1283 | else if (match_force_skip(parameters, "evade")) | ||
2864 | 1284 | attribute = TrainingAttribute::kEvade; | ||
2865 | 1285 | else | ||
2866 | 1286 | throw GameDataError("expected %s but found \"%s\"", | ||
2867 | 1287 | "{\"health\"|\"attack\"|\"defense\"|\"evade\"}", parameters); | ||
2872 | 1288 | 1057 | ||
2880 | 1289 | char* endp; | 1058 | if (arguments.front() != "soldier") { |
2881 | 1290 | unsigned long long int const value = strtoull(parameters, &endp, 0); | 1059 | throw GameDataError("Expected 'soldier' but found '%s'", arguments.front().c_str()); |
2875 | 1291 | level = value; | ||
2876 | 1292 | if (*endp || level != value) | ||
2877 | 1293 | throw GameDataError("expected %s but found \"%s\"", "level", parameters); | ||
2878 | 1294 | } catch (const WException& e) { | ||
2879 | 1295 | throw GameDataError("checksoldier: %s", e.what()); | ||
2882 | 1296 | } | 1060 | } |
2883 | 1061 | attribute_ = parse_training_attribute(arguments.at(1)); | ||
2884 | 1062 | level_ = read_int(arguments.at(2), 0); | ||
2885 | 1297 | } | 1063 | } |
2886 | 1298 | 1064 | ||
2887 | 1299 | void ProductionProgram::ActCheckSoldier::execute(Game& game, ProductionSite& ps) const { | 1065 | void ProductionProgram::ActCheckSoldier::execute(Game& game, ProductionSite& ps) const { |
2888 | @@ -1304,8 +1070,8 @@ | |||
2889 | 1304 | ps.set_production_result(_("No soldier to train!")); | 1070 | ps.set_production_result(_("No soldier to train!")); |
2890 | 1305 | return ps.program_end(game, ProgramResult::kSkipped); | 1071 | return ps.program_end(game, ProgramResult::kSkipped); |
2891 | 1306 | } | 1072 | } |
2894 | 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_), |
2895 | 1308 | static_cast<unsigned int>(level)); | 1074 | static_cast<unsigned int>(level_)); |
2896 | 1309 | 1075 | ||
2897 | 1310 | const std::vector<Soldier*>::const_iterator soldiers_end = soldiers.end(); | 1076 | const std::vector<Soldier*>::const_iterator soldiers_end = soldiers.end(); |
2898 | 1311 | for (std::vector<Soldier*>::const_iterator it = soldiers.begin();; ++it) { | 1077 | for (std::vector<Soldier*>::const_iterator it = soldiers.begin();; ++it) { |
2899 | @@ -1313,114 +1079,93 @@ | |||
2900 | 1313 | ps.set_production_result(_("No soldier found for this training level!")); | 1079 | ps.set_production_result(_("No soldier found for this training level!")); |
2901 | 1314 | return ps.program_end(game, ProgramResult::kSkipped); | 1080 | return ps.program_end(game, ProgramResult::kSkipped); |
2902 | 1315 | } | 1081 | } |
2915 | 1316 | if (attribute == TrainingAttribute::kHealth) { | 1082 | |
2916 | 1317 | if ((*it)->get_health_level() == level) | 1083 | if (attribute_ == TrainingAttribute::kHealth) { |
2917 | 1318 | break; | 1084 | if ((*it)->get_health_level() == level_) { |
2918 | 1319 | } else if (attribute == TrainingAttribute::kAttack) { | 1085 | break; |
2919 | 1320 | if ((*it)->get_attack_level() == level) | 1086 | } |
2920 | 1321 | break; | 1087 | } else if (attribute_ == TrainingAttribute::kAttack) { |
2921 | 1322 | } else if (attribute == TrainingAttribute::kDefense) { | 1088 | if ((*it)->get_attack_level() == level_) { |
2922 | 1323 | if ((*it)->get_defense_level() == level) | 1089 | break; |
2923 | 1324 | break; | 1090 | } |
2924 | 1325 | } else if (attribute == TrainingAttribute::kEvade) { | 1091 | } else if (attribute_ == TrainingAttribute::kDefense) { |
2925 | 1326 | if ((*it)->get_evade_level() == level) | 1092 | if ((*it)->get_defense_level() == level_) { |
2926 | 1327 | break; | 1093 | break; |
2927 | 1094 | } | ||
2928 | 1095 | } else if (attribute_ == TrainingAttribute::kEvade) { | ||
2929 | 1096 | if ((*it)->get_evade_level() == level_) { | ||
2930 | 1097 | break; | ||
2931 | 1098 | } | ||
2932 | 1328 | } | 1099 | } |
2933 | 1329 | } | 1100 | } |
2934 | 1330 | ps.molog(" okay\n"); // okay, do nothing | 1101 | ps.molog(" okay\n"); // okay, do nothing |
2935 | 1331 | 1102 | ||
2936 | 1332 | upcast(TrainingSite, ts, &ps); | 1103 | upcast(TrainingSite, ts, &ps); |
2938 | 1333 | ts->training_attempted(attribute, level); | 1104 | ts->training_attempted(attribute_, level_); |
2939 | 1334 | 1105 | ||
2940 | 1335 | ps.molog(" Check done!\n"); | 1106 | ps.molog(" Check done!\n"); |
2941 | 1336 | 1107 | ||
2942 | 1337 | return ps.program_step(game); | 1108 | return ps.program_step(game); |
2943 | 1338 | } | 1109 | } |
2944 | 1339 | 1110 | ||
2982 | 1340 | ProductionProgram::ActTrain::ActTrain(char* parameters) { | 1111 | ProductionProgram::ActTrain::ActTrain(const std::vector<std::string>& arguments) { |
2983 | 1341 | // TODO(unknown): This is currently hardcoded for "soldier", but should allow any | 1112 | if (arguments.size() != 4) { |
2984 | 1342 | // soldier type name. | 1113 | throw GameDataError("Usage: checksoldier=soldier <training attribute> <level before> <level after>"); |
2985 | 1343 | if (!match_force_skip(parameters, "soldier")) | 1114 | } |
2986 | 1344 | throw GameDataError("expected %s but found \"%s\"", "soldier type", parameters); | 1115 | |
2987 | 1345 | try { | 1116 | if (arguments.front() != "soldier") { |
2988 | 1346 | if (match_force_skip(parameters, "health")) | 1117 | throw GameDataError("Expected 'soldier' but found '%s'", arguments.front().c_str()); |
2989 | 1347 | attribute = TrainingAttribute::kHealth; | 1118 | } |
2990 | 1348 | else if (match_force_skip(parameters, "attack")) | 1119 | |
2991 | 1349 | attribute = TrainingAttribute::kAttack; | 1120 | attribute_ = parse_training_attribute(arguments.at(1)); |
2992 | 1350 | else if (match_force_skip(parameters, "defense")) | 1121 | level_ = read_int(arguments.at(2), 0); |
2993 | 1351 | attribute = TrainingAttribute::kDefense; | 1122 | target_level_ = read_positive(arguments.at(3)); |
2957 | 1352 | else if (match_force_skip(parameters, "evade")) | ||
2958 | 1353 | attribute = TrainingAttribute::kEvade; | ||
2959 | 1354 | else | ||
2960 | 1355 | throw GameDataError("expected %s but found \"%s\"", | ||
2961 | 1356 | "{\"health\"|\"attack\"|\"defense\"|\"evade\"}", parameters); | ||
2962 | 1357 | |||
2963 | 1358 | { | ||
2964 | 1359 | char* endp; | ||
2965 | 1360 | unsigned long long int const value = strtoull(parameters, &endp, 0); | ||
2966 | 1361 | level = value; | ||
2967 | 1362 | if (*endp != ' ' || level != value) | ||
2968 | 1363 | throw GameDataError("expected %s but found \"%s\"", "level", parameters); | ||
2969 | 1364 | parameters = endp; | ||
2970 | 1365 | } | ||
2971 | 1366 | |||
2972 | 1367 | { | ||
2973 | 1368 | char* endp; | ||
2974 | 1369 | unsigned long long int const value = strtoull(parameters, &endp, 0); | ||
2975 | 1370 | target_level = value; | ||
2976 | 1371 | if (*endp || target_level != value || target_level <= level) | ||
2977 | 1372 | throw GameDataError("expected level > %u but found \"%s\"", level, parameters); | ||
2978 | 1373 | } | ||
2979 | 1374 | } catch (const WException& e) { | ||
2980 | 1375 | throw GameDataError("train: %s", e.what()); | ||
2981 | 1376 | } | ||
2994 | 1377 | } | 1123 | } |
2995 | 1378 | 1124 | ||
2996 | 1379 | void ProductionProgram::ActTrain::execute(Game& game, ProductionSite& ps) const { | 1125 | void ProductionProgram::ActTrain::execute(Game& game, ProductionSite& ps) const { |
2997 | 1380 | const SoldierControl* ctrl = ps.soldier_control(); | 1126 | const SoldierControl* ctrl = ps.soldier_control(); |
2998 | 1381 | const std::vector<Soldier*> soldiers = ctrl->present_soldiers(); | 1127 | const std::vector<Soldier*> soldiers = ctrl->present_soldiers(); |
2999 | 1382 | const std::vector<Soldier*>::const_iterator soldiers_end = soldiers.end(); | 1128 | const std::vector<Soldier*>::const_iterator soldiers_end = soldiers.end(); |
3006 | 1383 | std::vector<Soldier*>::const_iterator it = soldiers.begin(); | 1129 | |
3007 | 1384 | 1130 | ps.molog(" Training soldier's %u (%d to %d)", static_cast<unsigned int>(attribute_), | |
3008 | 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_)); |
3009 | 1386 | static_cast<unsigned int>(level), static_cast<unsigned int>(target_level)); | 1132 | |
3010 | 1387 | 1133 | bool training_done = false; | |
3011 | 1388 | for (;; ++it) { | 1134 | for (auto it = soldiers.begin(); !training_done; ++it) { |
3012 | 1389 | if (it == soldiers_end) { | 1135 | if (it == soldiers_end) { |
3013 | 1390 | ps.set_production_result(_("No soldier found for this training level!")); | 1136 | ps.set_production_result(_("No soldier found for this training level!")); |
3014 | 1391 | return ps.program_end(game, ProgramResult::kSkipped); | 1137 | return ps.program_end(game, ProgramResult::kSkipped); |
3015 | 1392 | } | 1138 | } |
3028 | 1393 | if (attribute == TrainingAttribute::kHealth) { | 1139 | try { |
3029 | 1394 | if ((*it)->get_health_level() == level) | 1140 | switch (attribute_) { |
3030 | 1395 | break; | 1141 | case TrainingAttribute::kHealth: |
3031 | 1396 | } else if (attribute == TrainingAttribute::kAttack) { | 1142 | if ((*it)->get_health_level() == level_) { |
3032 | 1397 | if ((*it)->get_attack_level() == level) | 1143 | (*it)->set_health_level(target_level_); |
3033 | 1398 | break; | 1144 | training_done = true; |
3034 | 1399 | } else if (attribute == TrainingAttribute::kDefense) { | 1145 | } break; |
3035 | 1400 | if ((*it)->get_defense_level() == level) | 1146 | case TrainingAttribute::kAttack: |
3036 | 1401 | break; | 1147 | if ((*it)->get_attack_level() == level_) { |
3037 | 1402 | } else if (attribute == TrainingAttribute::kEvade) { | 1148 | (*it)->set_attack_level(target_level_); |
3038 | 1403 | if ((*it)->get_evade_level() == level) | 1149 | training_done = true; |
3039 | 1404 | break; | 1150 | } break; |
3040 | 1151 | case TrainingAttribute::kDefense: | ||
3041 | 1152 | if ((*it)->get_defense_level() == level_) { | ||
3042 | 1153 | (*it)->set_defense_level(target_level_); | ||
3043 | 1154 | training_done = true; | ||
3044 | 1155 | } break; | ||
3045 | 1156 | case TrainingAttribute::kEvade: | ||
3046 | 1157 | if ((*it)->get_evade_level() == level_) { | ||
3047 | 1158 | (*it)->set_evade_level(target_level_); | ||
3048 | 1159 | training_done = true; | ||
3049 | 1160 | } break; | ||
3050 | 1161 | default: | ||
3051 | 1162 | throw wexception("Unknown training attribute index %d", static_cast<unsigned int>(attribute_)); | ||
3052 | 1163 | } | ||
3053 | 1164 | } catch (...) { | ||
3054 | 1165 | throw wexception("Fail training soldier!!"); | ||
3055 | 1405 | } | 1166 | } |
3056 | 1406 | } | 1167 | } |
3074 | 1407 | ps.molog(" okay\n"); // okay, do nothing | 1168 | |
3058 | 1408 | |||
3059 | 1409 | try { | ||
3060 | 1410 | if (attribute == TrainingAttribute::kHealth) | ||
3061 | 1411 | (*it)->set_health_level(target_level); | ||
3062 | 1412 | else if (attribute == TrainingAttribute::kAttack) | ||
3063 | 1413 | (*it)->set_attack_level(target_level); | ||
3064 | 1414 | |||
3065 | 1415 | else if (attribute == TrainingAttribute::kDefense) | ||
3066 | 1416 | (*it)->set_defense_level(target_level); | ||
3067 | 1417 | |||
3068 | 1418 | else if (attribute == TrainingAttribute::kEvade) | ||
3069 | 1419 | (*it)->set_evade_level(target_level); | ||
3070 | 1420 | |||
3071 | 1421 | } catch (...) { | ||
3072 | 1422 | throw wexception("Fail training soldier!!"); | ||
3073 | 1423 | } | ||
3075 | 1424 | ps.molog(" Training done!\n"); | 1169 | ps.molog(" Training done!\n"); |
3076 | 1425 | ps.set_production_result( | 1170 | ps.set_production_result( |
3077 | 1426 | /** TRANSLATORS: Success message of a trainingsite '%s' stands for the description of the | 1171 | /** TRANSLATORS: Success message of a trainingsite '%s' stands for the description of the |
3078 | @@ -1428,61 +1173,32 @@ | |||
3079 | 1428 | (boost::format(_("Completed %s")) % ps.top_state().program->descname()).str()); | 1173 | (boost::format(_("Completed %s")) % ps.top_state().program->descname()).str()); |
3080 | 1429 | 1174 | ||
3081 | 1430 | upcast(TrainingSite, ts, &ps); | 1175 | upcast(TrainingSite, ts, &ps); |
3083 | 1431 | ts->training_successful(attribute, level); | 1176 | ts->training_successful(attribute_, level_); |
3084 | 1432 | 1177 | ||
3085 | 1433 | return ps.program_step(game); | 1178 | return ps.program_step(game); |
3086 | 1434 | } | 1179 | } |
3087 | 1435 | 1180 | ||
3110 | 1436 | ProductionProgram::ActPlaySound::ActPlaySound(char* parameters) { | 1181 | ProductionProgram::ActPlaySound::ActPlaySound(const std::vector<std::string>& arguments) { |
3111 | 1437 | try { | 1182 | parameters = MapObjectProgram::parse_act_play_sound(arguments, kFxPriorityAllowMultiple - 1); |
3090 | 1438 | bool reached_end; | ||
3091 | 1439 | const char* const name = next_word(parameters, reached_end); | ||
3092 | 1440 | fx = SoundHandler::register_fx(SoundType::kAmbient, name); | ||
3093 | 1441 | |||
3094 | 1442 | if (!reached_end) { | ||
3095 | 1443 | char* endp; | ||
3096 | 1444 | unsigned long long int const value = strtoull(parameters, &endp, 0); | ||
3097 | 1445 | priority = value; | ||
3098 | 1446 | if (*endp || priority != value) | ||
3099 | 1447 | throw GameDataError("expected %s but found \"%s\"", "priority", parameters); | ||
3100 | 1448 | } else { | ||
3101 | 1449 | priority = kFxPriorityAllowMultiple - 1; | ||
3102 | 1450 | } | ||
3103 | 1451 | if (priority < kFxPriorityLowest) { | ||
3104 | 1452 | throw GameDataError("Minmum priority for sounds is %d, but only %d was specified for %s", | ||
3105 | 1453 | kFxPriorityLowest, priority, name); | ||
3106 | 1454 | } | ||
3107 | 1455 | } catch (const WException& e) { | ||
3108 | 1456 | throw GameDataError("playsound: %s", e.what()); | ||
3109 | 1457 | } | ||
3112 | 1458 | } | 1183 | } |
3113 | 1459 | 1184 | ||
3114 | 1460 | void ProductionProgram::ActPlaySound::execute(Game& game, ProductionSite& ps) const { | 1185 | void ProductionProgram::ActPlaySound::execute(Game& game, ProductionSite& ps) const { |
3116 | 1461 | Notifications::publish(NoteSound(SoundType::kAmbient, fx, ps.position_, priority)); | 1186 | Notifications::publish(NoteSound(SoundType::kAmbient, parameters.fx, ps.position_, parameters.priority)); |
3117 | 1462 | return ps.program_step(game); | 1187 | return ps.program_step(game); |
3118 | 1463 | } | 1188 | } |
3119 | 1464 | 1189 | ||
3121 | 1465 | ProductionProgram::ActConstruct::ActConstruct(char* parameters, | 1190 | ProductionProgram::ActConstruct::ActConstruct(const std::vector<std::string>& arguments, |
3122 | 1466 | const std::string& production_program_name, | 1191 | const std::string& production_program_name, |
3123 | 1467 | ProductionSiteDescr* descr) { | 1192 | ProductionSiteDescr* descr) { |
3141 | 1468 | try { | 1193 | if (arguments.size() != 3) { |
3142 | 1469 | std::vector<std::string> params = split_string(parameters, " "); | 1194 | throw GameDataError("Usage: construct=<object name> <worker program> <workarea radius>"); |
3126 | 1470 | |||
3127 | 1471 | if (params.size() != 3) | ||
3128 | 1472 | throw GameDataError("usage: construct object-name worker-program radius:NN"); | ||
3129 | 1473 | |||
3130 | 1474 | objectname = params[0]; | ||
3131 | 1475 | workerprogram = params[1]; | ||
3132 | 1476 | radius = boost::lexical_cast<uint32_t>(params[2]); | ||
3133 | 1477 | |||
3134 | 1478 | std::set<std::string>& building_radius_infos = descr->workarea_info_[radius]; | ||
3135 | 1479 | std::string description = descr->name() + ' ' + production_program_name; | ||
3136 | 1480 | description += " construct "; | ||
3137 | 1481 | description += objectname; | ||
3138 | 1482 | building_radius_infos.insert(description); | ||
3139 | 1483 | } catch (const WException& e) { | ||
3140 | 1484 | throw GameDataError("construct: %s", e.what()); | ||
3143 | 1485 | } | 1195 | } |
3144 | 1196 | objectname = arguments.at(0); | ||
3145 | 1197 | workerprogram = arguments.at(1); | ||
3146 | 1198 | radius = read_positive(arguments.at(2)); | ||
3147 | 1199 | |||
3148 | 1200 | const std::string description = descr->name() + ' ' + production_program_name + " construct " + objectname; | ||
3149 | 1201 | descr->workarea_info_[radius].insert(description); | ||
3150 | 1486 | } | 1202 | } |
3151 | 1487 | 1203 | ||
3152 | 1488 | const ImmovableDescr& | 1204 | const ImmovableDescr& |
3153 | @@ -1632,96 +1348,93 @@ | |||
3154 | 1632 | const Tribes& tribes, | 1348 | const Tribes& tribes, |
3155 | 1633 | const World& world, | 1349 | const World& world, |
3156 | 1634 | ProductionSiteDescr* building) | 1350 | ProductionSiteDescr* building) |
3247 | 1635 | : name_(init_name), descname_(init_descname) { | 1351 | : MapObjectProgram(init_name), descname_(init_descname) { |
3248 | 1636 | 1352 | ||
3249 | 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>()) { |
3250 | 1638 | std::vector<std::string> parts; | 1354 | if (line.empty()) { |
3251 | 1639 | boost::split(parts, action_string, boost::is_any_of("=")); | 1355 | throw GameDataError("Empty line"); |
3252 | 1640 | if (parts.size() != 2) { | 1356 | } |
3253 | 1641 | throw GameDataError( | 1357 | try { |
3254 | 1642 | "invalid line: \"%s\" in production program \"%s\" for building \"%s\"", | 1358 | ProgramParseInput parseinput = parse_program_string(line); |
3255 | 1643 | action_string.c_str(), name().c_str(), building->name().c_str()); | 1359 | |
3256 | 1644 | } | 1360 | if (parseinput.name == "return") { |
3257 | 1645 | std::unique_ptr<char[]> arguments(new char[parts[1].size() + 1]); | 1361 | actions_.push_back(std::unique_ptr<ProductionProgram::Action>( |
3258 | 1646 | strncpy(arguments.get(), parts[1].c_str(), parts[1].size() + 1); | 1362 | new ActReturn(parseinput.arguments, *building, tribes))); |
3259 | 1647 | 1363 | } else if (parseinput.name == "call") { | |
3260 | 1648 | if (boost::iequals(parts[0], "return")) { | 1364 | actions_.push_back( |
3261 | 1649 | actions_.push_back(std::unique_ptr<ProductionProgram::Action>( | 1365 | std::unique_ptr<ProductionProgram::Action>(new ActCall(parseinput.arguments, *building))); |
3262 | 1650 | new ActReturn(arguments.get(), *building, tribes))); | 1366 | } else if (parseinput.name == "sleep") { |
3263 | 1651 | } else if (boost::iequals(parts[0], "call")) { | 1367 | actions_.push_back( |
3264 | 1652 | actions_.push_back( | 1368 | std::unique_ptr<ProductionProgram::Action>(new ActSleep(parseinput.arguments))); |
3265 | 1653 | std::unique_ptr<ProductionProgram::Action>(new ActCall(arguments.get(), *building))); | 1369 | } else if (parseinput.name == "animate") { |
3266 | 1654 | } else if (boost::iequals(parts[0], "sleep")) { | 1370 | actions_.push_back( |
3267 | 1655 | actions_.push_back( | 1371 | std::unique_ptr<ProductionProgram::Action>(new ActAnimate(parseinput.arguments, building))); |
3268 | 1656 | std::unique_ptr<ProductionProgram::Action>(new ActSleep(arguments.get()))); | 1372 | } else if (parseinput.name =="consume") { |
3269 | 1657 | } else if (boost::iequals(parts[0], "animate")) { | 1373 | actions_.push_back(std::unique_ptr<ProductionProgram::Action>( |
3270 | 1658 | actions_.push_back( | 1374 | new ActConsume(parseinput.arguments, *building, tribes))); |
3271 | 1659 | std::unique_ptr<ProductionProgram::Action>(new ActAnimate(arguments.get(), building))); | 1375 | } else if (parseinput.name == "produce") { |
3272 | 1660 | } else if (boost::iequals(parts[0], "consume")) { | 1376 | actions_.push_back(std::unique_ptr<ProductionProgram::Action>( |
3273 | 1661 | actions_.push_back(std::unique_ptr<ProductionProgram::Action>( | 1377 | new ActProduce(parseinput.arguments, *building, tribes))); |
3274 | 1662 | new ActConsume(arguments.get(), *building, tribes))); | 1378 | } else if (parseinput.name == "recruit") { |
3275 | 1663 | } else if (boost::iequals(parts[0], "produce")) { | 1379 | actions_.push_back(std::unique_ptr<ProductionProgram::Action>( |
3276 | 1664 | actions_.push_back(std::unique_ptr<ProductionProgram::Action>( | 1380 | new ActRecruit(parseinput.arguments, *building, tribes))); |
3277 | 1665 | new ActProduce(arguments.get(), *building, tribes))); | 1381 | } else if (parseinput.name =="callworker") { |
3278 | 1666 | } else if (boost::iequals(parts[0], "recruit")) { | 1382 | actions_.push_back(std::unique_ptr<ProductionProgram::Action>( |
3279 | 1667 | actions_.push_back(std::unique_ptr<ProductionProgram::Action>( | 1383 | new ActCallWorker(parseinput.arguments, name(), building, tribes))); |
3280 | 1668 | new ActRecruit(arguments.get(), *building, tribes))); | 1384 | } else if (parseinput.name == "mine") { |
3281 | 1669 | } else if (boost::iequals(parts[0], "callworker")) { | 1385 | actions_.push_back(std::unique_ptr<ProductionProgram::Action>( |
3282 | 1670 | actions_.push_back(std::unique_ptr<ProductionProgram::Action>( | 1386 | new ActMine(parseinput.arguments, world, name(), building))); |
3283 | 1671 | new ActCallWorker(arguments.get(), name(), building, tribes))); | 1387 | } else if (parseinput.name == "checksoldier") { |
3284 | 1672 | } else if (boost::iequals(parts[0], "mine")) { | 1388 | actions_.push_back( |
3285 | 1673 | actions_.push_back(std::unique_ptr<ProductionProgram::Action>( | 1389 | std::unique_ptr<ProductionProgram::Action>(new ActCheckSoldier(parseinput.arguments))); |
3286 | 1674 | new ActMine(arguments.get(), world, name(), building))); | 1390 | } else if (parseinput.name == "train") { |
3287 | 1675 | } else if (boost::iequals(parts[0], "checksoldier")) { | 1391 | actions_.push_back( |
3288 | 1676 | actions_.push_back( | 1392 | std::unique_ptr<ProductionProgram::Action>(new ActTrain(parseinput.arguments))); |
3289 | 1677 | std::unique_ptr<ProductionProgram::Action>(new ActCheckSoldier(arguments.get()))); | 1393 | } else if (parseinput.name == "playsound") { |
3290 | 1678 | } else if (boost::iequals(parts[0], "train")) { | 1394 | actions_.push_back( |
3291 | 1679 | actions_.push_back( | 1395 | std::unique_ptr<ProductionProgram::Action>(new ActPlaySound(parseinput.arguments))); |
3292 | 1680 | std::unique_ptr<ProductionProgram::Action>(new ActTrain(arguments.get()))); | 1396 | } else if (parseinput.name == "construct") { |
3293 | 1681 | } else if (boost::iequals(parts[0], "playsound")) { | 1397 | actions_.push_back(std::unique_ptr<ProductionProgram::Action>( |
3294 | 1682 | actions_.push_back( | 1398 | new ActConstruct(parseinput.arguments, name(), building))); |
3295 | 1683 | std::unique_ptr<ProductionProgram::Action>(new ActPlaySound(arguments.get()))); | 1399 | } else if (parseinput.name == "checkmap") { |
3296 | 1684 | } else if (boost::iequals(parts[0], "construct")) { | 1400 | actions_.push_back( |
3297 | 1685 | actions_.push_back(std::unique_ptr<ProductionProgram::Action>( | 1401 | std::unique_ptr<ProductionProgram::Action>(new ActCheckMap(parseinput.arguments))); |
3298 | 1686 | new ActConstruct(arguments.get(), name(), building))); | 1402 | } else { |
3299 | 1687 | } else if (boost::iequals(parts[0], "checkmap")) { | 1403 | throw GameDataError("Unknown command '%s' in line '%s'", parseinput.name.c_str(), line.c_str()); |
3300 | 1688 | actions_.push_back( | 1404 | } |
3301 | 1689 | std::unique_ptr<ProductionProgram::Action>(new ActCheckMap(arguments.get()))); | 1405 | |
3302 | 1690 | } else { | 1406 | const ProductionProgram::Action& action = *actions_.back(); |
3303 | 1691 | throw GameDataError( | 1407 | for (const auto& group : action.consumed_wares_workers()) { |
3304 | 1692 | "unknown command type \"%s\" in production program \"%s\" for building \"%s\"", | 1408 | consumed_wares_workers_.push_back(group); |
3305 | 1693 | arguments.get(), name().c_str(), building->name().c_str()); | 1409 | } |
3306 | 1694 | } | 1410 | |
3307 | 1695 | 1411 | // Add produced wares. If the ware already exists, increase the amount | |
3308 | 1696 | const ProductionProgram::Action& action = *actions_.back(); | 1412 | for (const auto& ware : action.produced_wares()) { |
3309 | 1697 | for (const auto& group : action.consumed_wares_workers()) { | 1413 | if (produced_wares_.count(ware.first) == 1) { |
3310 | 1698 | consumed_wares_workers_.push_back(group); | 1414 | produced_wares_.at(ware.first) += ware.second; |
3311 | 1699 | } | 1415 | } else { |
3312 | 1700 | // Add produced wares. If the ware already exists, increase the amount | 1416 | produced_wares_.insert(ware); |
3313 | 1701 | for (const auto& ware : action.produced_wares()) { | 1417 | } |
3314 | 1702 | if (produced_wares_.count(ware.first) == 1) { | 1418 | } |
3315 | 1703 | produced_wares_.at(ware.first) += ware.second; | 1419 | |
3316 | 1704 | } else { | 1420 | // Add recruited workers. If the worker already exists, increase the amount |
3317 | 1705 | produced_wares_.insert(ware); | 1421 | for (const auto& worker : action.recruited_workers()) { |
3318 | 1706 | } | 1422 | if (recruited_workers_.count(worker.first) == 1) { |
3319 | 1707 | } | 1423 | recruited_workers_.at(worker.first) += worker.second; |
3320 | 1708 | // Add recruited workers. If the worker already exists, increase the amount | 1424 | } else { |
3321 | 1709 | for (const auto& worker : action.recruited_workers()) { | 1425 | recruited_workers_.insert(worker); |
3322 | 1710 | if (recruited_workers_.count(worker.first) == 1) { | 1426 | } |
3323 | 1711 | recruited_workers_.at(worker.first) += worker.second; | 1427 | } |
3324 | 1712 | } else { | 1428 | } catch (const std::exception& e) { |
3325 | 1713 | recruited_workers_.insert(worker); | 1429 | throw GameDataError("Error reading line '%s': %s", line.c_str(), e.what()); |
3326 | 1714 | } | 1430 | } |
3327 | 1715 | } | 1431 | } |
3328 | 1716 | } | 1432 | |
3329 | 1717 | if (actions_.empty()) | 1433 | if (actions_.empty()) { |
3330 | 1718 | throw GameDataError("no actions in production program \"%s\" for building \"%s\"", | 1434 | throw GameDataError("No actions found"); |
3331 | 1719 | name().c_str(), building->name().c_str()); | 1435 | } |
3332 | 1720 | } | 1436 | } |
3333 | 1721 | 1437 | ||
3244 | 1722 | const std::string& ProductionProgram::name() const { | ||
3245 | 1723 | return name_; | ||
3246 | 1724 | } | ||
3334 | 1725 | const std::string& ProductionProgram::descname() const { | 1438 | const std::string& ProductionProgram::descname() const { |
3335 | 1726 | return descname_; | 1439 | return descname_; |
3336 | 1727 | } | 1440 | } |
3337 | 1728 | 1441 | ||
3338 | === modified file 'src/logic/map_objects/tribes/production_program.h' | |||
3339 | --- src/logic/map_objects/tribes/production_program.h 2019-05-11 13:48:12 +0000 | |||
3340 | +++ src/logic/map_objects/tribes/production_program.h 2019-08-31 15:58:01 +0000 | |||
3341 | @@ -32,6 +32,7 @@ | |||
3342 | 32 | #include "base/log.h" | 32 | #include "base/log.h" |
3343 | 33 | #include "base/macros.h" | 33 | #include "base/macros.h" |
3344 | 34 | #include "logic/map_objects/buildcost.h" | 34 | #include "logic/map_objects/buildcost.h" |
3345 | 35 | #include "logic/map_objects/map_object_program.h" | ||
3346 | 35 | #include "logic/map_objects/tribes/bill_of_materials.h" | 36 | #include "logic/map_objects/tribes/bill_of_materials.h" |
3347 | 36 | #include "logic/map_objects/tribes/program_result.h" | 37 | #include "logic/map_objects/tribes/program_result.h" |
3348 | 37 | #include "logic/map_objects/tribes/training_attribute.h" | 38 | #include "logic/map_objects/tribes/training_attribute.h" |
3349 | @@ -51,7 +52,7 @@ | |||
3350 | 51 | class World; | 52 | class World; |
3351 | 52 | 53 | ||
3352 | 53 | /// Ordered sequence of actions (at least 1). Has a name. | 54 | /// Ordered sequence of actions (at least 1). Has a name. |
3354 | 54 | struct ProductionProgram { | 55 | struct ProductionProgram : public MapObjectProgram { |
3355 | 55 | 56 | ||
3356 | 56 | /// A group of ware types with a count. | 57 | /// A group of ware types with a count. |
3357 | 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>; |
3358 | @@ -97,13 +98,11 @@ | |||
3359 | 97 | DISALLOW_COPY_AND_ASSIGN(Action); | 98 | DISALLOW_COPY_AND_ASSIGN(Action); |
3360 | 98 | }; | 99 | }; |
3361 | 99 | 100 | ||
3369 | 100 | /// Parse a group of ware types followed by an optional count and terminated | 101 | /// Parse a group of ware types followed by an optional count within a vector range. Example: "fish,meat:2". |
3370 | 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); |
3371 | 102 | static void parse_ware_type_group(char*& parameters, | 103 | |
3372 | 103 | WareTypeGroup& group, | 104 | /// Parse a ware or worker list with optional amounts and ensure that the building's outputs match. Example: "fish:2". |
3373 | 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); |
3367 | 105 | const BillOfMaterials& input_wares, | ||
3368 | 106 | const BillOfMaterials& input_workers); | ||
3374 | 107 | 106 | ||
3375 | 108 | /// Returns from the program. | 107 | /// Returns from the program. |
3376 | 109 | /// | 108 | /// |
3377 | @@ -156,7 +155,7 @@ | |||
3378 | 156 | /// Note: If the execution reaches the end of the program. the return value | 155 | /// Note: If the execution reaches the end of the program. the return value |
3379 | 157 | /// is implicitly set to Completed. | 156 | /// is implicitly set to Completed. |
3380 | 158 | struct ActReturn : public Action { | 157 | struct ActReturn : public Action { |
3382 | 159 | ActReturn(char* parameters, const ProductionSiteDescr&, const Tribes& tribes); | 158 | ActReturn(const std::vector<std::string>& arguments, const ProductionSiteDescr&, const Tribes& tribes); |
3383 | 160 | ~ActReturn() override; | 159 | ~ActReturn() override; |
3384 | 161 | void execute(Game&, ProductionSite&) const override; | 160 | void execute(Game&, ProductionSite&) const override; |
3385 | 162 | 161 | ||
3386 | @@ -167,11 +166,10 @@ | |||
3387 | 167 | virtual std::string description_negation(const Tribes&) const = 0; | 166 | virtual std::string description_negation(const Tribes&) const = 0; |
3388 | 168 | }; | 167 | }; |
3389 | 169 | static Condition* | 168 | static Condition* |
3391 | 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); |
3392 | 170 | |||
3393 | 171 | struct Negation : public Condition { | 171 | struct Negation : public Condition { |
3397 | 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); |
3395 | 173 | : operand(create_condition(parameters, descr, tribes)) { | ||
3396 | 174 | } | ||
3398 | 175 | ~Negation() override; | 173 | ~Negation() override; |
3399 | 176 | bool evaluate(const ProductionSite&) const override; | 174 | bool evaluate(const ProductionSite&) const override; |
3400 | 177 | // Just a dummy to satisfy the superclass interface. Do not use. | 175 | // Just a dummy to satisfy the superclass interface. Do not use. |
3401 | @@ -211,7 +209,7 @@ | |||
3402 | 211 | /// wares, combining from any of the types specified, in its input | 209 | /// wares, combining from any of the types specified, in its input |
3403 | 212 | /// queues. | 210 | /// queues. |
3404 | 213 | struct SiteHas : public Condition { | 211 | struct SiteHas : public Condition { |
3406 | 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); |
3407 | 215 | bool evaluate(const ProductionSite&) const override; | 213 | bool evaluate(const ProductionSite&) const override; |
3408 | 216 | std::string description(const Tribes& tribes) const override; | 214 | std::string description(const Tribes& tribes) const override; |
3409 | 217 | std::string description_negation(const Tribes& tribes) const override; | 215 | std::string description_negation(const Tribes& tribes) const override; |
3410 | @@ -269,7 +267,7 @@ | |||
3411 | 269 | /// but no statistics are calculated (with the same effect as | 267 | /// but no statistics are calculated (with the same effect as |
3412 | 270 | /// executing "return=no_stats") | 268 | /// executing "return=no_stats") |
3413 | 271 | struct ActCall : public Action { | 269 | struct ActCall : public Action { |
3415 | 272 | ActCall(char* parameters, const ProductionSiteDescr&); | 270 | ActCall(const std::vector<std::string>& arguments, const ProductionSiteDescr&); |
3416 | 273 | void execute(Game&, ProductionSite&) const override; | 271 | void execute(Game&, ProductionSite&) const override; |
3417 | 274 | 272 | ||
3418 | 275 | private: | 273 | private: |
3419 | @@ -285,7 +283,7 @@ | |||
3420 | 285 | /// program: | 283 | /// program: |
3421 | 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. |
3422 | 287 | struct ActCallWorker : public Action { | 285 | struct ActCallWorker : public Action { |
3424 | 288 | ActCallWorker(char* parameters, | 286 | ActCallWorker(const std::vector<std::string>& arguments, |
3425 | 289 | const std::string& production_program_name, | 287 | const std::string& production_program_name, |
3426 | 290 | ProductionSiteDescr*, | 288 | ProductionSiteDescr*, |
3427 | 291 | const Tribes& tribes); | 289 | const Tribes& tribes); |
3428 | @@ -311,7 +309,7 @@ | |||
3429 | 311 | /// | 309 | /// |
3430 | 312 | /// Blocks the execution of the program for the specified duration. | 310 | /// Blocks the execution of the program for the specified duration. |
3431 | 313 | struct ActSleep : public Action { | 311 | struct ActSleep : public Action { |
3433 | 314 | explicit ActSleep(char* parameters); | 312 | explicit ActSleep(const std::vector<std::string>& arguments); |
3434 | 315 | void execute(Game&, ProductionSite&) const override; | 313 | void execute(Game&, ProductionSite&) const override; |
3435 | 316 | 314 | ||
3436 | 317 | private: | 315 | private: |
3437 | @@ -329,12 +327,12 @@ | |||
3438 | 329 | /// | 327 | /// |
3439 | 330 | /// Ends the program if the feature is not enabled. | 328 | /// Ends the program if the feature is not enabled. |
3440 | 331 | struct ActCheckMap : public Action { | 329 | struct ActCheckMap : public Action { |
3442 | 332 | explicit ActCheckMap(char* parameters); | 330 | explicit ActCheckMap(const std::vector<std::string>& arguments); |
3443 | 333 | void execute(Game&, ProductionSite&) const override; | 331 | void execute(Game&, ProductionSite&) const override; |
3444 | 334 | 332 | ||
3445 | 335 | private: | 333 | private: |
3448 | 336 | enum { SEAFARING = 1 }; | 334 | enum class Feature { kSeafaring = 1 }; |
3449 | 337 | uint8_t feature_; | 335 | Feature feature_; |
3450 | 338 | }; | 336 | }; |
3451 | 339 | 337 | ||
3452 | 340 | /// Runs an animation. | 338 | /// Runs an animation. |
3453 | @@ -354,12 +352,11 @@ | |||
3454 | 354 | /// animation will not be stopped by this command. It will run until another | 352 | /// animation will not be stopped by this command. It will run until another |
3455 | 355 | /// animation is started.) | 353 | /// animation is started.) |
3456 | 356 | struct ActAnimate : public Action { | 354 | struct ActAnimate : public Action { |
3458 | 357 | ActAnimate(char* parameters, ProductionSiteDescr*); | 355 | ActAnimate(const std::vector<std::string>& arguments, ProductionSiteDescr*); |
3459 | 358 | void execute(Game&, ProductionSite&) const override; | 356 | void execute(Game&, ProductionSite&) const override; |
3460 | 359 | 357 | ||
3461 | 360 | private: | 358 | private: |
3464 | 361 | std::string animation_name_; | 359 | AnimationParameters parameters; |
3463 | 362 | Duration duration_; | ||
3465 | 363 | }; | 360 | }; |
3466 | 364 | 361 | ||
3467 | 365 | /// Consumes wares from the input storages. | 362 | /// Consumes wares from the input storages. |
3468 | @@ -406,7 +403,7 @@ | |||
3469 | 406 | /// types of a group are sorted. | 403 | /// types of a group are sorted. |
3470 | 407 | // TODO(unknown): change this! | 404 | // TODO(unknown): change this! |
3471 | 408 | struct ActConsume : public Action { | 405 | struct ActConsume : public Action { |
3473 | 409 | ActConsume(char* parameters, const ProductionSiteDescr&, const Tribes& tribes); | 406 | ActConsume(const std::vector<std::string>& arguments, const ProductionSiteDescr& descr, const Tribes& tribes); |
3474 | 410 | void execute(Game&, ProductionSite&) const override; | 407 | void execute(Game&, ProductionSite&) const override; |
3475 | 411 | }; | 408 | }; |
3476 | 412 | 409 | ||
3477 | @@ -426,7 +423,7 @@ | |||
3478 | 426 | /// produced wares are of the type specified in the group. How the produced | 423 | /// produced wares are of the type specified in the group. How the produced |
3479 | 427 | /// wares are handled is defined by the productionsite. | 424 | /// wares are handled is defined by the productionsite. |
3480 | 428 | struct ActProduce : public Action { | 425 | struct ActProduce : public Action { |
3482 | 429 | ActProduce(char* parameters, const ProductionSiteDescr&, const Tribes& tribes); | 426 | ActProduce(const std::vector<std::string>& arguments, const ProductionSiteDescr&, const Tribes& tribes); |
3483 | 430 | void execute(Game&, ProductionSite&) const override; | 427 | void execute(Game&, ProductionSite&) const override; |
3484 | 431 | bool get_building_work(Game&, ProductionSite&, Worker&) const override; | 428 | bool get_building_work(Game&, ProductionSite&, Worker&) const override; |
3485 | 432 | }; | 429 | }; |
3486 | @@ -447,13 +444,13 @@ | |||
3487 | 447 | /// The recruited workers are of the type specified in the group. How the | 444 | /// The recruited workers are of the type specified in the group. How the |
3488 | 448 | /// recruited workers are handled is defined by the productionsite. | 445 | /// recruited workers are handled is defined by the productionsite. |
3489 | 449 | struct ActRecruit : public Action { | 446 | struct ActRecruit : public Action { |
3491 | 450 | ActRecruit(char* parameters, const ProductionSiteDescr&, const Tribes& tribes); | 447 | ActRecruit(const std::vector<std::string>& arguments, const ProductionSiteDescr&, const Tribes& tribes); |
3492 | 451 | void execute(Game&, ProductionSite&) const override; | 448 | void execute(Game&, ProductionSite&) const override; |
3493 | 452 | bool get_building_work(Game&, ProductionSite&, Worker&) const override; | 449 | bool get_building_work(Game&, ProductionSite&, Worker&) const override; |
3494 | 453 | }; | 450 | }; |
3495 | 454 | 451 | ||
3496 | 455 | struct ActMine : public Action { | 452 | struct ActMine : public Action { |
3498 | 456 | ActMine(char* parameters, | 453 | ActMine(const std::vector<std::string>& arguments, |
3499 | 457 | const World&, | 454 | const World&, |
3500 | 458 | const std::string& production_program_name, | 455 | const std::string& production_program_name, |
3501 | 459 | ProductionSiteDescr*); | 456 | ProductionSiteDescr*); |
3502 | @@ -468,22 +465,22 @@ | |||
3503 | 468 | }; | 465 | }; |
3504 | 469 | 466 | ||
3505 | 470 | struct ActCheckSoldier : public Action { | 467 | struct ActCheckSoldier : public Action { |
3507 | 471 | explicit ActCheckSoldier(char* parameters); | 468 | explicit ActCheckSoldier(const std::vector<std::string>& arguments); |
3508 | 472 | void execute(Game&, ProductionSite&) const override; | 469 | void execute(Game&, ProductionSite&) const override; |
3509 | 473 | 470 | ||
3510 | 474 | private: | 471 | private: |
3513 | 475 | TrainingAttribute attribute; | 472 | TrainingAttribute attribute_; |
3514 | 476 | uint8_t level; | 473 | uint8_t level_; |
3515 | 477 | }; | 474 | }; |
3516 | 478 | 475 | ||
3517 | 479 | struct ActTrain : public Action { | 476 | struct ActTrain : public Action { |
3519 | 480 | explicit ActTrain(char* parameters); | 477 | explicit ActTrain(const std::vector<std::string>& arguments); |
3520 | 481 | void execute(Game&, ProductionSite&) const override; | 478 | void execute(Game&, ProductionSite&) const override; |
3521 | 482 | 479 | ||
3522 | 483 | private: | 480 | private: |
3526 | 484 | TrainingAttribute attribute; | 481 | TrainingAttribute attribute_; |
3527 | 485 | uint8_t level; | 482 | uint8_t level_; |
3528 | 486 | uint8_t target_level; | 483 | uint8_t target_level_; |
3529 | 487 | }; | 484 | }; |
3530 | 488 | 485 | ||
3531 | 489 | /// Plays a sound effect. | 486 | /// Plays a sound effect. |
3532 | @@ -501,12 +498,11 @@ | |||
3533 | 501 | /// Plays the specified sound effect with the specified priority. Whether the | 498 | /// Plays the specified sound effect with the specified priority. Whether the |
3534 | 502 | /// sound effect is actually played is determined by the sound handler. | 499 | /// sound effect is actually played is determined by the sound handler. |
3535 | 503 | struct ActPlaySound : public Action { | 500 | struct ActPlaySound : public Action { |
3537 | 504 | explicit ActPlaySound(char* parameters); | 501 | explicit ActPlaySound(const std::vector<std::string>& arguments); |
3538 | 505 | void execute(Game&, ProductionSite&) const override; | 502 | void execute(Game&, ProductionSite&) const override; |
3539 | 506 | 503 | ||
3540 | 507 | private: | 504 | private: |
3543 | 508 | FxId fx; | 505 | PlaySoundParameters parameters; |
3542 | 509 | uint8_t priority; | ||
3544 | 510 | }; | 506 | }; |
3545 | 511 | 507 | ||
3546 | 512 | /// Sends a building worker to construct at an immovable. | 508 | /// Sends a building worker to construct at an immovable. |
3547 | @@ -522,7 +518,7 @@ | |||
3548 | 522 | /// radius | 518 | /// radius |
3549 | 523 | /// Activity radius | 519 | /// Activity radius |
3550 | 524 | struct ActConstruct : public Action { | 520 | struct ActConstruct : public Action { |
3552 | 525 | ActConstruct(char* parameters, | 521 | ActConstruct(const std::vector<std::string>& arguments, |
3553 | 526 | const std::string& production_program_name, | 522 | const std::string& production_program_name, |
3554 | 527 | ProductionSiteDescr*); | 523 | ProductionSiteDescr*); |
3555 | 528 | void execute(Game&, ProductionSite&) const override; | 524 | void execute(Game&, ProductionSite&) const override; |
3556 | @@ -544,7 +540,6 @@ | |||
3557 | 544 | const World& world, | 540 | const World& world, |
3558 | 545 | ProductionSiteDescr* building); | 541 | ProductionSiteDescr* building); |
3559 | 546 | 542 | ||
3560 | 547 | const std::string& name() const; | ||
3561 | 548 | const std::string& descname() const; | 543 | const std::string& descname() const; |
3562 | 549 | 544 | ||
3563 | 550 | size_t size() const; | 545 | size_t size() const; |
3564 | @@ -555,7 +550,6 @@ | |||
3565 | 555 | const Buildcost& recruited_workers() const; | 550 | const Buildcost& recruited_workers() const; |
3566 | 556 | 551 | ||
3567 | 557 | private: | 552 | private: |
3568 | 558 | std::string name_; | ||
3569 | 559 | std::string descname_; | 553 | std::string descname_; |
3570 | 560 | std::vector<std::unique_ptr<Action>> actions_; | 554 | std::vector<std::unique_ptr<Action>> actions_; |
3571 | 561 | ProductionProgram::Groups consumed_wares_workers_; | 555 | ProductionProgram::Groups consumed_wares_workers_; |
3572 | 562 | 556 | ||
3573 | === modified file 'src/logic/map_objects/tribes/productionsite.cc' | |||
3574 | --- src/logic/map_objects/tribes/productionsite.cc 2019-08-20 17:35:33 +0000 | |||
3575 | +++ src/logic/map_objects/tribes/productionsite.cc 2019-08-31 15:58:01 +0000 | |||
3576 | @@ -93,6 +93,8 @@ | |||
3577 | 93 | const Tribes& tribes, | 93 | const Tribes& tribes, |
3578 | 94 | const World& world) | 94 | const World& world) |
3579 | 95 | : BuildingDescr(init_descname, init_type, table, tribes), | 95 | : BuildingDescr(init_descname, init_type, table, tribes), |
3580 | 96 | ware_demand_checks_(new std::set<DescriptionIndex>()), | ||
3581 | 97 | worker_demand_checks_(new std::set<DescriptionIndex>()), | ||
3582 | 96 | out_of_resource_productivity_threshold_(100) { | 98 | out_of_resource_productivity_threshold_(100) { |
3583 | 97 | if (msgctxt.empty()) { | 99 | if (msgctxt.empty()) { |
3584 | 98 | throw Widelands::GameDataError( | 100 | throw Widelands::GameDataError( |
3585 | @@ -187,10 +189,11 @@ | |||
3586 | 187 | items_table = table.get_table("programs"); | 189 | items_table = table.get_table("programs"); |
3587 | 188 | for (std::string program_name : items_table->keys<std::string>()) { | 190 | for (std::string program_name : items_table->keys<std::string>()) { |
3588 | 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); |
3589 | 192 | if (programs_.count(program_name)) { | ||
3590 | 193 | throw GameDataError("Program '%s' has already been declared for productionsite '%s'", | ||
3591 | 194 | program_name.c_str(), name().c_str()); | ||
3592 | 195 | } | ||
3593 | 190 | try { | 196 | try { |
3594 | 191 | if (programs_.count(program_name)) { | ||
3595 | 192 | throw wexception("this program has already been declared"); | ||
3596 | 193 | } | ||
3597 | 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); |
3598 | 195 | 198 | ||
3599 | 196 | // Allow use of both gettext and pgettext. This way, we can have a lower workload on | 199 | // Allow use of both gettext and pgettext. This way, we can have a lower workload on |
3600 | @@ -204,7 +207,7 @@ | |||
3601 | 204 | new ProductionProgram(program_name, program_descname, | 207 | new ProductionProgram(program_name, program_descname, |
3602 | 205 | program_table->get_table("actions"), tribes, world, this)); | 208 | program_table->get_table("actions"), tribes, world, this)); |
3603 | 206 | } catch (const std::exception& e) { | 209 | } catch (const std::exception& e) { |
3605 | 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()); |
3606 | 208 | } | 211 | } |
3607 | 209 | } | 212 | } |
3608 | 210 | 213 | ||
3609 | @@ -272,6 +275,19 @@ | |||
3610 | 272 | return *new ProductionSite(*this); | 275 | return *new ProductionSite(*this); |
3611 | 273 | } | 276 | } |
3612 | 274 | 277 | ||
3613 | 278 | std::set<DescriptionIndex>* ProductionSiteDescr::ware_demand_checks() const { | ||
3614 | 279 | return ware_demand_checks_.get(); | ||
3615 | 280 | } | ||
3616 | 281 | std::set<DescriptionIndex>* ProductionSiteDescr::worker_demand_checks() const { | ||
3617 | 282 | return worker_demand_checks_.get(); | ||
3618 | 283 | } | ||
3619 | 284 | void ProductionSiteDescr::clear_demand_checks() { | ||
3620 | 285 | ware_demand_checks_->clear(); | ||
3621 | 286 | ware_demand_checks_.reset(nullptr); | ||
3622 | 287 | worker_demand_checks_->clear(); | ||
3623 | 288 | worker_demand_checks_.reset(nullptr); | ||
3624 | 289 | } | ||
3625 | 290 | |||
3626 | 275 | /* | 291 | /* |
3627 | 276 | ============================== | 292 | ============================== |
3628 | 277 | 293 | ||
3629 | 278 | 294 | ||
3630 | === modified file 'src/logic/map_objects/tribes/productionsite.h' | |||
3631 | --- src/logic/map_objects/tribes/productionsite.h 2019-06-23 12:45:29 +0000 | |||
3632 | +++ src/logic/map_objects/tribes/productionsite.h 2019-08-31 15:58:01 +0000 | |||
3633 | @@ -73,6 +73,13 @@ | |||
3634 | 73 | 73 | ||
3635 | 74 | Building& create_object() const override; | 74 | Building& create_object() const override; |
3636 | 75 | 75 | ||
3637 | 76 | // List of wares to register having economy checks. Parsed by the tribes during postload and must be nullptr after loading has finished | ||
3638 | 77 | std::set<DescriptionIndex>* ware_demand_checks() const; | ||
3639 | 78 | // List of workers to register having economy checks. Parsed by the tribes during postload and must be nullptr after loading has finished | ||
3640 | 79 | std::set<DescriptionIndex>* worker_demand_checks() const; | ||
3641 | 80 | // Clear ware and worker demand check info | ||
3642 | 81 | void clear_demand_checks(); | ||
3643 | 82 | |||
3644 | 76 | uint32_t nr_working_positions() const { | 83 | uint32_t nr_working_positions() const { |
3645 | 77 | uint32_t result = 0; | 84 | uint32_t result = 0; |
3646 | 78 | for (const auto& working_pos : working_positions()) { | 85 | for (const auto& working_pos : working_positions()) { |
3647 | @@ -143,6 +150,8 @@ | |||
3648 | 143 | } | 150 | } |
3649 | 144 | 151 | ||
3650 | 145 | private: | 152 | private: |
3651 | 153 | std::unique_ptr<std::set<DescriptionIndex>> ware_demand_checks_; | ||
3652 | 154 | std::unique_ptr<std::set<DescriptionIndex>> worker_demand_checks_; | ||
3653 | 146 | BillOfMaterials working_positions_; | 155 | BillOfMaterials working_positions_; |
3654 | 147 | BillOfMaterials input_wares_; | 156 | BillOfMaterials input_wares_; |
3655 | 148 | BillOfMaterials input_workers_; | 157 | BillOfMaterials input_workers_; |
3656 | 149 | 158 | ||
3657 | === modified file 'src/logic/map_objects/tribes/soldier.cc' | |||
3658 | --- src/logic/map_objects/tribes/soldier.cc 2019-05-27 14:25:47 +0000 | |||
3659 | +++ src/logic/map_objects/tribes/soldier.cc 2019-08-31 15:58:01 +0000 | |||
3660 | @@ -32,7 +32,6 @@ | |||
3661 | 32 | #include "economy/flag.h" | 32 | #include "economy/flag.h" |
3662 | 33 | #include "graphic/graphic.h" | 33 | #include "graphic/graphic.h" |
3663 | 34 | #include "graphic/rendertarget.h" | 34 | #include "graphic/rendertarget.h" |
3664 | 35 | #include "helper.h" | ||
3665 | 36 | #include "io/fileread.h" | 35 | #include "io/fileread.h" |
3666 | 37 | #include "io/filewrite.h" | 36 | #include "io/filewrite.h" |
3667 | 38 | #include "logic/editor_game_base.h" | 37 | #include "logic/editor_game_base.h" |
3668 | 39 | 38 | ||
3669 | === modified file 'src/logic/map_objects/tribes/trainingsite.cc' | |||
3670 | --- src/logic/map_objects/tribes/trainingsite.cc 2019-06-23 11:41:17 +0000 | |||
3671 | +++ src/logic/map_objects/tribes/trainingsite.cc 2019-08-31 15:58:01 +0000 | |||
3672 | @@ -28,7 +28,6 @@ | |||
3673 | 28 | #include "base/macros.h" | 28 | #include "base/macros.h" |
3674 | 29 | #include "base/wexception.h" | 29 | #include "base/wexception.h" |
3675 | 30 | #include "economy/request.h" | 30 | #include "economy/request.h" |
3676 | 31 | #include "helper.h" | ||
3677 | 32 | #include "logic/editor_game_base.h" | 31 | #include "logic/editor_game_base.h" |
3678 | 33 | #include "logic/game.h" | 32 | #include "logic/game.h" |
3679 | 34 | #include "logic/map_objects/tribes/production_program.h" | 33 | #include "logic/map_objects/tribes/production_program.h" |
3680 | 35 | 34 | ||
3681 | === modified file 'src/logic/map_objects/tribes/tribes.cc' | |||
3682 | --- src/logic/map_objects/tribes/tribes.cc 2019-06-23 12:45:29 +0000 | |||
3683 | +++ src/logic/map_objects/tribes/tribes.cc 2019-08-31 15:58:01 +0000 | |||
3684 | @@ -293,15 +293,6 @@ | |||
3685 | 293 | return tribes_->get_mutable(tribeindex); | 293 | return tribes_->get_mutable(tribeindex); |
3686 | 294 | } | 294 | } |
3687 | 295 | 295 | ||
3688 | 296 | void Tribes::set_ware_type_has_demand_check(const DescriptionIndex& wareindex, | ||
3689 | 297 | const std::string& tribename) const { | ||
3690 | 298 | wares_->get_mutable(wareindex)->set_has_demand_check(tribename); | ||
3691 | 299 | } | ||
3692 | 300 | |||
3693 | 301 | void Tribes::set_worker_type_has_demand_check(const DescriptionIndex& workerindex) const { | ||
3694 | 302 | workers_->get_mutable(workerindex)->set_has_demand_check(); | ||
3695 | 303 | } | ||
3696 | 304 | |||
3697 | 305 | void Tribes::load_graphics() { | 296 | void Tribes::load_graphics() { |
3698 | 306 | for (size_t tribeindex = 0; tribeindex < nrtribes(); ++tribeindex) { | 297 | for (size_t tribeindex = 0; tribeindex < nrtribes(); ++tribeindex) { |
3699 | 307 | TribeDescr* tribe = tribes_->get_mutable(tribeindex); | 298 | TribeDescr* tribe = tribes_->get_mutable(tribeindex); |
3700 | @@ -366,6 +357,11 @@ | |||
3701 | 366 | // Some final checks on the gamedata | 357 | // Some final checks on the gamedata |
3702 | 367 | for (DescriptionIndex i = 0; i < tribes_->size(); ++i) { | 358 | for (DescriptionIndex i = 0; i < tribes_->size(); ++i) { |
3703 | 368 | TribeDescr* tribe_descr = tribes_->get_mutable(i); | 359 | TribeDescr* tribe_descr = tribes_->get_mutable(i); |
3704 | 360 | |||
3705 | 361 | // Register which wares and workers have economy demand checks for each tribe | ||
3706 | 362 | for (const DescriptionIndex bi : tribe_descr->buildings()) { | ||
3707 | 363 | postload_register_economy_demand_checks(*buildings_->get_mutable(bi), *tribe_descr); | ||
3708 | 364 | } | ||
3709 | 369 | // Verify that the preciousness has been set for all of the tribe's wares | 365 | // Verify that the preciousness has been set for all of the tribe's wares |
3710 | 370 | for (const DescriptionIndex wi : tribe_descr->wares()) { | 366 | for (const DescriptionIndex wi : tribe_descr->wares()) { |
3711 | 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()) == |
3712 | @@ -378,7 +374,36 @@ | |||
3713 | 378 | } | 374 | } |
3714 | 379 | } | 375 | } |
3715 | 380 | 376 | ||
3717 | 381 | // Set default trainingsites proportions for AI. Make sure that we get a sum of ca. 100 | 377 | /// Register wares and workers that have economy demand checks for a building |
3718 | 378 | void Tribes::postload_register_economy_demand_checks(BuildingDescr& building_descr, const TribeDescr& tribe_descr) | ||
3719 | 379 | { | ||
3720 | 380 | if (upcast(ProductionSiteDescr, prodsite, &building_descr)) { | ||
3721 | 381 | // This function can be called only once per loading of tribes | ||
3722 | 382 | assert(prodsite->ware_demand_checks() != nullptr); | ||
3723 | 383 | |||
3724 | 384 | for (const DescriptionIndex wi : *prodsite->ware_demand_checks()) { | ||
3725 | 385 | if (!tribe_descr.has_ware(wi)) { | ||
3726 | 386 | throw GameDataError("Productionsite '%s' for tribe '%s' has an economy demand check for ware '%s', but the tribe does not use this ware", | ||
3727 | 387 | prodsite->name().c_str(), | ||
3728 | 388 | tribe_descr.name().c_str(), | ||
3729 | 389 | get_ware_descr(wi)->name().c_str()); | ||
3730 | 390 | } | ||
3731 | 391 | wares_->get_mutable(wi)->set_has_demand_check(tribe_descr.name()); | ||
3732 | 392 | } | ||
3733 | 393 | for (const DescriptionIndex wi : *prodsite->worker_demand_checks()) { | ||
3734 | 394 | if (!tribe_descr.has_worker(wi)) { | ||
3735 | 395 | throw GameDataError("Productionsite '%s' for tribe '%s' has an economy demand check for worker '%s', but the tribe does not use this worker", | ||
3736 | 396 | prodsite->name().c_str(), | ||
3737 | 397 | tribe_descr.name().c_str(), | ||
3738 | 398 | get_worker_descr(wi)->name().c_str()); | ||
3739 | 399 | } | ||
3740 | 400 | workers_->get_mutable(wi)->set_has_demand_check(); | ||
3741 | 401 | } | ||
3742 | 402 | prodsite->clear_demand_checks(); | ||
3743 | 403 | } | ||
3744 | 404 | } | ||
3745 | 405 | |||
3746 | 406 | /// Set default trainingsites proportions for AI. Make sure that we get a sum of ca. 100 | ||
3747 | 382 | void Tribes::postload_calculate_trainingsites_proportions() { | 407 | void Tribes::postload_calculate_trainingsites_proportions() { |
3748 | 383 | for (DescriptionIndex i = 0; i < tribes_->size(); ++i) { | 408 | for (DescriptionIndex i = 0; i < tribes_->size(); ++i) { |
3749 | 384 | TribeDescr* tribe_descr = tribes_->get_mutable(i); | 409 | TribeDescr* tribe_descr = tribes_->get_mutable(i); |
3750 | 385 | 410 | ||
3751 | === modified file 'src/logic/map_objects/tribes/tribes.h' | |||
3752 | --- src/logic/map_objects/tribes/tribes.h 2019-05-16 09:15:03 +0000 | |||
3753 | +++ src/logic/map_objects/tribes/tribes.h 2019-08-31 15:58:01 +0000 | |||
3754 | @@ -133,10 +133,6 @@ | |||
3755 | 133 | const WorkerDescr* get_worker_descr(DescriptionIndex worker_index) const; | 133 | const WorkerDescr* get_worker_descr(DescriptionIndex worker_index) const; |
3756 | 134 | const TribeDescr* get_tribe_descr(DescriptionIndex tribe_index) const; | 134 | const TribeDescr* get_tribe_descr(DescriptionIndex tribe_index) const; |
3757 | 135 | 135 | ||
3758 | 136 | void set_ware_type_has_demand_check(const DescriptionIndex& ware_index, | ||
3759 | 137 | const std::string& tribename) const; | ||
3760 | 138 | void set_worker_type_has_demand_check(const DescriptionIndex& worker_index) const; | ||
3761 | 139 | |||
3762 | 140 | /// Load tribes' graphics | 136 | /// Load tribes' graphics |
3763 | 141 | void load_graphics(); | 137 | void load_graphics(); |
3764 | 142 | 138 | ||
3765 | @@ -147,6 +143,7 @@ | |||
3766 | 147 | 143 | ||
3767 | 148 | private: | 144 | private: |
3768 | 149 | void postload_calculate_trainingsites_proportions(); | 145 | void postload_calculate_trainingsites_proportions(); |
3769 | 146 | void postload_register_economy_demand_checks(BuildingDescr& building_descr, const TribeDescr& tribe_descr); | ||
3770 | 150 | 147 | ||
3771 | 151 | std::unique_ptr<DescriptionMaintainer<BuildingDescr>> buildings_; | 148 | std::unique_ptr<DescriptionMaintainer<BuildingDescr>> buildings_; |
3772 | 152 | std::unique_ptr<DescriptionMaintainer<ImmovableDescr>> immovables_; | 149 | std::unique_ptr<DescriptionMaintainer<ImmovableDescr>> immovables_; |
3773 | 153 | 150 | ||
3774 | === modified file 'src/logic/map_objects/tribes/worker.cc' | |||
3775 | --- src/logic/map_objects/tribes/worker.cc 2019-05-28 17:01:30 +0000 | |||
3776 | +++ src/logic/map_objects/tribes/worker.cc 2019-08-31 15:58:01 +0000 | |||
3777 | @@ -36,7 +36,6 @@ | |||
3778 | 36 | #include "graphic/graphic.h" | 36 | #include "graphic/graphic.h" |
3779 | 37 | #include "graphic/rendertarget.h" | 37 | #include "graphic/rendertarget.h" |
3780 | 38 | #include "graphic/text_layout.h" | 38 | #include "graphic/text_layout.h" |
3781 | 39 | #include "helper.h" | ||
3782 | 40 | #include "io/fileread.h" | 39 | #include "io/fileread.h" |
3783 | 41 | #include "io/filewrite.h" | 40 | #include "io/filewrite.h" |
3784 | 42 | #include "logic/cmd_incorporate.h" | 41 | #include "logic/cmd_incorporate.h" |
3785 | @@ -3146,7 +3145,7 @@ | |||
3786 | 3146 | return Bob::Loader::get_task(name); | 3145 | return Bob::Loader::get_task(name); |
3787 | 3147 | } | 3146 | } |
3788 | 3148 | 3147 | ||
3790 | 3149 | const BobProgramBase* Worker::Loader::get_program(const std::string& name) { | 3148 | const MapObjectProgram* Worker::Loader::get_program(const std::string& name) { |
3791 | 3150 | Worker& worker = get<Worker>(); | 3149 | Worker& worker = get<Worker>(); |
3792 | 3151 | return worker.descr().get_program(name); | 3150 | return worker.descr().get_program(name); |
3793 | 3152 | } | 3151 | } |
3794 | 3153 | 3152 | ||
3795 | === modified file 'src/logic/map_objects/tribes/worker.h' | |||
3796 | --- src/logic/map_objects/tribes/worker.h 2019-04-24 06:01:37 +0000 | |||
3797 | +++ src/logic/map_objects/tribes/worker.h 2019-08-31 15:58:01 +0000 | |||
3798 | @@ -303,7 +303,7 @@ | |||
3799 | 303 | 303 | ||
3800 | 304 | protected: | 304 | protected: |
3801 | 305 | const Task* get_task(const std::string& name) override; | 305 | const Task* get_task(const std::string& name) override; |
3803 | 306 | const BobProgramBase* get_program(const std::string& name) override; | 306 | const MapObjectProgram* get_program(const std::string& name) override; |
3804 | 307 | 307 | ||
3805 | 308 | private: | 308 | private: |
3806 | 309 | uint32_t location_; | 309 | uint32_t location_; |
3807 | 310 | 310 | ||
3808 | === modified file 'src/logic/map_objects/tribes/worker_descr.cc' | |||
3809 | --- src/logic/map_objects/tribes/worker_descr.cc 2019-05-25 08:51:42 +0000 | |||
3810 | +++ src/logic/map_objects/tribes/worker_descr.cc 2019-08-31 15:58:01 +0000 | |||
3811 | @@ -106,16 +106,14 @@ | |||
3812 | 106 | std::unique_ptr<LuaTable> programs_table = table.get_table("programs"); | 106 | std::unique_ptr<LuaTable> programs_table = table.get_table("programs"); |
3813 | 107 | for (std::string program_name : programs_table->keys<std::string>()) { | 107 | for (std::string program_name : programs_table->keys<std::string>()) { |
3814 | 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); |
3816 | 109 | 109 | if (programs_.count(program_name)) { | |
3817 | 110 | throw GameDataError("Program '%s' has already been declared for worker '%s'", program_name.c_str(), name().c_str()); | ||
3818 | 111 | } | ||
3819 | 110 | try { | 112 | try { |
3826 | 111 | if (programs_.count(program_name)) | 113 | programs_[program_name] = std::unique_ptr<WorkerProgram>( |
3827 | 112 | throw wexception("this program has already been declared"); | 114 | new WorkerProgram(program_name, *programs_table->get_table(program_name), *this, tribes_)); |
3822 | 113 | |||
3823 | 114 | programs_[program_name] = | ||
3824 | 115 | std::unique_ptr<WorkerProgram>(new WorkerProgram(program_name, *this, tribes_)); | ||
3825 | 116 | programs_[program_name]->parse(*programs_table->get_table(program_name)); | ||
3828 | 117 | } catch (const std::exception& e) { | 115 | } catch (const std::exception& e) { |
3830 | 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()); |
3831 | 119 | } | 117 | } |
3832 | 120 | } | 118 | } |
3833 | 121 | } | 119 | } |
3834 | @@ -136,8 +134,9 @@ | |||
3835 | 136 | WorkerProgram const* WorkerDescr::get_program(const std::string& programname) const { | 134 | WorkerProgram const* WorkerDescr::get_program(const std::string& programname) const { |
3836 | 137 | Programs::const_iterator it = programs_.find(programname); | 135 | Programs::const_iterator it = programs_.find(programname); |
3837 | 138 | 136 | ||
3840 | 139 | if (it == programs_.end()) | 137 | if (it == programs_.end()) { |
3841 | 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()); |
3842 | 139 | } | ||
3843 | 141 | 140 | ||
3844 | 142 | return it->second.get(); | 141 | return it->second.get(); |
3845 | 143 | } | 142 | } |
3846 | 144 | 143 | ||
3847 | === modified file 'src/logic/map_objects/tribes/worker_program.cc' | |||
3848 | --- src/logic/map_objects/tribes/worker_program.cc 2019-05-11 13:48:12 +0000 | |||
3849 | +++ src/logic/map_objects/tribes/worker_program.cc 2019-08-31 15:58:01 +0000 | |||
3850 | @@ -23,7 +23,6 @@ | |||
3851 | 23 | #include <string> | 23 | #include <string> |
3852 | 24 | 24 | ||
3853 | 25 | #include "base/log.h" | 25 | #include "base/log.h" |
3854 | 26 | #include "helper.h" | ||
3855 | 27 | #include "logic/game_data_error.h" | 26 | #include "logic/game_data_error.h" |
3856 | 28 | #include "logic/map_objects/findnode.h" | 27 | #include "logic/map_objects/findnode.h" |
3857 | 29 | #include "sound/sound_handler.h" | 28 | #include "sound/sound_handler.h" |
3858 | @@ -98,54 +97,45 @@ | |||
3859 | 98 | 97 | ||
3860 | 99 | {nullptr, nullptr}}; | 98 | {nullptr, nullptr}}; |
3861 | 100 | 99 | ||
3862 | 100 | |||
3863 | 101 | /** | 101 | /** |
3864 | 102 | * Parse a program | 102 | * Parse a program |
3865 | 103 | */ | 103 | */ |
3872 | 104 | void WorkerProgram::parse(const LuaTable& table) { | 104 | WorkerProgram::WorkerProgram(const std::string& init_name, const LuaTable& actions_table, const WorkerDescr& worker, const Tribes& tribes) |
3873 | 105 | for (const std::string& string : table.array_entries<std::string>()) { | 105 | : MapObjectProgram(init_name), worker_(worker), tribes_(tribes) { |
3874 | 106 | if (string.empty()) { | 106 | |
3875 | 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>()) { |
3876 | 108 | worker_.name().c_str()); | 108 | if (line.empty()) { |
3877 | 109 | break; | 109 | throw GameDataError("Empty line"); |
3878 | 110 | } | 110 | } |
3879 | 111 | try { | 111 | try { |
3884 | 112 | std::vector<std::string> cmd(split_string(string, "=")); | 112 | |
3885 | 113 | if (cmd.empty()) { | 113 | ProgramParseInput parseinput = parse_program_string(line); |
3882 | 114 | continue; | ||
3883 | 115 | } | ||
3886 | 116 | 114 | ||
3887 | 117 | // Find the appropriate parser | 115 | // Find the appropriate parser |
3888 | 118 | Worker::Action act; | 116 | Worker::Action act; |
3889 | 119 | uint32_t mapidx; | 117 | uint32_t mapidx; |
3890 | 120 | 118 | ||
3891 | 121 | for (mapidx = 0; parsemap_[mapidx].name; ++mapidx) { | 119 | for (mapidx = 0; parsemap_[mapidx].name; ++mapidx) { |
3893 | 122 | if (cmd[0] == parsemap_[mapidx].name) { | 120 | if (parseinput.name == parsemap_[mapidx].name) { |
3894 | 123 | break; | 121 | break; |
3895 | 124 | } | 122 | } |
3896 | 125 | } | 123 | } |
3897 | 126 | 124 | ||
3898 | 127 | if (!parsemap_[mapidx].name) { | 125 | if (!parsemap_[mapidx].name) { |
3913 | 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()); |
3914 | 129 | } | 127 | } |
3915 | 130 | 128 | ||
3916 | 131 | // TODO(GunChleoc): Quick and dirty solution, don't do it like that when we unify the | 129 | (this->*parsemap_[mapidx].function)(&act, parseinput.arguments); |
3903 | 132 | // program parsers | ||
3904 | 133 | if (cmd.size() == 2) { | ||
3905 | 134 | const std::vector<std::string> parameters(split_string(cmd[1], " \t\n")); | ||
3906 | 135 | cmd.pop_back(); | ||
3907 | 136 | for (const std::string& parameter : parameters) { | ||
3908 | 137 | cmd.push_back(parameter); | ||
3909 | 138 | } | ||
3910 | 139 | } | ||
3911 | 140 | |||
3912 | 141 | (this->*parsemap_[mapidx].function)(&act, cmd); | ||
3917 | 142 | 130 | ||
3918 | 143 | actions_.push_back(act); | 131 | actions_.push_back(act); |
3919 | 144 | } catch (const std::exception& e) { | 132 | } catch (const std::exception& e) { |
3922 | 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()); |
3921 | 146 | string.c_str(), name_.c_str(), worker_.name().c_str(), e.what()); | ||
3923 | 147 | } | 134 | } |
3924 | 148 | } | 135 | } |
3925 | 136 | if (actions_.empty()) { | ||
3926 | 137 | throw GameDataError("No actions found"); | ||
3927 | 138 | } | ||
3928 | 149 | } | 139 | } |
3929 | 150 | 140 | ||
3930 | 151 | /* RST | 141 | /* RST |
3931 | @@ -172,11 +162,12 @@ | |||
3932 | 172 | * iparam1 = ware index | 162 | * iparam1 = ware index |
3933 | 173 | */ | 163 | */ |
3934 | 174 | void WorkerProgram::parse_createware(Worker::Action* act, const std::vector<std::string>& cmd) { | 164 | void WorkerProgram::parse_createware(Worker::Action* act, const std::vector<std::string>& cmd) { |
3936 | 175 | if (cmd.size() != 2) | 165 | if (cmd.size() != 1) { |
3937 | 176 | throw wexception("Usage: createware=<ware type>"); | 166 | throw wexception("Usage: createware=<ware type>"); |
3938 | 167 | } | ||
3939 | 177 | 168 | ||
3940 | 178 | act->function = &Worker::run_createware; | 169 | act->function = &Worker::run_createware; |
3942 | 179 | act->iparam1 = tribes_.safe_ware_index(cmd[1]); | 170 | act->iparam1 = tribes_.safe_ware_index(cmd[0]); |
3943 | 180 | } | 171 | } |
3944 | 181 | 172 | ||
3945 | 182 | /* RST | 173 | /* RST |
3946 | @@ -207,16 +198,13 @@ | |||
3947 | 207 | * sparam1 = resource | 198 | * sparam1 = resource |
3948 | 208 | */ | 199 | */ |
3949 | 209 | void WorkerProgram::parse_mine(Worker::Action* act, const std::vector<std::string>& cmd) { | 200 | void WorkerProgram::parse_mine(Worker::Action* act, const std::vector<std::string>& cmd) { |
3952 | 210 | if (cmd.size() != 3) | 201 | if (cmd.size() != 2) { |
3953 | 211 | throw wexception("Usage: mine=<ware type> <area>"); | 202 | throw GameDataError("Usage: mine=<ware type> <workarea radius>"); |
3954 | 203 | } | ||
3955 | 212 | 204 | ||
3956 | 213 | act->function = &Worker::run_mine; | 205 | act->function = &Worker::run_mine; |
3963 | 214 | act->sparam1 = cmd[1]; | 206 | act->sparam1 = cmd[0]; |
3964 | 215 | char* endp; | 207 | act->iparam1 = read_positive(cmd[1]); |
3959 | 216 | act->iparam1 = strtol(cmd[2].c_str(), &endp, 0); | ||
3960 | 217 | |||
3961 | 218 | if (*endp) | ||
3962 | 219 | throw wexception("Bad area '%s'", cmd[2].c_str()); | ||
3965 | 220 | } | 208 | } |
3966 | 221 | 209 | ||
3967 | 222 | /* RST | 210 | /* RST |
3968 | @@ -244,16 +232,13 @@ | |||
3969 | 244 | * sparam1 = resource | 232 | * sparam1 = resource |
3970 | 245 | */ | 233 | */ |
3971 | 246 | void WorkerProgram::parse_breed(Worker::Action* act, const std::vector<std::string>& cmd) { | 234 | void WorkerProgram::parse_breed(Worker::Action* act, const std::vector<std::string>& cmd) { |
3974 | 247 | if (cmd.size() != 3) | 235 | if (cmd.size() != 2) { |
3975 | 248 | throw wexception("Usage: breed=<ware type> <area>"); | 236 | throw GameDataError("Usage: breed=<ware type> <workarea radius>"); |
3976 | 237 | } | ||
3977 | 249 | 238 | ||
3978 | 250 | act->function = &Worker::run_breed; | 239 | act->function = &Worker::run_breed; |
3985 | 251 | act->sparam1 = cmd[1]; | 240 | act->sparam1 = cmd[0]; |
3986 | 252 | char* endp; | 241 | act->iparam1 = read_positive(cmd[1]); |
3981 | 253 | act->iparam1 = strtol(cmd[2].c_str(), &endp, 0); | ||
3982 | 254 | |||
3983 | 255 | if (*endp) | ||
3984 | 256 | throw wexception("Bad area '%s'", cmd[2].c_str()); | ||
3987 | 257 | } | 242 | } |
3988 | 258 | 243 | ||
3989 | 259 | /* RST | 244 | /* RST |
3990 | @@ -296,37 +281,26 @@ | |||
3991 | 296 | * sparam1 = type | 281 | * sparam1 = type |
3992 | 297 | */ | 282 | */ |
3993 | 298 | void WorkerProgram::parse_findobject(Worker::Action* act, const std::vector<std::string>& cmd) { | 283 | void WorkerProgram::parse_findobject(Worker::Action* act, const std::vector<std::string>& cmd) { |
3994 | 299 | uint32_t i; | ||
3995 | 300 | |||
3996 | 301 | act->function = &Worker::run_findobject; | 284 | act->function = &Worker::run_findobject; |
3997 | 302 | act->iparam1 = -1; | 285 | act->iparam1 = -1; |
3998 | 303 | act->iparam2 = -1; | 286 | act->iparam2 = -1; |
3999 | 304 | act->sparam1 = "immovable"; | 287 | act->sparam1 = "immovable"; |
4000 | 305 | 288 | ||
4001 | 306 | // Parse predicates | 289 | // Parse predicates |
4020 | 307 | for (i = 1; i < cmd.size(); ++i) { | 290 | for (const std::string& argument : cmd) { |
4021 | 308 | uint32_t idx = cmd[i].find(':'); | 291 | const std::pair<std::string, std::string> item = read_key_value_pair(argument, ':'); |
4022 | 309 | std::string const key = cmd[i].substr(0, idx); | 292 | |
4023 | 310 | std::string const value = cmd[i].substr(idx + 1); | 293 | if (item.first == "radius") { |
4024 | 311 | 294 | act->iparam1 = read_positive(item.second); | |
4025 | 312 | if (key == "radius") { | 295 | } else if (item.first == "attrib") { |
4026 | 313 | char* endp; | 296 | act->iparam2 = MapObjectDescr::get_attribute_id(item.second); |
4027 | 314 | 297 | } else if (item.first == "type") { | |
4028 | 315 | act->iparam1 = strtol(value.c_str(), &endp, 0); | 298 | act->sparam1 = item.second; |
4029 | 316 | if (*endp) | 299 | } else { |
4030 | 317 | throw wexception("Bad findobject radius '%s'", value.c_str()); | 300 | throw GameDataError("Unknown findobject predicate %s", argument.c_str()); |
4031 | 318 | 301 | } | |
4014 | 319 | } else if (key == "attrib") { | ||
4015 | 320 | act->iparam2 = MapObjectDescr::get_attribute_id(value); | ||
4016 | 321 | } else if (key == "type") { | ||
4017 | 322 | act->sparam1 = value; | ||
4018 | 323 | } else | ||
4019 | 324 | throw wexception("Bad findobject predicate %s:%s", key.c_str(), value.c_str()); | ||
4032 | 325 | } | 302 | } |
4033 | 326 | 303 | ||
4034 | 327 | if (act->iparam1 <= 0) | ||
4035 | 328 | throw wexception("findobject: must specify radius"); | ||
4036 | 329 | |||
4037 | 330 | workarea_info_[act->iparam1].insert(" findobject"); | 304 | workarea_info_[act->iparam1].insert(" findobject"); |
4038 | 331 | } | 305 | } |
4039 | 332 | 306 | ||
4040 | @@ -408,8 +382,6 @@ | |||
4041 | 408 | * sparam1 = Resource | 382 | * sparam1 = Resource |
4042 | 409 | */ | 383 | */ |
4043 | 410 | void WorkerProgram::parse_findspace(Worker::Action* act, const std::vector<std::string>& cmd) { | 384 | void WorkerProgram::parse_findspace(Worker::Action* act, const std::vector<std::string>& cmd) { |
4044 | 411 | uint32_t i; | ||
4045 | 412 | |||
4046 | 413 | act->function = &Worker::run_findspace; | 385 | act->function = &Worker::run_findspace; |
4047 | 414 | act->iparam1 = -1; | 386 | act->iparam1 = -1; |
4048 | 415 | act->iparam2 = -1; | 387 | act->iparam2 = -1; |
4049 | @@ -420,61 +392,58 @@ | |||
4050 | 420 | act->sparam1 = ""; | 392 | act->sparam1 = ""; |
4051 | 421 | 393 | ||
4052 | 422 | // Parse predicates | 394 | // Parse predicates |
4097 | 423 | for (i = 1; i < cmd.size(); ++i) { | 395 | for (const std::string& argument : cmd) { |
4098 | 424 | uint32_t idx = cmd[i].find(':'); | 396 | try { |
4099 | 425 | std::string key = cmd[i].substr(0, idx); | 397 | const std::pair<std::string, std::string> item = read_key_value_pair(argument, ':'); |
4100 | 426 | std::string value = cmd[i].substr(idx + 1); | 398 | |
4101 | 427 | 399 | if (item.first == "radius") { | |
4102 | 428 | if (key == "radius") { | 400 | act->iparam1 = read_positive(item.second); |
4103 | 429 | char* endp; | 401 | } else if (item.first == "size") { |
4104 | 430 | 402 | static const struct { | |
4105 | 431 | act->iparam1 = strtol(value.c_str(), &endp, 0); | 403 | char const* name; |
4106 | 432 | if (*endp) | 404 | int32_t val; |
4107 | 433 | throw wexception("Bad findspace radius '%s'", value.c_str()); | 405 | } sizenames[] = {{"any", FindNodeSize::sizeAny}, {"build", FindNodeSize::sizeBuild}, |
4108 | 434 | 406 | {"small", FindNodeSize::sizeSmall}, {"medium", FindNodeSize::sizeMedium}, | |
4109 | 435 | } else if (key == "size") { | 407 | {"big", FindNodeSize::sizeBig}, {"mine", FindNodeSize::sizeMine}, |
4110 | 436 | static const struct { | 408 | {"port", FindNodeSize::sizePort}, {nullptr, 0}}; |
4111 | 437 | char const* name; | 409 | |
4112 | 438 | int32_t val; | 410 | int32_t index; |
4113 | 439 | } sizenames[] = {{"any", FindNodeSize::sizeAny}, {"build", FindNodeSize::sizeBuild}, | 411 | |
4114 | 440 | {"small", FindNodeSize::sizeSmall}, {"medium", FindNodeSize::sizeMedium}, | 412 | for (index = 0; sizenames[index].name; ++index) { |
4115 | 441 | {"big", FindNodeSize::sizeBig}, {"mine", FindNodeSize::sizeMine}, | 413 | if (item.second == sizenames[index].name) { |
4116 | 442 | {"port", FindNodeSize::sizePort}, {nullptr, 0}}; | 414 | break; |
4117 | 443 | 415 | } | |
4118 | 444 | int32_t index; | 416 | } |
4119 | 445 | 417 | ||
4120 | 446 | for (index = 0; sizenames[index].name; ++index) | 418 | if (!sizenames[index].name) { |
4121 | 447 | if (value == sizenames[index].name) | 419 | throw GameDataError("Bad findspace size '%s'", item.second.c_str()); |
4122 | 448 | break; | 420 | } |
4123 | 449 | 421 | ||
4124 | 450 | if (!sizenames[index].name) | 422 | act->iparam2 = sizenames[index].val; |
4125 | 451 | throw wexception("Bad findspace size '%s'", value.c_str()); | 423 | } else if (item.first == "breed") { |
4126 | 452 | 424 | act->iparam4 = 1; | |
4127 | 453 | act->iparam2 = sizenames[index].val; | 425 | } else if (item.first == "resource") { |
4128 | 454 | } else if (key == "breed") { | 426 | act->sparam1 = item.second; |
4129 | 455 | act->iparam4 = 1; | 427 | } else if (item.first == "space") { |
4130 | 456 | } else if (key == "resource") { | 428 | act->iparam3 = 1; |
4131 | 457 | act->sparam1 = value; | 429 | } else if (item.first == "avoid") { |
4132 | 458 | } else if (key == "space") { | 430 | act->iparam5 = MapObjectDescr::get_attribute_id(item.second); |
4133 | 459 | act->iparam3 = 1; | 431 | } else if (item.first == "saplingsearches") { |
4134 | 460 | } else if (key == "avoid") { | 432 | act->iparam6 = read_positive(item.second); |
4091 | 461 | act->iparam5 = MapObjectDescr::get_attribute_id(value); | ||
4092 | 462 | } else if (key == "saplingsearches") { | ||
4093 | 463 | int ival = strtol(value.c_str(), nullptr, 10); | ||
4094 | 464 | if (1 != act->iparam6 || 1 > ival) { | ||
4095 | 465 | throw wexception("Findspace: Forester should consider at least one spot.%d %d %s", | ||
4096 | 466 | act->iparam6, ival, value.c_str()); | ||
4135 | 467 | } else { | 433 | } else { |
4137 | 468 | act->iparam6 = ival; | 434 | throw GameDataError("Unknown findspace predicate %s", item.first.c_str()); |
4138 | 469 | } | 435 | } |
4141 | 470 | } else | 436 | } catch (const GameDataError& e) { |
4142 | 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()); |
4143 | 438 | } | ||
4144 | 472 | } | 439 | } |
4145 | 473 | 440 | ||
4150 | 474 | if (act->iparam1 <= 0) | 441 | if (act->iparam1 <= 0) { |
4151 | 475 | throw wexception("findspace: must specify radius"); | 442 | throw GameDataError("findspace: must specify radius"); |
4152 | 476 | if (act->iparam2 < 0) | 443 | } |
4153 | 477 | throw wexception("findspace: must specify size"); | 444 | if (act->iparam2 < 0) { |
4154 | 445 | throw GameDataError("findspace: must specify size"); | ||
4155 | 446 | } | ||
4156 | 478 | workarea_info_[act->iparam1].insert(" findspace"); | 447 | workarea_info_[act->iparam1].insert(" findspace"); |
4157 | 479 | } | 448 | } |
4158 | 480 | 449 | ||
4159 | @@ -526,19 +495,21 @@ | |||
4160 | 526 | * iparam1 = walkXXX | 495 | * iparam1 = walkXXX |
4161 | 527 | */ | 496 | */ |
4162 | 528 | void WorkerProgram::parse_walk(Worker::Action* act, const std::vector<std::string>& cmd) { | 497 | void WorkerProgram::parse_walk(Worker::Action* act, const std::vector<std::string>& cmd) { |
4165 | 529 | if (cmd.size() != 2) | 498 | if (cmd.size() != 1) { |
4166 | 530 | throw wexception("Usage: walk=<where>"); | 499 | throw GameDataError("Usage: walk=object|coords|object-or-coords"); |
4167 | 500 | } | ||
4168 | 531 | 501 | ||
4169 | 532 | act->function = &Worker::run_walk; | 502 | act->function = &Worker::run_walk; |
4170 | 533 | 503 | ||
4172 | 534 | if (cmd[1] == "object") | 504 | if (cmd[0] == "object") { |
4173 | 535 | act->iparam1 = Worker::Action::walkObject; | 505 | act->iparam1 = Worker::Action::walkObject; |
4175 | 536 | else if (cmd[1] == "coords") | 506 | } else if (cmd[0] == "coords") { |
4176 | 537 | act->iparam1 = Worker::Action::walkCoords; | 507 | act->iparam1 = Worker::Action::walkCoords; |
4178 | 538 | else if (cmd[1] == "object-or-coords") | 508 | } else if (cmd[0] == "object-or-coords") { |
4179 | 539 | act->iparam1 = Worker::Action::walkObject | Worker::Action::walkCoords; | 509 | act->iparam1 = Worker::Action::walkObject | Worker::Action::walkCoords; |
4182 | 540 | else | 510 | } else { |
4183 | 541 | throw wexception("Bad walk destination '%s'", cmd[1].c_str()); | 511 | throw GameDataError("Bad walk destination '%s'", cmd[0].c_str()); |
4184 | 512 | } | ||
4185 | 542 | } | 513 | } |
4186 | 543 | 514 | ||
4187 | 544 | /* RST | 515 | /* RST |
4188 | @@ -565,28 +536,14 @@ | |||
4189 | 565 | * iparam2 = duration | 536 | * iparam2 = duration |
4190 | 566 | */ | 537 | */ |
4191 | 567 | void WorkerProgram::parse_animate(Worker::Action* act, const std::vector<std::string>& cmd) { | 538 | void WorkerProgram::parse_animate(Worker::Action* act, const std::vector<std::string>& cmd) { |
4201 | 568 | char* endp; | 539 | AnimationParameters parameters = MapObjectProgram::parse_act_animate(cmd, worker_, true); |
4193 | 569 | |||
4194 | 570 | if (cmd.size() != 3) | ||
4195 | 571 | throw GameDataError("Usage: animate=<name> <time>"); | ||
4196 | 572 | |||
4197 | 573 | if (!worker_.is_animation_known(cmd[1])) { | ||
4198 | 574 | throw GameDataError("unknown animation \"%s\" in worker program for worker \"%s\"", | ||
4199 | 575 | cmd[1].c_str(), worker_.name().c_str()); | ||
4200 | 576 | } | ||
4202 | 577 | 540 | ||
4203 | 578 | act->function = &Worker::run_animate; | 541 | act->function = &Worker::run_animate; |
4204 | 579 | // If the second parameter to MapObjectDescr::get_animation is ever used for anything other than | 542 | // If the second parameter to MapObjectDescr::get_animation is ever used for anything other than |
4205 | 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, |
4215 | 581 | // we will need to store the animation name as a string in an iparam | 544 | // we will need to store the animation name as a string in an sparam |
4216 | 582 | act->iparam1 = worker_.get_animation(cmd[1], nullptr); | 545 | act->iparam1 = parameters.animation; |
4217 | 583 | 546 | act->iparam2 = parameters.duration; | |
4209 | 584 | act->iparam2 = strtol(cmd[2].c_str(), &endp, 0); | ||
4210 | 585 | if (*endp) | ||
4211 | 586 | throw GameDataError("Bad duration '%s'", cmd[2].c_str()); | ||
4212 | 587 | |||
4213 | 588 | if (act->iparam2 <= 0) | ||
4214 | 589 | throw GameDataError("animation duration must be positive"); | ||
4218 | 590 | } | 547 | } |
4219 | 591 | 548 | ||
4220 | 592 | /* RST | 549 | /* RST |
4221 | @@ -604,7 +561,10 @@ | |||
4222 | 604 | /** | 561 | /** |
4223 | 605 | * iparam1 = 0: don't drop ware on flag, 1: do drop ware on flag | 562 | * iparam1 = 0: don't drop ware on flag, 1: do drop ware on flag |
4224 | 606 | */ | 563 | */ |
4226 | 607 | void WorkerProgram::parse_return(Worker::Action* act, const std::vector<std::string>&) { | 564 | void WorkerProgram::parse_return(Worker::Action* act, const std::vector<std::string>& cmd) { |
4227 | 565 | if (!cmd.empty()) { | ||
4228 | 566 | throw GameDataError("Usage: return"); | ||
4229 | 567 | } | ||
4230 | 608 | act->function = &Worker::run_return; | 568 | act->function = &Worker::run_return; |
4231 | 609 | act->iparam1 = 1; // drop a ware on our owner's flag | 569 | act->iparam1 = 1; // drop a ware on our owner's flag |
4232 | 610 | } | 570 | } |
4233 | @@ -634,11 +594,12 @@ | |||
4234 | 634 | * sparam1 = callobject command name | 594 | * sparam1 = callobject command name |
4235 | 635 | */ | 595 | */ |
4236 | 636 | void WorkerProgram::parse_callobject(Worker::Action* act, const std::vector<std::string>& cmd) { | 596 | void WorkerProgram::parse_callobject(Worker::Action* act, const std::vector<std::string>& cmd) { |
4239 | 637 | if (cmd.size() != 2) | 597 | if (cmd.size() != 1) { |
4240 | 638 | throw wexception("Usage: callobject=<program name>"); | 598 | throw GameDataError("Usage: callobject=<program name>"); |
4241 | 599 | } | ||
4242 | 639 | 600 | ||
4243 | 640 | act->function = &Worker::run_callobject; | 601 | act->function = &Worker::run_callobject; |
4245 | 641 | act->sparam1 = cmd[1]; | 602 | act->sparam1 = cmd[0]; |
4246 | 642 | } | 603 | } |
4247 | 643 | 604 | ||
4248 | 644 | /* RST | 605 | /* RST |
4249 | @@ -691,36 +652,28 @@ | |||
4250 | 691 | * iparam1 one of plantXXX | 652 | * iparam1 one of plantXXX |
4251 | 692 | */ | 653 | */ |
4252 | 693 | void WorkerProgram::parse_plant(Worker::Action* act, const std::vector<std::string>& cmd) { | 654 | void WorkerProgram::parse_plant(Worker::Action* act, const std::vector<std::string>& cmd) { |
4256 | 694 | if (cmd.size() < 2) | 655 | if (cmd.empty()) { |
4257 | 695 | throw wexception( | 656 | throw GameDataError( |
4258 | 696 | "Usage: plant plant=attrib:<attribute> [attrib:<attribute> ...] [unless object]"); | 657 | "Usage: plant=attrib:<attribute> [attrib:<attribute> ...] [unless object]"); |
4259 | 658 | } | ||
4260 | 697 | 659 | ||
4261 | 698 | act->function = &Worker::run_plant; | 660 | act->function = &Worker::run_plant; |
4262 | 699 | act->iparam1 = Worker::Action::plantAlways; | 661 | act->iparam1 = Worker::Action::plantAlways; |
4265 | 700 | for (uint32_t i = 1; i < cmd.size(); ++i) { | 662 | for (uint32_t i = 0; i < cmd.size(); ++i) { |
4266 | 701 | if (i >= 2 && cmd[i] == "unless") { | 663 | if (cmd[i] == "unless") { |
4267 | 702 | ++i; | 664 | ++i; |
4271 | 703 | if (i >= cmd.size()) | 665 | if (i >= cmd.size()) { |
4272 | 704 | throw GameDataError("plant: something expected after unless"); | 666 | throw GameDataError("plant: something expected after 'unless'"); |
4273 | 705 | if (cmd[i] == "object") | 667 | } |
4274 | 668 | if (cmd[i] == "object") { | ||
4275 | 706 | act->iparam1 = Worker::Action::plantUnlessObject; | 669 | act->iparam1 = Worker::Action::plantUnlessObject; |
4277 | 707 | else | 670 | } else { |
4278 | 708 | throw GameDataError("plant: 'unless %s' not understood", cmd[i].c_str()); | 671 | throw GameDataError("plant: 'unless %s' not understood", cmd[i].c_str()); |
4280 | 709 | 672 | } | |
4281 | 710 | continue; | 673 | continue; |
4282 | 711 | } | 674 | } |
4283 | 712 | 675 | ||
4295 | 713 | // Check if immovable type exists | 676 | const std::string attrib_name = read_key_value_pair(cmd[i], ':', "", "attrib").second; |
4285 | 714 | std::vector<std::string> const list(split_string(cmd[i], ":")); | ||
4286 | 715 | if (list.size() != 2 || list.at(0) != "attrib") { | ||
4287 | 716 | throw GameDataError("plant takes a list of attrib:<attribute> that reference world and/or " | ||
4288 | 717 | "tribe immovables"); | ||
4289 | 718 | } | ||
4290 | 719 | |||
4291 | 720 | const std::string& attrib_name = list[1]; | ||
4292 | 721 | if (attrib_name.empty()) { | ||
4293 | 722 | throw GameDataError("plant has an empty attrib:<attribute> at position %d", i); | ||
4294 | 723 | } | ||
4296 | 724 | 677 | ||
4297 | 725 | // This will throw a GameDataError if the attribute doesn't exist. | 678 | // This will throw a GameDataError if the attribute doesn't exist. |
4298 | 726 | ImmovableDescr::get_attribute_id(attrib_name); | 679 | ImmovableDescr::get_attribute_id(attrib_name); |
4299 | @@ -750,14 +703,12 @@ | |||
4300 | 750 | */ | 703 | */ |
4301 | 751 | // TODO(GunChleoc): attrib:eatable would be much better, then depend on terrain too | 704 | // TODO(GunChleoc): attrib:eatable would be much better, then depend on terrain too |
4302 | 752 | void WorkerProgram::parse_createbob(Worker::Action* act, const std::vector<std::string>& cmd) { | 705 | void WorkerProgram::parse_createbob(Worker::Action* act, const std::vector<std::string>& cmd) { |
4305 | 753 | if (cmd.size() < 2) | 706 | if (cmd.empty()) { |
4306 | 754 | throw wexception("Usage: createbob=<bob name> <bob name> ..."); | 707 | throw GameDataError("Usage: createbob=<bob name> <bob name> ..."); |
4307 | 708 | } | ||
4308 | 755 | 709 | ||
4309 | 756 | act->function = &Worker::run_createbob; | 710 | act->function = &Worker::run_createbob; |
4314 | 757 | 711 | act->sparamv = std::move(cmd); | |
4311 | 758 | for (uint32_t i = 1; i < cmd.size(); ++i) { | ||
4312 | 759 | act->sparamv.push_back(cmd[i]); | ||
4313 | 760 | } | ||
4315 | 761 | } | 712 | } |
4316 | 762 | 713 | ||
4317 | 763 | /* RST | 714 | /* RST |
4318 | @@ -804,22 +755,14 @@ | |||
4319 | 804 | * sparam1 = subcommand | 755 | * sparam1 = subcommand |
4320 | 805 | */ | 756 | */ |
4321 | 806 | void WorkerProgram::parse_repeatsearch(Worker::Action* act, const std::vector<std::string>& cmd) { | 757 | void WorkerProgram::parse_repeatsearch(Worker::Action* act, const std::vector<std::string>& cmd) { |
4326 | 807 | char* endp; | 758 | if (cmd.size() != 3) { |
4327 | 808 | 759 | throw GameDataError("Usage: repeatsearch=<repeat #> <radius> <subcommand>"); | |
4328 | 809 | if (cmd.size() != 4) | 760 | } |
4325 | 810 | throw wexception("Usage: repeatsearch=<repeat #> <radius> <subcommand>"); | ||
4329 | 811 | 761 | ||
4330 | 812 | act->function = &Worker::run_repeatsearch; | 762 | act->function = &Worker::run_repeatsearch; |
4341 | 813 | 763 | act->iparam1 = read_positive(cmd[0]); | |
4342 | 814 | act->iparam1 = strtol(cmd[1].c_str(), &endp, 0); | 764 | act->iparam2 = read_positive(cmd[1]); |
4343 | 815 | if (*endp) | 765 | act->sparam1 = cmd[2]; |
4334 | 816 | throw wexception("Bad repeat count '%s'", cmd[1].c_str()); | ||
4335 | 817 | |||
4336 | 818 | act->iparam2 = strtol(cmd[2].c_str(), &endp, 0); | ||
4337 | 819 | if (*endp) | ||
4338 | 820 | throw wexception("Bad radius '%s'", cmd[2].c_str()); | ||
4339 | 821 | |||
4340 | 822 | act->sparam1 = cmd[3]; | ||
4344 | 823 | } | 766 | } |
4345 | 824 | 767 | ||
4346 | 825 | /* RST | 768 | /* RST |
4347 | @@ -840,8 +783,9 @@ | |||
4348 | 840 | } | 783 | } |
4349 | 841 | */ | 784 | */ |
4350 | 842 | void WorkerProgram::parse_findresources(Worker::Action* act, const std::vector<std::string>& cmd) { | 785 | void WorkerProgram::parse_findresources(Worker::Action* act, const std::vector<std::string>& cmd) { |
4353 | 843 | if (cmd.size() != 1) | 786 | if (!cmd.empty()) { |
4354 | 844 | throw wexception("Usage: findresources"); | 787 | throw GameDataError("Usage: findresources"); |
4355 | 788 | } | ||
4356 | 845 | 789 | ||
4357 | 846 | act->function = &Worker::run_findresources; | 790 | act->function = &Worker::run_findresources; |
4358 | 847 | } | 791 | } |
4359 | @@ -867,11 +811,12 @@ | |||
4360 | 867 | * iparam2 = time | 811 | * iparam2 = time |
4361 | 868 | */ | 812 | */ |
4362 | 869 | void WorkerProgram::parse_scout(Worker::Action* act, const std::vector<std::string>& cmd) { | 813 | void WorkerProgram::parse_scout(Worker::Action* act, const std::vector<std::string>& cmd) { |
4365 | 870 | if (cmd.size() != 3) | 814 | if (cmd.size() != 2) { |
4366 | 871 | throw wexception("Usage: scout=<radius> <time>"); | 815 | throw GameDataError("Usage: scout=<radius> <time>"); |
4367 | 816 | } | ||
4368 | 872 | 817 | ||
4371 | 873 | act->iparam1 = atoi(cmd[1].c_str()); | 818 | act->iparam1 = read_positive(cmd[0]); |
4372 | 874 | act->iparam2 = atoi(cmd[2].c_str()); | 819 | act->iparam2 = read_positive(cmd[1]); |
4373 | 875 | act->function = &Worker::run_scout; | 820 | act->function = &Worker::run_scout; |
4374 | 876 | } | 821 | } |
4375 | 877 | 822 | ||
4376 | @@ -902,17 +847,12 @@ | |||
4377 | 902 | } | 847 | } |
4378 | 903 | */ | 848 | */ |
4379 | 904 | void WorkerProgram::parse_playsound(Worker::Action* act, const std::vector<std::string>& cmd) { | 849 | void WorkerProgram::parse_playsound(Worker::Action* act, const std::vector<std::string>& cmd) { |
4385 | 905 | if (cmd.size() < 3 || cmd.size() > 4) | 850 | // 50% chance to play, only one instance at a time |
4386 | 906 | throw wexception("Usage: playsound <sound_dir> <sound_name> [priority]"); | 851 | PlaySoundParameters parameters = MapObjectProgram::parse_act_play_sound(cmd, kFxPriorityMedium); |
4387 | 907 | 852 | ||
4388 | 908 | act->iparam2 = SoundHandler::register_fx(SoundType::kAmbient, cmd[1]); | 853 | act->iparam1 = parameters.priority; |
4389 | 909 | 854 | act->iparam2 = parameters.fx; | |
4390 | 910 | act->function = &Worker::run_playsound; | 855 | act->function = &Worker::run_playsound; |
4391 | 911 | act->iparam1 = cmd.size() == 2 ? kFxPriorityMedium : atoi(cmd[2].c_str()); | ||
4392 | 912 | if (act->iparam1 < kFxPriorityLowest) { | ||
4393 | 913 | throw GameDataError("Minmum priority for sounds is %d, but only %d was specified for %s", | ||
4394 | 914 | kFxPriorityLowest, act->iparam1, cmd[1].c_str()); | ||
4395 | 915 | } | ||
4396 | 916 | } | 856 | } |
4397 | 917 | 857 | ||
4398 | 918 | /* RST | 858 | /* RST |
4399 | @@ -943,8 +883,9 @@ | |||
4400 | 943 | * for construction. This is used in ship building. | 883 | * for construction. This is used in ship building. |
4401 | 944 | */ | 884 | */ |
4402 | 945 | void WorkerProgram::parse_construct(Worker::Action* act, const std::vector<std::string>& cmd) { | 885 | void WorkerProgram::parse_construct(Worker::Action* act, const std::vector<std::string>& cmd) { |
4405 | 946 | if (cmd.size() != 1) | 886 | if (!cmd.empty()) { |
4406 | 947 | throw wexception("Usage: construct"); | 887 | throw GameDataError("Usage: construct"); |
4407 | 888 | } | ||
4408 | 948 | 889 | ||
4409 | 949 | act->function = &Worker::run_construct; | 890 | act->function = &Worker::run_construct; |
4410 | 950 | } | 891 | } |
4411 | 951 | 892 | ||
4412 | === modified file 'src/logic/map_objects/tribes/worker_program.h' | |||
4413 | --- src/logic/map_objects/tribes/worker_program.h 2019-02-23 11:00:49 +0000 | |||
4414 | +++ src/logic/map_objects/tribes/worker_program.h 2019-08-31 15:58:01 +0000 | |||
4415 | @@ -24,6 +24,7 @@ | |||
4416 | 24 | 24 | ||
4417 | 25 | #include "base/macros.h" | 25 | #include "base/macros.h" |
4418 | 26 | #include "logic/map_objects/bob.h" | 26 | #include "logic/map_objects/bob.h" |
4419 | 27 | #include "logic/map_objects/map_object_program.h" | ||
4420 | 27 | #include "logic/map_objects/tribes/tribes.h" | 28 | #include "logic/map_objects/tribes/tribes.h" |
4421 | 28 | #include "logic/map_objects/tribes/workarea_info.h" | 29 | #include "logic/map_objects/tribes/workarea_info.h" |
4422 | 29 | #include "logic/map_objects/tribes/worker.h" | 30 | #include "logic/map_objects/tribes/worker.h" |
4423 | @@ -35,20 +36,13 @@ | |||
4424 | 35 | // declaration (Chicken-and-egg problem) | 36 | // declaration (Chicken-and-egg problem) |
4425 | 36 | class WorkerDescr; | 37 | class WorkerDescr; |
4426 | 37 | 38 | ||
4428 | 38 | struct WorkerProgram : public BobProgramBase { | 39 | struct WorkerProgram : public MapObjectProgram { |
4429 | 39 | 40 | ||
4430 | 40 | using ParseWorkerProgramFn = void (WorkerProgram::*)(Worker::Action*, | 41 | using ParseWorkerProgramFn = void (WorkerProgram::*)(Worker::Action*, |
4431 | 41 | const std::vector<std::string>&); | 42 | const std::vector<std::string>&); |
4432 | 42 | 43 | ||
4438 | 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); |
4434 | 44 | : name_(name), worker_(worker), tribes_(tribes) { | ||
4435 | 45 | } | ||
4436 | 46 | ~WorkerProgram() override { | ||
4437 | 47 | } | ||
4439 | 48 | 45 | ||
4440 | 49 | std::string get_name() const override { | ||
4441 | 50 | return name_; | ||
4442 | 51 | } | ||
4443 | 52 | using Actions = std::vector<Worker::Action>; | 46 | using Actions = std::vector<Worker::Action>; |
4444 | 53 | Actions::size_type get_size() const { | 47 | Actions::size_type get_size() const { |
4445 | 54 | return actions_.size(); | 48 | return actions_.size(); |
4446 | @@ -62,7 +56,6 @@ | |||
4447 | 62 | return &actions_[idx]; | 56 | return &actions_[idx]; |
4448 | 63 | } | 57 | } |
4449 | 64 | 58 | ||
4450 | 65 | void parse(const LuaTable& table); | ||
4451 | 66 | const WorkareaInfo& get_workarea_info() const { | 59 | const WorkareaInfo& get_workarea_info() const { |
4452 | 67 | return workarea_info_; | 60 | return workarea_info_; |
4453 | 68 | } | 61 | } |
4454 | @@ -92,7 +85,6 @@ | |||
4455 | 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); |
4456 | 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); |
4457 | 94 | 87 | ||
4458 | 95 | const std::string name_; | ||
4459 | 96 | const WorkerDescr& worker_; | 88 | const WorkerDescr& worker_; |
4460 | 97 | const Tribes& tribes_; | 89 | const Tribes& tribes_; |
4461 | 98 | Actions actions_; | 90 | Actions actions_; |
4462 | 99 | 91 | ||
4463 | === modified file 'src/logic/map_objects/world/critter.cc' | |||
4464 | --- src/logic/map_objects/world/critter.cc 2019-06-23 10:30:26 +0000 | |||
4465 | +++ src/logic/map_objects/world/critter.cc 2019-08-31 15:58:01 +0000 | |||
4466 | @@ -27,22 +27,21 @@ | |||
4467 | 27 | #include <stdint.h> | 27 | #include <stdint.h> |
4468 | 28 | 28 | ||
4469 | 29 | #include "base/wexception.h" | 29 | #include "base/wexception.h" |
4470 | 30 | #include "helper.h" | ||
4471 | 31 | #include "io/fileread.h" | 30 | #include "io/fileread.h" |
4472 | 32 | #include "io/filewrite.h" | 31 | #include "io/filewrite.h" |
4473 | 33 | #include "logic/field.h" | 32 | #include "logic/field.h" |
4474 | 34 | #include "logic/game.h" | 33 | #include "logic/game.h" |
4475 | 35 | #include "logic/game_data_error.h" | 34 | #include "logic/game_data_error.h" |
4476 | 35 | #include "logic/map_objects/map_object_program.h" | ||
4477 | 36 | #include "logic/map_objects/tribes/tribe_descr.h" | 36 | #include "logic/map_objects/tribes/tribe_descr.h" |
4478 | 37 | #include "logic/map_objects/world/critter_program.h" | ||
4479 | 38 | #include "logic/map_objects/world/world.h" | 37 | #include "logic/map_objects/world/world.h" |
4480 | 39 | #include "map_io/world_legacy_lookup_table.h" | 38 | #include "map_io/world_legacy_lookup_table.h" |
4481 | 40 | #include "scripting/lua_table.h" | 39 | #include "scripting/lua_table.h" |
4482 | 41 | 40 | ||
4483 | 42 | namespace Widelands { | 41 | namespace Widelands { |
4484 | 43 | 42 | ||
4487 | 44 | void CritterProgram::parse(const std::vector<std::string>& lines) { | 43 | CritterProgram::CritterProgram(const std::string& program_name, const LuaTable& actions_table) : MapObjectProgram(program_name) { |
4488 | 45 | for (const std::string& line : lines) { | 44 | for (const std::string& line : actions_table.array_entries<std::string>()) { |
4489 | 46 | try { | 45 | try { |
4490 | 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")); |
4491 | 48 | if (cmd.empty()) | 47 | if (cmd.empty()) |
4492 | @@ -109,9 +108,7 @@ | |||
4493 | 109 | std::unique_ptr<LuaTable> programs = table.get_table("programs"); | 108 | std::unique_ptr<LuaTable> programs = table.get_table("programs"); |
4494 | 110 | for (const std::string& program_name : programs->keys<std::string>()) { | 109 | for (const std::string& program_name : programs->keys<std::string>()) { |
4495 | 111 | try { | 110 | try { |
4499 | 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())); |
4497 | 113 | prog->parse(programs->get_table(program_name)->array_entries<std::string>()); | ||
4498 | 114 | programs_[program_name] = prog.release(); | ||
4500 | 115 | } catch (const std::exception& e) { | 112 | } catch (const std::exception& e) { |
4501 | 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()); |
4502 | 117 | } | 114 | } |
4503 | @@ -126,9 +123,6 @@ | |||
4504 | 126 | } | 123 | } |
4505 | 127 | 124 | ||
4506 | 128 | CritterDescr::~CritterDescr() { | 125 | CritterDescr::~CritterDescr() { |
4507 | 129 | for (auto program : programs_) { | ||
4508 | 130 | delete program.second; | ||
4509 | 131 | } | ||
4510 | 132 | } | 126 | } |
4511 | 133 | 127 | ||
4512 | 134 | bool CritterDescr::is_swimming() const { | 128 | bool CritterDescr::is_swimming() const { |
4513 | @@ -141,11 +135,11 @@ | |||
4514 | 141 | Get a program from the workers description. | 135 | Get a program from the workers description. |
4515 | 142 | =============== | 136 | =============== |
4516 | 143 | */ | 137 | */ |
4519 | 144 | CritterProgram const* CritterDescr::get_program(const std::string& programname) const { | 138 | CritterProgram const* CritterDescr::get_program(const std::string& program_name) const { |
4520 | 145 | Programs::const_iterator const it = programs_.find(programname); | 139 | Programs::const_iterator const it = programs_.find(program_name); |
4521 | 146 | if (it == programs_.end()) | 140 | if (it == programs_.end()) |
4524 | 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()); |
4525 | 148 | return it->second; | 142 | return it->second.get(); |
4526 | 149 | } | 143 | } |
4527 | 150 | 144 | ||
4528 | 151 | uint32_t CritterDescr::movecaps() const { | 145 | uint32_t CritterDescr::movecaps() const { |
4529 | @@ -283,7 +277,7 @@ | |||
4530 | 283 | return Bob::Loader::get_task(name); | 277 | return Bob::Loader::get_task(name); |
4531 | 284 | } | 278 | } |
4532 | 285 | 279 | ||
4534 | 286 | const BobProgramBase* Critter::Loader::get_program(const std::string& name) { | 280 | const MapObjectProgram* Critter::Loader::get_program(const std::string& name) { |
4535 | 287 | Critter& critter = get<Critter>(); | 281 | Critter& critter = get<Critter>(); |
4536 | 288 | return critter.descr().get_program(name); | 282 | return critter.descr().get_program(name); |
4537 | 289 | } | 283 | } |
4538 | 290 | 284 | ||
4539 | === modified file 'src/logic/map_objects/world/critter.h' | |||
4540 | --- src/logic/map_objects/world/critter.h 2019-02-23 11:00:49 +0000 | |||
4541 | +++ src/logic/map_objects/world/critter.h 2019-08-31 15:58:01 +0000 | |||
4542 | @@ -20,9 +20,12 @@ | |||
4543 | 20 | #ifndef WL_LOGIC_MAP_OBJECTS_WORLD_CRITTER_H | 20 | #ifndef WL_LOGIC_MAP_OBJECTS_WORLD_CRITTER_H |
4544 | 21 | #define WL_LOGIC_MAP_OBJECTS_WORLD_CRITTER_H | 21 | #define WL_LOGIC_MAP_OBJECTS_WORLD_CRITTER_H |
4545 | 22 | 22 | ||
4546 | 23 | #include <memory> | ||
4547 | 24 | |||
4548 | 23 | #include "base/macros.h" | 25 | #include "base/macros.h" |
4549 | 24 | #include "graphic/diranimations.h" | 26 | #include "graphic/diranimations.h" |
4550 | 25 | #include "logic/map_objects/bob.h" | 27 | #include "logic/map_objects/bob.h" |
4551 | 28 | #include "logic/map_objects/world/critter_program.h" | ||
4552 | 26 | 29 | ||
4553 | 27 | class LuaTable; | 30 | class LuaTable; |
4554 | 28 | class WorldLegacyLookupTable; | 31 | class WorldLegacyLookupTable; |
4555 | @@ -48,13 +51,13 @@ | |||
4556 | 48 | return walk_anims_; | 51 | return walk_anims_; |
4557 | 49 | } | 52 | } |
4558 | 50 | 53 | ||
4560 | 51 | CritterProgram const* get_program(const std::string&) const; | 54 | CritterProgram const* get_program(const std::string& program_name) const; |
4561 | 52 | 55 | ||
4562 | 53 | const EditorCategory* editor_category() const; | 56 | const EditorCategory* editor_category() const; |
4563 | 54 | 57 | ||
4564 | 55 | private: | 58 | private: |
4565 | 56 | DirAnimations walk_anims_; | 59 | DirAnimations walk_anims_; |
4567 | 57 | using Programs = std::map<std::string, CritterProgram*>; | 60 | using Programs = std::map<std::string, std::unique_ptr<const CritterProgram>>; |
4568 | 58 | Programs programs_; | 61 | Programs programs_; |
4569 | 59 | EditorCategory* editor_category_; // not owned. | 62 | EditorCategory* editor_category_; // not owned. |
4570 | 60 | DISALLOW_COPY_AND_ASSIGN(CritterDescr); | 63 | DISALLOW_COPY_AND_ASSIGN(CritterDescr); |
4571 | @@ -83,7 +86,7 @@ | |||
4572 | 83 | Loader(); | 86 | Loader(); |
4573 | 84 | 87 | ||
4574 | 85 | const Task* get_task(const std::string& name) override; | 88 | const Task* get_task(const std::string& name) override; |
4576 | 86 | const BobProgramBase* get_program(const std::string& name) override; | 89 | const MapObjectProgram* get_program(const std::string& name) override; |
4577 | 87 | }; | 90 | }; |
4578 | 88 | 91 | ||
4579 | 89 | private: | 92 | private: |
4580 | 90 | 93 | ||
4581 | === modified file 'src/logic/map_objects/world/critter_program.h' | |||
4582 | --- src/logic/map_objects/world/critter_program.h 2019-02-23 11:00:49 +0000 | |||
4583 | +++ src/logic/map_objects/world/critter_program.h 2019-08-31 15:58:01 +0000 | |||
4584 | @@ -21,8 +21,10 @@ | |||
4585 | 21 | #define WL_LOGIC_MAP_OBJECTS_WORLD_CRITTER_PROGRAM_H | 21 | #define WL_LOGIC_MAP_OBJECTS_WORLD_CRITTER_PROGRAM_H |
4586 | 22 | 22 | ||
4587 | 23 | #include "logic/map_objects/bob.h" | 23 | #include "logic/map_objects/bob.h" |
4588 | 24 | #include "logic/map_objects/map_object_program.h" | ||
4589 | 24 | 25 | ||
4590 | 25 | namespace Widelands { | 26 | namespace Widelands { |
4591 | 27 | class Critter; | ||
4592 | 26 | 28 | ||
4593 | 27 | struct CritterAction { | 29 | struct CritterAction { |
4594 | 28 | using CritterExecuteActionFn = bool (Critter::*)(Game&, Bob::State&, const CritterAction&); | 30 | using CritterExecuteActionFn = bool (Critter::*)(Game&, Bob::State&, const CritterAction&); |
4595 | @@ -40,15 +42,9 @@ | |||
4596 | 40 | std::vector<std::string> sparamv; | 42 | std::vector<std::string> sparamv; |
4597 | 41 | }; | 43 | }; |
4598 | 42 | 44 | ||
4604 | 43 | struct CritterProgram : public BobProgramBase { | 45 | struct CritterProgram : public MapObjectProgram { |
4605 | 44 | explicit CritterProgram(const std::string& name) : name_(name) { | 46 | explicit CritterProgram(const std::string& program_name, const LuaTable& actions_table); |
4601 | 45 | } | ||
4602 | 46 | ~CritterProgram() override { | ||
4603 | 47 | } | ||
4606 | 48 | 47 | ||
4607 | 49 | std::string get_name() const override { | ||
4608 | 50 | return name_; | ||
4609 | 51 | } | ||
4610 | 52 | int32_t get_size() const { | 48 | int32_t get_size() const { |
4611 | 53 | return actions_.size(); | 49 | return actions_.size(); |
4612 | 54 | } | 50 | } |
4613 | @@ -57,10 +53,7 @@ | |||
4614 | 57 | return actions_[idx]; | 53 | return actions_[idx]; |
4615 | 58 | } | 54 | } |
4616 | 59 | 55 | ||
4617 | 60 | void parse(const std::vector<std::string>& lines); | ||
4618 | 61 | |||
4619 | 62 | private: | 56 | private: |
4620 | 63 | std::string name_; | ||
4621 | 64 | std::vector<CritterAction> actions_; | 57 | std::vector<CritterAction> actions_; |
4622 | 65 | }; | 58 | }; |
4623 | 66 | } // namespace Widelands | 59 | } // namespace Widelands |
4624 | 67 | 60 | ||
4625 | === modified file 'src/logic/map_objects/world/resource_description.cc' | |||
4626 | --- src/logic/map_objects/world/resource_description.cc 2019-02-23 11:00:49 +0000 | |||
4627 | +++ src/logic/map_objects/world/resource_description.cc 2019-08-31 15:58:01 +0000 | |||
4628 | @@ -21,7 +21,6 @@ | |||
4629 | 21 | 21 | ||
4630 | 22 | #include <memory> | 22 | #include <memory> |
4631 | 23 | 23 | ||
4632 | 24 | #include "helper.h" | ||
4633 | 25 | #include "logic/game_data_error.h" | 24 | #include "logic/game_data_error.h" |
4634 | 26 | #include "scripting/lua_table.h" | 25 | #include "scripting/lua_table.h" |
4635 | 27 | 26 | ||
4636 | 28 | 27 | ||
4637 | === modified file 'src/network/CMakeLists.txt' | |||
4638 | --- src/network/CMakeLists.txt 2019-08-27 19:00:30 +0000 | |||
4639 | +++ src/network/CMakeLists.txt 2019-08-31 15:58:01 +0000 | |||
4640 | @@ -44,7 +44,6 @@ | |||
4641 | 44 | build_info | 44 | build_info |
4642 | 45 | chat | 45 | chat |
4643 | 46 | game_io | 46 | game_io |
4644 | 47 | helper | ||
4645 | 48 | io_fileread | 47 | io_fileread |
4646 | 49 | io_filesystem | 48 | io_filesystem |
4647 | 50 | io_stream | 49 | io_stream |
4648 | 51 | 50 | ||
4649 | === modified file 'src/network/gameclient.cc' | |||
4650 | --- src/network/gameclient.cc 2019-08-28 06:12:07 +0000 | |||
4651 | +++ src/network/gameclient.cc 2019-08-31 15:58:01 +0000 | |||
4652 | @@ -31,7 +31,6 @@ | |||
4653 | 31 | #include "build_info.h" | 31 | #include "build_info.h" |
4654 | 32 | #include "config.h" | 32 | #include "config.h" |
4655 | 33 | #include "game_io/game_loader.h" | 33 | #include "game_io/game_loader.h" |
4656 | 34 | #include "helper.h" | ||
4657 | 35 | #include "io/fileread.h" | 34 | #include "io/fileread.h" |
4658 | 36 | #include "io/filesystem/filesystem_exceptions.h" | 35 | #include "io/filesystem/filesystem_exceptions.h" |
4659 | 37 | #include "io/filewrite.h" | 36 | #include "io/filewrite.h" |
4660 | 38 | 37 | ||
4661 | === modified file 'src/network/gamehost.cc' | |||
4662 | --- src/network/gamehost.cc 2019-08-28 06:12:07 +0000 | |||
4663 | +++ src/network/gamehost.cc 2019-08-31 15:58:01 +0000 | |||
4664 | @@ -40,7 +40,6 @@ | |||
4665 | 40 | #include "chat/chat.h" | 40 | #include "chat/chat.h" |
4666 | 41 | #include "game_io/game_loader.h" | 41 | #include "game_io/game_loader.h" |
4667 | 42 | #include "game_io/game_preload_packet.h" | 42 | #include "game_io/game_preload_packet.h" |
4668 | 43 | #include "helper.h" | ||
4669 | 44 | #include "io/fileread.h" | 43 | #include "io/fileread.h" |
4670 | 45 | #include "io/filesystem/layered_filesystem.h" | 44 | #include "io/filesystem/layered_filesystem.h" |
4671 | 46 | #include "logic/filesystem_constants.h" | 45 | #include "logic/filesystem_constants.h" |
4672 | 47 | 46 | ||
4673 | === modified file 'src/scripting/CMakeLists.txt' | |||
4674 | --- src/scripting/CMakeLists.txt 2019-08-27 19:00:30 +0000 | |||
4675 | +++ src/scripting/CMakeLists.txt 2019-08-31 15:58:01 +0000 | |||
4676 | @@ -70,7 +70,6 @@ | |||
4677 | 70 | base_i18n | 70 | base_i18n |
4678 | 71 | base_macros | 71 | base_macros |
4679 | 72 | build_info | 72 | build_info |
4680 | 73 | helper | ||
4681 | 74 | io_filesystem | 73 | io_filesystem |
4682 | 75 | scripting_base | 74 | scripting_base |
4683 | 76 | scripting_errors | 75 | scripting_errors |
4684 | 77 | 76 | ||
4685 | === modified file 'src/scripting/lua_path.cc' | |||
4686 | --- src/scripting/lua_path.cc 2019-04-28 10:14:42 +0000 | |||
4687 | +++ src/scripting/lua_path.cc 2019-08-31 15:58:01 +0000 | |||
4688 | @@ -24,7 +24,6 @@ | |||
4689 | 24 | #include <boost/lexical_cast.hpp> | 24 | #include <boost/lexical_cast.hpp> |
4690 | 25 | 25 | ||
4691 | 26 | #include "base/macros.h" | 26 | #include "base/macros.h" |
4692 | 27 | #include "helper.h" | ||
4693 | 28 | #include "io/filesystem/layered_filesystem.h" | 27 | #include "io/filesystem/layered_filesystem.h" |
4694 | 29 | 28 | ||
4695 | 30 | namespace { | 29 | namespace { |
4696 | 31 | 30 | ||
4697 | === modified file 'src/sound/CMakeLists.txt' | |||
4698 | --- src/sound/CMakeLists.txt 2019-08-27 19:00:30 +0000 | |||
4699 | +++ src/sound/CMakeLists.txt 2019-08-31 15:58:01 +0000 | |||
4700 | @@ -28,7 +28,6 @@ | |||
4701 | 28 | DEPENDS | 28 | DEPENDS |
4702 | 29 | base_i18n | 29 | base_i18n |
4703 | 30 | base_log | 30 | base_log |
4704 | 31 | helper | ||
4705 | 32 | io_fileread | 31 | io_fileread |
4706 | 33 | io_filesystem | 32 | io_filesystem |
4707 | 34 | logic_exceptions | 33 | logic_exceptions |
4708 | 35 | 34 | ||
4709 | === modified file 'src/sound/fxset.cc' | |||
4710 | --- src/sound/fxset.cc 2019-05-25 08:52:09 +0000 | |||
4711 | +++ src/sound/fxset.cc 2019-08-31 15:58:01 +0000 | |||
4712 | @@ -25,7 +25,6 @@ | |||
4713 | 25 | #include <boost/regex.hpp> | 25 | #include <boost/regex.hpp> |
4714 | 26 | 26 | ||
4715 | 27 | #include "base/log.h" | 27 | #include "base/log.h" |
4716 | 28 | #include "helper.h" | ||
4717 | 29 | #include "io/fileread.h" | 28 | #include "io/fileread.h" |
4718 | 30 | #include "io/filesystem/layered_filesystem.h" | 29 | #include "io/filesystem/layered_filesystem.h" |
4719 | 31 | #include "logic/game_data_error.h" | 30 | #include "logic/game_data_error.h" |
4720 | 32 | 31 | ||
4721 | === modified file 'src/sound/songset.cc' | |||
4722 | --- src/sound/songset.cc 2019-04-28 09:14:59 +0000 | |||
4723 | +++ src/sound/songset.cc 2019-08-31 15:58:01 +0000 | |||
4724 | @@ -25,7 +25,6 @@ | |||
4725 | 25 | #include <boost/regex.hpp> | 25 | #include <boost/regex.hpp> |
4726 | 26 | 26 | ||
4727 | 27 | #include "base/log.h" | 27 | #include "base/log.h" |
4728 | 28 | #include "helper.h" | ||
4729 | 29 | #include "io/fileread.h" | 28 | #include "io/fileread.h" |
4730 | 30 | #include "io/filesystem/layered_filesystem.h" | 29 | #include "io/filesystem/layered_filesystem.h" |
4731 | 31 | 30 | ||
4732 | 32 | 31 | ||
4733 | === modified file 'src/ui_fsmenu/CMakeLists.txt' | |||
4734 | --- src/ui_fsmenu/CMakeLists.txt 2019-08-27 19:00:30 +0000 | |||
4735 | +++ src/ui_fsmenu/CMakeLists.txt 2019-08-31 15:58:01 +0000 | |||
4736 | @@ -10,7 +10,6 @@ | |||
4737 | 10 | graphic_fonthandler | 10 | graphic_fonthandler |
4738 | 11 | graphic_text | 11 | graphic_text |
4739 | 12 | graphic_text_layout | 12 | graphic_text_layout |
4740 | 13 | helper | ||
4741 | 14 | io_filesystem | 13 | io_filesystem |
4742 | 15 | logic_filesystem_constants | 14 | logic_filesystem_constants |
4743 | 16 | scripting_lua_interface | 15 | scripting_lua_interface |
4744 | @@ -101,7 +100,6 @@ | |||
4745 | 101 | base_i18n | 100 | base_i18n |
4746 | 102 | graphic | 101 | graphic |
4747 | 103 | graphic_playercolor | 102 | graphic_playercolor |
4748 | 104 | helper | ||
4749 | 105 | io_filesystem | 103 | io_filesystem |
4750 | 106 | io_profile | 104 | io_profile |
4751 | 107 | logic | 105 | logic |
4752 | 108 | 106 | ||
4753 | === modified file 'src/ui_fsmenu/launch_spg.cc' | |||
4754 | --- src/ui_fsmenu/launch_spg.cc 2019-06-01 14:16:25 +0000 | |||
4755 | +++ src/ui_fsmenu/launch_spg.cc 2019-08-31 15:58:01 +0000 | |||
4756 | @@ -26,7 +26,6 @@ | |||
4757 | 26 | #include "base/i18n.h" | 26 | #include "base/i18n.h" |
4758 | 27 | #include "base/warning.h" | 27 | #include "base/warning.h" |
4759 | 28 | #include "base/wexception.h" | 28 | #include "base/wexception.h" |
4760 | 29 | #include "helper.h" | ||
4761 | 30 | #include "io/filesystem/layered_filesystem.h" | 29 | #include "io/filesystem/layered_filesystem.h" |
4762 | 31 | #include "logic/game.h" | 30 | #include "logic/game.h" |
4763 | 32 | #include "logic/game_controller.h" | 31 | #include "logic/game_controller.h" |
4764 | 33 | 32 | ||
4765 | === modified file 'src/ui_fsmenu/options.cc' | |||
4766 | --- src/ui_fsmenu/options.cc 2019-08-25 14:50:16 +0000 | |||
4767 | +++ src/ui_fsmenu/options.cc 2019-08-31 15:58:01 +0000 | |||
4768 | @@ -36,7 +36,6 @@ | |||
4769 | 36 | #include "graphic/text/bidi.h" | 36 | #include "graphic/text/bidi.h" |
4770 | 37 | #include "graphic/text/font_set.h" | 37 | #include "graphic/text/font_set.h" |
4771 | 38 | #include "graphic/text_layout.h" | 38 | #include "graphic/text_layout.h" |
4772 | 39 | #include "helper.h" | ||
4773 | 40 | #include "io/filesystem/disk_filesystem.h" | 39 | #include "io/filesystem/disk_filesystem.h" |
4774 | 41 | #include "io/filesystem/layered_filesystem.h" | 40 | #include "io/filesystem/layered_filesystem.h" |
4775 | 42 | #include "logic/filesystem_constants.h" | 41 | #include "logic/filesystem_constants.h" |
4776 | 43 | 42 | ||
4777 | === modified file 'src/wui/CMakeLists.txt' | |||
4778 | --- src/wui/CMakeLists.txt 2019-08-27 19:00:30 +0000 | |||
4779 | +++ src/wui/CMakeLists.txt 2019-08-31 15:58:01 +0000 | |||
4780 | @@ -88,7 +88,6 @@ | |||
4781 | 88 | base_i18n | 88 | base_i18n |
4782 | 89 | base_log | 89 | base_log |
4783 | 90 | base_time_string | 90 | base_time_string |
4784 | 91 | helper | ||
4785 | 92 | game_io | 91 | game_io |
4786 | 93 | graphic | 92 | graphic |
4787 | 94 | graphic_fonthandler | 93 | graphic_fonthandler |
4788 | 95 | 94 | ||
4789 | === modified file 'src/wui/load_or_save_game.cc' | |||
4790 | --- src/wui/load_or_save_game.cc 2019-05-27 14:28:34 +0000 | |||
4791 | +++ src/wui/load_or_save_game.cc 2019-08-31 15:58:01 +0000 | |||
4792 | @@ -31,8 +31,6 @@ | |||
4793 | 31 | #include "game_io/game_loader.h" | 31 | #include "game_io/game_loader.h" |
4794 | 32 | #include "game_io/game_preload_packet.h" | 32 | #include "game_io/game_preload_packet.h" |
4795 | 33 | #include "graphic/font_handler.h" | 33 | #include "graphic/font_handler.h" |
4796 | 34 | #include "graphic/text_layout.h" | ||
4797 | 35 | #include "helper.h" | ||
4798 | 36 | #include "io/filesystem/filesystem_exceptions.h" | 34 | #include "io/filesystem/filesystem_exceptions.h" |
4799 | 37 | #include "io/filesystem/layered_filesystem.h" | 35 | #include "io/filesystem/layered_filesystem.h" |
4800 | 38 | #include "logic/filesystem_constants.h" | 36 | #include "logic/filesystem_constants.h" |
Continuous integration builds have changed state:
Travis build 5061. State: failed. Details: https:/ /travis- ci.org/ widelands/ widelands/ builds/ 537401669. /ci.appveyor. com/project/ widelands- dev/widelands/ build/_ widelands_ dev_widelands_ unify_program_ parsers- 4841.
Appveyor build 4841. State: success. Details: https:/