widelands-dev team mailing list archive
-
widelands-dev team
-
Mailing list archive
-
Message #17389
[Merge] lp:~widelands-dev/widelands/unify-program-parsers into lp:widelands
GunChleoc has proposed merging lp:~widelands-dev/widelands/unify-program-parsers into lp:widelands with lp:~widelands-dev/widelands/list-directories-in-cpp as a prerequisite.
Commit message:
Refactor program parsers
- Pull out common code into new class MapObjectProgram
- Fix ware demand checks so that they only affect the correct tribes
- Get rid of extraneous calls to EditorGameBase::postload()
- Get rid of now empty helper library
Requested reviews:
Widelands Developers (widelands-dev)
For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/unify-program-parsers/+merge/367936
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.
--
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/unify-program-parsers into lp:widelands.
=== modified file 'data/tribes/immovables/shipconstruction_atlanteans/init.lua'
--- data/tribes/immovables/shipconstruction_atlanteans/init.lua 2019-04-20 08:40:37 +0000
+++ data/tribes/immovables/shipconstruction_atlanteans/init.lua 2019-05-26 08:37:46 +0000
@@ -11,7 +11,7 @@
programs = {
program = {
"construct=idle 5000 210000",
- "transform=bob tribe:atlanteans_ship",
+ "transform=bob atlanteans_ship",
}
},
buildcost = {
=== modified file 'data/tribes/immovables/shipconstruction_barbarians/init.lua'
--- data/tribes/immovables/shipconstruction_barbarians/init.lua 2019-04-20 08:40:37 +0000
+++ data/tribes/immovables/shipconstruction_barbarians/init.lua 2019-05-26 08:37:46 +0000
@@ -11,7 +11,7 @@
programs = {
program = {
"construct=idle 5000 210000",
- "transform=bob tribe:barbarians_ship",
+ "transform=bob barbarians_ship",
}
},
buildcost = {
=== modified file 'data/tribes/immovables/shipconstruction_empire/init.lua'
--- data/tribes/immovables/shipconstruction_empire/init.lua 2019-04-20 08:40:37 +0000
+++ data/tribes/immovables/shipconstruction_empire/init.lua 2019-05-26 08:37:46 +0000
@@ -11,7 +11,7 @@
programs = {
program = {
"construct=idle 5000 210000",
- "transform=bob tribe:empire_ship",
+ "transform=bob empire_ship",
}
},
buildcost = {
=== modified file 'data/tribes/immovables/shipconstruction_frisians/init.lua'
--- data/tribes/immovables/shipconstruction_frisians/init.lua 2019-04-26 05:32:54 +0000
+++ data/tribes/immovables/shipconstruction_frisians/init.lua 2019-05-26 08:37:46 +0000
@@ -11,7 +11,7 @@
programs = {
program = {
"construct=idle 5000 210000",
- "transform=bob tribe:frisians_ship",
+ "transform=bob frisians_ship",
}
},
buildcost = {
=== modified file 'data/tribes/workers/empire/stonemason/init.lua'
--- data/tribes/workers/empire/stonemason/init.lua 2019-05-26 08:37:42 +0000
+++ data/tribes/workers/empire/stonemason/init.lua 2019-05-26 08:37:46 +0000
@@ -41,7 +41,7 @@
"return"
},
cut_marble = {
- "findobject= attrib:rocks radius:6",
+ "findobject=attrib:rocks radius:6",
"walk=object",
"playsound=sound/stonecutting/stonecutter 220",
"animate=hacking 10000",
=== modified file 'src/CMakeLists.txt'
--- src/CMakeLists.txt 2019-05-26 08:37:42 +0000
+++ src/CMakeLists.txt 2019-05-26 08:37:46 +0000
@@ -132,16 +132,6 @@
wui
)
-# TODO(sirver): Split into libs with useful names.
-wl_library(helper
- SRCS
- helper.cc
- helper.h
- USES_SDL2
- DEPENDS
- base_exceptions
-)
-
if (CMAKE_SYSTEM_NAME MATCHES "FreeBSD" OR CMAKE_SYSTEM_NAME MATCHES "OpenBSD")
target_link_libraries(widelands_ball_of_mud ${EXECINFO_LIBRARY})
endif (CMAKE_SYSTEM_NAME MATCHES "FreeBSD" OR CMAKE_SYSTEM_NAME MATCHES "OpenBSD")
=== modified file 'src/editor/editorinteractive.cc'
--- src/editor/editorinteractive.cc 2019-04-26 05:52:49 +0000
+++ src/editor/editorinteractive.cc 2019-05-26 08:37:46 +0000
@@ -190,8 +190,7 @@
}
ml->load_map_complete(egbase(), Widelands::MapLoader::LoadType::kEditor);
- egbase().postload();
- egbase().load_graphics(loader_ui);
+ egbase().create_tempfile_and_save_mapdata(FileSystem::ZIP);
map_changed(MapWas::kReplaced);
}
=== modified file 'src/editor/ui_menus/main_menu_new_map.cc'
--- src/editor/ui_menus/main_menu_new_map.cc 2019-04-26 05:52:49 +0000
+++ src/editor/ui_menus/main_menu_new_map.cc 2019-05-26 08:37:46 +0000
@@ -137,8 +137,7 @@
list_.get_selected(), _("No Name"),
g_options.pull_section("global").get_string("realname", pgettext("author_name", "Unknown")));
- egbase.postload();
- egbase.load_graphics(loader_ui);
+ egbase.create_tempfile_and_save_mapdata(FileSystem::ZIP);
map->recalc_whole_map(egbase.world());
parent.map_changed(EditorInteractive::MapWas::kReplaced);
=== modified file 'src/editor/ui_menus/main_menu_random_map.cc'
--- src/editor/ui_menus/main_menu_random_map.cc 2019-04-24 07:09:29 +0000
+++ src/editor/ui_menus/main_menu_random_map.cc 2019-05-26 08:37:46 +0000
@@ -559,8 +559,7 @@
gen.create_random_map();
- egbase.postload();
- egbase.load_graphics(loader_ui);
+ egbase.create_tempfile_and_save_mapdata(FileSystem::ZIP);
map->recalc_whole_map(egbase.world());
eia.map_changed(EditorInteractive::MapWas::kReplaced);
=== removed file 'src/helper.cc'
--- src/helper.cc 2019-02-23 11:00:49 +0000
+++ src/helper.cc 1970-01-01 00:00:00 +0000
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2002-2019 by the Widelands Development Team
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "helper.h"
-
-#include <cstdarg>
-#include <memory>
-#include <string>
-
-#include <boost/algorithm/string/replace.hpp>
-#include <boost/format.hpp>
-#include <boost/lexical_cast.hpp>
-
-std::vector<std::string> split_string(const std::string& s, const char* const separators) {
- std::vector<std::string> result;
- for (std::string::size_type pos = 0, endpos;
- (pos = s.find_first_not_of(separators, pos)) != std::string::npos; pos = endpos) {
- endpos = s.find_first_of(separators, pos);
- result.push_back(s.substr(pos, endpos - pos));
- }
- return result;
-}
-
-char* next_word(char*& p, bool& reached_end, char const terminator) {
- assert(terminator);
- char* const result = p;
- for (; *p != terminator; ++p)
- if (*p == '\0') {
- reached_end = true;
- goto end;
- }
- reached_end = false;
- *p = '\0'; // terminate the word
- ++p; // move past the terminator
-end:
- if (result < p)
- return result;
- throw wexception("expected word");
-}
=== removed file 'src/helper.h'
--- src/helper.h 2019-05-26 08:37:42 +0000
+++ src/helper.h 1970-01-01 00:00:00 +0000
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2006-2019 by the Widelands Development Team
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#ifndef WL_HELPER_H
-#define WL_HELPER_H
-
-#include <cassert>
-#include <cstring>
-#include <string>
-#include <vector>
-
-#include <SDL_keyboard.h>
-#include <boost/utility.hpp>
-
-#include "base/wexception.h"
-
-/// Returns the word starting at the character that p points to and ending
-/// before the first terminator character. Replaces the terminator with null.
-// TODO(sirver): move into a logic/strings lib or so.
-char* next_word(char*& p, bool& reached_end, char terminator = ' ');
-
-/// Split a string by separators.
-/// \note This ignores empty elements, so do not use this for example to split
-/// a string with newline characters into lines, because it would ignore empty
-/// lines.
-std::vector<std::string> split_string(const std::string&, char const* separators);
-
-#endif // end of include guard: WL_HELPER_H
=== modified file 'src/logic/editor_game_base.cc'
--- src/logic/editor_game_base.cc 2019-04-26 05:52:49 +0000
+++ src/logic/editor_game_base.cc 2019-05-26 08:37:46 +0000
@@ -113,50 +113,58 @@
* throws an exception if something goes wrong
*/
void EditorGameBase::create_tempfile_and_save_mapdata(FileSystem::Type const type) {
- // should only be called when a map was already loaded
- assert(map_.filesystem());
-
- g_fs->ensure_directory_exists(kTempFileDir);
-
- std::string filename = kTempFileDir + g_fs->file_separator() + timestring() + "_mapdata";
- std::string complete_filename = filename + kTempFileExtension;
-
- // if a file with that name already exists, then try a few name modifications
- if (g_fs->file_exists(complete_filename)) {
- int suffix;
- for (suffix = 0; suffix <= 9; suffix++) {
- complete_filename = filename + "-" + std::to_string(suffix) + kTempFileExtension;
- if (!g_fs->file_exists(complete_filename))
- break;
- }
- if (suffix > 9) {
- throw wexception("EditorGameBase::create_tempfile_and_save_mapdata(): for all considered "
- "filenames a file already existed");
- }
- }
-
- // create tmp_fs_
- tmp_fs_.reset(g_fs->create_sub_file_system(complete_filename, type));
-
- // save necessary map data (we actually save the whole map)
- std::unique_ptr<Widelands::MapSaver> wms(new Widelands::MapSaver(*tmp_fs_, *this));
- wms->save();
-
- // swap map fs
- std::unique_ptr<FileSystem> mapfs(tmp_fs_->make_sub_file_system("."));
- map_.swap_filesystem(mapfs);
- mapfs.reset();
-
- // This is just a convenience hack:
- // If tmp_fs_ is a zip filesystem then - because of the way zip filesystems are currently
- // implemented -
- // the file is still in zip mode right now, which means that the file isn't finalized yet, i.e.,
- // not even a valid zip file until zip mode ends. To force ending the zip mode (thus finalizing
- // the file)
- // we simply perform a (otherwise useless) filesystem request.
- // It's not strictly necessary, but this way we get a valid zip file immediately istead of
- // at some unkown later point (when an unzip operation happens or a filesystem object destructs).
- tmp_fs_->file_exists("binary");
+ if (!map_.filesystem()) {
+ return;
+ }
+
+ // save map data to temporary file and reassign map fs
+ try {
+
+ g_fs->ensure_directory_exists(kTempFileDir);
+
+ std::string filename = kTempFileDir + g_fs->file_separator() + timestring() + "_mapdata";
+ std::string complete_filename = filename + kTempFileExtension;
+
+ // if a file with that name already exists, then try a few name modifications
+ if (g_fs->file_exists(complete_filename)) {
+ int suffix;
+ for (suffix = 0; suffix <= 9; suffix++) {
+ complete_filename = filename + "-" + std::to_string(suffix) + kTempFileExtension;
+ if (!g_fs->file_exists(complete_filename))
+ break;
+ }
+ if (suffix > 9) {
+ throw wexception("EditorGameBase::create_tempfile_and_save_mapdata(): for all considered "
+ "filenames a file already existed");
+ }
+ }
+
+ // create tmp_fs_
+ tmp_fs_.reset(g_fs->create_sub_file_system(complete_filename, type));
+
+ // save necessary map data (we actually save the whole map)
+ std::unique_ptr<Widelands::MapSaver> wms(new Widelands::MapSaver(*tmp_fs_, *this));
+ wms->save();
+
+ // swap map fs
+ std::unique_ptr<FileSystem> mapfs(tmp_fs_->make_sub_file_system("."));
+ map_.swap_filesystem(mapfs);
+ mapfs.reset();
+
+ // This is just a convenience hack:
+ // If tmp_fs_ is a zip filesystem then - because of the way zip filesystems are currently
+ // implemented -
+ // the file is still in zip mode right now, which means that the file isn't finalized yet, i.e.,
+ // not even a valid zip file until zip mode ends. To force ending the zip mode (thus finalizing
+ // the file)
+ // we simply perform a (otherwise useless) filesystem request.
+ // It's not strictly necessary, but this way we get a valid zip file immediately istead of
+ // at some unkown later point (when an unzip operation happens or a filesystem object destructs).
+ tmp_fs_->file_exists("binary");
+ } catch (const WException& e) {
+ log("EditorGameBase: saving map to temporary file failed: %s", e.what());
+ throw;
+ }
}
void EditorGameBase::think() {
@@ -275,20 +283,11 @@
}
/**
- * Load and prepare detailed game data.
- * This happens once just after the host has started the game and before the
- * graphics are loaded.
+ * Load and prepare detailed game and map data.
+ * This happens once just after the host has started the game / the editor has started and before the graphics are loaded.
*/
void EditorGameBase::postload() {
- if (map_.filesystem()) {
- // save map data to temporary file and reassign map fs
- try {
- create_tempfile_and_save_mapdata(FileSystem::ZIP);
- } catch (const WException& e) {
- log("EditorGameBase::postload: saving map to temporary file failed: %s", e.what());
- throw;
- }
- }
+ create_tempfile_and_save_mapdata(FileSystem::ZIP);
// Postload tribes
assert(tribes_);
=== modified file 'src/logic/editor_game_base.h'
--- src/logic/editor_game_base.h 2019-02-27 19:00:36 +0000
+++ src/logic/editor_game_base.h 2019-05-26 08:37:46 +0000
@@ -198,6 +198,8 @@
// Returns the mutable tribes. Prefer tribes() whenever possible.
Tribes* mutable_tribes();
+ void create_tempfile_and_save_mapdata(FileSystem::Type type);
+
private:
/// Common function for create_critter and create_ship.
Bob& create_bob(Coords, const BobDescr&, Player* owner = nullptr);
@@ -262,7 +264,6 @@
/// a temporary file (in a special dir) is created for such data.
std::unique_ptr<FileSystem> tmp_fs_;
void delete_tempfile();
- void create_tempfile_and_save_mapdata(FileSystem::Type type);
DISALLOW_COPY_AND_ASSIGN(EditorGameBase);
};
=== modified file 'src/logic/map_objects/CMakeLists.txt'
--- src/logic/map_objects/CMakeLists.txt 2019-05-05 18:53:14 +0000
+++ src/logic/map_objects/CMakeLists.txt 2019-05-26 08:37:46 +0000
@@ -39,8 +39,11 @@
immovable.cc
immovable.h
immovable_program.h
+ immovable_program.cc
map_object.cc
map_object.h
+ map_object_program.cc
+ map_object_program.h
terrain_affinity.cc
terrain_affinity.h
tribes/attack_target.h
@@ -127,7 +130,6 @@
graphic_surface
graphic_text_constants
graphic_text_layout
- helper
io_fileread
io_filesystem
logic # TODO(GunChleoc): Circular dependency
=== modified file 'src/logic/map_objects/bob.cc'
--- src/logic/map_objects/bob.cc 2019-05-11 13:48:12 +0000
+++ src/logic/map_objects/bob.cc 2019-05-26 08:37:46 +0000
@@ -1092,8 +1092,8 @@
throw GameDataError("unknown bob task '%s'", name.c_str());
}
-const BobProgramBase* Bob::Loader::get_program(const std::string& name) {
- throw GameDataError("unknown bob program '%s'", name.c_str());
+const MapObjectProgram* Bob::Loader::get_program(const std::string& name) {
+ throw GameDataError("unknown map object program '%s'", name.c_str());
}
void Bob::save(EditorGameBase& eg, MapObjectSaver& mos, FileWrite& fw) {
@@ -1156,7 +1156,7 @@
fw.unsigned_8(0);
}
- fw.c_string(state.program ? state.program->get_name() : "");
+ fw.c_string(state.program ? state.program->name() : "");
}
}
} // namespace Widelands
=== modified file 'src/logic/map_objects/bob.h'
--- src/logic/map_objects/bob.h 2019-04-24 06:01:37 +0000
+++ src/logic/map_objects/bob.h 2019-05-26 08:37:46 +0000
@@ -27,29 +27,19 @@
#include "graphic/diranimations.h"
#include "logic/map_objects/draw_text.h"
#include "logic/map_objects/map_object.h"
+#include "logic/map_objects/map_object_program.h"
#include "logic/map_objects/walkingdir.h"
#include "logic/widelands_geometry.h"
namespace Widelands {
+
+class Bob;
class Map;
struct Route;
struct Transfer;
class TribeDescr;
/**
- * BobProgramBase is only used that
- * get_name always works
- */
-
-struct BobProgramBase {
- virtual ~BobProgramBase() {
- }
- virtual std::string get_name() const = 0;
-};
-
-class Bob;
-
-/**
* Implement MapObjectDescr for the following \ref Bob class.
*/
class BobDescr : public MapObjectDescr {
@@ -223,7 +213,7 @@
DirAnimations diranims;
Path* path;
Route* route;
- const BobProgramBase* program; ///< pointer to current program
+ const MapObjectProgram* program; ///< pointer to current program
};
MO_DESCR(BobDescr)
@@ -419,7 +409,7 @@
protected:
virtual const Task* get_task(const std::string& name);
- virtual const BobProgramBase* get_program(const std::string& name);
+ virtual const MapObjectProgram* get_program(const std::string& name);
private:
struct LoadState {
=== modified file 'src/logic/map_objects/immovable.cc'
--- src/logic/map_objects/immovable.cc 2019-05-19 12:25:24 +0000
+++ src/logic/map_objects/immovable.cc 2019-05-26 08:37:46 +0000
@@ -19,42 +19,18 @@
#include "logic/map_objects/immovable.h"
-#include <cstdio>
-#include <cstring>
#include <memory>
-#include <boost/algorithm/string.hpp>
-#include <boost/format.hpp>
-
-#include "base/log.h"
-#include "base/macros.h"
-#include "base/wexception.h"
-#include "config.h"
-#include "graphic/graphic.h"
-#include "graphic/rendertarget.h"
#include "graphic/text_constants.h"
-#include "helper.h"
#include "io/fileread.h"
#include "io/filewrite.h"
-#include "logic/editor_game_base.h"
-#include "logic/field.h"
-#include "logic/game.h"
#include "logic/game_data_error.h"
-#include "logic/map.h"
#include "logic/map_objects/immovable_program.h"
#include "logic/map_objects/terrain_affinity.h"
-#include "logic/map_objects/tribes/tribe_descr.h"
-#include "logic/map_objects/tribes/worker.h"
#include "logic/map_objects/world/world.h"
-#include "logic/mapfringeregion.h"
#include "logic/player.h"
#include "logic/widelands_geometry_io.h"
-#include "map_io/tribes_legacy_lookup_table.h"
#include "map_io/world_legacy_lookup_table.h"
-#include "notifications/notifications.h"
-#include "scripting/lua_table.h"
-#include "sound/note_sound.h"
-#include "sound/sound_handler.h"
namespace Widelands {
@@ -141,52 +117,6 @@
/*
==============================================================================
-ImmovableProgram IMPLEMENTATION
-
-==============================================================================
-*/
-
-ImmovableProgram::ImmovableProgram(const std::string& init_name,
- const std::vector<std::string>& lines,
- ImmovableDescr* immovable)
- : name_(init_name) {
- for (const std::string& line : lines) {
- std::vector<std::string> parts;
- boost::split(parts, line, boost::is_any_of("="));
- if (parts.size() != 2) {
- throw GameDataError("invalid line: %s.", line.c_str());
- }
- std::unique_ptr<char[]> arguments(new char[parts[1].size() + 1]);
- strncpy(arguments.get(), parts[1].c_str(), parts[1].size() + 1);
-
- Action* action;
- if (parts[0] == "animate") {
- action = new ActAnimate(arguments.get(), *immovable);
- } else if (parts[0] == "transform") {
- action = new ActTransform(arguments.get(), *immovable);
- } else if (parts[0] == "grow") {
- action = new ActGrow(arguments.get(), *immovable);
- } else if (parts[0] == "remove") {
- action = new ActRemove(arguments.get(), *immovable);
- } else if (parts[0] == "seed") {
- action = new ActSeed(arguments.get(), *immovable);
- } else if (parts[0] == "playsound") {
- action = new ActPlaySound(arguments.get(), *immovable);
- } else if (parts[0] == "construct") {
- action = new ActConstruct(arguments.get(), *immovable);
- } else {
- throw GameDataError("unknown command type \"%s\" in immovable \"%s\"", parts[0].c_str(),
- immovable->name().c_str());
- }
- actions_.push_back(action);
- }
- if (actions_.empty())
- throw GameDataError("no actions");
-}
-
-/*
-==============================================================================
-
ImmovableDescr IMPLEMENTATION
==============================================================================
@@ -250,12 +180,16 @@
}
std::unique_ptr<LuaTable> programs = table.get_table("programs");
- for (const std::string& program_name : programs->keys<std::string>()) {
+ for (std::string program_name : programs->keys<std::string>()) {
+ std::transform(program_name.begin(), program_name.end(), program_name.begin(), tolower);
+ if (programs_.count(program_name)) {
+ throw GameDataError("Program '%s' has already been declared for immovable '%s'", program_name.c_str(), name().c_str());
+ }
try {
programs_[program_name] = new ImmovableProgram(
- program_name, programs->get_table(program_name)->array_entries<std::string>(), this);
+ program_name, programs->get_table(program_name)->array_entries<std::string>(), *this);
} catch (const std::exception& e) {
- throw wexception("Error in program %s: %s", program_name.c_str(), e.what());
+ throw GameDataError("%s: Error in immovable program %s: %s", name().c_str(), program_name.c_str(), e.what());
}
}
@@ -309,9 +243,9 @@
void ImmovableDescr::make_sure_default_program_is_there() {
if (!programs_.count("program")) { // default program
assert(is_animation_known("idle"));
- char parameters[] = "idle";
+ std::vector<std::string> arguments{"idle"};
programs_["program"] =
- new ImmovableProgram("program", new ImmovableProgram::ActAnimate(parameters, *this));
+ new ImmovableProgram("program", std::unique_ptr<ImmovableProgram::Action>(new ImmovableProgram::ActAnimate(arguments, *this)));
}
}
@@ -419,10 +353,12 @@
ImmovableProgram const* prog = program_;
if (!prog) {
prog = descr().get_program("program");
- assert(prog != nullptr);
- }
- if (upcast(ImmovableProgram::ActAnimate const, act_animate, &(*prog)[program_ptr_]))
- start_animation(egbase, descr().get_animation(act_animate->animation(), this));
+ }
+ assert(prog != nullptr);
+
+ if (upcast(ImmovableProgram::ActAnimate const, act_animate, &(*prog)[program_ptr_])) {
+ start_animation(egbase, act_animate->animation());
+ }
if (upcast(Game, game, &egbase)) {
switch_program(*game, "program");
@@ -762,391 +698,6 @@
return loader.release();
}
-ImmovableProgram::Action::~Action() {
-}
-
-ImmovableProgram::ActAnimate::ActAnimate(char* parameters, ImmovableDescr& descr) {
- try {
- bool reached_end;
- animation_name_ = std::string(next_word(parameters, reached_end));
- if (!descr.is_animation_known(animation_name_)) {
- throw GameDataError("Unknown animation: %s.", animation_name_.c_str());
- }
-
- if (!reached_end) { // The next parameter is the duration.
- char* endp;
- long int const value = strtol(parameters, &endp, 0);
- if (*endp || value <= 0)
- throw GameDataError("expected %s but found \"%s\"", "duration in ms", parameters);
- duration_ = value;
- } else {
- duration_ = 0; // forever
- }
- } catch (const WException& e) {
- throw GameDataError("animate: %s", e.what());
- }
-}
-
-/// Use convolutuion to make the animation time a random variable with binomial
-/// distribution and the configured time as the expected value.
-void ImmovableProgram::ActAnimate::execute(Game& game, Immovable& immovable) const {
- immovable.start_animation(game, immovable.descr().get_animation(animation_name_, &immovable));
- immovable.program_step(
- game, duration_ ? 1 + game.logic_rand() % duration_ + game.logic_rand() % duration_ : 0);
-}
-
-ImmovableProgram::ActPlaySound::ActPlaySound(char* parameters, const ImmovableDescr&) {
- try {
- bool reached_end;
- std::string name = next_word(parameters, reached_end);
-
- if (!reached_end) {
- char* endp;
- unsigned long long int const value = strtoull(parameters, &endp, 0);
- priority = value;
- if (*endp || priority != value)
- throw GameDataError("expected %s but found \"%s\"", "priority", parameters);
- } else
- priority = 127;
-
- fx = g_sh->register_fx(SoundType::kAmbient, name);
- } catch (const WException& e) {
- throw GameDataError("playsound: %s", e.what());
- }
-}
-
-/** Demand from the g_sound_handler to play a certain sound effect.
- * Whether the effect actually gets played
- * is decided only by the sound server*/
-void ImmovableProgram::ActPlaySound::execute(Game& game, Immovable& immovable) const {
- Notifications::publish(NoteSound(SoundType::kAmbient, fx, immovable.get_position(), priority));
- immovable.program_step(game);
-}
-
-ImmovableProgram::ActTransform::ActTransform(char* parameters, ImmovableDescr& descr) {
- try {
- tribe = true;
- bob = false;
- probability = 0;
-
- std::vector<std::string> params = split_string(parameters, " ");
- for (uint32_t i = 0; i < params.size(); ++i) {
- if (params[i] == "bob")
- bob = true;
- else if (params[i] == "immovable")
- bob = false;
- else if (params[i][0] >= '0' && params[i][0] <= '9') {
- long int const value = atoi(params[i].c_str());
- if (value < 1 || 254 < value)
- throw GameDataError("expected %s but found \"%s\"", "probability in range [1, 254]",
- params[i].c_str());
- probability = value;
- } else {
- std::vector<std::string> segments = split_string(params[i], ":");
-
- if (segments.size() > 2)
- throw GameDataError("object type has more than 2 segments");
- if (segments.size() == 2) {
- if (segments[0] == "world")
- tribe = false;
- else if (segments[0] == "tribe") {
- if (descr.owner_type() != MapObjectDescr::OwnerType::kTribe)
- throw GameDataError("scope \"tribe\" does not match the immovable type");
- tribe = true;
- } else
- throw GameDataError("unknown scope \"%s\" given for target type (must be "
- "\"world\" or \"tribe\")",
- parameters);
-
- type_name = segments[1];
- } else {
- type_name = segments[0];
- }
- }
- }
- if (type_name == descr.name())
- throw GameDataError("illegal transformation to the same type");
- } catch (const WException& e) {
- throw GameDataError("transform: %s", e.what());
- }
-}
-
-void ImmovableProgram::ActTransform::execute(Game& game, Immovable& immovable) const {
- if (probability == 0 || game.logic_rand() % 256 < probability) {
- Player* player = immovable.get_owner();
- Coords const c = immovable.get_position();
- MapObjectDescr::OwnerType owner_type = immovable.descr().owner_type();
- immovable.remove(game); // Now immovable is a dangling reference!
-
- if (bob) {
- game.create_ship(c, type_name, player);
- } else {
- game.create_immovable_with_name(
- c, type_name, owner_type, player, nullptr /* former_building_descr */);
- }
- } else
- immovable.program_step(game);
-}
-
-ImmovableProgram::ActGrow::ActGrow(char* parameters, ImmovableDescr& descr) {
- if (!descr.has_terrain_affinity()) {
- throw GameDataError(
- "Immovable %s can 'grow', but has no terrain_affinity entry.", descr.name().c_str());
- }
-
- try {
- tribe = true;
- for (char* p = parameters;;)
- switch (*p) {
- case ':': {
- *p = '\0';
- ++p;
- if (descr.owner_type() != MapObjectDescr::OwnerType::kTribe)
- throw GameDataError("immovable type not in tribes but target type has scope "
- "(\"%s\")",
- parameters);
- else if (strcmp(parameters, "world"))
- throw GameDataError("scope \"%s\" given for target type (must be "
- "\"world\")",
- parameters);
- tribe = false;
- parameters = p;
- break;
- }
- case '\0':
- goto end;
- default:
- ++p;
- break;
- }
- end:
- type_name = parameters;
- } catch (const WException& e) {
- throw GameDataError("grow: %s", e.what());
- }
-}
-
-void ImmovableProgram::ActGrow::execute(Game& game, Immovable& immovable) const {
- const Map& map = game.map();
- FCoords const f = map.get_fcoords(immovable.get_position());
- const ImmovableDescr& descr = immovable.descr();
-
- if ((game.logic_rand() % TerrainAffinity::kPrecisionFactor) <
- probability_to_grow(descr.terrain_affinity(), f, map, game.world().terrains())) {
- MapObjectDescr::OwnerType owner_type = descr.owner_type();
- Player* owner = immovable.get_owner();
- immovable.remove(game); // Now immovable is a dangling reference!
- game.create_immovable_with_name(
- f, type_name, owner_type, owner, nullptr /* former_building_descr */);
- } else {
- immovable.program_step(game);
- }
-}
-
-/**
- * remove
- */
-ImmovableProgram::ActRemove::ActRemove(char* parameters, ImmovableDescr&) {
- try {
- if (*parameters) {
- char* endp;
- long int const value = strtol(parameters, &endp, 0);
- if (*endp || value < 1 || 254 < value)
- throw GameDataError(
- "expected %s but found \"%s\"", "probability in range [1, 254]", parameters);
- probability = value;
- } else
- probability = 0;
- } catch (const WException& e) {
- throw GameDataError("remove: %s", e.what());
- }
-}
-
-void ImmovableProgram::ActRemove::execute(Game& game, Immovable& immovable) const {
- if (probability == 0 || game.logic_rand() % 256 < probability)
- immovable.remove(game); // Now immovable is a dangling reference!
- else
- immovable.program_step(game);
-}
-
-ImmovableProgram::ActSeed::ActSeed(char* parameters, ImmovableDescr& descr) {
- try {
- probability = 0;
- for (char* p = parameters;;)
- switch (*p) {
- case ':': {
- *p = '\0';
- ++p;
- if (descr.owner_type() != MapObjectDescr::OwnerType::kTribe)
- throw GameDataError("immovable type not in tribes but target type has scope "
- "(\"%s\")",
- parameters);
- else if (strcmp(parameters, "world"))
- throw GameDataError("scope \"%s\" given for target type (must be "
- "\"world\")",
- parameters);
- parameters = p;
- break;
- }
- case ' ': {
- *p = '\0';
- ++p;
- char* endp;
- long int const value = strtol(p, &endp, 0);
- if (*endp || value < 1 || 254 < value)
- throw GameDataError(
- "expected %s but found \"%s\"", "probability in range [1, 254]", p);
- probability = value;
- // fallthrough
- }
- FALLS_THROUGH;
- case '\0':
- goto end;
- default:
- ++p;
- break;
- }
- end:
- type_name = parameters;
- } catch (const WException& e) {
- throw GameDataError("seed: %s", e.what());
- }
-}
-
-void ImmovableProgram::ActSeed::execute(Game& game, Immovable& immovable) const {
- const Map& map = game.map();
- FCoords const f = map.get_fcoords(immovable.get_position());
- const ImmovableDescr& descr = immovable.descr();
-
- if ((game.logic_rand() % TerrainAffinity::kPrecisionFactor) <
- probability_to_grow(descr.terrain_affinity(), f, map, game.world().terrains())) {
- // Seed a new tree.
- MapFringeRegion<> mr(map, Area<>(f, 0));
- uint32_t fringe_size = 0;
- do {
- mr.extend(map);
- fringe_size += 6;
- } while (game.logic_rand() % std::numeric_limits<uint8_t>::max() < probability);
-
- for (uint32_t n = game.logic_rand() % fringe_size; n; --n) {
- mr.advance(map);
- }
-
- const FCoords new_location = map.get_fcoords(mr.location());
- if (!new_location.field->get_immovable() &&
- (new_location.field->nodecaps() & MOVECAPS_WALK) &&
- (game.logic_rand() % TerrainAffinity::kPrecisionFactor) <
- probability_to_grow(
- descr.terrain_affinity(), new_location, map, game.world().terrains())) {
- game.create_immovable_with_name(mr.location(), type_name, descr.owner_type(),
- nullptr /* owner */, nullptr /* former_building_descr */);
- }
- }
-
- immovable.program_step(game);
-}
-
-ImmovableProgram::ActConstruct::ActConstruct(char* parameters, ImmovableDescr& descr) {
- try {
- if (descr.owner_type() != MapObjectDescr::OwnerType::kTribe)
- throw GameDataError("only usable for tribe immovable");
-
- std::vector<std::string> params = split_string(parameters, " ");
-
- if (params.size() != 3)
- throw GameDataError("usage: animation-name buildtime decaytime");
-
- buildtime_ = atoi(params[1].c_str());
- decaytime_ = atoi(params[2].c_str());
-
- animation_name_ = params[0];
- if (!descr.is_animation_known(animation_name_)) {
- throw GameDataError("unknown animation \"%s\" in immovable program for immovable \"%s\"",
- animation_name_.c_str(), descr.name().c_str());
- }
- } catch (const WException& e) {
- throw GameDataError("construct: %s", e.what());
- }
-}
-
-constexpr uint8_t kCurrentPacketVersionConstructionData = 1;
-
-struct ActConstructData : ImmovableActionData {
- const char* name() const override {
- return "construct";
- }
- void save(FileWrite& fw, Immovable& imm) override {
- fw.unsigned_8(kCurrentPacketVersionConstructionData);
- delivered.save(fw, imm.get_owner()->tribe());
- }
-
- static ActConstructData* load(FileRead& fr, Immovable& imm) {
- ActConstructData* d = new ActConstructData;
-
- try {
- uint8_t packet_version = fr.unsigned_8();
- if (packet_version == kCurrentPacketVersionConstructionData) {
- d->delivered.load(fr, imm.get_owner()->tribe());
- } else {
- throw UnhandledVersionError(
- "ActConstructData", packet_version, kCurrentPacketVersionConstructionData);
- }
- } catch (const WException& e) {
- delete d;
- d = nullptr;
- throw GameDataError("ActConstructData: %s", e.what());
- }
-
- return d;
- }
-
- Buildcost delivered;
-};
-
-void ImmovableProgram::ActConstruct::execute(Game& g, Immovable& imm) const {
- ActConstructData* d = imm.get_action_data<ActConstructData>();
- if (!d) {
- // First execution
- d = new ActConstructData;
- imm.set_action_data(d);
-
- imm.start_animation(g, imm.descr().get_animation(animation_name_, &imm));
- imm.anim_construction_total_ = imm.descr().buildcost().total();
- } else {
- // Perhaps we are called due to the construction timeout of the last construction step
- Buildcost remaining;
- imm.construct_remaining_buildcost(g, &remaining);
- if (remaining.empty()) {
- imm.program_step(g);
- return;
- }
-
- // Otherwise, this is a decay timeout
- uint32_t totaldelivered = 0;
- for (Buildcost::const_iterator it = d->delivered.begin(); it != d->delivered.end(); ++it)
- totaldelivered += it->second;
-
- if (!totaldelivered) {
- imm.remove(g);
- return;
- }
-
- uint32_t randdecay = g.logic_rand() % totaldelivered;
- for (Buildcost::iterator it = d->delivered.begin(); it != d->delivered.end(); ++it) {
- if (randdecay < it->second) {
- it->second--;
- break;
- }
-
- randdecay -= it->second;
- }
-
- imm.anim_construction_done_ = d->delivered.total();
- }
-
- imm.program_step_ = imm.schedule_act(g, decaytime_);
-}
-
/**
* For an immovable that is currently in construction mode, return \c true and
* compute the remaining buildcost.
@@ -1209,16 +760,6 @@
return true;
}
-ImmovableActionData*
-ImmovableActionData::load(FileRead& fr, Immovable& imm, const std::string& name) {
- // TODO(GunChleoc): Use "construct" only after Build 20
- if (name == "construction" || name == "construct")
- return ActConstructData::load(fr, imm);
- else {
- log("ImmovableActionData::load: type %s not known", name.c_str());
- return nullptr;
- }
-}
/*
==============================================================================
=== added file 'src/logic/map_objects/immovable_program.cc'
--- src/logic/map_objects/immovable_program.cc 1970-01-01 00:00:00 +0000
+++ src/logic/map_objects/immovable_program.cc 2019-05-26 08:37:46 +0000
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2002-2019 by the Widelands Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "logic/map_objects/immovable_program.h"
+
+#include <memory>
+
+#include "logic/game.h"
+#include "logic/game_data_error.h"
+#include "logic/map_objects/terrain_affinity.h"
+#include "logic/map_objects/world/world.h"
+#include "logic/mapfringeregion.h"
+#include "logic/player.h"
+#include "sound/note_sound.h"
+
+namespace Widelands {
+
+ImmovableProgram::ImmovableProgram(const std::string& init_name, std::unique_ptr<Action> action) : MapObjectProgram(init_name) {
+ actions_.push_back(std::move(action));
+}
+
+ImmovableProgram::ImmovableProgram(const std::string& init_name,
+ const std::vector<std::string>& lines,
+ const ImmovableDescr& immovable)
+ : MapObjectProgram(init_name) {
+ for (const std::string& line : lines) {
+ if (line.empty()) {
+ throw GameDataError("Empty line");
+ }
+ try {
+ ProgramParseInput parseinput = parse_program_string(line);
+
+ if (parseinput.name == "animate") {
+ actions_.push_back(std::unique_ptr<Action>(new ActAnimate(parseinput.arguments, immovable)));
+ } else if (parseinput.name == "transform") {
+ actions_.push_back(std::unique_ptr<Action>(new ActTransform(parseinput.arguments, immovable)));
+ } else if (parseinput.name == "grow") {
+ actions_.push_back(std::unique_ptr<Action>(new ActGrow(parseinput.arguments, immovable)));
+ } else if (parseinput.name == "remove") {
+ actions_.push_back(std::unique_ptr<Action>(new ActRemove(parseinput.arguments)));
+ } else if (parseinput.name == "seed") {
+ actions_.push_back(std::unique_ptr<Action>(new ActSeed(parseinput.arguments, immovable)));
+ } else if (parseinput.name == "playsound") {
+ actions_.push_back(std::unique_ptr<Action>(new ActPlaySound(parseinput.arguments)));
+ } else if (parseinput.name == "construct") {
+ actions_.push_back(std::unique_ptr<Action>(new ActConstruct(parseinput.arguments, immovable)));
+ } else {
+ throw GameDataError("Unknown command '%s' in line '%s'", parseinput.name.c_str(), line.c_str());
+ }
+ } catch (const GameDataError& e) {
+ throw GameDataError("Error reading line '%s': %s", line.c_str(), e.what());
+ }
+ }
+ if (actions_.empty()) {
+ throw GameDataError("No actions found");
+ }
+}
+
+ImmovableProgram::Action::~Action() {
+}
+
+ImmovableProgram::ActAnimate::ActAnimate(const std::vector<std::string>& arguments, const ImmovableDescr& descr) {
+ parameters = MapObjectProgram::parse_act_animate(arguments, descr, true);
+}
+
+/// Use convolutuion to make the animation time a random variable with binomial
+/// distribution and the configured time as the expected value.
+void ImmovableProgram::ActAnimate::execute(Game& game, Immovable& immovable) const {
+ immovable.start_animation(game, parameters.animation);
+ immovable.program_step(
+ game, parameters.duration ? 1 + game.logic_rand() % parameters.duration + game.logic_rand() % parameters.duration : 0);
+}
+
+ImmovableProgram::ActPlaySound::ActPlaySound(const std::vector<std::string>& arguments) {
+ parameters = MapObjectProgram::parse_act_play_sound(arguments, kFxPriorityAllowMultiple - 1);
+}
+
+/** Demand from the g_sound_handler to play a certain sound effect.
+ * Whether the effect actually gets played
+ * is decided only by the sound server*/
+void ImmovableProgram::ActPlaySound::execute(Game& game, Immovable& immovable) const {
+ Notifications::publish(NoteSound(SoundType::kAmbient, parameters.fx, immovable.get_position(), parameters.priority));
+ immovable.program_step(game);
+}
+
+ImmovableProgram::ActTransform::ActTransform(std::vector<std::string>& arguments, const ImmovableDescr& descr) {
+ if (arguments.empty()) {
+ throw GameDataError("Usage: transform=[bob] <name> [<probability>]");
+ }
+ try {
+ bob = false;
+ probability = 0;
+
+ for (uint32_t i = 0; i < arguments.size(); ++i) {
+ if (arguments[i] == "bob") {
+ bob = true;
+ } else if (arguments[i][0] >= '0' && arguments[i][0] <= '9') {
+ probability = read_positive(arguments[i], 254);
+ } else {
+ // TODO(GunChleoc): If would be nice to check if target exists, but we can't guarantee the load order. Maybe in postload() one day.
+ type_name = arguments[i];
+ }
+ }
+ if (type_name == descr.name()) {
+ throw GameDataError("illegal transformation to the same type");
+ }
+ } catch (const WException& e) {
+ throw GameDataError("transform: %s", e.what());
+ }
+}
+
+void ImmovableProgram::ActTransform::execute(Game& game, Immovable& immovable) const {
+ if (probability == 0 || game.logic_rand() % 256 < probability) {
+ Player* player = immovable.get_owner();
+ Coords const c = immovable.get_position();
+ MapObjectDescr::OwnerType owner_type = immovable.descr().owner_type();
+ immovable.remove(game); // Now immovable is a dangling reference!
+
+ if (bob) {
+ game.create_ship(c, type_name, player);
+ } else {
+ game.create_immovable_with_name(
+ c, type_name, owner_type, player, nullptr /* former_building_descr */);
+ }
+ } else
+ immovable.program_step(game);
+}
+
+ImmovableProgram::ActGrow::ActGrow(std::vector<std::string>& arguments, const ImmovableDescr& descr) {
+ if (arguments.size() != 1) {
+ throw GameDataError("Usage: grow=<immovable name>");
+ }
+ if (!descr.has_terrain_affinity()) {
+ throw GameDataError(
+ "Immovable %s can 'grow', but has no terrain_affinity entry.", descr.name().c_str());
+ }
+
+ // TODO(GunChleoc): If would be nice to check if target exists, but we can't guarantee the load order. Maybe in postload() one day.
+ type_name = arguments.front();
+}
+
+void ImmovableProgram::ActGrow::execute(Game& game, Immovable& immovable) const {
+ const Map& map = game.map();
+ FCoords const f = map.get_fcoords(immovable.get_position());
+ const ImmovableDescr& descr = immovable.descr();
+
+ if ((game.logic_rand() % TerrainAffinity::kPrecisionFactor) <
+ probability_to_grow(descr.terrain_affinity(), f, map, game.world().terrains())) {
+ MapObjectDescr::OwnerType owner_type = descr.owner_type();
+ Player* owner = immovable.get_owner();
+ immovable.remove(game); // Now immovable is a dangling reference!
+ game.create_immovable_with_name(
+ f, type_name, owner_type, owner, nullptr /* former_building_descr */);
+ } else {
+ immovable.program_step(game);
+ }
+}
+
+/**
+ * remove
+ */
+ImmovableProgram::ActRemove::ActRemove(std::vector<std::string>& arguments) {
+ if (arguments.size() > 1) {
+ throw GameDataError("Usage: remove=[<probability>]");
+ }
+ probability = arguments.empty() ? 0 : read_positive(arguments.front(), 254);
+}
+
+void ImmovableProgram::ActRemove::execute(Game& game, Immovable& immovable) const {
+ if (probability == 0 || game.logic_rand() % 256 < probability)
+ immovable.remove(game); // Now immovable is a dangling reference!
+ else
+ immovable.program_step(game);
+}
+
+ImmovableProgram::ActSeed::ActSeed(std::vector<std::string>& arguments, const ImmovableDescr& descr) {
+ if (arguments.size() != 1) {
+ throw GameDataError("Usage: seed=<immovable name>");
+ }
+ if (!descr.has_terrain_affinity()) {
+ throw GameDataError(
+ "Immovable %s can 'seed', but has no terrain_affinity entry.", descr.name().c_str());
+ }
+
+ // TODO(GunChleoc): If would be nice to check if target exists, but we can't guarantee the load order. Maybe in postload() one day.
+ type_name = arguments.front();
+}
+
+void ImmovableProgram::ActSeed::execute(Game& game, Immovable& immovable) const {
+ const Map& map = game.map();
+ FCoords const f = map.get_fcoords(immovable.get_position());
+ const ImmovableDescr& descr = immovable.descr();
+
+ if ((game.logic_rand() % TerrainAffinity::kPrecisionFactor) <
+ probability_to_grow(descr.terrain_affinity(), f, map, game.world().terrains())) {
+ // Seed a new tree.
+ MapFringeRegion<> mr(map, Area<>(f, 0));
+ uint32_t fringe_size = 0;
+ do {
+ mr.extend(map);
+ fringe_size += 6;
+ } while (game.logic_rand() % std::numeric_limits<uint8_t>::max() < probability);
+
+ for (uint32_t n = game.logic_rand() % fringe_size; n; --n) {
+ mr.advance(map);
+ }
+
+ const FCoords new_location = map.get_fcoords(mr.location());
+ if (!new_location.field->get_immovable() &&
+ (new_location.field->nodecaps() & MOVECAPS_WALK) &&
+ (game.logic_rand() % TerrainAffinity::kPrecisionFactor) <
+ probability_to_grow(
+ descr.terrain_affinity(), new_location, map, game.world().terrains())) {
+ game.create_immovable_with_name(mr.location(), type_name, descr.owner_type(),
+ nullptr /* owner */, nullptr /* former_building_descr */);
+ }
+ }
+
+ immovable.program_step(game);
+}
+
+ImmovableProgram::ActConstruct::ActConstruct(std::vector<std::string>& arguments, const ImmovableDescr& descr) {
+ if (arguments.size() != 3) {
+ throw GameDataError("Usage: construct=<animation> <build duration> <decay duration>");
+ }
+ try {
+ animation_name_ = arguments[0];
+ if (!descr.is_animation_known(animation_name_)) {
+ throw GameDataError("Unknown animation '%s' in immovable program for immovable '%s'",
+ animation_name_.c_str(), descr.name().c_str());
+ }
+
+ buildtime_ = read_positive(arguments[1]);
+ decaytime_ = read_positive(arguments[2]);
+ } catch (const WException& e) {
+ throw GameDataError("construct: %s", e.what());
+ }
+}
+
+constexpr uint8_t kCurrentPacketVersionConstructionData = 1;
+
+
+const char* ActConstructData::name() const {
+ return "construct";
+}
+void ActConstructData::save(FileWrite& fw, Immovable& imm) const {
+ fw.unsigned_8(kCurrentPacketVersionConstructionData);
+ delivered.save(fw, imm.get_owner()->tribe());
+}
+
+ActConstructData* ActConstructData::load(FileRead& fr, Immovable& imm) {
+ ActConstructData* d = new ActConstructData;
+
+ try {
+ uint8_t packet_version = fr.unsigned_8();
+ if (packet_version == kCurrentPacketVersionConstructionData) {
+ d->delivered.load(fr, imm.get_owner()->tribe());
+ } else {
+ throw UnhandledVersionError(
+ "ActConstructData", packet_version, kCurrentPacketVersionConstructionData);
+ }
+ } catch (const WException& e) {
+ delete d;
+ d = nullptr;
+ throw GameDataError("ActConstructData: %s", e.what());
+ }
+
+ return d;
+}
+
+
+void ImmovableProgram::ActConstruct::execute(Game& g, Immovable& imm) const {
+ ActConstructData* d = imm.get_action_data<ActConstructData>();
+ if (!d) {
+ // First execution
+ d = new ActConstructData;
+ imm.set_action_data(d);
+
+ imm.start_animation(g, imm.descr().get_animation(animation_name_, &imm));
+ imm.anim_construction_total_ = imm.descr().buildcost().total();
+ } else {
+ // Perhaps we are called due to the construction timeout of the last construction step
+ Buildcost remaining;
+ imm.construct_remaining_buildcost(g, &remaining);
+ if (remaining.empty()) {
+ imm.program_step(g);
+ return;
+ }
+
+ // Otherwise, this is a decay timeout
+ uint32_t totaldelivered = 0;
+ for (Buildcost::const_iterator it = d->delivered.begin(); it != d->delivered.end(); ++it)
+ totaldelivered += it->second;
+
+ if (!totaldelivered) {
+ imm.remove(g);
+ return;
+ }
+
+ uint32_t randdecay = g.logic_rand() % totaldelivered;
+ for (Buildcost::iterator it = d->delivered.begin(); it != d->delivered.end(); ++it) {
+ if (randdecay < it->second) {
+ it->second--;
+ break;
+ }
+
+ randdecay -= it->second;
+ }
+
+ imm.anim_construction_done_ = d->delivered.total();
+ }
+
+ imm.program_step_ = imm.schedule_act(g, decaytime_);
+}
+
+ImmovableActionData*
+ImmovableActionData::load(FileRead& fr, Immovable& imm, const std::string& name) {
+ // TODO(GunChleoc): Use "construct" only after Build 20
+ if (name == "construction" || name == "construct")
+ return ActConstructData::load(fr, imm);
+ else {
+ log("ImmovableActionData::load: type %s not known", name.c_str());
+ return nullptr;
+ }
+}
+} // namespace Widelands
=== modified file 'src/logic/map_objects/immovable_program.h'
--- src/logic/map_objects/immovable_program.h 2019-04-26 12:46:40 +0000
+++ src/logic/map_objects/immovable_program.h 2019-05-26 08:37:46 +0000
@@ -21,21 +21,19 @@
#define WL_LOGIC_MAP_OBJECTS_IMMOVABLE_PROGRAM_H
#include <cstring>
+#include <memory>
#include <string>
#include "base/macros.h"
-/*
- * Implementation is in immovable.cc
- */
-
#include "logic/map_objects/buildcost.h"
#include "logic/map_objects/immovable.h"
+#include "logic/map_objects/map_object_program.h"
namespace Widelands {
/// Ordered sequence of actions (at least 1). Has a name.
-struct ImmovableProgram {
+struct ImmovableProgram : public MapObjectProgram {
/// Can be executed on an Immovable.
struct Action {
@@ -63,15 +61,14 @@
/// will not be stopped by this command. It will run until another animation
/// is started.)
struct ActAnimate : public Action {
- ActAnimate(char* parameters, ImmovableDescr&);
+ ActAnimate(const std::vector<std::string>& arguments, const ImmovableDescr&);
void execute(Game&, Immovable&) const override;
- const std::string& animation() const {
- return animation_name_;
+ uint32_t animation() const {
+ return parameters.animation;
}
private:
- std::string animation_name_;
- Duration duration_;
+ AnimationParameters parameters;
};
/// Transforms the immovable into another immovable or into a bob
@@ -91,19 +88,18 @@
/// name:
/// name of the replacement object
struct ActTransform : public Action {
- ActTransform(char* parameters, ImmovableDescr&);
+ ActTransform(std::vector<std::string>& arguments, const ImmovableDescr&);
void execute(Game&, Immovable&) const override;
private:
std::string type_name;
bool bob;
- bool tribe;
uint8_t probability;
};
/// Like ActTransform but the probability is determined by the suitability.
struct ActGrow : public Action {
- ActGrow(char* parameters, ImmovableDescr&);
+ ActGrow(std::vector<std::string>& arguments, const ImmovableDescr&);
void execute(Game&, Immovable&) const override;
private:
@@ -112,7 +108,7 @@
};
struct ActRemove : public Action {
- ActRemove(char* parameters, ImmovableDescr&);
+ ActRemove(std::vector<std::string>& arguments);
void execute(Game&, Immovable&) const override;
private:
@@ -120,7 +116,7 @@
};
struct ActSeed : public Action {
- ActSeed(char* parameters, ImmovableDescr&);
+ ActSeed(std::vector<std::string>& arguments, const ImmovableDescr&);
void execute(Game&, Immovable&) const override;
private:
@@ -142,12 +138,11 @@
/// Plays the specified sound effect with the specified priority. Whether the
/// sound effect is actually played is determined by the sound handler.
struct ActPlaySound : public Action {
- ActPlaySound(char* parameters, const ImmovableDescr&);
+ ActPlaySound(const std::vector<std::string>& arguments);
void execute(Game&, Immovable&) const override;
private:
- FxId fx;
- uint8_t priority;
+ PlaySoundParameters parameters;
};
/**
@@ -164,7 +159,7 @@
* Time until construction decays one step if no progress has been made.
*/
struct ActConstruct : public Action {
- ActConstruct(char* parameters, ImmovableDescr&);
+ ActConstruct(std::vector<std::string>& arguments, const ImmovableDescr&);
void execute(Game&, Immovable&) const override;
Duration buildtime() const {
@@ -181,24 +176,16 @@
};
/// Create a program with a single action.
- ImmovableProgram(char const* const init_name, Action* const action) : name_(init_name) {
- actions_.push_back(action);
- }
+ ImmovableProgram(const std::string& init_name, std::unique_ptr<Action> action);
- // Create an immovable program from a number of lines.
+ /// Create an immovable program from a number of lines.
ImmovableProgram(const std::string& init_name,
const std::vector<std::string>& lines,
- ImmovableDescr* immovable);
+ const ImmovableDescr& immovable);
~ImmovableProgram() {
- for (Action* action : actions_) {
- delete action;
- }
}
- const std::string& name() const {
- return name_;
- }
size_t size() const {
return actions_.size();
}
@@ -207,14 +194,8 @@
return *actions_[idx];
}
- using Actions = std::vector<Action*>;
- const Actions& actions() const {
- return actions_;
- }
-
private:
- std::string name_;
- Actions actions_;
+ std::vector<std::unique_ptr<Action>> actions_;
};
struct ImmovableActionData {
@@ -224,10 +205,18 @@
}
virtual const char* name() const = 0;
- virtual void save(FileWrite& fw, Immovable& imm) = 0;
+ virtual void save(FileWrite& fw, Immovable& imm) const = 0;
static ImmovableActionData* load(FileRead& fr, Immovable& imm, const std::string& name);
};
+
+struct ActConstructData : ImmovableActionData {
+ const char* name() const override;
+ void save(FileWrite& fw, Immovable& imm) const override;
+ static ActConstructData* load(FileRead& fr, Immovable& imm);
+
+ Buildcost delivered;
+};
} // namespace Widelands
#endif // end of include guard: WL_LOGIC_MAP_OBJECTS_IMMOVABLE_PROGRAM_H
=== modified file 'src/logic/map_objects/map_object.h'
--- src/logic/map_objects/map_object.h 2019-05-26 08:37:42 +0000
+++ src/logic/map_objects/map_object.h 2019-05-26 08:37:46 +0000
@@ -125,6 +125,7 @@
}
virtual uint32_t get_animation(const std::string& animname, const MapObject* mo) const;
+
uint32_t main_animation() const;
std::string get_animation_name(uint32_t) const; ///< needed for save, debug
=== added file 'src/logic/map_objects/map_object_program.cc'
--- src/logic/map_objects/map_object_program.cc 1970-01-01 00:00:00 +0000
+++ src/logic/map_objects/map_object_program.cc 2019-05-26 08:37:46 +0000
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2002-2019 by the Widelands Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "logic/map_objects/map_object_program.h"
+
+#include "io/filesystem/layered_filesystem.h"
+#include "logic/game_data_error.h"
+#include "logic/map_objects/map_object.h"
+#include "sound/sound_handler.h"
+
+namespace Widelands {
+
+MapObjectProgram::MapObjectProgram(const std::string& init_name) : name_ (init_name) {}
+
+const std::string& MapObjectProgram::name() const {
+ return name_;
+}
+
+std::vector<std::string> MapObjectProgram::split_string(const std::string& s, const char* const separators) {
+ std::vector<std::string> result;
+ for (std::string::size_type pos = 0, endpos;
+ (pos = s.find_first_not_of(separators, pos)) != std::string::npos; pos = endpos) {
+ endpos = s.find_first_of(separators, pos);
+ result.push_back(s.substr(pos, endpos - pos));
+ }
+ return result;
+}
+
+
+unsigned int MapObjectProgram::read_int(const std::string& input, int min_value, int max_value) {
+ unsigned int result = 0U;
+ char* endp;
+ long int const value = strtol(input.c_str(), &endp, 0);
+ result = value;
+ if (*endp || result != value) {
+ throw GameDataError("Expected a number but found \"%s\"", input.c_str());
+ }
+ if (value < min_value) {
+ throw GameDataError("Expected a number >= %d but found \"%s\"", min_value, input.c_str());
+ }
+ if (value > max_value) {
+ throw GameDataError("Expected a number <= %d but found \"%s\"", max_value, input.c_str());
+ }
+ return result;
+}
+
+unsigned int MapObjectProgram::read_positive(const std::string& input, int max_value) {
+ return read_int(input, 1, max_value);
+}
+
+MapObjectProgram::ProgramParseInput MapObjectProgram::parse_program_string(const std::string& line) {
+ const std::pair<std::string, std::string> key_values = MapObjectProgram::read_key_value_pair(line, '=');
+ return ProgramParseInput{key_values.first, split_string(key_values.second, " \t\n")};
+}
+
+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) {
+ const size_t idx = input.find(separator);
+ const std::string key = input.substr(0, idx);
+
+ if (!expected_key.empty()) {
+ if (idx == input.npos) {
+ throw GameDataError("Empty value in '%s' for separator '%c'\n", input.c_str(), separator);
+ }
+ if (key != expected_key) {
+ throw GameDataError("Expected key '%s' but found '%s' in '%s'\n", expected_key.c_str(), key.c_str(), input.c_str());
+ }
+ }
+
+ return std::make_pair(key, idx == input.npos ? default_value : input.substr(idx + 1));
+}
+
+MapObjectProgram::AnimationParameters MapObjectProgram::parse_act_animate(const std::vector<std::string>& arguments, const MapObjectDescr& descr, bool is_idle_allowed) {
+ if (arguments.size() < 1 || arguments.size() > 2) {
+ throw GameDataError("Usage: animate=<name> <duration>");
+ }
+
+ AnimationParameters result;
+ const std::string animation_name = arguments.at(0);
+
+ if (!is_idle_allowed && animation_name == "idle") {
+ throw GameDataError("'idle' animation is default; calling is not allowed");
+ }
+ if (!descr.is_animation_known(animation_name)) {
+ throw GameDataError("Unknown animation '%s'", animation_name.c_str());
+ }
+ result.animation = descr.get_animation(animation_name, nullptr);
+
+ if (arguments.size() == 2) {
+ result.duration = read_positive(arguments.at(1));
+ }
+ return result;
+}
+
+MapObjectProgram::PlaySoundParameters MapObjectProgram::parse_act_play_sound(const std::vector<std::string>& arguments, uint8_t default_priority) {
+ if (arguments.size() < 2 || arguments.size() > 3) {
+ throw GameDataError("Usage: playsound <sound_dir> <sound_name> [priority]");
+ }
+ PlaySoundParameters result;
+ result.fx = SoundHandler::register_fx(SoundType::kAmbient, arguments.at(0));
+
+ result.priority = arguments.size() == 2 ? read_positive(arguments.at(1)) : default_priority;
+ if (result.priority < kFxPriorityLowest) {
+ throw GameDataError("Minmum priority for sounds is %d, but only %d was specified for %s",
+ kFxPriorityLowest, result.priority, arguments.at(0).c_str());
+ }
+ return result;
+}
+
+} // namespace Widelands
=== added file 'src/logic/map_objects/map_object_program.h'
--- src/logic/map_objects/map_object_program.h 1970-01-01 00:00:00 +0000
+++ src/logic/map_objects/map_object_program.h 2019-05-26 08:37:46 +0000
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2002-2019 by the Widelands Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef WL_LOGIC_MAP_OBJECTS_MAP_OBJECT_PROGRAM_H
+#define WL_LOGIC_MAP_OBJECTS_MAP_OBJECT_PROGRAM_H
+
+#include <limits>
+#include <string>
+#include <vector>
+
+#include "logic/widelands.h"
+#include "sound/constants.h"
+
+namespace Widelands {
+
+struct MapObjectDescr;
+
+/// 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.
+struct MapObjectProgram {
+ const std::string& name() const;
+
+ explicit MapObjectProgram(const std::string& init_name);
+ virtual ~MapObjectProgram() = default;
+
+protected:
+ /// Splits a string by separators.
+ /// \note This ignores empty elements, so do not use this for example to split
+ /// a string with newline characters into lines, because it would ignore empty
+ /// lines.
+ static std::vector<std::string> split_string(const std::string&, char const* separators);
+
+ /// Reads an int value from a string. Throws a GameDataError if 'min_value' or 'max_value' are exceeded
+ static unsigned int read_int(const std::string& input, int min_value, int max_value = std::numeric_limits<int32_t>::max());
+ /// Same as 'read_int', with 'min_value' == 1
+ static unsigned int read_positive(const std::string& input, int max_value = std::numeric_limits<int32_t>::max());
+
+ /**
+ * @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"
+ * @param input The string to parse
+ * @param separator The separator for splitting the string, e.g. ':' or '='
+ * @param default_value A default to assign to the right-hand value if the separator is not found
+ * @param expected_key If this is not empty, the left-hand key must match this string
+ * @return A key, value pair
+ */
+ 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 = "");
+
+ /// Left-hand and right-hand elements of a line in a program, e.g. parsed from "return=skipped unless economy needs meal"
+ struct ProgramParseInput {
+ /// Program name, e.g. "return"
+ std::string name;
+ /// Program arguments, e.g. { "skipped", "unless", "economy", "needs", "meal" }
+ std::vector<std::string> arguments;
+ };
+ /// Reads the program name and arguments from a string
+ static ProgramParseInput parse_program_string(const std::string& line);
+
+ /// Animation information
+ struct AnimationParameters {
+ /// Animation ID
+ uint32_t animation = 0;
+ /// Animation duration. 0 will play the animation forever.
+ Duration duration = 0;
+ };
+ /// 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".
+ static AnimationParameters parse_act_animate(const std::vector<std::string>& arguments, const MapObjectDescr& descr, bool is_idle_allowed);
+
+ /// Sound effect information
+ struct PlaySoundParameters {
+ /// Sound effect ID
+ FxId fx;
+ /// Sound effect priority
+ uint8_t priority = 0;
+ };
+ /// Parses the arguments for a play_sound action, e.g. { "sound/smiths", "sharpening", "120" }
+ static PlaySoundParameters parse_act_play_sound(const std::vector<std::string>& arguments, uint8_t default_priority);
+
+private:
+ const std::string name_;
+};
+} // namespace Widelands
+
+#endif // end of include guard: WL_LOGIC_MAP_OBJECTS_MAP_OBJECT_PROGRAM_H
=== modified file 'src/logic/map_objects/tribes/production_program.cc'
--- src/logic/map_objects/tribes/production_program.cc 2019-05-18 11:58:43 +0000
+++ src/logic/map_objects/tribes/production_program.cc 2019-05-26 08:37:46 +0000
@@ -20,10 +20,6 @@
#include "logic/map_objects/tribes/production_program.h"
#include <memory>
-#include <sstream>
-
-#include <boost/algorithm/string.hpp>
-#include <boost/format.hpp>
#include "base/i18n.h"
#include "base/macros.h"
@@ -32,7 +28,6 @@
#include "economy/economy.h"
#include "economy/flag.h"
#include "economy/input_queue.h"
-#include "helper.h"
#include "io/filesystem/layered_filesystem.h"
#include "logic/game.h"
#include "logic/game_data_error.h"
@@ -56,135 +51,28 @@
namespace Widelands {
namespace {
-
-/// Matches the string that candidate points to against the string that
-/// template points to. Stops at when reaching a null character or the
-/// character terminator. If a match is found, candidate is moved beyond the
-/// matched part.
-///
-/// example:
-/// char const * candidate = "return 75";
-/// bool const result = match(candidate, "return");
-/// now candidate points to " 75" and result is true
-bool match(char*& candidate, const char* pattern) {
- for (char* p = candidate;; ++p, ++pattern)
- if (!*pattern) {
- candidate = p;
- return true;
- } else if (*p != *pattern)
- break;
- return false;
-}
-
-/// Skips a sequence of consecutive characters with the value c, starting at p.
-/// Throws WException if no characters were skipped.
-void force_skip(char*& p, char const c = ' ') {
- char* t = p;
- while (*t == c)
- ++t;
- if (p < t)
- p = t;
- else
- throw wexception("expected '%c' but found \"%s\"", c, p);
-}
-
-/// Skips a sequence of consecutive characters with the value c, starting at p.
-/// Returns whether any characters were skipped.
-bool skip(char*& p, char const c = ' ') {
- char* t = p;
- while (*t == c)
- ++t;
- if (p < t) {
- p = t;
- return true;
- } else
- return false;
-}
-
-/// Combines match and force_skip.
-///
-/// example:
-/// char const * candidate = "return 75";
-/// bool const result = match_force_skip(candidate, "return");
-/// now candidate points to "75" and result is true
-///
-/// example:
-/// char const * candidate = "return75";
-/// bool const result = match_force_skip(candidate, "return");
-/// throws WException
-bool match_force_skip(char*& candidate, const char* pattern) {
- for (char* p = candidate;; ++p, ++pattern)
- if (!*pattern) {
- force_skip(p);
- candidate = p;
- return true;
- } else if (*p != *pattern)
- return false;
-
- NEVER_HERE();
-}
-
-ProductionProgram::ActReturn::Condition* create_economy_condition(char*& parameters,
- const Tribes& tribes) {
- try {
- if (match_force_skip(parameters, "needs"))
- try {
- bool reached_end;
- char const* const type_name = next_word(parameters, reached_end);
- const DescriptionIndex& wareindex = tribes.ware_index(type_name);
- if (tribes.ware_exists(wareindex)) {
- for (size_t i = 0; i < tribes.nrtribes(); ++i) {
- const TribeDescr& tribe_descr = *tribes.get_tribe_descr(i);
- if (tribe_descr.has_ware(wareindex)) {
- tribes.set_ware_type_has_demand_check(wareindex, tribe_descr.name());
- }
- }
- return new ProductionProgram::ActReturn::EconomyNeedsWare(wareindex);
- } else if (tribes.worker_exists(tribes.worker_index(type_name))) {
- const DescriptionIndex& workerindex = tribes.worker_index(type_name);
- for (size_t i = 0; i < tribes.nrtribes(); ++i) {
- const TribeDescr* tribe_descr = tribes.get_tribe_descr(i);
- if (tribe_descr->has_worker(workerindex)) {
- tribes.set_worker_type_has_demand_check(workerindex);
- }
- }
- return new ProductionProgram::ActReturn::EconomyNeedsWorker(workerindex);
- } else
- throw GameDataError(
- "expected %s but found \"%s\"", "ware type or worker type", type_name);
- } catch (const WException& e) {
- throw GameDataError("needs: %s", e.what());
- }
- else
- throw GameDataError("expected %s but found \"%s\"", "\"needs\"", parameters);
- } catch (const WException& e) {
- throw GameDataError("economy: %s", e.what());
- }
-}
-
-ProductionProgram::ActReturn::Condition*
-create_site_condition(char*& parameters, const ProductionSiteDescr& descr, const Tribes& tribes) {
- try {
- if (match_force_skip(parameters, "has"))
- return new ProductionProgram::ActReturn::SiteHas(parameters, descr, tribes);
- else
- throw GameDataError("expected %s but found \"%s\"", "\"has\"", parameters);
- } catch (const WException& e) {
- throw GameDataError("site: %s", e.what());
- }
-}
-
-ProductionProgram::ActReturn::Condition* create_workers_condition(char*& parameters) {
- try {
- if (match(parameters, "need experience"))
- return new ProductionProgram::ActReturn::WorkersNeedExperience;
- else
- throw GameDataError("expected %s but found \"%s\"", "\"need experience\"", parameters);
- } catch (const WException& e) {
- throw GameDataError("workers: %s", e.what());
- }
-}
-
+/// If the iterator contents match the string, increment the iterator. Returns whether it matched.
+bool match_and_skip(std::vector<std::string>::const_iterator& it, const std::string& matchme) {
+ bool result = *it == matchme;
+ if (result) {
+ ++it;
+ }
+ return result;
+}
+
+ProductionProgram::ActReturn::Condition* create_economy_condition(const std::string& item, const ProductionSiteDescr& descr, const Tribes& tribes) {
+ DescriptionIndex index = tribes.ware_index(item);
+ if (tribes.ware_exists(index)) {
+ descr.ware_demand_checks()->insert(index);
+ return new ProductionProgram::ActReturn::EconomyNeedsWare(index);
+ } else if (tribes.worker_exists(tribes.worker_index(item))) {
+ index = tribes.worker_index(item);
+ descr.worker_demand_checks()->insert(index);
+ return new ProductionProgram::ActReturn::EconomyNeedsWorker(index);
+ } else {
+ throw GameDataError("Expected ware or worker type but found '%s'", item.c_str());
+ }
+}
} // namespace
ProductionProgram::Action::~Action() {
@@ -197,90 +85,101 @@
void ProductionProgram::Action::building_work_failed(Game&, ProductionSite&, Worker&) const {
}
-void ProductionProgram::parse_ware_type_group(char*& parameters,
- WareTypeGroup& group,
- const Tribes& tribes,
- const BillOfMaterials& input_wares,
- const BillOfMaterials& input_workers) {
- std::set<std::pair<DescriptionIndex, WareWorker>>::iterator last_insert_pos = group.first.end();
- uint8_t count = 1;
- uint8_t count_max = 0;
- for (;;) {
- char const* ware = parameters;
- while (*parameters && *parameters != ',' && *parameters != ':' && *parameters != ' ')
- ++parameters;
- char const terminator = *parameters;
- *parameters = '\0';
-
- // Try as ware
- WareWorker type = wwWARE;
- const BillOfMaterials* input_list = &input_wares;
- DescriptionIndex ware_index = tribes.ware_index(ware);
- if (!tribes.ware_exists(ware_index)) {
- ware_index = tribes.worker_index(ware);
- if (tribes.worker_exists(ware_index)) {
- // It is a worker
- type = wwWORKER;
- input_list = &input_workers;
- } else {
- throw GameDataError("Unknown ware or worker type \"%s\"", ware);
- }
- }
-
- bool found = false;
- for (const WareAmount& input : *input_list) {
- if (input.first == ware_index) {
- count_max += input.second;
- found = true;
- break;
- }
- }
- if (!found) {
- throw GameDataError("%s is not declared as an input (\"%s=<count>\" was not "
- "found in the [inputs] section)",
- ware, ware);
- }
-
- if (group.first.size() && ware_index <= group.first.begin()->first)
- throw GameDataError("wrong order of ware types within group: ware type %s appears "
- "after ware type %s (fix order!)",
- ware,
- tribes.get_ware_descr(group.first.begin()->first)->name().c_str());
- last_insert_pos = group.first.insert(last_insert_pos, std::make_pair(ware_index, type));
- *parameters = terminator;
- switch (terminator) {
- case ':': {
- ++parameters;
- char* endp;
- unsigned long long int const value = strtoull(parameters, &endp, 0);
- count = value;
- if ((*endp && *endp != ' ') || value < 1 || count != value)
- throw GameDataError("expected %s but found \"%s\"", "count", parameters);
- parameters = endp;
- if (count_max < count)
- throw GameDataError("group count is %u but (total) input storage capacity of "
- "the specified ware type(s) is only %u, so the group can "
+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) {
+ ProductionProgram::Groups result;
+
+ for (auto& it = begin; it != end; ++it) {
+ const std::pair<std::string, std::string> names_to_amount = read_key_value_pair(*it, ':', "1");
+ const uint8_t amount = read_positive(names_to_amount.second);
+ uint8_t max_amount = 0;
+ std::set<std::pair<DescriptionIndex, WareWorker>> ware_worker_names;
+ for (const std::string& item_name : split_string(names_to_amount.first, ",")) {
+ // Try as ware
+ WareWorker type = wwWARE;
+ DescriptionIndex item_index = tribes.ware_index(item_name);
+ if (!tribes.ware_exists(item_index)) {
+ item_index = tribes.worker_index(item_name);
+ if (tribes.worker_exists(item_index)) {
+ // It is a worker
+ type = wwWORKER;
+ } else {
+ throw GameDataError("Expected ware or worker type but found '%s'", item_name.c_str());
+ }
+ }
+
+ // Sanity checks
+ bool found = false;
+ const BillOfMaterials& inputs = (type == wwWARE) ? descr.input_wares() : descr.input_workers();
+ for (const WareAmount& input : inputs) {
+ if (input.first == item_index) {
+ max_amount += input.second;
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ throw GameDataError("%s was not declared in the building's 'inputs' table", item_name.c_str());
+ }
+
+ if (max_amount < amount) {
+ throw GameDataError("Ware/worker count is %u but (total) input storage capacity of "
+ "the specified ware type(s) is only %u, so the ware/worker requirement can "
"never be fulfilled by the site",
- count, count_max);
+ static_cast<unsigned int>(amount), static_cast<unsigned int>(max_amount));
+ }
+ // Add item
+ ware_worker_names.insert(std::make_pair(item_index, type));
}
- FALLS_THROUGH;
- case '\0':
- case ' ':
- group.second = count;
- return;
- case ',':
- ++parameters;
- break;
+ // Add set
+ result.push_back(std::make_pair(ware_worker_names, amount));
+ }
+ if (result.empty()) {
+ throw GameDataError("No wares or workers found");
+ }
+ return result;
+}
+
+
+BillOfMaterials ProductionProgram::parse_bill_of_materials(const std::vector<std::string>& arguments,
+ WareWorker ww,
+ const ProductionSiteDescr& descr,
+ const Tribes& tribes) {
+ BillOfMaterials result;
+ for (const std::string& argument : arguments) {
+ const std::pair<std::string, std::string> produceme = read_key_value_pair(argument, ':', "1");
+
+ const DescriptionIndex index = ww == WareWorker::wwWARE ?
+ tribes.safe_ware_index(produceme.first) :
+ tribes.safe_worker_index(produceme.first);
+
+ // Verify the building outputs
+ switch (ww) {
+ case WareWorker::wwWARE:
+ if (!descr.is_output_ware_type(index)) {
+ throw GameDataError("Ware '%s' is not listed in the building's 'outputs' table",
+ produceme.first.c_str());
+ } break;
+ case WareWorker::wwWORKER:
+ if (!descr.is_output_worker_type(index)) {
+ throw GameDataError("Worker '%s' is not listed in the building's 'outputs' table",
+ produceme.first.c_str());
+ } break;
default:
- // scan for terminator should ensure that this cannot happen
NEVER_HERE();
}
+
+ result.push_back(std::make_pair(index, read_positive(produceme.second)));
}
+ return result;
}
ProductionProgram::ActReturn::Condition::~Condition() {
}
+ProductionProgram::ActReturn::Negation::Negation(std::vector<std::string>::const_iterator& begin, std::vector<std::string>::const_iterator& end, const ProductionSiteDescr& descr, const Tribes& tribes)
+ : operand(create_condition(begin, end, descr, tribes)) {
+}
+
ProductionProgram::ActReturn::Negation::~Negation() {
delete operand;
}
@@ -343,13 +242,14 @@
return result;
}
-ProductionProgram::ActReturn::SiteHas::SiteHas(char*& parameters,
+ProductionProgram::ActReturn::SiteHas::SiteHas(std::vector<std::string>::const_iterator begin,
+ std::vector<std::string>::const_iterator end,
const ProductionSiteDescr& descr,
const Tribes& tribes) {
try {
- parse_ware_type_group(parameters, group, tribes, descr.input_wares(), descr.input_workers());
- } catch (const WException& e) {
- throw GameDataError("has ware_type1[,ware_type2[,...]][:N]: %s", e.what());
+ group = parse_ware_type_groups(begin, end, descr, tribes).front();
+ } catch (const GameDataError& e) {
+ throw GameDataError("Expected <ware or worker>[,<ware or worker>[,...]][:<amount>] after 'site has' but got %s", e.what());
}
}
bool ProductionProgram::ActReturn::SiteHas::evaluate(const ProductionSite& ps) const {
@@ -439,75 +339,87 @@
}
ProductionProgram::ActReturn::Condition* ProductionProgram::ActReturn::create_condition(
- char*& parameters, const ProductionSiteDescr& descr, const Tribes& tribes) {
+ std::vector<std::string>::const_iterator& begin, std::vector<std::string>::const_iterator& end, const ProductionSiteDescr& descr, const Tribes& tribes) {
+ if (begin == end) {
+ throw GameDataError("Expected a condition after '%s'", (begin - 1)->c_str());
+ }
try {
- if (match_force_skip(parameters, "not"))
- return new ActReturn::Negation(parameters, descr, tribes);
- else if (match_force_skip(parameters, "economy"))
- return create_economy_condition(parameters, tribes);
- else if (match_force_skip(parameters, "site"))
- return create_site_condition(parameters, descr, tribes);
- else if (match_force_skip(parameters, "workers"))
- return create_workers_condition(parameters);
- else
+ if (match_and_skip(begin, "not")) {
+ return new ActReturn::Negation(begin, end, descr, tribes);
+ } else if (match_and_skip(begin, "economy")) {
+ if (!match_and_skip(begin, "needs")) {
+ throw GameDataError("Expected 'needs' after 'economy' but found '%s'", begin->c_str());
+ }
+ return create_economy_condition(*begin, descr, tribes);
+ } else if (match_and_skip(begin, "site")) {
+ if (!match_and_skip(begin, "has")) {
+ throw GameDataError("Expected 'has' after 'site' but found '%s'", begin->c_str());
+ }
+ return new ProductionProgram::ActReturn::SiteHas(begin, end, descr, tribes);
+ } else if (match_and_skip(begin, "workers")) {
+ if (!match_and_skip(begin, "need")) {
+ throw GameDataError("Expected 'need experience' after 'workers' but found '%s'", begin->c_str());
+ }
+ if (!match_and_skip(begin, "experience")) {
+ throw GameDataError("Expected 'experience' after 'workers need' but found '%s'", begin->c_str());
+ }
+ return new ProductionProgram::ActReturn::WorkersNeedExperience();
+ } else {
throw GameDataError(
- "expected %s but found \"%s\"", "{\"not\"|\"economy\"|\"workers\"}", parameters);
+ "Expected not|economy|site|workers after '%s' but found '%s'", (begin - 1)->c_str(), begin->c_str());
+ }
} catch (const WException& e) {
- throw GameDataError("invalid condition: %s", e.what());
+ throw GameDataError("Invalid condition. %s", e.what());
}
}
-ProductionProgram::ActReturn::ActReturn(char* parameters,
+ProductionProgram::ActReturn::ActReturn(const std::vector<std::string>& arguments,
const ProductionSiteDescr& descr,
const Tribes& tribes) {
- try {
- if (match(parameters, "failed"))
- result_ = ProgramResult::kFailed;
- else if (match(parameters, "completed"))
- result_ = ProgramResult::kCompleted;
- else if (match(parameters, "skipped"))
- result_ = ProgramResult::kSkipped;
- else if (match(parameters, "no_stats"))
- result_ = ProgramResult::kNone;
- else
- throw GameDataError("expected %s but found \"%s\"",
- "{\"failed\"|\"completed\"|\"skipped\"|\"no_stats\"}", parameters);
-
- if (skip(parameters)) {
- if (match_force_skip(parameters, "when")) {
- is_when_ = true;
- for (;;) {
- conditions_.push_back(create_condition(parameters, descr, tribes));
- if (*parameters) {
- skip(parameters);
- if (!match_force_skip(parameters, "and"))
- throw GameDataError("expected \"%s\" or end of input", "and");
- } else
- break;
- }
- } else if (match_force_skip(parameters, "unless")) {
- is_when_ = false;
- for (;;) {
- if (!*parameters)
- throw GameDataError("expected condition at end of input");
- conditions_.push_back(create_condition(parameters, descr, tribes));
- if (*parameters) {
- skip(parameters);
- if (!match_force_skip(parameters, "or"))
- throw GameDataError("expected \"%s\" or end of input", "or");
- } else
- break;
- }
- } else
- throw GameDataError(
- "expected %s but found \"%s\"", "{\"when\"|\"unless\"}", parameters);
- } else if (*parameters)
- throw GameDataError("expected %s but found \"%s\"", ("space or end of input"), parameters);
- else
- is_when_ = true;
-
- } catch (const WException& e) {
- throw GameDataError("return: %s", e.what());
+ if (arguments.empty()) {
+ throw GameDataError("Usage: return=failed|completed|skipped|no_stats [when|unless <conditions>]");
+ }
+ auto begin = arguments.begin();
+
+ if (match_and_skip(begin, "failed")) {
+ result_ = ProgramResult::kFailed;
+ } else if (match_and_skip(begin, "completed")) {
+ result_ = ProgramResult::kCompleted;
+ } else if (match_and_skip(begin, "skipped")) {
+ result_ = ProgramResult::kSkipped;
+ } else if (match_and_skip(begin, "no_stats")) {
+ result_ = ProgramResult::kNone;
+ } else {
+ throw GameDataError("Usage: return=failed|completed|skipped|no_stats [when|unless <conditions>]");
+ }
+
+
+ // Parse all arguments starting from the given iterator into our 'conditions_', splitting individual conditions by the given 'separator'
+ auto parse_conditions = [this, &descr, &tribes] (const std::vector<std::string>& args, std::vector<std::string>::const_iterator it, const std::string& separator) {
+ while (it != args.end()) {
+ auto end = it + 1;
+ while (end != args.end() && *end != separator) {
+ ++end;
+ }
+ if (it == end) {
+ throw GameDataError("Expected: [%s] <condition> after '%s'", separator.c_str(), (it - 1)->c_str());
+ }
+ conditions_.push_back(create_condition(it, end, descr, tribes));
+ match_and_skip(end, separator);
+ it = end;
+ }
+ };
+
+ is_when_ = true;
+ if (begin != arguments.end()) {
+ if (match_and_skip(begin, "when")) {
+ parse_conditions(arguments, begin, "and");
+ } else if (match_and_skip(begin, "unless")) {
+ is_when_ = false;
+ parse_conditions(arguments, begin, "or");
+ } else {
+ throw GameDataError("Expected when|unless but found '%s'", begin->c_str());
+ }
}
}
@@ -573,7 +485,11 @@
return ps.program_end(game, result_);
}
-ProductionProgram::ActCall::ActCall(char* parameters, const ProductionSiteDescr& descr) {
+ProductionProgram::ActCall::ActCall(const std::vector<std::string>& arguments, const ProductionSiteDescr& descr) {
+ if (arguments.size() < 1 || arguments.size() > 4) {
+ throw GameDataError("Usage: call=<program name> [on failure|completion|skip fail|complete|skip|repeat]");
+ }
+
// Initialize with default handling methods.
handling_methods_[program_result_index(ProgramResult::kFailed)] =
ProgramResultHandlingMethod::kContinue;
@@ -582,61 +498,60 @@
handling_methods_[program_result_index(ProgramResult::kSkipped)] =
ProgramResultHandlingMethod::kContinue;
- try {
- bool reached_end;
- {
- char const* const program_name = next_word(parameters, reached_end);
- const ProductionSiteDescr::Programs& programs = descr.programs();
- ProductionSiteDescr::Programs::const_iterator const it = programs.find(program_name);
- if (it == programs.end())
- throw GameDataError("the program \"%s\" has not (yet) been declared in %s "
- "(wrong declaration order?)",
- program_name, descr.name().c_str());
- program_ = it->second.get();
- }
-
- // Override with specified handling methods.
- while (!reached_end) {
- skip(parameters);
- match_force_skip(parameters, "on");
-
- ProgramResult result_to_set_method_for;
- if (match_force_skip(parameters, "failure")) {
- if (handling_methods_[program_result_index(ProgramResult::kFailed)] !=
- ProgramResultHandlingMethod::kContinue)
- throw GameDataError("%s handling method already defined", "failure");
- result_to_set_method_for = ProgramResult::kFailed;
- } else if (match_force_skip(parameters, "completion")) {
- if (handling_methods_[program_result_index(ProgramResult::kCompleted)] !=
- ProgramResultHandlingMethod::kContinue)
- throw GameDataError("%s handling method already defined", "completion");
- result_to_set_method_for = ProgramResult::kCompleted;
- } else if (match_force_skip(parameters, "skip")) {
- if (handling_methods_[program_result_index(ProgramResult::kSkipped)] !=
- ProgramResultHandlingMethod::kContinue)
- throw GameDataError("%s handling method already defined", "skip");
- result_to_set_method_for = ProgramResult::kSkipped;
- } else
- throw GameDataError(
- "expected %s but found \"%s\"", "{\"failure\"|\"completion\"|\"skip\"}", parameters);
-
- ProgramResultHandlingMethod handling_method;
- if (match(parameters, "fail"))
- handling_method = ProgramResultHandlingMethod::kFail;
- else if (match(parameters, "complete"))
- handling_method = ProgramResultHandlingMethod::kComplete;
- else if (match(parameters, "skip"))
- handling_method = ProgramResultHandlingMethod::kSkip;
- else if (match(parameters, "repeat"))
- handling_method = ProgramResultHandlingMethod::kRepeat;
- else
- throw GameDataError("expected %s but found \"%s\"",
- "{\"fail\"|\"complete\"|\"skip\"|\"repeat\"}", parameters);
- handling_methods_[program_result_index(result_to_set_method_for)] = handling_method;
- reached_end = !*parameters;
- }
- } catch (const WException& e) {
- throw GameDataError("call: %s", e.what());
+ // Fetch program to call
+ const std::string& program_name = arguments.front();
+ const ProductionSiteDescr::Programs& programs = descr.programs();
+ ProductionSiteDescr::Programs::const_iterator const it = programs.find(program_name);
+ if (it == programs.end()) {
+ throw GameDataError("The program '%s' has not (yet) been declared in %s "
+ "(wrong declaration order?)",
+ program_name.c_str(), descr.name().c_str());
+ }
+ program_ = it->second.get();
+
+ // Override with specified handling methods.
+ if (arguments.size() > 1) {
+ if (arguments.at(1) != "on") {
+ throw GameDataError("Expected 'on' keyword in second position");
+ }
+
+ ProgramResult result_to_set_method_for;
+ if (arguments.at(2) == "failure") {
+ if (handling_methods_[program_result_index(ProgramResult::kFailed)] !=
+ ProgramResultHandlingMethod::kContinue) {
+ throw GameDataError("%s handling method already defined", "failure");
+ }
+ result_to_set_method_for = ProgramResult::kFailed;
+ } else if (arguments.at(2) == "completion") {
+ if (handling_methods_[program_result_index(ProgramResult::kCompleted)] !=
+ ProgramResultHandlingMethod::kContinue) {
+ throw GameDataError("%s handling method already defined", "completion");
+ }
+ result_to_set_method_for = ProgramResult::kCompleted;
+ } else if (arguments.at(2) == "skip") {
+ if (handling_methods_[program_result_index(ProgramResult::kSkipped)] !=
+ ProgramResultHandlingMethod::kContinue) {
+ throw GameDataError("%s handling method already defined", "skip");
+ }
+ result_to_set_method_for = ProgramResult::kSkipped;
+ } else {
+ throw GameDataError(
+ "Expected failure|completion|skip after 'on' but found '%s'", arguments.at(2).c_str());
+ }
+
+ ProgramResultHandlingMethod handling_method;
+ if (arguments.at(3) == "fail") {
+ handling_method = ProgramResultHandlingMethod::kFail;
+ } else if (arguments.at(3) == "complete") {
+ handling_method = ProgramResultHandlingMethod::kComplete;
+ } else if (arguments.at(3) == "skip") {
+ handling_method = ProgramResultHandlingMethod::kSkip;
+ } else if (arguments.at(3) == "repeat") {
+ handling_method = ProgramResultHandlingMethod::kRepeat;
+ } else {
+ throw GameDataError("Expected fail|complete|skip|repeat in final position but found '%s'", arguments.at(3).c_str());
+ }
+ handling_methods_[program_result_index(result_to_set_method_for)] = handling_method;
}
}
@@ -662,38 +577,38 @@
}
}
-ProductionProgram::ActCallWorker::ActCallWorker(char* parameters,
+ProductionProgram::ActCallWorker::ActCallWorker(const std::vector<std::string>& arguments,
const std::string& production_program_name,
ProductionSiteDescr* descr,
const Tribes& tribes) {
- try {
- program_ = parameters;
-
- // Quote form "void ProductionSite::program_act(Game &)":
- // "Always main worker is doing stuff"
- const WorkerDescr& main_worker_descr =
- *tribes.get_worker_descr(descr->working_positions()[0].first);
-
- // This will fail unless the main worker has a program with the given
- // name, so it also validates the parameter.
- const WorkareaInfo& worker_workarea_info =
- main_worker_descr.get_program(program_)->get_workarea_info();
-
- for (const auto& area_info : worker_workarea_info) {
- std::set<std::string>& building_radius_infos = descr->workarea_info_[area_info.first];
-
- for (const std::string& worker_name : area_info.second) {
- std::string description = descr->name();
- description += ' ';
- description += production_program_name;
- description += " worker ";
- description += main_worker_descr.name();
- description += worker_name;
- building_radius_infos.insert(description);
- }
+ if (arguments.size() != 1) {
+ throw GameDataError("Usage: callworker=<worker program name>");
+ }
+
+ program_ = arguments.front();
+
+ // Quote from "void ProductionSite::program_act(Game &)":
+ // "Always main worker is doing stuff"
+ const WorkerDescr& main_worker_descr =
+ *tribes.get_worker_descr(descr->working_positions().front().first);
+
+ // This will fail unless the main worker has a program with the given
+ // name, so it also validates the parameter.
+ const WorkareaInfo& worker_workarea_info =
+ main_worker_descr.get_program(program_)->get_workarea_info();
+
+ for (const auto& area_info : worker_workarea_info) {
+ std::set<std::string>& building_radius_infos = descr->workarea_info_[area_info.first];
+
+ for (const std::string& worker_name : area_info.second) {
+ std::string description = descr->name();
+ description += ' ';
+ description += production_program_name;
+ description += " worker ";
+ description += main_worker_descr.name();
+ description += worker_name;
+ building_radius_infos.insert(description);
}
- } catch (const WException& e) {
- throw GameDataError("worker: %s", e.what());
}
}
@@ -722,37 +637,22 @@
psite.program_end(game, ProgramResult::kFailed);
}
-ProductionProgram::ActSleep::ActSleep(char* parameters) {
- try {
- if (*parameters) {
- char* endp;
- long long int const value = strtoll(parameters, &endp, 0);
- duration_ = value;
- if (*endp || value <= 0 || duration_ != value)
- throw GameDataError("expected %s but found \"%s\"", "duration in ms", parameters);
- } else
- duration_ = 0; // Get duration from the result of a previous action.
- } catch (const WException& e) {
- throw GameDataError("sleep: %s", e.what());
+ProductionProgram::ActSleep::ActSleep(const std::vector<std::string>& arguments) {
+ if (arguments.size() != 1) {
+ throw GameDataError("Usage: sleep=<duration>");
}
+ duration_ = read_positive(arguments.front());
}
void ProductionProgram::ActSleep::execute(Game& game, ProductionSite& ps) const {
return ps.program_step(game, duration_ ? duration_ : 0, ps.top_state().phase);
}
-ProductionProgram::ActCheckMap::ActCheckMap(char* parameters) {
- try {
- if (*parameters) {
- if (!strcmp(parameters, "seafaring"))
- feature_ = SEAFARING;
- else
- throw GameDataError("Unknown parameter \"%s\"", parameters);
- } else
- throw GameDataError("No parameter given!");
- } catch (const WException& e) {
- throw GameDataError("sleep: %s", e.what());
+ProductionProgram::ActCheckMap::ActCheckMap(const std::vector<std::string>& arguments) {
+ if (arguments.size() != 1 || arguments.front() != "seafaring") {
+ throw GameDataError("Usage: checkmap=seafaring");
}
+ feature_ = SEAFARING;
}
void ProductionProgram::ActCheckMap::execute(Game& game, ProductionSite& ps) const {
@@ -770,52 +670,22 @@
}
}
-ProductionProgram::ActAnimate::ActAnimate(char* parameters, ProductionSiteDescr* descr) {
- try {
- bool reached_end;
- animation_name_ = std::string(next_word(parameters, reached_end));
- if (animation_name_ == "idle") {
- throw GameDataError("idle animation is default; calling is not allowed");
- }
- if (!descr->is_animation_known(animation_name_)) {
- throw GameDataError("Unknown animation '%s'", animation_name_.c_str());
- }
- if (!reached_end) { // The next parameter is the duration.
- char* endp;
- long long int const value = strtoll(parameters, &endp, 0);
- duration_ = value;
- if (*endp || value <= 0 || duration_ != value)
- throw GameDataError("expected %s but found \"%s\"", "duration in ms", parameters);
- } else
- duration_ = 0; // Get duration from the result of a previous action.
- } catch (const WException& e) {
- throw GameDataError("animate: %s", e.what());
- }
+ProductionProgram::ActAnimate::ActAnimate(const std::vector<std::string>& arguments, ProductionSiteDescr* descr) {
+ parameters = MapObjectProgram::parse_act_animate(arguments, *descr, false);
}
void ProductionProgram::ActAnimate::execute(Game& game, ProductionSite& ps) const {
- ps.start_animation(game, ps.descr().get_animation(animation_name_, &ps));
- return ps.program_step(game, duration_ ? duration_ : 0, ps.top_state().phase);
+ ps.start_animation(game, parameters.animation);
+ return ps.program_step(game, parameters.duration ? parameters.duration : 0, ps.top_state().phase);
}
-ProductionProgram::ActConsume::ActConsume(char* parameters,
+ProductionProgram::ActConsume::ActConsume(const std::vector<std::string>& arguments,
const ProductionSiteDescr& descr,
const Tribes& tribes) {
- try {
- for (;;) {
- consumed_wares_workers_.resize(consumed_wares_workers_.size() + 1);
- parse_ware_type_group(parameters, *consumed_wares_workers_.rbegin(), tribes,
- descr.input_wares(), descr.input_workers());
- if (!*parameters)
- break;
- force_skip(parameters);
- }
- if (consumed_wares_workers_.empty()) {
- throw GameDataError("expected ware_type1[,ware_type2[,...]][:N] ...");
- }
- } catch (const WException& e) {
- throw GameDataError("consume: %s", e.what());
+ if (arguments.empty()) {
+ throw GameDataError("Usage: consume=<ware or worker>[,<ware or worker>[,...]][:<amount>] ...");
}
+ consumed_wares_workers_ = parse_ware_type_groups(arguments.begin(), arguments.end(), descr, tribes);
}
void ProductionProgram::ActConsume::execute(Game& game, ProductionSite& ps) const {
@@ -938,47 +808,13 @@
}
}
-ProductionProgram::ActProduce::ActProduce(char* parameters,
+ProductionProgram::ActProduce::ActProduce(const std::vector<std::string>& arguments,
const ProductionSiteDescr& descr,
const Tribes& tribes) {
- try {
- for (bool more = true; more; ++parameters) {
- produced_wares_.resize(produced_wares_.size() + 1);
- WareAmount& item = *produced_wares_.rbegin();
- skip(parameters);
- char const* ware = parameters;
- for (;; ++parameters) {
- switch (*parameters) {
- default:
- break;
- case '\0':
- case ' ':
- item.second = 1;
- goto item_end;
- case ':': {
- *parameters = '\0';
- ++parameters;
- char* endp;
- unsigned long long int const value = strtoull(parameters, &endp, 0);
- item.second = value;
- if ((*endp && *endp != ' ') || value < 1 || item.second != value)
- throw GameDataError("expected %s but found \"%s\"", "count", parameters);
- parameters = endp;
- goto item_end;
- }
- }
- }
- item_end:
- more = *parameters != '\0';
- *parameters = '\0';
- if (!descr.is_output_ware_type(item.first = tribes.safe_ware_index(ware)))
- throw GameDataError("%s is not declared as an output (\"%s\" was not "
- "found in the \"outputs\" table)",
- ware, ware);
- }
- } catch (const WException& e) {
- throw GameDataError("produce: %s", e.what());
+ if (arguments.empty()) {
+ throw GameDataError("Usage: produce=<ware name>[:<amount>] [<ware name>[:<amount>]...]");
}
+ produced_wares_ = parse_bill_of_materials(arguments, WareWorker::wwWARE, descr, tribes);
}
void ProductionProgram::ActProduce::execute(Game& game, ProductionSite& ps) const {
@@ -1024,47 +860,13 @@
return false;
}
-ProductionProgram::ActRecruit::ActRecruit(char* parameters,
+ProductionProgram::ActRecruit::ActRecruit(const std::vector<std::string>& arguments,
const ProductionSiteDescr& descr,
const Tribes& tribes) {
- try {
- for (bool more = true; more; ++parameters) {
- recruited_workers_.resize(recruited_workers_.size() + 1);
- WareAmount& item = *recruited_workers_.rbegin();
- skip(parameters);
- char const* worker = parameters;
- for (;; ++parameters) {
- switch (*parameters) {
- default:
- break;
- case '\0':
- case ' ':
- item.second = 1;
- goto item_end;
- case ':': {
- *parameters = '\0';
- ++parameters;
- char* endp;
- unsigned long long int const value = strtoull(parameters, &endp, 0);
- item.second = value;
- if ((*endp && *endp != ' ') || value < 1 || item.second != value)
- throw GameDataError("expected %s but found \"%s\"", "count", parameters);
- parameters = endp;
- goto item_end;
- }
- }
- }
- item_end:
- more = *parameters != '\0';
- *parameters = '\0';
- if (!descr.is_output_worker_type(item.first = tribes.safe_worker_index(worker)))
- throw GameDataError("%s is not declared as an output (\"%s\" was not "
- "found in the \"outputs\" table)",
- worker, worker);
- }
- } catch (const WException& e) {
- throw GameDataError("recruit: %s", e.what());
+ if (arguments.empty()) {
+ throw GameDataError("Usage: recruit=<worker name>[:<amount>] [<worker name>[:<amount>]...]");
}
+ recruited_workers_ = parse_bill_of_materials(arguments, WareWorker::wwWORKER, descr, tribes);
}
void ProductionProgram::ActRecruit::execute(Game& game, ProductionSite& ps) const {
@@ -1106,55 +908,24 @@
return false;
}
-ProductionProgram::ActMine::ActMine(char* parameters,
+ProductionProgram::ActMine::ActMine(const std::vector<std::string>& arguments,
const World& world,
const std::string& production_program_name,
ProductionSiteDescr* descr) {
- try {
- bool reached_end;
- resource_ = world.safe_resource_index(next_word(parameters, reached_end));
-
- {
- char* endp;
- unsigned long long int const value = strtoull(parameters, &endp, 0);
- distance_ = value;
- if (*endp != ' ' || distance_ != value)
- throw GameDataError("expected %s but found \"%s\"", "distance", parameters);
- parameters = endp;
- }
-
- {
- char* endp;
- unsigned long long int const value = strtoull(parameters, &endp, 0);
- max_ = value;
- if (*endp != ' ' || value < 1 || 100 < value)
- throw GameDataError("expected %s but found \"%s\"", "percentage", parameters);
- parameters = endp;
- }
-
- {
- char* endp;
- unsigned long long int const value = strtoull(parameters, &endp, 0);
- chance_ = value;
- if (*endp != ' ' || value < 1 || 100 < value)
- throw GameDataError("expected %s but found \"%s\"", "percentage", parameters);
- parameters = endp;
- }
- {
- char* endp;
- unsigned long long int const value = strtoull(parameters, &endp, 0);
- training_ = value;
- if (*endp || value < 1 || 100 < value)
- throw GameDataError("expected %s but found \"%s\"", "percentage", parameters);
- }
- std::string description = (boost::format("%1$s %2$s mine %3$s") % descr->name() %
- production_program_name % world.get_resource(resource_)->name())
- .str();
-
- descr->workarea_info_[distance_].insert(description);
- } catch (const WException& e) {
- throw GameDataError("mine: %s", e.what());
+ if (arguments.size() != 5) {
+ throw GameDataError("Usage: mine=resource <workarea radius> <max> <chance> <worker experience gained>");
}
+
+ resource_ = world.safe_resource_index(arguments.front().c_str());
+ distance_ = read_positive(arguments.at(1));
+ max_ = read_positive(arguments.at(2));
+ chance_ = read_positive(arguments.at(3));
+ training_ = read_positive(arguments.at(4));
+
+ const std::string description = (boost::format("%1$s %2$s mine %3$s") % descr->name() %
+ production_program_name % world.get_resource(resource_)->name())
+ .str();
+ descr->workarea_info_[distance_].insert(description);
}
void ProductionProgram::ActMine::execute(Game& game, ProductionSite& ps) const {
@@ -1268,32 +1039,27 @@
return ps.program_step(game);
}
-ProductionProgram::ActCheckSoldier::ActCheckSoldier(char* parameters) {
- // TODO(unknown): This is currently hardcoded for "soldier", but should allow any
- // soldier type name.
- if (!match_force_skip(parameters, "soldier"))
- throw GameDataError("expected %s but found \"%s\"", "soldier type", parameters);
- try {
- if (match_force_skip(parameters, "health"))
- attribute = TrainingAttribute::kHealth;
- else if (match_force_skip(parameters, "attack"))
- attribute = TrainingAttribute::kAttack;
- else if (match_force_skip(parameters, "defense"))
- attribute = TrainingAttribute::kDefense;
- else if (match_force_skip(parameters, "evade"))
- attribute = TrainingAttribute::kEvade;
- else
- throw GameDataError("expected %s but found \"%s\"",
- "{\"health\"|\"attack\"|\"defense\"|\"evade\"}", parameters);
-
- char* endp;
- unsigned long long int const value = strtoull(parameters, &endp, 0);
- level = value;
- if (*endp || level != value)
- throw GameDataError("expected %s but found \"%s\"", "level", parameters);
- } catch (const WException& e) {
- throw GameDataError("checksoldier: %s", e.what());
- }
+ProductionProgram::ActCheckSoldier::ActCheckSoldier(const std::vector<std::string>& arguments) {
+ if (arguments.size() != 3) {
+ throw GameDataError("Usage: checksoldier=soldier <training attribute> <level>");
+ }
+
+ if (arguments.front() != "soldier") {
+ throw GameDataError("Expected 'soldier' but found '%s'", arguments.front().c_str());
+ }
+
+ if (arguments.at(1) == "health") {
+ attribute_ = TrainingAttribute::kHealth;
+ } else if (arguments.at(1) == "attack") {
+ attribute_ = TrainingAttribute::kAttack;
+ } else if (arguments.at(1) == "defense") {
+ attribute_ = TrainingAttribute::kDefense;
+ } else if (arguments.at(1) == "evade") {
+ attribute_ = TrainingAttribute::kEvade;
+ } else {
+ throw GameDataError("Expected health|attack|defense|evade after 'soldier' but found '%s'", arguments.at(1).c_str());
+ }
+ level_ = read_int(arguments.at(2), 0);
}
void ProductionProgram::ActCheckSoldier::execute(Game& game, ProductionSite& ps) const {
@@ -1304,8 +1070,8 @@
ps.set_production_result(_("No soldier to train!"));
return ps.program_end(game, ProgramResult::kSkipped);
}
- ps.molog(" Checking soldier (%u) level %d)\n", static_cast<unsigned int>(attribute),
- static_cast<unsigned int>(level));
+ ps.molog(" Checking soldier (%u) level %d)\n", static_cast<unsigned int>(attribute_),
+ static_cast<unsigned int>(level_));
const std::vector<Soldier*>::const_iterator soldiers_end = soldiers.end();
for (std::vector<Soldier*>::const_iterator it = soldiers.begin();; ++it) {
@@ -1313,67 +1079,52 @@
ps.set_production_result(_("No soldier found for this training level!"));
return ps.program_end(game, ProgramResult::kSkipped);
}
- if (attribute == TrainingAttribute::kHealth) {
- if ((*it)->get_health_level() == level)
- break;
- } else if (attribute == TrainingAttribute::kAttack) {
- if ((*it)->get_attack_level() == level)
- break;
- } else if (attribute == TrainingAttribute::kDefense) {
- if ((*it)->get_defense_level() == level)
- break;
- } else if (attribute == TrainingAttribute::kEvade) {
- if ((*it)->get_evade_level() == level)
+ if (attribute_ == TrainingAttribute::kHealth) {
+ if ((*it)->get_health_level() == level_)
+ break;
+ } else if (attribute_ == TrainingAttribute::kAttack) {
+ if ((*it)->get_attack_level() == level_)
+ break;
+ } else if (attribute_ == TrainingAttribute::kDefense) {
+ if ((*it)->get_defense_level() == level_)
+ break;
+ } else if (attribute_ == TrainingAttribute::kEvade) {
+ if ((*it)->get_evade_level() == level_)
break;
}
}
ps.molog(" okay\n"); // okay, do nothing
upcast(TrainingSite, ts, &ps);
- ts->training_attempted(attribute, level);
+ ts->training_attempted(attribute_, level_);
ps.molog(" Check done!\n");
return ps.program_step(game);
}
-ProductionProgram::ActTrain::ActTrain(char* parameters) {
- // TODO(unknown): This is currently hardcoded for "soldier", but should allow any
- // soldier type name.
- if (!match_force_skip(parameters, "soldier"))
- throw GameDataError("expected %s but found \"%s\"", "soldier type", parameters);
- try {
- if (match_force_skip(parameters, "health"))
- attribute = TrainingAttribute::kHealth;
- else if (match_force_skip(parameters, "attack"))
- attribute = TrainingAttribute::kAttack;
- else if (match_force_skip(parameters, "defense"))
- attribute = TrainingAttribute::kDefense;
- else if (match_force_skip(parameters, "evade"))
- attribute = TrainingAttribute::kEvade;
- else
- throw GameDataError("expected %s but found \"%s\"",
- "{\"health\"|\"attack\"|\"defense\"|\"evade\"}", parameters);
-
- {
- char* endp;
- unsigned long long int const value = strtoull(parameters, &endp, 0);
- level = value;
- if (*endp != ' ' || level != value)
- throw GameDataError("expected %s but found \"%s\"", "level", parameters);
- parameters = endp;
- }
-
- {
- char* endp;
- unsigned long long int const value = strtoull(parameters, &endp, 0);
- target_level = value;
- if (*endp || target_level != value || target_level <= level)
- throw GameDataError("expected level > %u but found \"%s\"", level, parameters);
- }
- } catch (const WException& e) {
- throw GameDataError("train: %s", e.what());
- }
+ProductionProgram::ActTrain::ActTrain(const std::vector<std::string>& arguments) {
+ if (arguments.size() != 4) {
+ throw GameDataError("Usage: checksoldier=soldier <training attribute> <level before> <level after>");
+ }
+
+ if (arguments.front() != "soldier") {
+ throw GameDataError("Expected 'soldier' but found '%s'", arguments.front().c_str());
+ }
+
+ if (arguments.at(1) == "health") {
+ attribute_ = TrainingAttribute::kHealth;
+ } else if (arguments.at(1) == "attack") {
+ attribute_ = TrainingAttribute::kAttack;
+ } else if (arguments.at(1) == "defense") {
+ attribute_ = TrainingAttribute::kDefense;
+ } else if (arguments.at(1) == "evade") {
+ attribute_ = TrainingAttribute::kEvade;
+ } else {
+ throw GameDataError("Expected health|attack|defense|evade after 'soldier' but found '%s'", arguments.at(1).c_str());
+ }
+ level_ = read_int(arguments.at(2), 0);
+ target_level_ = read_positive(arguments.at(3));
}
void ProductionProgram::ActTrain::execute(Game& game, ProductionSite& ps) const {
@@ -1382,41 +1133,41 @@
const std::vector<Soldier*>::const_iterator soldiers_end = soldiers.end();
std::vector<Soldier*>::const_iterator it = soldiers.begin();
- ps.molog(" Training soldier's %u (%d to %d)", static_cast<unsigned int>(attribute),
- static_cast<unsigned int>(level), static_cast<unsigned int>(target_level));
+ ps.molog(" Training soldier's %u (%d to %d)", static_cast<unsigned int>(attribute_),
+ static_cast<unsigned int>(level_), static_cast<unsigned int>(target_level_));
for (;; ++it) {
if (it == soldiers_end) {
ps.set_production_result(_("No soldier found for this training level!"));
return ps.program_end(game, ProgramResult::kSkipped);
}
- if (attribute == TrainingAttribute::kHealth) {
- if ((*it)->get_health_level() == level)
- break;
- } else if (attribute == TrainingAttribute::kAttack) {
- if ((*it)->get_attack_level() == level)
- break;
- } else if (attribute == TrainingAttribute::kDefense) {
- if ((*it)->get_defense_level() == level)
- break;
- } else if (attribute == TrainingAttribute::kEvade) {
- if ((*it)->get_evade_level() == level)
+ if (attribute_ == TrainingAttribute::kHealth) {
+ if ((*it)->get_health_level() == level_)
+ break;
+ } else if (attribute_ == TrainingAttribute::kAttack) {
+ if ((*it)->get_attack_level() == level_)
+ break;
+ } else if (attribute_ == TrainingAttribute::kDefense) {
+ if ((*it)->get_defense_level() == level_)
+ break;
+ } else if (attribute_ == TrainingAttribute::kEvade) {
+ if ((*it)->get_evade_level() == level_)
break;
}
}
ps.molog(" okay\n"); // okay, do nothing
try {
- if (attribute == TrainingAttribute::kHealth)
- (*it)->set_health_level(target_level);
- else if (attribute == TrainingAttribute::kAttack)
- (*it)->set_attack_level(target_level);
-
- else if (attribute == TrainingAttribute::kDefense)
- (*it)->set_defense_level(target_level);
-
- else if (attribute == TrainingAttribute::kEvade)
- (*it)->set_evade_level(target_level);
+ if (attribute_ == TrainingAttribute::kHealth)
+ (*it)->set_health_level(target_level_);
+ else if (attribute_ == TrainingAttribute::kAttack)
+ (*it)->set_attack_level(target_level_);
+
+ else if (attribute_ == TrainingAttribute::kDefense)
+ (*it)->set_defense_level(target_level_);
+
+ else if (attribute_ == TrainingAttribute::kEvade)
+ (*it)->set_evade_level(target_level_);
} catch (...) {
throw wexception("Fail training soldier!!");
@@ -1428,61 +1179,32 @@
(boost::format(_("Completed %s")) % ps.top_state().program->descname()).str());
upcast(TrainingSite, ts, &ps);
- ts->training_successful(attribute, level);
+ ts->training_successful(attribute_, level_);
return ps.program_step(game);
}
-ProductionProgram::ActPlaySound::ActPlaySound(char* parameters) {
- try {
- bool reached_end;
- const char* const name = next_word(parameters, reached_end);
- fx = SoundHandler::register_fx(SoundType::kAmbient, name);
-
- if (!reached_end) {
- char* endp;
- unsigned long long int const value = strtoull(parameters, &endp, 0);
- priority = value;
- if (*endp || priority != value)
- throw GameDataError("expected %s but found \"%s\"", "priority", parameters);
- } else {
- priority = kFxPriorityAllowMultiple - 1;
- }
- if (priority < kFxPriorityLowest) {
- throw GameDataError("Minmum priority for sounds is %d, but only %d was specified for %s",
- kFxPriorityLowest, priority, name);
- }
- } catch (const WException& e) {
- throw GameDataError("playsound: %s", e.what());
- }
+ProductionProgram::ActPlaySound::ActPlaySound(const std::vector<std::string>& arguments) {
+ parameters = MapObjectProgram::parse_act_play_sound(arguments, kFxPriorityAllowMultiple - 1);
}
void ProductionProgram::ActPlaySound::execute(Game& game, ProductionSite& ps) const {
- Notifications::publish(NoteSound(SoundType::kAmbient, fx, ps.position_, priority));
+ Notifications::publish(NoteSound(SoundType::kAmbient, parameters.fx, ps.position_, parameters.priority));
return ps.program_step(game);
}
-ProductionProgram::ActConstruct::ActConstruct(char* parameters,
+ProductionProgram::ActConstruct::ActConstruct(const std::vector<std::string>& arguments,
const std::string& production_program_name,
ProductionSiteDescr* descr) {
- try {
- std::vector<std::string> params = split_string(parameters, " ");
-
- if (params.size() != 3)
- throw GameDataError("usage: construct object-name worker-program radius:NN");
-
- objectname = params[0];
- workerprogram = params[1];
- radius = boost::lexical_cast<uint32_t>(params[2]);
-
- std::set<std::string>& building_radius_infos = descr->workarea_info_[radius];
- std::string description = descr->name() + ' ' + production_program_name;
- description += " construct ";
- description += objectname;
- building_radius_infos.insert(description);
- } catch (const WException& e) {
- throw GameDataError("construct: %s", e.what());
+ if (arguments.size() != 3) {
+ throw GameDataError("Usage: construct=<object name> <worker program> <workarea radius>");
}
+ objectname = arguments.at(0);
+ workerprogram = arguments.at(1);
+ radius = read_positive(arguments.at(2));
+
+ const std::string description = descr->name() + ' ' + production_program_name + " construct " + objectname;
+ descr->workarea_info_[radius].insert(description);
}
const ImmovableDescr&
@@ -1632,96 +1354,90 @@
const Tribes& tribes,
const World& world,
ProductionSiteDescr* building)
- : name_(init_name), descname_(init_descname) {
-
- for (const std::string& action_string : actions_table->array_entries<std::string>()) {
- std::vector<std::string> parts;
- boost::split(parts, action_string, boost::is_any_of("="));
- if (parts.size() != 2) {
- throw GameDataError(
- "invalid line: \"%s\" in production program \"%s\" for building \"%s\"",
- action_string.c_str(), name().c_str(), building->name().c_str());
- }
- std::unique_ptr<char[]> arguments(new char[parts[1].size() + 1]);
- strncpy(arguments.get(), parts[1].c_str(), parts[1].size() + 1);
-
- if (boost::iequals(parts[0], "return")) {
- actions_.push_back(std::unique_ptr<ProductionProgram::Action>(
- new ActReturn(arguments.get(), *building, tribes)));
- } else if (boost::iequals(parts[0], "call")) {
- actions_.push_back(
- std::unique_ptr<ProductionProgram::Action>(new ActCall(arguments.get(), *building)));
- } else if (boost::iequals(parts[0], "sleep")) {
- actions_.push_back(
- std::unique_ptr<ProductionProgram::Action>(new ActSleep(arguments.get())));
- } else if (boost::iequals(parts[0], "animate")) {
- actions_.push_back(
- std::unique_ptr<ProductionProgram::Action>(new ActAnimate(arguments.get(), building)));
- } else if (boost::iequals(parts[0], "consume")) {
- actions_.push_back(std::unique_ptr<ProductionProgram::Action>(
- new ActConsume(arguments.get(), *building, tribes)));
- } else if (boost::iequals(parts[0], "produce")) {
- actions_.push_back(std::unique_ptr<ProductionProgram::Action>(
- new ActProduce(arguments.get(), *building, tribes)));
- } else if (boost::iequals(parts[0], "recruit")) {
- actions_.push_back(std::unique_ptr<ProductionProgram::Action>(
- new ActRecruit(arguments.get(), *building, tribes)));
- } else if (boost::iequals(parts[0], "callworker")) {
- actions_.push_back(std::unique_ptr<ProductionProgram::Action>(
- new ActCallWorker(arguments.get(), name(), building, tribes)));
- } else if (boost::iequals(parts[0], "mine")) {
- actions_.push_back(std::unique_ptr<ProductionProgram::Action>(
- new ActMine(arguments.get(), world, name(), building)));
- } else if (boost::iequals(parts[0], "checksoldier")) {
- actions_.push_back(
- std::unique_ptr<ProductionProgram::Action>(new ActCheckSoldier(arguments.get())));
- } else if (boost::iequals(parts[0], "train")) {
- actions_.push_back(
- std::unique_ptr<ProductionProgram::Action>(new ActTrain(arguments.get())));
- } else if (boost::iequals(parts[0], "playsound")) {
- actions_.push_back(
- std::unique_ptr<ProductionProgram::Action>(new ActPlaySound(arguments.get())));
- } else if (boost::iequals(parts[0], "construct")) {
- actions_.push_back(std::unique_ptr<ProductionProgram::Action>(
- new ActConstruct(arguments.get(), name(), building)));
- } else if (boost::iequals(parts[0], "checkmap")) {
- actions_.push_back(
- std::unique_ptr<ProductionProgram::Action>(new ActCheckMap(arguments.get())));
- } else {
- throw GameDataError(
- "unknown command type \"%s\" in production program \"%s\" for building \"%s\"",
- arguments.get(), name().c_str(), building->name().c_str());
- }
-
- const ProductionProgram::Action& action = *actions_.back();
- for (const auto& group : action.consumed_wares_workers()) {
- consumed_wares_workers_.push_back(group);
- }
- // Add produced wares. If the ware already exists, increase the amount
- for (const auto& ware : action.produced_wares()) {
- if (produced_wares_.count(ware.first) == 1) {
- produced_wares_.at(ware.first) += ware.second;
- } else {
- produced_wares_.insert(ware);
- }
- }
- // Add recruited workers. If the worker already exists, increase the amount
- for (const auto& worker : action.recruited_workers()) {
- if (recruited_workers_.count(worker.first) == 1) {
- recruited_workers_.at(worker.first) += worker.second;
- } else {
- recruited_workers_.insert(worker);
- }
- }
- }
- if (actions_.empty())
- throw GameDataError("no actions in production program \"%s\" for building \"%s\"",
- name().c_str(), building->name().c_str());
-}
-
-const std::string& ProductionProgram::name() const {
- return name_;
-}
+ : MapObjectProgram(init_name), descname_(init_descname) {
+
+ for (const std::string& line : actions_table->array_entries<std::string>()) {
+ if (line.empty()) {
+ throw GameDataError("Empty line");
+ }
+ try {
+ ProgramParseInput parseinput = parse_program_string(line);
+
+ if (parseinput.name == "return") {
+ actions_.push_back(std::unique_ptr<ProductionProgram::Action>(
+ new ActReturn(parseinput.arguments, *building, tribes)));
+ } else if (parseinput.name == "call") {
+ actions_.push_back(
+ std::unique_ptr<ProductionProgram::Action>(new ActCall(parseinput.arguments, *building)));
+ } else if (parseinput.name == "sleep") {
+ actions_.push_back(
+ std::unique_ptr<ProductionProgram::Action>(new ActSleep(parseinput.arguments)));
+ } else if (parseinput.name == "animate") {
+ actions_.push_back(
+ std::unique_ptr<ProductionProgram::Action>(new ActAnimate(parseinput.arguments, building)));
+ } else if (parseinput.name =="consume") {
+ actions_.push_back(std::unique_ptr<ProductionProgram::Action>(
+ new ActConsume(parseinput.arguments, *building, tribes)));
+ } else if (parseinput.name == "produce") {
+ actions_.push_back(std::unique_ptr<ProductionProgram::Action>(
+ new ActProduce(parseinput.arguments, *building, tribes)));
+ } else if (parseinput.name == "recruit") {
+ actions_.push_back(std::unique_ptr<ProductionProgram::Action>(
+ new ActRecruit(parseinput.arguments, *building, tribes)));
+ } else if (parseinput.name =="callworker") {
+ actions_.push_back(std::unique_ptr<ProductionProgram::Action>(
+ new ActCallWorker(parseinput.arguments, name(), building, tribes)));
+ } else if (parseinput.name == "mine") {
+ actions_.push_back(std::unique_ptr<ProductionProgram::Action>(
+ new ActMine(parseinput.arguments, world, name(), building)));
+ } else if (parseinput.name == "checksoldier") {
+ actions_.push_back(
+ std::unique_ptr<ProductionProgram::Action>(new ActCheckSoldier(parseinput.arguments)));
+ } else if (parseinput.name == "train") {
+ actions_.push_back(
+ std::unique_ptr<ProductionProgram::Action>(new ActTrain(parseinput.arguments)));
+ } else if (parseinput.name == "playsound") {
+ actions_.push_back(
+ std::unique_ptr<ProductionProgram::Action>(new ActPlaySound(parseinput.arguments)));
+ } else if (parseinput.name == "construct") {
+ actions_.push_back(std::unique_ptr<ProductionProgram::Action>(
+ new ActConstruct(parseinput.arguments, name(), building)));
+ } else if (parseinput.name == "checkmap") {
+ actions_.push_back(
+ std::unique_ptr<ProductionProgram::Action>(new ActCheckMap(parseinput.arguments)));
+ } else {
+ throw GameDataError("Unknown command '%s' in line '%s'", parseinput.name.c_str(), line.c_str());
+ }
+
+ const ProductionProgram::Action& action = *actions_.back();
+ for (const auto& group : action.consumed_wares_workers()) {
+ consumed_wares_workers_.push_back(group);
+ }
+ // Add produced wares. If the ware already exists, increase the amount
+ for (const auto& ware : action.produced_wares()) {
+ if (produced_wares_.count(ware.first) == 1) {
+ produced_wares_.at(ware.first) += ware.second;
+ } else {
+ produced_wares_.insert(ware);
+ }
+ }
+ // Add recruited workers. If the worker already exists, increase the amount
+ for (const auto& worker : action.recruited_workers()) {
+ if (recruited_workers_.count(worker.first) == 1) {
+ recruited_workers_.at(worker.first) += worker.second;
+ } else {
+ recruited_workers_.insert(worker);
+ }
+ }
+ } catch (const std::exception& e) {
+ throw GameDataError("Error reading line '%s': %s", line.c_str(), e.what());
+ }
+ }
+ if (actions_.empty()) {
+ throw GameDataError("No actions found");
+ }
+}
+
const std::string& ProductionProgram::descname() const {
return descname_;
}
=== modified file 'src/logic/map_objects/tribes/production_program.h'
--- src/logic/map_objects/tribes/production_program.h 2019-05-11 13:48:12 +0000
+++ src/logic/map_objects/tribes/production_program.h 2019-05-26 08:37:46 +0000
@@ -32,6 +32,7 @@
#include "base/log.h"
#include "base/macros.h"
#include "logic/map_objects/buildcost.h"
+#include "logic/map_objects/map_object_program.h"
#include "logic/map_objects/tribes/bill_of_materials.h"
#include "logic/map_objects/tribes/program_result.h"
#include "logic/map_objects/tribes/training_attribute.h"
@@ -51,7 +52,7 @@
class World;
/// Ordered sequence of actions (at least 1). Has a name.
-struct ProductionProgram {
+struct ProductionProgram : public MapObjectProgram {
/// A group of ware types with a count.
using WareTypeGroup = std::pair<std::set<std::pair<DescriptionIndex, WareWorker>>, uint8_t>;
@@ -97,13 +98,11 @@
DISALLOW_COPY_AND_ASSIGN(Action);
};
- /// Parse a group of ware types followed by an optional count and terminated
- /// by a space or null. Example: "fish,meat:2".
- static void parse_ware_type_group(char*& parameters,
- WareTypeGroup& group,
- const Tribes& tribes,
- const BillOfMaterials& input_wares,
- const BillOfMaterials& input_workers);
+ /// Parse a group of ware types followed by an optional count within a vector range. Example: "fish,meat:2".
+ 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);
+
+ /// Parse a ware or worker list with optional amounts and ensure that the building's outputs match. Example: "fish:2".
+ static BillOfMaterials parse_bill_of_materials(const std::vector<std::string>& arguments, WareWorker ww, const ProductionSiteDescr& descr, const Tribes& tribes);
/// Returns from the program.
///
@@ -156,7 +155,7 @@
/// Note: If the execution reaches the end of the program. the return value
/// is implicitly set to Completed.
struct ActReturn : public Action {
- ActReturn(char* parameters, const ProductionSiteDescr&, const Tribes& tribes);
+ ActReturn(const std::vector<std::string>& arguments, const ProductionSiteDescr&, const Tribes& tribes);
~ActReturn() override;
void execute(Game&, ProductionSite&) const override;
@@ -167,11 +166,10 @@
virtual std::string description_negation(const Tribes&) const = 0;
};
static Condition*
- create_condition(char*& parameters, const ProductionSiteDescr&, const Tribes& tribes);
+ create_condition(std::vector<std::string>::const_iterator& begin, std::vector<std::string>::const_iterator& end, const ProductionSiteDescr&, const Tribes& tribes);
+
struct Negation : public Condition {
- Negation(char*& parameters, const ProductionSiteDescr& descr, const Tribes& tribes)
- : operand(create_condition(parameters, descr, tribes)) {
- }
+ Negation(std::vector<std::string>::const_iterator& begin, std::vector<std::string>::const_iterator& end, const ProductionSiteDescr& descr, const Tribes& tribes);
~Negation() override;
bool evaluate(const ProductionSite&) const override;
// Just a dummy to satisfy the superclass interface. Do not use.
@@ -211,7 +209,7 @@
/// wares, combining from any of the types specified, in its input
/// queues.
struct SiteHas : public Condition {
- SiteHas(char*& parameters, const ProductionSiteDescr&, const Tribes& tribes);
+ SiteHas(std::vector<std::string>::const_iterator begin, std::vector<std::string>::const_iterator end, const ProductionSiteDescr& descr, const Tribes& tribes);
bool evaluate(const ProductionSite&) const override;
std::string description(const Tribes& tribes) const override;
std::string description_negation(const Tribes& tribes) const override;
@@ -269,7 +267,7 @@
/// but no statistics are calculated (with the same effect as
/// executing "return=no_stats")
struct ActCall : public Action {
- ActCall(char* parameters, const ProductionSiteDescr&);
+ ActCall(const std::vector<std::string>& arguments, const ProductionSiteDescr&);
void execute(Game&, ProductionSite&) const override;
private:
@@ -285,7 +283,7 @@
/// program:
/// The name of a program defined in the productionsite's main worker.
struct ActCallWorker : public Action {
- ActCallWorker(char* parameters,
+ ActCallWorker(const std::vector<std::string>& arguments,
const std::string& production_program_name,
ProductionSiteDescr*,
const Tribes& tribes);
@@ -311,7 +309,7 @@
///
/// Blocks the execution of the program for the specified duration.
struct ActSleep : public Action {
- explicit ActSleep(char* parameters);
+ explicit ActSleep(const std::vector<std::string>& arguments);
void execute(Game&, ProductionSite&) const override;
private:
@@ -329,7 +327,7 @@
///
/// Ends the program if the feature is not enabled.
struct ActCheckMap : public Action {
- explicit ActCheckMap(char* parameters);
+ explicit ActCheckMap(const std::vector<std::string>& arguments);
void execute(Game&, ProductionSite&) const override;
private:
@@ -354,12 +352,11 @@
/// animation will not be stopped by this command. It will run until another
/// animation is started.)
struct ActAnimate : public Action {
- ActAnimate(char* parameters, ProductionSiteDescr*);
+ ActAnimate(const std::vector<std::string>& arguments, ProductionSiteDescr*);
void execute(Game&, ProductionSite&) const override;
private:
- std::string animation_name_;
- Duration duration_;
+ AnimationParameters parameters;
};
/// Consumes wares from the input storages.
@@ -406,7 +403,7 @@
/// types of a group are sorted.
// TODO(unknown): change this!
struct ActConsume : public Action {
- ActConsume(char* parameters, const ProductionSiteDescr&, const Tribes& tribes);
+ ActConsume(const std::vector<std::string>& arguments, const ProductionSiteDescr& descr, const Tribes& tribes);
void execute(Game&, ProductionSite&) const override;
};
@@ -426,7 +423,7 @@
/// produced wares are of the type specified in the group. How the produced
/// wares are handled is defined by the productionsite.
struct ActProduce : public Action {
- ActProduce(char* parameters, const ProductionSiteDescr&, const Tribes& tribes);
+ ActProduce(const std::vector<std::string>& arguments, const ProductionSiteDescr&, const Tribes& tribes);
void execute(Game&, ProductionSite&) const override;
bool get_building_work(Game&, ProductionSite&, Worker&) const override;
};
@@ -447,13 +444,13 @@
/// The recruited workers are of the type specified in the group. How the
/// recruited workers are handled is defined by the productionsite.
struct ActRecruit : public Action {
- ActRecruit(char* parameters, const ProductionSiteDescr&, const Tribes& tribes);
+ ActRecruit(const std::vector<std::string>& arguments, const ProductionSiteDescr&, const Tribes& tribes);
void execute(Game&, ProductionSite&) const override;
bool get_building_work(Game&, ProductionSite&, Worker&) const override;
};
struct ActMine : public Action {
- ActMine(char* parameters,
+ ActMine(const std::vector<std::string>& arguments,
const World&,
const std::string& production_program_name,
ProductionSiteDescr*);
@@ -468,22 +465,22 @@
};
struct ActCheckSoldier : public Action {
- explicit ActCheckSoldier(char* parameters);
+ explicit ActCheckSoldier(const std::vector<std::string>& arguments);
void execute(Game&, ProductionSite&) const override;
private:
- TrainingAttribute attribute;
- uint8_t level;
+ TrainingAttribute attribute_;
+ uint8_t level_;
};
struct ActTrain : public Action {
- explicit ActTrain(char* parameters);
+ explicit ActTrain(const std::vector<std::string>& arguments);
void execute(Game&, ProductionSite&) const override;
private:
- TrainingAttribute attribute;
- uint8_t level;
- uint8_t target_level;
+ TrainingAttribute attribute_;
+ uint8_t level_;
+ uint8_t target_level_;
};
/// Plays a sound effect.
@@ -501,12 +498,11 @@
/// Plays the specified sound effect with the specified priority. Whether the
/// sound effect is actually played is determined by the sound handler.
struct ActPlaySound : public Action {
- explicit ActPlaySound(char* parameters);
+ explicit ActPlaySound(const std::vector<std::string>& arguments);
void execute(Game&, ProductionSite&) const override;
private:
- FxId fx;
- uint8_t priority;
+ PlaySoundParameters parameters;
};
/// Sends a building worker to construct at an immovable.
@@ -522,7 +518,7 @@
/// radius
/// Activity radius
struct ActConstruct : public Action {
- ActConstruct(char* parameters,
+ ActConstruct(const std::vector<std::string>& arguments,
const std::string& production_program_name,
ProductionSiteDescr*);
void execute(Game&, ProductionSite&) const override;
@@ -544,7 +540,6 @@
const World& world,
ProductionSiteDescr* building);
- const std::string& name() const;
const std::string& descname() const;
size_t size() const;
@@ -555,7 +550,6 @@
const Buildcost& recruited_workers() const;
private:
- std::string name_;
std::string descname_;
std::vector<std::unique_ptr<Action>> actions_;
ProductionProgram::Groups consumed_wares_workers_;
=== modified file 'src/logic/map_objects/tribes/productionsite.cc'
--- src/logic/map_objects/tribes/productionsite.cc 2019-05-22 15:46:08 +0000
+++ src/logic/map_objects/tribes/productionsite.cc 2019-05-26 08:37:46 +0000
@@ -91,9 +91,11 @@
const std::string& msgctxt,
MapObjectType init_type,
const LuaTable& table,
- const Tribes& tribes,
+ const Tribes& tribes,
const World& world)
: BuildingDescr(init_descname, init_type, table, tribes),
+ ware_demand_checks_(new std::set<DescriptionIndex>()),
+ worker_demand_checks_(new std::set<DescriptionIndex>()),
out_of_resource_productivity_threshold_(100) {
if (msgctxt.empty()) {
throw Widelands::GameDataError(
@@ -188,10 +190,11 @@
items_table = table.get_table("programs");
for (std::string program_name : items_table->keys<std::string>()) {
std::transform(program_name.begin(), program_name.end(), program_name.begin(), tolower);
+ if (programs_.count(program_name)) {
+ throw GameDataError("Program '%s' has already been declared for productionsite '%s'",
+ program_name.c_str(), name().c_str());
+ }
try {
- if (programs_.count(program_name)) {
- throw wexception("this program has already been declared");
- }
std::unique_ptr<LuaTable> program_table = items_table->get_table(program_name);
// Allow use of both gettext and pgettext. This way, we can have a lower workload on
@@ -205,7 +208,7 @@
new ProductionProgram(program_name, program_descname,
program_table->get_table("actions"), tribes, world, this));
} catch (const std::exception& e) {
- throw wexception("program %s: %s", program_name.c_str(), e.what());
+ throw GameDataError("%s: Error in productionsite program %s: %s", name().c_str(), program_name.c_str(), e.what());
}
}
@@ -251,6 +254,19 @@
return *new ProductionSite(*this);
}
+std::set<DescriptionIndex>* ProductionSiteDescr::ware_demand_checks() const {
+ return ware_demand_checks_.get();
+}
+std::set<DescriptionIndex>* ProductionSiteDescr::worker_demand_checks() const {
+ return worker_demand_checks_.get();
+}
+void ProductionSiteDescr::clear_demand_checks() {
+ ware_demand_checks_->clear();
+ ware_demand_checks_.reset(nullptr);
+ worker_demand_checks_->clear();
+ worker_demand_checks_.reset(nullptr);
+}
+
/*
==============================
=== modified file 'src/logic/map_objects/tribes/productionsite.h'
--- src/logic/map_objects/tribes/productionsite.h 2019-05-22 15:46:08 +0000
+++ src/logic/map_objects/tribes/productionsite.h 2019-05-26 08:37:46 +0000
@@ -73,6 +73,13 @@
Building& create_object() const override;
+ // List of wares to register having economy checks. Parsed by the tribes during postload and must be nullptr after loading has finished
+ std::set<DescriptionIndex>* ware_demand_checks() const;
+ // List of workers to register having economy checks. Parsed by the tribes during postload and must be nullptr after loading has finished
+ std::set<DescriptionIndex>* worker_demand_checks() const;
+ // Clear ware and worker demand check info
+ void clear_demand_checks();
+
uint32_t nr_working_positions() const {
uint32_t result = 0;
for (const auto& working_pos : working_positions()) {
@@ -129,6 +136,8 @@
}
private:
+ std::unique_ptr<std::set<DescriptionIndex>> ware_demand_checks_;
+ std::unique_ptr<std::set<DescriptionIndex>> worker_demand_checks_;
BillOfMaterials working_positions_;
BillOfMaterials input_wares_;
BillOfMaterials input_workers_;
=== modified file 'src/logic/map_objects/tribes/soldier.cc'
--- src/logic/map_objects/tribes/soldier.cc 2019-05-18 20:43:25 +0000
+++ src/logic/map_objects/tribes/soldier.cc 2019-05-26 08:37:46 +0000
@@ -32,7 +32,6 @@
#include "economy/flag.h"
#include "graphic/graphic.h"
#include "graphic/rendertarget.h"
-#include "helper.h"
#include "io/fileread.h"
#include "io/filewrite.h"
#include "logic/editor_game_base.h"
=== modified file 'src/logic/map_objects/tribes/trainingsite.cc'
--- src/logic/map_objects/tribes/trainingsite.cc 2019-05-05 14:05:07 +0000
+++ src/logic/map_objects/tribes/trainingsite.cc 2019-05-26 08:37:46 +0000
@@ -28,7 +28,6 @@
#include "base/macros.h"
#include "base/wexception.h"
#include "economy/request.h"
-#include "helper.h"
#include "logic/editor_game_base.h"
#include "logic/game.h"
#include "logic/map_objects/tribes/production_program.h"
=== modified file 'src/logic/map_objects/tribes/tribes.cc'
--- src/logic/map_objects/tribes/tribes.cc 2019-05-22 11:23:14 +0000
+++ src/logic/map_objects/tribes/tribes.cc 2019-05-26 08:37:46 +0000
@@ -293,15 +293,6 @@
return tribes_->get_mutable(tribeindex);
}
-void Tribes::set_ware_type_has_demand_check(const DescriptionIndex& wareindex,
- const std::string& tribename) const {
- wares_->get_mutable(wareindex)->set_has_demand_check(tribename);
-}
-
-void Tribes::set_worker_type_has_demand_check(const DescriptionIndex& workerindex) const {
- workers_->get_mutable(workerindex)->set_has_demand_check();
-}
-
void Tribes::load_graphics() {
for (size_t tribeindex = 0; tribeindex < nrtribes(); ++tribeindex) {
TribeDescr* tribe = tribes_->get_mutable(tribeindex);
@@ -355,6 +346,11 @@
TribeDescr* tribe_descr = tribes_->get_mutable(i);
tribe_descr->resize_ware_orders(number);
+ // Register which wares and workers have economy demand checks for each tribe
+ for (const DescriptionIndex building_index : tribe_descr->buildings()) {
+ postload_register_economy_demand_checks(*buildings_->get_mutable(building_index), *tribe_descr);
+ }
+
// Verify that the preciousness has been set for all of the tribe's wares
for (const DescriptionIndex wi : tribe_descr->wares()) {
if (tribe_descr->get_ware_descr(wi)->ai_hints().preciousness(tribe_descr->name()) ==
@@ -367,7 +363,36 @@
}
}
-// Set default trainingsites proportions for AI. Make sure that we get a sum of ca. 100
+/// Register wares and workers that have economy demand checks for a building
+void Tribes::postload_register_economy_demand_checks(BuildingDescr& building_descr, const TribeDescr& tribe_descr)
+{
+ if (upcast(ProductionSiteDescr, prodsite, &building_descr)) {
+ // This function can be called only once per loading of tribes
+ assert(prodsite->ware_demand_checks() != nullptr);
+ // NOCOM
+ for (const DescriptionIndex ware_index : *prodsite->ware_demand_checks()) {
+ if (!tribe_descr.has_ware(ware_index)) {
+ throw GameDataError("Productionsite '%s' for tribe '%s' has an economy demand check for ware '%s', but the tribe does not use this ware",
+ prodsite->name().c_str(),
+ tribe_descr.name().c_str(),
+ get_ware_descr(ware_index)->name().c_str());
+ }
+ wares_->get_mutable(ware_index)->set_has_demand_check(tribe_descr.name());
+ }
+ for (const DescriptionIndex worker_index : *prodsite->worker_demand_checks()) {
+ if (!tribe_descr.has_worker(worker_index)) {
+ throw GameDataError("Productionsite '%s' for tribe '%s' has an economy demand check for worker '%s', but the tribe does not use this worker",
+ prodsite->name().c_str(),
+ tribe_descr.name().c_str(),
+ get_worker_descr(worker_index)->name().c_str());
+ }
+ workers_->get_mutable(worker_index)->set_has_demand_check();
+ }
+ prodsite->clear_demand_checks();
+ }
+}
+
+/// Set default trainingsites proportions for AI. Make sure that we get a sum of ca. 100
void Tribes::postload_calculate_trainingsites_proportions() {
for (DescriptionIndex i = 0; i < tribes_->size(); ++i) {
TribeDescr* tribe_descr = tribes_->get_mutable(i);
=== modified file 'src/logic/map_objects/tribes/tribes.h'
--- src/logic/map_objects/tribes/tribes.h 2019-05-16 09:15:03 +0000
+++ src/logic/map_objects/tribes/tribes.h 2019-05-26 08:37:46 +0000
@@ -133,10 +133,6 @@
const WorkerDescr* get_worker_descr(DescriptionIndex worker_index) const;
const TribeDescr* get_tribe_descr(DescriptionIndex tribe_index) const;
- void set_ware_type_has_demand_check(const DescriptionIndex& ware_index,
- const std::string& tribename) const;
- void set_worker_type_has_demand_check(const DescriptionIndex& worker_index) const;
-
/// Load tribes' graphics
void load_graphics();
@@ -147,6 +143,7 @@
private:
void postload_calculate_trainingsites_proportions();
+ void postload_register_economy_demand_checks(BuildingDescr& building_descr, const TribeDescr& tribe_descr);
std::unique_ptr<DescriptionMaintainer<BuildingDescr>> buildings_;
std::unique_ptr<DescriptionMaintainer<ImmovableDescr>> immovables_;
=== modified file 'src/logic/map_objects/tribes/worker.cc'
--- src/logic/map_objects/tribes/worker.cc 2019-05-25 10:47:18 +0000
+++ src/logic/map_objects/tribes/worker.cc 2019-05-26 08:37:46 +0000
@@ -35,7 +35,6 @@
#include "economy/transfer.h"
#include "graphic/rendertarget.h"
#include "graphic/text_constants.h"
-#include "helper.h"
#include "io/fileread.h"
#include "io/filewrite.h"
#include "logic/cmd_incorporate.h"
@@ -3146,7 +3145,7 @@
return Bob::Loader::get_task(name);
}
-const BobProgramBase* Worker::Loader::get_program(const std::string& name) {
+const MapObjectProgram* Worker::Loader::get_program(const std::string& name) {
Worker& worker = get<Worker>();
return worker.descr().get_program(name);
}
=== modified file 'src/logic/map_objects/tribes/worker.h'
--- src/logic/map_objects/tribes/worker.h 2019-04-24 06:01:37 +0000
+++ src/logic/map_objects/tribes/worker.h 2019-05-26 08:37:46 +0000
@@ -303,7 +303,7 @@
protected:
const Task* get_task(const std::string& name) override;
- const BobProgramBase* get_program(const std::string& name) override;
+ const MapObjectProgram* get_program(const std::string& name) override;
private:
uint32_t location_;
=== modified file 'src/logic/map_objects/tribes/worker_descr.cc'
--- src/logic/map_objects/tribes/worker_descr.cc 2019-05-26 08:37:42 +0000
+++ src/logic/map_objects/tribes/worker_descr.cc 2019-05-26 08:37:46 +0000
@@ -106,16 +106,14 @@
std::unique_ptr<LuaTable> programs_table = table.get_table("programs");
for (std::string program_name : programs_table->keys<std::string>()) {
std::transform(program_name.begin(), program_name.end(), program_name.begin(), tolower);
-
+ if (programs_.count(program_name)) {
+ throw GameDataError("Program '%s' has already been declared for worker '%s'", program_name.c_str(), name().c_str());
+ }
try {
- if (programs_.count(program_name))
- throw wexception("this program has already been declared");
-
- programs_[program_name] =
- std::unique_ptr<WorkerProgram>(new WorkerProgram(program_name, *this, tribes_));
- programs_[program_name]->parse(*programs_table->get_table(program_name));
+ programs_[program_name] = std::unique_ptr<WorkerProgram>(
+ new WorkerProgram(program_name, *programs_table->get_table(program_name), *this, tribes_));
} catch (const std::exception& e) {
- throw wexception("program %s: %s", program_name.c_str(), e.what());
+ throw GameDataError("%s: Error in worker program %s: %s", name().c_str(), program_name.c_str(), e.what());
}
}
}
@@ -136,8 +134,9 @@
WorkerProgram const* WorkerDescr::get_program(const std::string& programname) const {
Programs::const_iterator it = programs_.find(programname);
- if (it == programs_.end())
- throw wexception("%s has no program '%s'", name().c_str(), programname.c_str());
+ if (it == programs_.end()) {
+ throw GameDataError("%s has no program '%s'", name().c_str(), programname.c_str());
+ }
return it->second.get();
}
=== modified file 'src/logic/map_objects/tribes/worker_program.cc'
--- src/logic/map_objects/tribes/worker_program.cc 2019-05-11 13:48:12 +0000
+++ src/logic/map_objects/tribes/worker_program.cc 2019-05-26 08:37:46 +0000
@@ -23,7 +23,6 @@
#include <string>
#include "base/log.h"
-#include "helper.h"
#include "logic/game_data_error.h"
#include "logic/map_objects/findnode.h"
#include "sound/sound_handler.h"
@@ -98,54 +97,45 @@
{nullptr, nullptr}};
+
/**
* Parse a program
*/
-void WorkerProgram::parse(const LuaTable& table) {
- for (const std::string& string : table.array_entries<std::string>()) {
- if (string.empty()) {
- log("Worker program %s for worker %s contains empty string\n", name_.c_str(),
- worker_.name().c_str());
- break;
+WorkerProgram::WorkerProgram(const std::string& init_name, const LuaTable& actions_table, const WorkerDescr& worker, const Tribes& tribes)
+ : MapObjectProgram(init_name), worker_(worker), tribes_(tribes) {
+
+ for (const std::string& line : actions_table.array_entries<std::string>()) {
+ if (line.empty()) {
+ throw GameDataError("Empty line");
}
try {
- std::vector<std::string> cmd(split_string(string, "="));
- if (cmd.empty()) {
- continue;
- }
+
+ ProgramParseInput parseinput = parse_program_string(line);
// Find the appropriate parser
Worker::Action act;
uint32_t mapidx;
for (mapidx = 0; parsemap_[mapidx].name; ++mapidx) {
- if (cmd[0] == parsemap_[mapidx].name) {
+ if (parseinput.name == parsemap_[mapidx].name) {
break;
}
}
if (!parsemap_[mapidx].name) {
- throw wexception("unknown command type \"%s\"", cmd[0].c_str());
- }
-
- // TODO(GunChleoc): Quick and dirty solution, don't do it like that when we unify the
- // program parsers
- if (cmd.size() == 2) {
- const std::vector<std::string> parameters(split_string(cmd[1], " \t\n"));
- cmd.pop_back();
- for (const std::string& parameter : parameters) {
- cmd.push_back(parameter);
- }
- }
-
- (this->*parsemap_[mapidx].function)(&act, cmd);
+ throw GameDataError("Unknown command '%s' in line '%s'", parseinput.name.c_str(), line.c_str());
+ }
+
+ (this->*parsemap_[mapidx].function)(&act, parseinput.arguments);
actions_.push_back(act);
} catch (const std::exception& e) {
- throw wexception("Error reading line '%s' in worker program %s for worker %s: %s",
- string.c_str(), name_.c_str(), worker_.name().c_str(), e.what());
+ throw GameDataError("Error reading line '%s': %s", line.c_str(), e.what());
}
}
+ if (actions_.empty()) {
+ throw GameDataError("No actions found");
+ }
}
/* RST
@@ -172,11 +162,12 @@
* iparam1 = ware index
*/
void WorkerProgram::parse_createware(Worker::Action* act, const std::vector<std::string>& cmd) {
- if (cmd.size() != 2)
+ if (cmd.size() != 1) {
throw wexception("Usage: createware=<ware type>");
+ }
act->function = &Worker::run_createware;
- act->iparam1 = tribes_.safe_ware_index(cmd[1]);
+ act->iparam1 = tribes_.safe_ware_index(cmd[0]);
}
/* RST
@@ -207,16 +198,13 @@
* sparam1 = resource
*/
void WorkerProgram::parse_mine(Worker::Action* act, const std::vector<std::string>& cmd) {
- if (cmd.size() != 3)
- throw wexception("Usage: mine=<ware type> <area>");
+ if (cmd.size() != 2) {
+ throw GameDataError("Usage: mine=<ware type> <workarea radius>");
+ }
act->function = &Worker::run_mine;
- act->sparam1 = cmd[1];
- char* endp;
- act->iparam1 = strtol(cmd[2].c_str(), &endp, 0);
-
- if (*endp)
- throw wexception("Bad area '%s'", cmd[2].c_str());
+ act->sparam1 = cmd[0];
+ act->iparam1 = read_positive(cmd[1]);
}
/* RST
@@ -244,16 +232,13 @@
* sparam1 = resource
*/
void WorkerProgram::parse_breed(Worker::Action* act, const std::vector<std::string>& cmd) {
- if (cmd.size() != 3)
- throw wexception("Usage: breed=<ware type> <area>");
+ if (cmd.size() != 2) {
+ throw GameDataError("Usage: breed=<ware type> <workarea radius>");
+ }
act->function = &Worker::run_breed;
- act->sparam1 = cmd[1];
- char* endp;
- act->iparam1 = strtol(cmd[2].c_str(), &endp, 0);
-
- if (*endp)
- throw wexception("Bad area '%s'", cmd[2].c_str());
+ act->sparam1 = cmd[0];
+ act->iparam1 = read_positive(cmd[1]);
}
/* RST
@@ -296,37 +281,26 @@
* sparam1 = type
*/
void WorkerProgram::parse_findobject(Worker::Action* act, const std::vector<std::string>& cmd) {
- uint32_t i;
-
act->function = &Worker::run_findobject;
act->iparam1 = -1;
act->iparam2 = -1;
act->sparam1 = "immovable";
// Parse predicates
- for (i = 1; i < cmd.size(); ++i) {
- uint32_t idx = cmd[i].find(':');
- std::string const key = cmd[i].substr(0, idx);
- std::string const value = cmd[i].substr(idx + 1);
-
- if (key == "radius") {
- char* endp;
-
- act->iparam1 = strtol(value.c_str(), &endp, 0);
- if (*endp)
- throw wexception("Bad findobject radius '%s'", value.c_str());
-
- } else if (key == "attrib") {
- act->iparam2 = MapObjectDescr::get_attribute_id(value);
- } else if (key == "type") {
- act->sparam1 = value;
- } else
- throw wexception("Bad findobject predicate %s:%s", key.c_str(), value.c_str());
+ for (const std::string& argument : cmd) {
+ const std::pair<std::string, std::string> item = read_key_value_pair(argument, ':');
+
+ if (item.first == "radius") {
+ act->iparam1 = read_positive(item.second);
+ } else if (item.first == "attrib") {
+ act->iparam2 = MapObjectDescr::get_attribute_id(item.second);
+ } else if (item.first == "type") {
+ act->sparam1 = item.second;
+ } else {
+ throw GameDataError("Unknown findobject predicate %s", argument.c_str());
+ }
}
- if (act->iparam1 <= 0)
- throw wexception("findobject: must specify radius");
-
workarea_info_[act->iparam1].insert(" findobject");
}
@@ -408,8 +382,6 @@
* sparam1 = Resource
*/
void WorkerProgram::parse_findspace(Worker::Action* act, const std::vector<std::string>& cmd) {
- uint32_t i;
-
act->function = &Worker::run_findspace;
act->iparam1 = -1;
act->iparam2 = -1;
@@ -420,61 +392,58 @@
act->sparam1 = "";
// Parse predicates
- for (i = 1; i < cmd.size(); ++i) {
- uint32_t idx = cmd[i].find(':');
- std::string key = cmd[i].substr(0, idx);
- std::string value = cmd[i].substr(idx + 1);
-
- if (key == "radius") {
- char* endp;
-
- act->iparam1 = strtol(value.c_str(), &endp, 0);
- if (*endp)
- throw wexception("Bad findspace radius '%s'", value.c_str());
-
- } else if (key == "size") {
- static const struct {
- char const* name;
- int32_t val;
- } sizenames[] = {{"any", FindNodeSize::sizeAny}, {"build", FindNodeSize::sizeBuild},
- {"small", FindNodeSize::sizeSmall}, {"medium", FindNodeSize::sizeMedium},
- {"big", FindNodeSize::sizeBig}, {"mine", FindNodeSize::sizeMine},
- {"port", FindNodeSize::sizePort}, {nullptr, 0}};
-
- int32_t index;
-
- for (index = 0; sizenames[index].name; ++index)
- if (value == sizenames[index].name)
- break;
-
- if (!sizenames[index].name)
- throw wexception("Bad findspace size '%s'", value.c_str());
-
- act->iparam2 = sizenames[index].val;
- } else if (key == "breed") {
- act->iparam4 = 1;
- } else if (key == "resource") {
- act->sparam1 = value;
- } else if (key == "space") {
- act->iparam3 = 1;
- } else if (key == "avoid") {
- act->iparam5 = MapObjectDescr::get_attribute_id(value);
- } else if (key == "saplingsearches") {
- int ival = strtol(value.c_str(), nullptr, 10);
- if (1 != act->iparam6 || 1 > ival) {
- throw wexception("Findspace: Forester should consider at least one spot.%d %d %s",
- act->iparam6, ival, value.c_str());
+ for (const std::string& argument : cmd) {
+ try {
+ const std::pair<std::string, std::string> item = read_key_value_pair(argument, ':');
+
+ if (item.first == "radius") {
+ act->iparam1 = read_positive(item.second);
+ } else if (item.first == "size") {
+ static const struct {
+ char const* name;
+ int32_t val;
+ } sizenames[] = {{"any", FindNodeSize::sizeAny}, {"build", FindNodeSize::sizeBuild},
+ {"small", FindNodeSize::sizeSmall}, {"medium", FindNodeSize::sizeMedium},
+ {"big", FindNodeSize::sizeBig}, {"mine", FindNodeSize::sizeMine},
+ {"port", FindNodeSize::sizePort}, {nullptr, 0}};
+
+ int32_t index;
+
+ for (index = 0; sizenames[index].name; ++index) {
+ if (item.second == sizenames[index].name) {
+ break;
+ }
+ }
+
+ if (!sizenames[index].name) {
+ throw GameDataError("Bad findspace size '%s'", item.second.c_str());
+ }
+
+ act->iparam2 = sizenames[index].val;
+ } else if (item.first == "breed") {
+ act->iparam4 = 1;
+ } else if (item.first == "resource") {
+ act->sparam1 = item.second;
+ } else if (item.first == "space") {
+ act->iparam3 = 1;
+ } else if (item.first == "avoid") {
+ act->iparam5 = MapObjectDescr::get_attribute_id(item.second);
+ } else if (item.first == "saplingsearches") {
+ act->iparam6 = read_positive(item.second);
} else {
- act->iparam6 = ival;
+ throw GameDataError("Unknown findspace predicate %s", item.first.c_str());
}
- } else
- throw wexception("Bad findspace predicate %s:%s", key.c_str(), value.c_str());
+ } catch (const GameDataError& e) {
+ throw GameDataError("Malformed findspace argument %s: %s", argument.c_str(), e.what());
+ }
}
- if (act->iparam1 <= 0)
- throw wexception("findspace: must specify radius");
- if (act->iparam2 < 0)
- throw wexception("findspace: must specify size");
+ if (act->iparam1 <= 0) {
+ throw GameDataError("findspace: must specify radius");
+ }
+ if (act->iparam2 < 0) {
+ throw GameDataError("findspace: must specify size");
+ }
workarea_info_[act->iparam1].insert(" findspace");
}
@@ -526,19 +495,21 @@
* iparam1 = walkXXX
*/
void WorkerProgram::parse_walk(Worker::Action* act, const std::vector<std::string>& cmd) {
- if (cmd.size() != 2)
- throw wexception("Usage: walk=<where>");
+ if (cmd.size() != 1) {
+ throw GameDataError("Usage: walk=object|coords|object-or-coords");
+ }
act->function = &Worker::run_walk;
- if (cmd[1] == "object")
+ if (cmd[0] == "object") {
act->iparam1 = Worker::Action::walkObject;
- else if (cmd[1] == "coords")
+ } else if (cmd[0] == "coords") {
act->iparam1 = Worker::Action::walkCoords;
- else if (cmd[1] == "object-or-coords")
+ } else if (cmd[0] == "object-or-coords") {
act->iparam1 = Worker::Action::walkObject | Worker::Action::walkCoords;
- else
- throw wexception("Bad walk destination '%s'", cmd[1].c_str());
+ } else {
+ throw GameDataError("Bad walk destination '%s'", cmd[0].c_str());
+ }
}
/* RST
@@ -565,28 +536,14 @@
* iparam2 = duration
*/
void WorkerProgram::parse_animate(Worker::Action* act, const std::vector<std::string>& cmd) {
- char* endp;
-
- if (cmd.size() != 3)
- throw GameDataError("Usage: animate=<name> <time>");
-
- if (!worker_.is_animation_known(cmd[1])) {
- throw GameDataError("unknown animation \"%s\" in worker program for worker \"%s\"",
- cmd[1].c_str(), worker_.name().c_str());
- }
+ AnimationParameters parameters = MapObjectProgram::parse_act_animate(cmd, worker_, true);
act->function = &Worker::run_animate;
// If the second parameter to MapObjectDescr::get_animation is ever used for anything other than
// level-dependent soldier animations, or we want to write a worker program for a soldier,
- // we will need to store the animation name as a string in an iparam
- act->iparam1 = worker_.get_animation(cmd[1], nullptr);
-
- act->iparam2 = strtol(cmd[2].c_str(), &endp, 0);
- if (*endp)
- throw GameDataError("Bad duration '%s'", cmd[2].c_str());
-
- if (act->iparam2 <= 0)
- throw GameDataError("animation duration must be positive");
+ // we will need to store the animation name as a string in an sparam
+ act->iparam1 = parameters.animation;
+ act->iparam2 = parameters.duration;
}
/* RST
@@ -604,7 +561,10 @@
/**
* iparam1 = 0: don't drop ware on flag, 1: do drop ware on flag
*/
-void WorkerProgram::parse_return(Worker::Action* act, const std::vector<std::string>&) {
+void WorkerProgram::parse_return(Worker::Action* act, const std::vector<std::string>& cmd) {
+ if (!cmd.empty()) {
+ throw GameDataError("Usage: return");
+ }
act->function = &Worker::run_return;
act->iparam1 = 1; // drop a ware on our owner's flag
}
@@ -634,11 +594,12 @@
* sparam1 = callobject command name
*/
void WorkerProgram::parse_callobject(Worker::Action* act, const std::vector<std::string>& cmd) {
- if (cmd.size() != 2)
- throw wexception("Usage: callobject=<program name>");
+ if (cmd.size() != 1) {
+ throw GameDataError("Usage: callobject=<program name>");
+ }
act->function = &Worker::run_callobject;
- act->sparam1 = cmd[1];
+ act->sparam1 = cmd[0];
}
/* RST
@@ -691,36 +652,28 @@
* iparam1 one of plantXXX
*/
void WorkerProgram::parse_plant(Worker::Action* act, const std::vector<std::string>& cmd) {
- if (cmd.size() < 2)
- throw wexception(
- "Usage: plant plant=attrib:<attribute> [attrib:<attribute> ...] [unless object]");
+ if (cmd.empty()) {
+ throw GameDataError(
+ "Usage: plant=attrib:<attribute> [attrib:<attribute> ...] [unless object]");
+ }
act->function = &Worker::run_plant;
act->iparam1 = Worker::Action::plantAlways;
- for (uint32_t i = 1; i < cmd.size(); ++i) {
- if (i >= 2 && cmd[i] == "unless") {
+ for (uint32_t i = 0; i < cmd.size(); ++i) {
+ if (cmd[i] == "unless") {
++i;
- if (i >= cmd.size())
- throw GameDataError("plant: something expected after unless");
- if (cmd[i] == "object")
+ if (i >= cmd.size()) {
+ throw GameDataError("plant: something expected after 'unless'");
+ }
+ if (cmd[i] == "object") {
act->iparam1 = Worker::Action::plantUnlessObject;
- else
+ } else {
throw GameDataError("plant: 'unless %s' not understood", cmd[i].c_str());
-
+ }
continue;
}
- // Check if immovable type exists
- std::vector<std::string> const list(split_string(cmd[i], ":"));
- if (list.size() != 2 || list.at(0) != "attrib") {
- throw GameDataError("plant takes a list of attrib:<attribute> that reference world and/or "
- "tribe immovables");
- }
-
- const std::string& attrib_name = list[1];
- if (attrib_name.empty()) {
- throw GameDataError("plant has an empty attrib:<attribute> at position %d", i);
- }
+ const std::string attrib_name = read_key_value_pair(cmd[i], ':', "", "attrib").second;
// This will throw a GameDataError if the attribute doesn't exist.
ImmovableDescr::get_attribute_id(attrib_name);
@@ -750,14 +703,12 @@
*/
// TODO(GunChleoc): attrib:eatable would be much better, then depend on terrain too
void WorkerProgram::parse_createbob(Worker::Action* act, const std::vector<std::string>& cmd) {
- if (cmd.size() < 2)
- throw wexception("Usage: createbob=<bob name> <bob name> ...");
+ if (cmd.empty()) {
+ throw GameDataError("Usage: createbob=<bob name> <bob name> ...");
+ }
act->function = &Worker::run_createbob;
-
- for (uint32_t i = 1; i < cmd.size(); ++i) {
- act->sparamv.push_back(cmd[i]);
- }
+ act->sparamv = std::move(cmd);
}
/* RST
@@ -804,22 +755,14 @@
* sparam1 = subcommand
*/
void WorkerProgram::parse_repeatsearch(Worker::Action* act, const std::vector<std::string>& cmd) {
- char* endp;
-
- if (cmd.size() != 4)
- throw wexception("Usage: repeatsearch=<repeat #> <radius> <subcommand>");
+ if (cmd.size() != 3) {
+ throw GameDataError("Usage: repeatsearch=<repeat #> <radius> <subcommand>");
+ }
act->function = &Worker::run_repeatsearch;
-
- act->iparam1 = strtol(cmd[1].c_str(), &endp, 0);
- if (*endp)
- throw wexception("Bad repeat count '%s'", cmd[1].c_str());
-
- act->iparam2 = strtol(cmd[2].c_str(), &endp, 0);
- if (*endp)
- throw wexception("Bad radius '%s'", cmd[2].c_str());
-
- act->sparam1 = cmd[3];
+ act->iparam1 = read_positive(cmd[0]);
+ act->iparam2 = read_positive(cmd[1]);
+ act->sparam1 = cmd[2];
}
/* RST
@@ -840,8 +783,9 @@
}
*/
void WorkerProgram::parse_findresources(Worker::Action* act, const std::vector<std::string>& cmd) {
- if (cmd.size() != 1)
- throw wexception("Usage: findresources");
+ if (!cmd.empty()) {
+ throw GameDataError("Usage: findresources");
+ }
act->function = &Worker::run_findresources;
}
@@ -867,11 +811,12 @@
* iparam2 = time
*/
void WorkerProgram::parse_scout(Worker::Action* act, const std::vector<std::string>& cmd) {
- if (cmd.size() != 3)
- throw wexception("Usage: scout=<radius> <time>");
+ if (cmd.size() != 2) {
+ throw GameDataError("Usage: scout=<radius> <time>");
+ }
- act->iparam1 = atoi(cmd[1].c_str());
- act->iparam2 = atoi(cmd[2].c_str());
+ act->iparam1 = atoi(cmd[0].c_str());
+ act->iparam2 = atoi(cmd[1].c_str());
act->function = &Worker::run_scout;
}
@@ -902,17 +847,12 @@
}
*/
void WorkerProgram::parse_playsound(Worker::Action* act, const std::vector<std::string>& cmd) {
- if (cmd.size() < 3 || cmd.size() > 4)
- throw wexception("Usage: playsound <sound_dir> <sound_name> [priority]");
-
- act->iparam2 = SoundHandler::register_fx(SoundType::kAmbient, cmd[1]);
-
+ // 50% chance to play, only one instance at a time
+ PlaySoundParameters parameters = MapObjectProgram::parse_act_play_sound(cmd, kFxPriorityMedium);
+
+ act->iparam1 = parameters.priority;
+ act->iparam2 = parameters.fx;
act->function = &Worker::run_playsound;
- act->iparam1 = cmd.size() == 2 ? kFxPriorityMedium : atoi(cmd[2].c_str());
- if (act->iparam1 < kFxPriorityLowest) {
- throw GameDataError("Minmum priority for sounds is %d, but only %d was specified for %s",
- kFxPriorityLowest, act->iparam1, cmd[1].c_str());
- }
}
/* RST
@@ -943,8 +883,9 @@
* for construction. This is used in ship building.
*/
void WorkerProgram::parse_construct(Worker::Action* act, const std::vector<std::string>& cmd) {
- if (cmd.size() != 1)
- throw wexception("Usage: construct");
+ if (!cmd.empty()) {
+ throw GameDataError("Usage: construct");
+ }
act->function = &Worker::run_construct;
}
=== modified file 'src/logic/map_objects/tribes/worker_program.h'
--- src/logic/map_objects/tribes/worker_program.h 2019-02-23 11:00:49 +0000
+++ src/logic/map_objects/tribes/worker_program.h 2019-05-26 08:37:46 +0000
@@ -24,6 +24,7 @@
#include "base/macros.h"
#include "logic/map_objects/bob.h"
+#include "logic/map_objects/map_object_program.h"
#include "logic/map_objects/tribes/tribes.h"
#include "logic/map_objects/tribes/workarea_info.h"
#include "logic/map_objects/tribes/worker.h"
@@ -35,20 +36,13 @@
// declaration (Chicken-and-egg problem)
class WorkerDescr;
-struct WorkerProgram : public BobProgramBase {
+struct WorkerProgram : public MapObjectProgram {
using ParseWorkerProgramFn = void (WorkerProgram::*)(Worker::Action*,
const std::vector<std::string>&);
- WorkerProgram(const std::string& name, const WorkerDescr& worker, const Tribes& tribes)
- : name_(name), worker_(worker), tribes_(tribes) {
- }
- ~WorkerProgram() override {
- }
+ WorkerProgram(const std::string& init_name, const LuaTable& actions_table, const WorkerDescr& worker, const Tribes& tribes);
- std::string get_name() const override {
- return name_;
- }
using Actions = std::vector<Worker::Action>;
Actions::size_type get_size() const {
return actions_.size();
@@ -62,7 +56,6 @@
return &actions_[idx];
}
- void parse(const LuaTable& table);
const WorkareaInfo& get_workarea_info() const {
return workarea_info_;
}
@@ -92,7 +85,6 @@
void parse_playsound(Worker::Action* act, const std::vector<std::string>& cmd);
void parse_construct(Worker::Action* act, const std::vector<std::string>& cmd);
- const std::string name_;
const WorkerDescr& worker_;
const Tribes& tribes_;
Actions actions_;
=== modified file 'src/logic/map_objects/world/critter.cc'
--- src/logic/map_objects/world/critter.cc 2019-05-26 08:37:42 +0000
+++ src/logic/map_objects/world/critter.cc 2019-05-26 08:37:46 +0000
@@ -27,22 +27,21 @@
#include <stdint.h>
#include "base/wexception.h"
-#include "helper.h"
#include "io/fileread.h"
#include "io/filewrite.h"
#include "logic/field.h"
#include "logic/game.h"
#include "logic/game_data_error.h"
+#include "logic/map_objects/map_object_program.h"
#include "logic/map_objects/tribes/tribe_descr.h"
-#include "logic/map_objects/world/critter_program.h"
#include "logic/map_objects/world/world.h"
#include "map_io/world_legacy_lookup_table.h"
#include "scripting/lua_table.h"
namespace Widelands {
-void CritterProgram::parse(const std::vector<std::string>& lines) {
- for (const std::string& line : lines) {
+CritterProgram::CritterProgram(const std::string& name, const LuaTable& actions_table) : MapObjectProgram(name) {
+ for (const std::string& line : actions_table.array_entries<std::string>()) {
try {
const std::vector<std::string> cmd(split_string(line, " \t\r\n"));
if (cmd.empty())
@@ -109,9 +108,7 @@
std::unique_ptr<LuaTable> programs = table.get_table("programs");
for (const std::string& program_name : programs->keys<std::string>()) {
try {
- std::unique_ptr<CritterProgram> prog(new CritterProgram(program_name));
- prog->parse(programs->get_table(program_name)->array_entries<std::string>());
- programs_[program_name] = prog.release();
+ programs_[program_name] = std::unique_ptr<CritterProgram>(new CritterProgram(program_name, *programs->get_table(program_name).get()));
} catch (const std::exception& e) {
throw wexception("Parse error in program %s: %s", program_name.c_str(), e.what());
}
@@ -126,9 +123,6 @@
}
CritterDescr::~CritterDescr() {
- for (auto program : programs_) {
- delete program.second;
- }
}
bool CritterDescr::is_swimming() const {
@@ -145,7 +139,7 @@
Programs::const_iterator const it = programs_.find(programname);
if (it == programs_.end())
throw wexception("%s has no program '%s'", name().c_str(), programname.c_str());
- return it->second;
+ return it->second.get();
}
uint32_t CritterDescr::movecaps() const {
@@ -281,7 +275,7 @@
return Bob::Loader::get_task(name);
}
-const BobProgramBase* Critter::Loader::get_program(const std::string& name) {
+const MapObjectProgram* Critter::Loader::get_program(const std::string& name) {
Critter& critter = get<Critter>();
return critter.descr().get_program(name);
}
=== modified file 'src/logic/map_objects/world/critter.h'
--- src/logic/map_objects/world/critter.h 2019-02-23 11:00:49 +0000
+++ src/logic/map_objects/world/critter.h 2019-05-26 08:37:46 +0000
@@ -20,9 +20,12 @@
#ifndef WL_LOGIC_MAP_OBJECTS_WORLD_CRITTER_H
#define WL_LOGIC_MAP_OBJECTS_WORLD_CRITTER_H
+#include <memory>
+
#include "base/macros.h"
#include "graphic/diranimations.h"
#include "logic/map_objects/bob.h"
+#include "logic/map_objects/world/critter_program.h"
class LuaTable;
class WorldLegacyLookupTable;
@@ -54,7 +57,7 @@
private:
DirAnimations walk_anims_;
- using Programs = std::map<std::string, CritterProgram*>;
+ using Programs = std::map<std::string, std::unique_ptr<const CritterProgram>>;
Programs programs_;
EditorCategory* editor_category_; // not owned.
DISALLOW_COPY_AND_ASSIGN(CritterDescr);
@@ -83,7 +86,7 @@
Loader();
const Task* get_task(const std::string& name) override;
- const BobProgramBase* get_program(const std::string& name) override;
+ const MapObjectProgram* get_program(const std::string& name) override;
};
private:
=== modified file 'src/logic/map_objects/world/critter_program.h'
--- src/logic/map_objects/world/critter_program.h 2019-02-23 11:00:49 +0000
+++ src/logic/map_objects/world/critter_program.h 2019-05-26 08:37:46 +0000
@@ -21,8 +21,10 @@
#define WL_LOGIC_MAP_OBJECTS_WORLD_CRITTER_PROGRAM_H
#include "logic/map_objects/bob.h"
+#include "logic/map_objects/map_object_program.h"
namespace Widelands {
+class Critter;
struct CritterAction {
using CritterExecuteActionFn = bool (Critter::*)(Game&, Bob::State&, const CritterAction&);
@@ -40,15 +42,9 @@
std::vector<std::string> sparamv;
};
-struct CritterProgram : public BobProgramBase {
- explicit CritterProgram(const std::string& name) : name_(name) {
- }
- ~CritterProgram() override {
- }
+struct CritterProgram : public MapObjectProgram {
+ explicit CritterProgram(const std::string& name, const LuaTable& actions_table);
- std::string get_name() const override {
- return name_;
- }
int32_t get_size() const {
return actions_.size();
}
@@ -57,10 +53,7 @@
return actions_[idx];
}
- void parse(const std::vector<std::string>& lines);
-
private:
- std::string name_;
std::vector<CritterAction> actions_;
};
} // namespace Widelands
=== modified file 'src/logic/map_objects/world/resource_description.cc'
--- src/logic/map_objects/world/resource_description.cc 2019-02-23 11:00:49 +0000
+++ src/logic/map_objects/world/resource_description.cc 2019-05-26 08:37:46 +0000
@@ -21,7 +21,6 @@
#include <memory>
-#include "helper.h"
#include "logic/game_data_error.h"
#include "scripting/lua_table.h"
=== modified file 'src/network/CMakeLists.txt'
--- src/network/CMakeLists.txt 2019-05-04 15:37:33 +0000
+++ src/network/CMakeLists.txt 2019-05-26 08:37:46 +0000
@@ -44,7 +44,6 @@
build_info
chat
game_io
- helper
io_fileread
io_filesystem
io_stream
=== modified file 'src/network/gameclient.cc'
--- src/network/gameclient.cc 2019-04-29 16:22:08 +0000
+++ src/network/gameclient.cc 2019-05-26 08:37:46 +0000
@@ -31,7 +31,6 @@
#include "build_info.h"
#include "config.h"
#include "game_io/game_loader.h"
-#include "helper.h"
#include "io/fileread.h"
#include "io/filesystem/filesystem_exceptions.h"
#include "io/filewrite.h"
=== modified file 'src/network/gamehost.cc'
--- src/network/gamehost.cc 2019-05-11 22:55:40 +0000
+++ src/network/gamehost.cc 2019-05-26 08:37:46 +0000
@@ -40,7 +40,6 @@
#include "chat/chat.h"
#include "game_io/game_loader.h"
#include "game_io/game_preload_packet.h"
-#include "helper.h"
#include "io/fileread.h"
#include "io/filesystem/layered_filesystem.h"
#include "logic/filesystem_constants.h"
=== modified file 'src/scripting/CMakeLists.txt'
--- src/scripting/CMakeLists.txt 2019-05-05 18:53:14 +0000
+++ src/scripting/CMakeLists.txt 2019-05-26 08:37:46 +0000
@@ -70,7 +70,6 @@
base_i18n
base_macros
build_info
- helper
io_filesystem
scripting_base
scripting_errors
=== modified file 'src/scripting/lua_path.cc'
--- src/scripting/lua_path.cc 2019-05-26 08:37:42 +0000
+++ src/scripting/lua_path.cc 2019-05-26 08:37:46 +0000
@@ -24,7 +24,6 @@
#include <boost/lexical_cast.hpp>
#include "base/macros.h"
-#include "helper.h"
#include "io/filesystem/layered_filesystem.h"
namespace {
=== modified file 'src/sound/CMakeLists.txt'
--- src/sound/CMakeLists.txt 2019-04-08 13:32:28 +0000
+++ src/sound/CMakeLists.txt 2019-05-26 08:37:46 +0000
@@ -28,7 +28,6 @@
DEPENDS
base_i18n
base_log
- helper
io_fileread
io_filesystem
logic_exceptions
=== modified file 'src/sound/fxset.cc'
--- src/sound/fxset.cc 2019-05-26 08:37:42 +0000
+++ src/sound/fxset.cc 2019-05-26 08:37:46 +0000
@@ -25,7 +25,6 @@
#include <boost/regex.hpp>
#include "base/log.h"
-#include "helper.h"
#include "io/fileread.h"
#include "io/filesystem/layered_filesystem.h"
#include "logic/game_data_error.h"
=== modified file 'src/sound/songset.cc'
--- src/sound/songset.cc 2019-05-26 08:37:42 +0000
+++ src/sound/songset.cc 2019-05-26 08:37:46 +0000
@@ -25,7 +25,6 @@
#include <boost/regex.hpp>
#include "base/log.h"
-#include "helper.h"
#include "io/fileread.h"
#include "io/filesystem/layered_filesystem.h"
=== modified file 'src/ui_fsmenu/CMakeLists.txt'
--- src/ui_fsmenu/CMakeLists.txt 2019-05-04 15:37:33 +0000
+++ src/ui_fsmenu/CMakeLists.txt 2019-05-26 08:37:46 +0000
@@ -11,7 +11,6 @@
graphic_text
graphic_text_constants
graphic_text_layout
- helper
io_filesystem
logic_filesystem_constants
scripting_lua_interface
@@ -104,7 +103,6 @@
graphic
graphic_playercolor
graphic_text_constants
- helper
io_filesystem
logic
logic_game_controller
=== modified file 'src/ui_fsmenu/launch_spg.cc'
--- src/ui_fsmenu/launch_spg.cc 2019-04-26 11:33:10 +0000
+++ src/ui_fsmenu/launch_spg.cc 2019-05-26 08:37:46 +0000
@@ -27,7 +27,6 @@
#include "base/warning.h"
#include "base/wexception.h"
#include "graphic/text_constants.h"
-#include "helper.h"
#include "io/filesystem/layered_filesystem.h"
#include "logic/game.h"
#include "logic/game_controller.h"
=== modified file 'src/ui_fsmenu/options.cc'
--- src/ui_fsmenu/options.cc 2019-03-18 18:03:08 +0000
+++ src/ui_fsmenu/options.cc 2019-05-26 08:37:46 +0000
@@ -37,7 +37,6 @@
#include "graphic/text/font_set.h"
#include "graphic/text_constants.h"
#include "graphic/text_layout.h"
-#include "helper.h"
#include "io/filesystem/layered_filesystem.h"
#include "logic/filesystem_constants.h"
#include "profile/profile.h"
=== modified file 'src/wui/CMakeLists.txt'
--- src/wui/CMakeLists.txt 2019-05-05 18:53:14 +0000
+++ src/wui/CMakeLists.txt 2019-05-26 08:37:46 +0000
@@ -84,7 +84,6 @@
base_i18n
base_log
base_time_string
- helper
game_io
graphic_fonthandler
graphic_image_io
=== modified file 'src/wui/load_or_save_game.cc'
--- src/wui/load_or_save_game.cc 2019-05-26 08:37:42 +0000
+++ src/wui/load_or_save_game.cc 2019-05-26 08:37:46 +0000
@@ -31,7 +31,6 @@
#include "game_io/game_loader.h"
#include "game_io/game_preload_packet.h"
#include "graphic/font_handler.h"
-#include "helper.h"
#include "io/filesystem/filesystem_exceptions.h"
#include "io/filesystem/layered_filesystem.h"
#include "logic/filesystem_constants.h"
Follow ups
-
[Merge] lp:~widelands-dev/widelands/unify-program-parsers into lp:widelands
From: GunChleoc, 2019-09-01
-
Re: [Merge] lp:~widelands-dev/widelands/unify-program-parsers into lp:widelands
From: GunChleoc, 2019-09-01
-
Re: [Merge] lp:~widelands-dev/widelands/unify-program-parsers into lp:widelands
From: GunChleoc, 2019-08-26
-
Re: [Merge] lp:~widelands-dev/widelands/unify-program-parsers into lp:widelands
From: Toni Förster, 2019-08-25
-
[Merge] lp:~widelands-dev/widelands/unify-program-parsers into lp:widelands
From: bunnybot, 2019-06-25
-
[Merge] lp:~widelands-dev/widelands/unify-program-parsers into lp:widelands
From: bunnybot, 2019-06-14
-
Re: [Merge] lp:~widelands-dev/widelands/unify-program-parsers into lp:widelands
From: GunChleoc, 2019-06-14
-
Re: [Merge] lp:~widelands-dev/widelands/unify-program-parsers into lp:widelands
From: Notabilis, 2019-06-13
-
Re: [Merge] lp:~widelands-dev/widelands/unify-program-parsers into lp:widelands
From: GunChleoc, 2019-06-08
-
Re: [Merge] lp:~widelands-dev/widelands/unify-program-parsers into lp:widelands
From: Notabilis, 2019-06-08
-
[Merge] lp:~widelands-dev/widelands/unify-program-parsers into lp:widelands
From: bunnybot, 2019-05-27
-
[Merge] lp:~widelands-dev/widelands/unify-program-parsers into lp:widelands
From: GunChleoc, 2019-05-26