GunChleoc has proposed merging lp:~widelands-dev/widelands/editor_help into lp:widelands.
Commit message: Exposed terrain and resource descriptions to the Lua interface and added tree and terrain help to the editor. Requested reviews: Widelands Developers (widelands-dev) Related bugs: Bug #1204482 in widelands: "Improve the help system for the editor" https://bugs.launchpad.net/widelands/+bug/1204482 For more details, see: https://code.launchpad.net/~widelands-dev/widelands/editor_help/+merge/283900 Added tree and terrain help to the editor. The design is discussed in the forum: https://wl.widelands.org/forum/topic/1870/ We are also thinking of making terrains on the map clickable to popup a smaller help window, or maybe adding it to the info tool, but that can be done in a separate branch. -- Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/editor_help into lp:widelands.
=== added directory 'scripting/editor' === added file 'scripting/editor/format_editor.lua' --- scripting/editor/format_editor.lua 1970-01-01 00:00:00 +0000 +++ scripting/editor/format_editor.lua 2016-01-26 07:30:52 +0000 @@ -0,0 +1,27 @@ +include "scripting/formatting.lua" + +function picture_li(imagepath, text) + return "<rt image=" .. imagepath .. " image-align=left>" .. p(text) .. "</rt>" +end + +function spacer() + return rt(p("font-size=3", "")) +end + +-- RST +-- .. function text_line(t1, t2[, imgstr = nil]) +-- +-- Creates a line of h3 formatted text followed by normal text and an image. +-- +-- :arg t1: text in h3 format. +-- :arg t2: text in p format. +-- :arg imgstr: image aligned right. +-- :returns: header followed by normal text and image. +-- +function text_line(t1, t2, imgstr) + if imgstr then + return "<rt text-align=left image=" .. imgstr .. " image-align=right><p font-size=13 font-color=D1D1D1>" .. t1 .. "</p><p line-spacing=3 font-size=12>" .. t2 .. "<br></p><p font-size=8> <br></p></rt>" + else + return "<rt text-align=left><p font-size=13 font-color=D1D1D1>" .. t1 .. "</p><p line-spacing=3 font-size=12>" .. t2 .. "<br></p><p font-size=8> <br></p></rt>" + end +end === added file 'scripting/editor/terrain_help.lua' --- scripting/editor/terrain_help.lua 1970-01-01 00:00:00 +0000 +++ scripting/editor/terrain_help.lua 2016-01-26 07:30:52 +0000 @@ -0,0 +1,66 @@ +include "scripting/editor/format_editor.lua" + +return { + func = function(terrain_name) + set_textdomain("widelands") + local world = wl.World(); + local terrain = wl.Editor():get_terrain_description(terrain_name) + + local result = picture_li(terrain.representative_image, "") + + -- Resources + local valid_resources = terrain.valid_resources_names + if (#valid_resources > 0) then + result = result .. spacer() .. rt(h2(_"Resources")) + if (#valid_resources > 0) then + -- TRANSLATORS: A header in the editor help + result = result .. rt(h3(ngettext("Valid Resource:", "Valid Resources:", #valid_resources))) + for count, resourcename in pairs(valid_resources) do + local valid_resource = wl.Editor():get_resource_description(resourcename) + result = result .. picture_li(valid_resource.representative_image, valid_resource.descname) + end + end + + local default_resource_name = terrain.default_resource_name + if (default_resource_name ~= nil) then + local default_resource = wl.Editor():get_resource_description(default_resource_name) + -- TRANSLATORS: e.g. "5x Water" + result = result .. text_line(_"Default:", _"%1%x %2%":bformat(terrain.default_resource_amount, default_resource.descname), default_resource:editor_image(terrain.default_resource_amount)) + end + end + + -- Trees + local tree_list = {} + for i, tree_name in ipairs(world:immovable_descriptions("tree")) do + local probability = terrain:probability_to_grow(tree_name) + if (probability > 0.01) then + -- sort the trees by percentage + i = 1 + while (tree_list[i] and (tree_list[i].probability_ > probability)) do + i = i + 1 + end + + for j = #tree_list, i, -1 do + tree_list[j+1] = tree_list[j] + end + tree_list[i] = {tree_name_ = tree_name, probability_ = probability} + end + end + + local tree_string = "" + for k,v in ipairs(tree_list) do + local tree = wl.Editor():get_immovable_description(v.tree_name_) + tree_string = tree_string .. picture_li(tree.representative_image, tree.basename .. ("<br>%2.1f%%"):bformat(100 * v.probability_)) .. spacer() + end + + -- TRANSLATORS: A header in the editor help + result = result .. spacer() .. rt(h2(_"Probability of trees growing")) .. spacer() + + if (tree_string ~="") then + result = result .. tree_string + else + result = result .. rt(p(_"No trees will grow here.")) + end + return result + end +} === added file 'scripting/editor/tree_help.lua' --- scripting/editor/tree_help.lua 1970-01-01 00:00:00 +0000 +++ scripting/editor/tree_help.lua 2016-01-26 07:30:52 +0000 @@ -0,0 +1,38 @@ +include "scripting/editor/format_editor.lua" + +return { + func = function(tree_name) + set_textdomain("widelands") + local world = wl.World(); + local tree = wl.Editor():get_immovable_description(tree_name) + local result = picture_li(tree.representative_image, "") + + -- TRANSLATORS: A header in the editor help. Terrains preferred by a type of tree. + result = result .. rt(p("font-size=3", "")) .. rt(h2(_"Preferred terrains")) .. spacer() + terrain_list = {} + for i, terrain_name in ipairs(world:terrain_descriptions()) do + local terrain = wl.Editor():get_terrain_description(terrain_name) + local probability = terrain:probability_to_grow(tree_name) + if (probability > 0.01) then + -- sort the terrains by percentage + i = 1 + while (terrain_list[i] and (terrain_list[i].probability_ > probability)) do + i = i + 1 + end + + for j = #terrain_list, i, -1 do + terrain_list[j+1] = terrain_list[j] + end + terrain_list[i] = {terrain_name_ = terrain_name, probability_ = probability} + end + end + + for k,v in ipairs(terrain_list) do + local terrain = wl.Editor():get_terrain_description(v.terrain_name_) + -- TRANSLATORS: Terrain name (Climate) + result = result .. picture_li(terrain.representative_image, (_"%1% (%2%)"):bformat(terrain.descname, terrain.editor_category.descname) .. "<br>" .. ("%2.1f%%"):bformat(100 * v.probability_)) .. spacer() + end + + return result + end +} === modified file 'src/editor/CMakeLists.txt' --- src/editor/CMakeLists.txt 2016-01-14 22:17:50 +0000 +++ src/editor/CMakeLists.txt 2016-01-26 07:30:52 +0000 @@ -47,6 +47,8 @@ tools/editor_tool_action.h tools/multi_select.h ui_menus/categorized_item_selection_menu.h + ui_menus/editor_help.cc + ui_menus/editor_help.h ui_menus/editor_main_menu.cc ui_menus/editor_main_menu.h ui_menus/editor_main_menu_load_map.cc === modified file 'src/editor/editorinteractive.cc' --- src/editor/editorinteractive.cc 2016-01-16 15:57:31 +0000 +++ src/editor/editorinteractive.cc 2016-01-26 07:30:52 +0000 @@ -30,6 +30,7 @@ #include "base/scoped_timer.h" #include "base/warning.h" #include "editor/tools/editor_delete_immovable_tool.h" +#include "editor/ui_menus/editor_help.h" #include "editor/ui_menus/editor_main_menu.h" #include "editor/ui_menus/editor_main_menu_load_map.h" #include "editor/ui_menus/editor_main_menu_save_map.h" @@ -69,7 +70,7 @@ // Ok, we're doing something. First remove the current overlays. if (note.old_resource != Widelands::kNoResource) { const std::string str = - world.get_resource(note.old_resource)->get_editor_pic(note.old_amount); + world.get_resource(note.old_resource)->editor_image(note.old_amount); const Image* pic = g_gr->images().get(str); field_overlay_manager->remove_overlay(note.fc, pic); } @@ -78,7 +79,7 @@ const auto resource_type = note.fc.field->get_resources(); if (amount > 0 && resource_type != Widelands::kNoResource) { const std::string str = - world.get_resource(note.fc.field->get_resources())->get_editor_pic(amount); + world.get_resource(note.fc.field->get_resources())->editor_image(amount); const Image* pic = g_gr->images().get(str); field_overlay_manager->register_overlay(note.fc, pic, 0); } @@ -123,7 +124,10 @@ ("editor_undo", "undo", _("Undo"))), redo_ (INIT_BUTTON - ("editor_redo", "redo", _("Redo"))) + ("editor_redo", "redo", _("Redo"))), + toggle_help_ + (INIT_BUTTON + ("menu_help", "help", _("Help"))) { toggle_main_menu_.sigclicked.connect(boost::bind(&EditorInteractive::toggle_mainmenu, this)); toggle_tool_menu_.sigclicked.connect(boost::bind(&EditorInteractive::tool_menu_btn, this)); @@ -133,6 +137,7 @@ toggle_player_menu_.sigclicked.connect(boost::bind(&EditorInteractive::toggle_playermenu, this)); undo_.sigclicked.connect([this] {history_->undo_action(egbase().world());}); redo_.sigclicked.connect([this] {history_->redo_action(egbase().world());}); + toggle_help_.sigclicked.connect(boost::bind(&EditorInteractive::toggle_help, this)); toolbar_.set_layout_toplevel(true); toolbar_.add(&toggle_main_menu_, UI::Box::AlignLeft); @@ -143,6 +148,7 @@ toolbar_.add(&toggle_player_menu_, UI::Box::AlignLeft); toolbar_.add(&undo_, UI::Box::AlignLeft); toolbar_.add(&redo_, UI::Box::AlignLeft); + toolbar_.add(&toggle_help_, UI::Box::AlignLeft); adjust_toolbar_position(); #ifndef NDEBUG @@ -184,7 +190,7 @@ iterate_Map_FCoords(map, extent, fc) { if (uint8_t const amount = fc.field->get_resources_amount()) { const std::string& immname = - egbase().world().get_resource(fc.field->get_resources())->get_editor_pic(amount); + egbase().world().get_resource(fc.field->get_resources())->editor_image(amount); if (immname.size()) mutable_field_overlay_manager()->register_overlay(fc, g_gr->images().get(immname), 4); } @@ -349,6 +355,14 @@ } } +void EditorInteractive::toggle_help() { + if (helpmenu_.window) + delete helpmenu_.window; + else + new EditorHelp(*this, helpmenu_); +} + + bool EditorInteractive::handle_key(bool const down, SDL_Keysym const code) { bool handled = InteractiveBase::handle_key(down, code); @@ -476,6 +490,12 @@ history_->redo_action(egbase().world()); handled = true; break; + + case SDLK_F1: + toggle_help(); + handled = true; + break; + default: break; } === modified file 'src/editor/editorinteractive.h' --- src/editor/editorinteractive.h 2016-01-16 15:57:31 +0000 +++ src/editor/editorinteractive.h 2016-01-26 07:30:52 +0000 @@ -150,6 +150,7 @@ void toolsize_menu_btn(); void toggle_mainmenu(); void toggle_playermenu(); + void toggle_help(); // state variables bool need_save_; @@ -173,6 +174,7 @@ UI::UniqueWindow::Registry immovablemenu_; UI::UniqueWindow::Registry bobmenu_; UI::UniqueWindow::Registry resourcesmenu_; + UI::UniqueWindow::Registry helpmenu_; UI::Button toggle_main_menu_; UI::Button toggle_tool_menu_; @@ -182,6 +184,7 @@ UI::Button toggle_player_menu_; UI::Button undo_; UI::Button redo_; + UI::Button toggle_help_; }; #endif // end of include guard: WL_EDITOR_EDITORINTERACTIVE_H === added file 'src/editor/ui_menus/editor_help.cc' --- src/editor/ui_menus/editor_help.cc 1970-01-01 00:00:00 +0000 +++ src/editor/ui_menus/editor_help.cc 2016-01-26 07:30:52 +0000 @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2015-2016 by the Widelands Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "editor/ui_menus/editor_help.h" + +#include <algorithm> +#include <map> +#include <memory> +#include <string> +#include <vector> + +#include <boost/format.hpp> + +#include "base/i18n.h" +#include "editor/editorinteractive.h" +#include "graphic/graphic.h" +#include "graphic/texture.h" +#include "io/filesystem/layered_filesystem.h" +#include "logic/map_objects/world/editor_category.h" +#include "logic/map_objects/world/terrain_description.h" +#include "logic/map_objects/world/world.h" +#include "scripting/lua_interface.h" +#include "scripting/lua_table.h" + +#define WINDOW_WIDTH std::min(700, g_gr->get_xres() - 40) +#define WINDOW_HEIGHT std::min(550, g_gr->get_yres() - 40) + +constexpr int kPadding = 5; +constexpr int kTabHeight = 35; + +using namespace Widelands; + +inline EditorInteractive& EditorHelp::eia() const { + return dynamic_cast<EditorInteractive&>(*get_parent()); +} + +namespace { +const std::string heading(const std::string& text) { + return ((boost::format("<rt><p font-size=18 font-weight=bold font-color=D1D1D1>" + "%s<br></p><p font-size=8> <br></p></rt>") % + text).str()); +} +} // namespace + +EditorHelp::EditorHelp(EditorInteractive& parent, UI::UniqueWindow::Registry& registry) + : UI::UniqueWindow(&parent, "encyclopedia", ®istry, WINDOW_WIDTH, WINDOW_HEIGHT, _("Help")), + tabs_(this, 0, 0, nullptr) { + + const int contents_height = WINDOW_HEIGHT - kTabHeight - 2 * kPadding; + const int contents_width = WINDOW_WIDTH / 2 - 1.5 * kPadding; + + std::vector<std::unique_ptr<HelpTab>> tab_definitions; + + tab_definitions.push_back( + std::unique_ptr<HelpTab>(new HelpTab("terrains", + "pics/editor_menu_tool_set_terrain.png", + _("Terrains"), + "scripting/editor/terrain_help.lua", + HelpEntry::Type::kTerrain))); + + tab_definitions.push_back( + std::unique_ptr<HelpTab>(new HelpTab("trees", + "world/immovables/trees/alder/old/idle_0.png", + _("Trees"), + "scripting/editor/tree_help.lua", + HelpEntry::Type::kTree))); + + for (const auto& tab : tab_definitions) { + // Make sure that all paths exist + if (!g_fs->file_exists(tab->script_path)) { + throw wexception("Script path %s for tab %s does not exist!", + tab->script_path.c_str(), + tab->key.c_str()); + } + if (!g_fs->file_exists(tab->image_filename)) { + throw wexception("Image path %s for tab %s does not exist!", + tab->image_filename.c_str(), + tab->key.c_str()); + } + + wrapper_boxes_.insert(std::make_pair( + tab->key, std::unique_ptr<UI::Box>(new UI::Box(&tabs_, 0, 0, UI::Box::Horizontal)))); + + boxes_.insert( + std::make_pair(tab->key, + std::unique_ptr<UI::Box>(new UI::Box( + wrapper_boxes_.at(tab->key).get(), 0, 0, UI::Box::Horizontal)))); + + lists_.insert( + std::make_pair(tab->key, + std::unique_ptr<UI::Listselect<Widelands::DescriptionIndex>>( + new UI::Listselect<Widelands::DescriptionIndex>( + boxes_.at(tab->key).get(), 0, 0, contents_width, contents_height)))); + lists_.at(tab->key)->selected.connect( + boost::bind(&EditorHelp::entry_selected, this, tab->key, tab->script_path, tab->type)); + + contents_.insert( + std::make_pair(tab->key, + std::unique_ptr<UI::MultilineTextarea>(new UI::MultilineTextarea( + boxes_.at(tab->key).get(), 0, 0, contents_width, contents_height)))); + + boxes_.at(tab->key)->add(lists_.at(tab->key).get(), UI::Align_Left); + boxes_.at(tab->key)->add_space(kPadding); + boxes_.at(tab->key)->add(contents_.at(tab->key).get(), UI::Align_Left); + + wrapper_boxes_.at(tab->key)->add_space(kPadding); + wrapper_boxes_.at(tab->key)->add(boxes_.at(tab->key).get(), UI::Align_Left); + + tabs_.add("editor_help_" + tab->key, + g_gr->images().get(tab->image_filename), + wrapper_boxes_.at(tab->key).get(), + tab->tooltip); + } + tabs_.set_size(WINDOW_WIDTH, WINDOW_HEIGHT); + + fill_terrains(); + fill_trees(); + + if (get_usedefaultpos()) { + center_to_parent(); + } +} + +void EditorHelp::fill_entries(const char* key, std::vector<HelpEntry>& entries) { + std::sort(entries.begin(), entries.end()); + for (uint32_t i = 0; i < entries.size(); i++) { + HelpEntry cur = entries[i]; + lists_.at(key)->add(cur.descname, cur.index, cur.icon); + } + lists_.at(key)->select(0); +} + +void EditorHelp::fill_terrains() { + const World& world = eia().egbase().world(); + std::vector<HelpEntry> entries; + + for (Widelands::DescriptionIndex i = 0; i < world.terrains().size(); ++i) { + const TerrainDescription& terrain = world.terrain_descr(i); + upcast(Image const, icon, &terrain.get_texture(0)); + /** TRANSLATORS: Terrain name + editor category, e.g. Steppe (Summer) */ + HelpEntry entry(i, (boost::format(_("%1% (%2%)")) + % terrain.descname().c_str() + % terrain.editor_category().descname()).str(), icon); + entries.push_back(entry); + } + fill_entries("terrains", entries); +} + +void EditorHelp::fill_trees() { + const World& world = eia().egbase().world(); + std::vector<HelpEntry> entries; + + for (Widelands::DescriptionIndex i = 0; i < world.get_nr_immovables(); ++i) { + const ImmovableDescr* immovable = world.get_immovable_descr(i); + uint32_t attribute_id = immovable->get_attribute_id("tree"); + if (immovable->has_attribute(attribute_id)) { + const Image* icon = immovable->representative_image(); + HelpEntry entry(i, immovable->basename(), icon); + entries.push_back(entry); + } + } + fill_entries("trees", entries); +} + +void EditorHelp::entry_selected(const std::string& key, + const std::string& script_path, + const HelpEntry::Type& type) { + try { + std::unique_ptr<LuaTable> table(eia().egbase().lua().run_script(script_path)); + std::unique_ptr<LuaCoroutine> cr(table->get_coroutine("func")); + + std::string descname = ""; + + switch (type) { + case (HelpEntry::Type::kTerrain): { + const TerrainDescription& descr = + eia().egbase().world().terrain_descr(lists_.at(key)->get_selected()); + /** TRANSLATORS: Terrain name + editor category, e.g. Steppe (Summer) */ + descname = (boost::format(_("%1% (%2%)")) + % descr.descname().c_str() + % descr.editor_category().descname()).str(); + cr->push_arg(descr.name()); + break; + } + case (HelpEntry::Type::kTree): { + const ImmovableDescr* descr = + eia().egbase().world().get_immovable_descr(lists_.at(key)->get_selected()); + descname = descr->basename(); + cr->push_arg(descr->name()); + break; + } + default: + throw wexception("EditorHelp: No Type defined for tab."); + } + + cr->resume(); + const std::string help_text = cr->pop_string(); + contents_.at(key)->set_text((boost::format("%s%s") % heading(descname) % help_text).str()); + + } catch (LuaError& err) { + contents_.at(key)->set_text(err.what()); + } + contents_.at(key)->scroll_to_top(); +} === added file 'src/editor/ui_menus/editor_help.h' --- src/editor/ui_menus/editor_help.h 1970-01-01 00:00:00 +0000 +++ src/editor/ui_menus/editor_help.h 2016-01-26 07:30:52 +0000 @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2015-2016 by the Widelands Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef WL_EDITOR_UI_MENUS_EDITOR_HELP_H +#define WL_EDITOR_UI_MENUS_EDITOR_HELP_H + +#include <map> +#include <memory> +#include <vector> + +#include "logic/map_objects/map_object.h" +#include "ui_basic/box.h" +#include "ui_basic/listselect.h" +#include "ui_basic/multilinetextarea.h" +#include "ui_basic/table.h" +#include "ui_basic/tabpanel.h" +#include "ui_basic/unique_window.h" +#include "ui_basic/window.h" + +class EditorInteractive; + +struct EditorHelp : public UI::UniqueWindow { + EditorHelp(EditorInteractive&, UI::UniqueWindow::Registry&); + +private: + struct HelpEntry { + enum class Type { + kTerrain, + kTree + }; + + HelpEntry(const HelpEntry& other) : HelpEntry(other.index, other.descname, other.icon) { + } + HelpEntry(const Widelands::DescriptionIndex _index, + const std::string& _descname, + const Image* _icon) + : index(_index), descname(_descname), icon(_icon) { + } + Widelands::DescriptionIndex index; + std::string descname; + const Image* icon; + + bool operator<(const HelpEntry other) const { + return descname < other.descname; + } + }; + + struct HelpTab { + HelpTab(const std::string& _key, + const std::string& _image_filename, + const std::string& _tooltip, + const std::string& _script_path, + const EditorHelp::HelpEntry::Type _type) + : key(_key), + image_filename(_image_filename), + tooltip(_tooltip), + script_path(_script_path), + type(_type) { + } + const std::string key; + const std::string image_filename; + const std::string tooltip; + const std::string script_path; + const EditorHelp::HelpEntry::Type type; + }; + + EditorInteractive& eia() const; + + // Fill table of contents + void fill_entries(const char* key, std::vector<HelpEntry>& entries); + void fill_terrains(); + void fill_trees(); + + // Update contents when an entry is selected + void entry_selected(const std::string& key, + const std::string& script_path, + const HelpEntry::Type& type); + + // UI elements + UI::TabPanel tabs_; + + // Wrapper boxes so we can add some padding + std::map<std::string, std::unique_ptr<UI::Box>> wrapper_boxes_; + // Main contents boxes for each tab + std::map<std::string, std::unique_ptr<UI::Box>> boxes_; + // A tab's table of contents + std::map<std::string, std::unique_ptr<UI::Listselect<Widelands::DescriptionIndex>>> lists_; + // The contents shown when an entry is selected in a tab + std::map<std::string, std::unique_ptr<UI::MultilineTextarea>> contents_; +}; + +#endif // end of include guard: WL_EDITOR_UI_MENUS_EDITOR_HELP_H === modified file 'src/editor/ui_menus/editor_tool_change_resources_options_menu.cc' --- src/editor/ui_menus/editor_tool_change_resources_options_menu.cc 2016-01-18 19:35:25 +0000 +++ src/editor/ui_menus/editor_tool_change_resources_options_menu.cc 2016-01-26 07:30:52 +0000 @@ -139,7 +139,7 @@ // Find the maximal width and height for the resource pictures. int resource_pic_max_width = 0, resource_pic_max_height = 0; for (Widelands::DescriptionIndex i = 0; i < nr_resources; ++i) { - const Image* pic = g_gr->images().get(world.get_resource(i)->get_editor_pic(100000)); + const Image* pic = g_gr->images().get(world.get_resource(i)->representative_image()); resource_pic_max_width = std::max(resource_pic_max_width, pic->width()); resource_pic_max_height = std::max(resource_pic_max_height, pic->height()); } @@ -170,7 +170,7 @@ m_radiogroup.add_button (this, pos, - g_gr->images().get(world.get_resource(i)->get_editor_pic(100000))); + g_gr->images().get(world.get_resource(i)->representative_image())); } pos.y += resource_pic_max_height + vspacing(); === modified file 'src/logic/field.h' --- src/logic/field.h 2016-01-14 22:09:24 +0000 +++ src/logic/field.h 2016-01-26 07:30:52 +0000 @@ -134,7 +134,9 @@ uint16_t get_caps() const {return caps;} Terrains get_terrains() const {return terrains;} + // The terrain on the downward triangle DescriptionIndex terrain_d () const {return terrains.d;} + // The terrain on the triangle to the right DescriptionIndex terrain_r () const {return terrains.r;} void set_terrains(const Terrains & i) {terrains = i;} void set_terrain === modified file 'src/logic/map_objects/immovable.cc' --- src/logic/map_objects/immovable.cc 2016-01-17 19:54:32 +0000 +++ src/logic/map_objects/immovable.cc 2016-01-26 07:30:52 +0000 @@ -189,7 +189,8 @@ MapObjectDescr( MapObjectType::IMMOVABLE, table.get_string("name"), init_descname, table), m_size(BaseImmovable::NONE), - owner_type_(input_type) { + owner_type_(input_type), + editor_category_(nullptr) { if (!is_animation_known("idle")) { throw GameDataError("Immovable %s has no idle animation", table.get_string("name").c_str()); } @@ -203,8 +204,23 @@ } if (table.has_key("attributes")) { - add_attributes(table.get_table("attributes")-> - array_entries<std::string>(), {MapObject::Attribute::RESI}); + std::vector<std::string> attributes = table.get_table("attributes")->array_entries<std::string>(); + add_attributes(attributes, {MapObject::Attribute::RESI}); + + // Old trees get an extra basename so we can use it in help lists. + bool is_tree = false; + for (const std::string& attribute : attributes) { + if (attribute == "tree") { + is_tree = true; + break; + } + } + if (is_tree) { + if (!table.has_key("basename")) { + throw wexception("Immovable '%s' with type 'tree' must define a basename", name().c_str()); + } + basename_ = table.get_string("basename"); + } } std::unique_ptr<LuaTable> programs = table.get_table("programs"); @@ -247,6 +263,10 @@ } } +bool ImmovableDescr::has_editor_category() const { + return editor_category_ != nullptr; +} + const EditorCategory& ImmovableDescr::editor_category() const { return *editor_category_; } === modified file 'src/logic/map_objects/immovable.h' --- src/logic/map_objects/immovable.h 2015-11-29 09:43:15 +0000 +++ src/logic/map_objects/immovable.h 2016-01-26 07:30:52 +0000 @@ -129,10 +129,15 @@ const Buildcost & buildcost() const {return m_buildcost;} + // Returns whether this immovable has an editor category. E.g. Tribe immovables never have one. + bool has_editor_category() const; // Returns the editor category. const EditorCategory& editor_category() const; + // A basic localized name for the immovable, used by trees + const std::string& basename() const {return basename_;} + // Every immovable that can 'grow' needs to have terrain affinity defined, // all others do not. Returns true if this one has it defined. bool has_terrain_affinity() const; @@ -152,6 +157,8 @@ /// \see ActConstruction Buildcost m_buildcost; + std::string basename_; + private: // Common constructor functions for tribes and world. ImmovableDescr(const std::string& init_descname, const LuaTable&, MapObjectDescr::OwnerType type); === modified file 'src/logic/map_objects/terrain_affinity.cc' --- src/logic/map_objects/terrain_affinity.cc 2015-11-28 22:29:26 +0000 +++ src/logic/map_objects/terrain_affinity.cc 2016-01-26 07:30:52 +0000 @@ -37,6 +37,30 @@ return a * a; } +// Helper function for probability_to_grow +// Calculates the probability to grow for the given affinity and terrain values +double calculate_probability_to_grow(const TerrainAffinity& affinity, + double terrain_humidity, + double terrain_fertility, + double terrain_temperature) { + + constexpr double kHumidityWeight = 0.500086642549548; + constexpr double kFertilityWeight = 0.5292268046607387; + constexpr double kTemperatureWeight = 61.31300863608306; + + const double sigma_humidity = (1. - affinity.pickiness()); + const double sigma_temperature = (1. - affinity.pickiness()); + const double sigma_fertility = (1. - affinity.pickiness()); + + return exp((-pow2((affinity.preferred_fertility() - terrain_fertility) / + (kFertilityWeight * sigma_fertility)) - + pow2((affinity.preferred_humidity() - terrain_humidity) / + (kHumidityWeight * sigma_humidity)) - + pow2((affinity.preferred_temperature() - terrain_temperature) / + (kTemperatureWeight * sigma_temperature))) / + 2); +} + } // namespace TerrainAffinity::TerrainAffinity(const LuaTable& table, const std::string& immovable_name) @@ -111,21 +135,20 @@ average(ln.field->terrain_r()); } - constexpr double kHumidityWeight = 0.500086642549548; - constexpr double kFertilityWeight = 0.5292268046607387; - constexpr double kTemperatureWeight = 61.31300863608306; - - const double sigma_humidity = (1. - affinity.pickiness()); - const double sigma_temperature = (1. - affinity.pickiness()); - const double sigma_fertility = (1. - affinity.pickiness()); - - return exp((-pow2((affinity.preferred_fertility() - terrain_fertility) / - (kFertilityWeight * sigma_fertility)) - - pow2((affinity.preferred_humidity() - terrain_humidity) / - (kHumidityWeight * sigma_humidity)) - - pow2((affinity.preferred_temperature() - terrain_temperature) / - (kTemperatureWeight * sigma_temperature))) / - 2); + return calculate_probability_to_grow(affinity, + terrain_humidity, + terrain_fertility, + terrain_temperature); +} + + +double probability_to_grow(const TerrainAffinity& affinity, + const TerrainDescription& terrain) { + + return calculate_probability_to_grow(affinity, + terrain.humidity(), + terrain.fertility(), + terrain.temperature()); } } // namespace Widelands === modified file 'src/logic/map_objects/terrain_affinity.h' --- src/logic/map_objects/terrain_affinity.h 2015-11-28 22:29:26 +0000 +++ src/logic/map_objects/terrain_affinity.h 2016-01-26 07:30:52 +0000 @@ -69,6 +69,10 @@ (const TerrainAffinity& immovable_affinity, const FCoords& fcoords, const Map& map, const DescriptionMaintainer<TerrainDescription>& terrains); +// Probability to grow for a single terrain +double probability_to_grow + (const TerrainAffinity& immovable_affinity, const TerrainDescription& terrain); + } // namespace Widelands #endif // end of include guard: WL_LOGIC_MAP_OBJECTS_TERRAIN_AFFINITY_H === modified file 'src/logic/map_objects/tribes/worker.cc' --- src/logic/map_objects/tribes/worker.cc 2016-01-17 19:54:32 +0000 +++ src/logic/map_objects/tribes/worker.cc 2016-01-26 07:30:52 +0000 @@ -960,7 +960,7 @@ if (rdescr && rdescr->detectable() && position.field->get_resources_amount()) { const std::string message = (boost::format("<rt image=%s><p font-face=serif font-size=14>%s</p></rt>") - % rdescr->get_editor_pic(rdescr->max_amount()) + % rdescr->representative_image() % _("A geologist found resources.")).str(); Message::Type message_type = Message::Type::kGeologists; === modified file 'src/logic/map_objects/world/resource_description.cc' --- src/logic/map_objects/world/resource_description.cc 2015-11-28 22:29:26 +0000 +++ src/logic/map_objects/world/resource_description.cc 2016-01-26 07:30:52 +0000 @@ -31,7 +31,8 @@ : name_(table.get_string("name")), descname_(table.get_string("descname")), detectable_(table.get_bool("detectable")), - max_amount_(table.get_int("max_amount")) { + max_amount_(table.get_int("max_amount")), + representative_image_(table.get_string("representative_image")) { std::unique_ptr<LuaTable> st = table.get_table("editor_pictures"); const std::set<int> keys = st->keys<int>(); @@ -44,7 +45,7 @@ } } -const std::string & ResourceDescription::get_editor_pic +const std::string & ResourceDescription::editor_image (uint32_t const amount) const { uint32_t bestmatch = 0; === modified file 'src/logic/map_objects/world/resource_description.h' --- src/logic/map_objects/world/resource_description.h 2015-11-28 22:29:26 +0000 +++ src/logic/map_objects/world/resource_description.h 2016-01-26 07:30:52 +0000 @@ -53,13 +53,17 @@ /// Returns the path to the image that should be used in the editor to /// represent an 'amount' of this resource. - const std::string& get_editor_pic(uint32_t amount) const; + const std::string& editor_image(uint32_t amount) const; + + /// Returns the path to the image that should be used in menus to represent this resource + const std::string& representative_image() const {return representative_image_;} private: const std::string name_; const std::string descname_; const bool detectable_; const int32_t max_amount_; + const std::string representative_image_; std::vector<EditorPicture> editor_pictures_; DISALLOW_COPY_AND_ASSIGN(ResourceDescription); === modified file 'src/logic/map_objects/world/terrain_description.cc' --- src/logic/map_objects/world/terrain_description.cc 2016-01-17 09:55:27 +0000 +++ src/logic/map_objects/world/terrain_description.cc 2016-01-26 07:30:52 +0000 @@ -222,6 +222,10 @@ return valid_resources_.size(); } +std::vector<uint8_t> TerrainDescription::valid_resources() const { + return valid_resources_; +} + bool TerrainDescription::is_resource_valid(const int res) const { for (const uint8_t resource_index : valid_resources_) { if (resource_index == res) { === modified file 'src/logic/map_objects/world/terrain_description.h' --- src/logic/map_objects/world/terrain_description.h 2016-01-13 07:27:55 +0000 +++ src/logic/map_objects/world/terrain_description.h 2016-01-26 07:30:52 +0000 @@ -94,6 +94,9 @@ /// Returns the number of valid resources. int get_num_valid_resources() const; + /// Returns the the valid resources. + std::vector<uint8_t> valid_resources() const; + /// Returns true if this resource can be found in this terrain type. bool is_resource_valid(int32_t res) const; === modified file 'src/scripting/lua_bases.cc' --- src/scripting/lua_bases.cc 2015-11-28 22:29:26 +0000 +++ src/scripting/lua_bases.cc 2016-01-26 07:30:52 +0000 @@ -27,6 +27,7 @@ #include "logic/map_objects/tribes/tribe_descr.h" #include "logic/map_objects/tribes/tribes.h" #include "logic/map_objects/tribes/ware_descr.h" +#include "logic/map_objects/world/world.h" #include "logic/player.h" #include "scripting/factory.h" #include "scripting/globals.h" @@ -78,10 +79,13 @@ const char LuaEditorGameBase::className[] = "EditorGameBase"; const MethodType<LuaEditorGameBase> LuaEditorGameBase::Methods[] = { + METHOD(LuaEditorGameBase, get_immovable_description), METHOD(LuaEditorGameBase, get_building_description), METHOD(LuaEditorGameBase, get_tribe_description), METHOD(LuaEditorGameBase, get_ware_description), METHOD(LuaEditorGameBase, get_worker_description), + METHOD(LuaEditorGameBase, get_resource_description), + METHOD(LuaEditorGameBase, get_terrain_description), {nullptr, nullptr}, }; const PropertyType<LuaEditorGameBase> LuaEditorGameBase::Properties[] = { @@ -150,6 +154,38 @@ */ /* RST + .. function:: get_immovable_description(immovable_name) + + :arg immovable_name: the name of the immovable + + Registers an immovable description so Lua can reference it from the editor. + + (RO) The :class:`~wl.Game.Immovable_description`. +*/ +int LuaEditorGameBase::get_immovable_description(lua_State* L) { + if (lua_gettop(L) != 2) { + report_error(L, "Wrong number of arguments"); + } + const std::string immovable_name = luaL_checkstring(L, 2); + const World& world = get_egbase(L).world(); + DescriptionIndex idx = world.get_immovable_index(immovable_name); + if (idx != INVALID_INDEX) { + const ImmovableDescr* descr = world.get_immovable_descr(idx); + return to_lua<LuaMaps::LuaImmovableDescription>(L, new LuaMaps::LuaImmovableDescription(descr)); + } else { + const Tribes& tribes = get_egbase(L).tribes(); + idx = tribes.immovable_index(immovable_name); + if (!tribes.immovable_exists(idx)) { + report_error(L, "Immovable %s does not exist", immovable_name.c_str()); + } + const ImmovableDescr* descr = tribes.get_immovable_descr(idx); + return to_lua<LuaMaps::LuaImmovableDescription>(L, new LuaMaps::LuaImmovableDescription(descr)); + } + return 0; +} + + +/* RST .. function:: get_building_description(building_description.name) :arg building_name: the name of the building @@ -244,6 +280,53 @@ return LuaMaps::upcasted_map_object_descr_to_lua(L, worker_description); } +/* RST + .. function:: get_resource_description(resource_name) + + :arg resource_name: the name of the resource + + Registers a resource description so Lua can reference it from the editor. + + (RO) The :class:`~wl.Game.Resource_description`. +*/ +int LuaEditorGameBase::get_resource_description(lua_State* L) { + if (lua_gettop(L) != 2) { + report_error(L, "Wrong number of arguments"); + } + const std::string resource_name = luaL_checkstring(L, 2); + const World& world = get_egbase(L).world(); + DescriptionIndex idx = world.get_resource(resource_name.c_str()); + + if (idx == INVALID_INDEX) { + report_error(L, "Resource %s does not exist", resource_name.c_str()); + } + + const ResourceDescription* descr = world.get_resource(idx); + return to_lua<LuaMaps::LuaResourceDescription>(L, new LuaMaps::LuaResourceDescription(descr)); +} + +/* RST + .. function:: get_terrain_description(terrain_name) + + :arg terrain_name: the name of the terrain + + Registers a terrain description so Lua can reference it from the editor. + + (RO) The :class:`~wl.Game.Terrain_description`. +*/ +int LuaEditorGameBase::get_terrain_description(lua_State* L) { + if (lua_gettop(L) != 2) { + report_error(L, "Wrong number of arguments"); + } + const std::string terrain_name = luaL_checkstring(L, 2); + const TerrainDescription* descr = get_egbase(L).world().get_ter(terrain_name.c_str()); + if (!descr) { + report_error(L, "Terrain %s does not exist", terrain_name.c_str()); + } + return to_lua<LuaMaps::LuaTerrainDescription>(L, new LuaMaps::LuaTerrainDescription(descr)); +} + + /* ========================================================== C METHODS === modified file 'src/scripting/lua_bases.h' --- src/scripting/lua_bases.h 2015-04-11 08:58:15 +0000 +++ src/scripting/lua_bases.h 2016-01-26 07:30:52 +0000 @@ -58,10 +58,13 @@ /* * Lua methods */ + int get_immovable_description(lua_State * L); int get_building_description(lua_State * L); int get_tribe_description(lua_State * L); int get_ware_description(lua_State * L); int get_worker_description(lua_State * L); + int get_resource_description(lua_State * L); + int get_terrain_description(lua_State * L); /* * C methods === modified file 'src/scripting/lua_map.cc' --- src/scripting/lua_map.cc 2016-01-24 17:01:59 +0000 +++ src/scripting/lua_map.cc 2016-01-26 07:30:52 +0000 @@ -31,11 +31,13 @@ #include "logic/findimmovable.h" #include "logic/map_objects/checkstep.h" #include "logic/map_objects/immovable.h" +#include "logic/map_objects/terrain_affinity.h" #include "logic/map_objects/tribes/carrier.h" #include "logic/map_objects/tribes/ship.h" #include "logic/map_objects/tribes/soldier.h" #include "logic/map_objects/tribes/tribes.h" #include "logic/map_objects/tribes/warelist.h" +#include "logic/map_objects/world/editor_category.h" #include "logic/map_objects/world/resource_description.h" #include "logic/map_objects/world/terrain_description.h" #include "logic/map_objects/world/world.h" @@ -605,6 +607,8 @@ return CAST_TO_LUA(WorkerDescr, LuaWorkerDescription); case MapObjectType::SOLDIER: return CAST_TO_LUA(WorkerDescr, LuaWorkerDescription); + case MapObjectType::IMMOVABLE: + return CAST_TO_LUA(ImmovableDescr, LuaImmovableDescription); default: return CAST_TO_LUA(MapObjectDescr, LuaMapObjectDescription); } @@ -1406,6 +1410,168 @@ return 1; } + +/* RST +BuildingDescription +------------------- + +.. class:: LuaImmovableDescription + + A static description of a base immovable, so it can be used in help files + without having to access an actual immovalbe on the map. + See also class MapObjectDescription for more properties. +*/ +const char LuaImmovableDescription::className[] = "ImmovableDescription"; +const MethodType<LuaImmovableDescription> LuaImmovableDescription::Methods[] = { + {nullptr, nullptr}, +}; +const PropertyType<LuaImmovableDescription> LuaImmovableDescription::Properties[] = { + PROP_RO(LuaImmovableDescription, basename), + PROP_RO(LuaImmovableDescription, build_cost), + PROP_RO(LuaImmovableDescription, editor_category), + PROP_RO(LuaImmovableDescription, has_terrain_affinity), + PROP_RO(LuaImmovableDescription, pickiness), + PROP_RO(LuaImmovableDescription, preferred_fertility), + PROP_RO(LuaImmovableDescription, preferred_humidity), + PROP_RO(LuaImmovableDescription, preferred_temperature), + PROP_RO(LuaImmovableDescription, owner_type), + PROP_RO(LuaImmovableDescription, size), + {nullptr, nullptr, nullptr}, +}; + + +void LuaImmovableDescription::__persist(lua_State* L) { + const ImmovableDescr* descr = get(); + PERS_STRING("name", descr->name()); +} + +void LuaImmovableDescription::__unpersist(lua_State* L) { + std::string name; + UNPERS_STRING("name", name); + const World& world = get_egbase(L).world(); + DescriptionIndex idx = world.get_immovable_index(name); + if (idx == INVALID_INDEX) { + throw LuaError((boost::format("Immovable '%s' doesn't exist.") % name).str()); + } + set_description_pointer(world.get_immovable_descr(idx)); +} + + +/* RST + .. attribute:: the basename of a tree for editor lists + + (RO) the basename of the immovable, or an empty string if it has none. +*/ +int LuaImmovableDescription::get_basename(lua_State * L) { + lua_pushstring(L, get()->basename()); + return 1; +} + + +/* RST + .. attribute:: build_cost + + (RO) a list of ware build cost for the immovable. +*/ +int LuaImmovableDescription::get_build_cost(lua_State * L) { + return wares_or_workers_map_to_lua(L, get()->buildcost(), MapObjectType::WARE); +} + +/* RST + .. attribute:: the name and descname of the editor category of this immovable + + (RO) a table with "name" and "descname" entries for the editor category, or nil if it has none. +*/ +int LuaImmovableDescription::get_editor_category(lua_State * L) { + if (get()->has_editor_category()) { + const EditorCategory& editor_category = get()->editor_category(); + lua_newtable(L); + lua_pushstring(L, "name"); + lua_pushstring(L, editor_category.name()); + lua_settable(L, -3); + lua_pushstring(L, "descname"); + lua_pushstring(L, editor_category.descname()); + lua_settable(L, -3); + } else { + lua_pushnil(L); + } + return 1; +} + +/* RST + .. attribute:: whether this immovable has terrain affinity + + (RO) true if this immovable has terrain affinity +*/ +int LuaImmovableDescription::get_has_terrain_affinity(lua_State * L) { + lua_pushboolean(L, get()->has_terrain_affinity()); + return 1; +} + +/* RST + .. attribute:: the terrain affinity's pickiness + + (RO) the immovable's pickiness terrain affinity value. 0 if it has no terrain affinity. +*/ +int LuaImmovableDescription::get_pickiness(lua_State * L) { + lua_pushnumber(L, get()->has_terrain_affinity() ? get()->terrain_affinity().pickiness() : 0); + return 1; +} + +/* RST + .. attribute:: the terrain affinity's preferred_fertility + + (RO) the immovable's preferred_fertility terrain affinity value. 0 if it has no terrain affinity. +*/ +int LuaImmovableDescription::get_preferred_fertility(lua_State * L) { + lua_pushnumber(L, get()->has_terrain_affinity() ? get()->terrain_affinity().preferred_fertility() : 0); + return 1; +} + +/* RST + .. attribute:: the terrain affinity's preferred_humidity + + (RO) the immovable's preferred_humidity terrain affinity value. 0 if it has no terrain affinity. +*/ +int LuaImmovableDescription::get_preferred_humidity(lua_State * L) { + lua_pushnumber(L, get()->has_terrain_affinity() ? get()->terrain_affinity().preferred_humidity() : 0); + return 1; +} + + +/* RST + .. attribute:: the terrain affinity's preferred_temperature + + (RO) the immovable's preferred_temperature terrain affinity value. 0 if it has no terrain affinity. +*/ +int LuaImmovableDescription::get_preferred_temperature(lua_State * L) { + lua_pushnumber(L, get()->has_terrain_affinity() ? get()->terrain_affinity().preferred_temperature() : 0); + return 1; +} + + +/* RST + .. attribute:: the owner type of this immovable + + (RO) "world" for world immovables and "tribe" for tribe immovables. +*/ +int LuaImmovableDescription::get_owner_type(lua_State * L) { + lua_pushstring(L, get()->owner_type() == MapObjectDescr::OwnerType::kWorld ? "world" : "tribe"); + return 1; +} + + +/* RST + .. attribute:: size + + (RO) the size of the immovable as an int. +*/ +int LuaImmovableDescription::get_size(lua_State * L) { + lua_pushinteger(L, get()->get_size()); + return 1; +} + + /* RST BuildingDescription ------------------- @@ -2311,7 +2477,7 @@ /* RST - .. attribute:: is_construction_material + .. .. method:: is_construction_material :arg tribename: the name of the tribe that this ware gets checked for :type tribename: :class:`string` @@ -2477,6 +2643,340 @@ ========================================================== */ +/* RST +ResourceDescription +-------------------- +.. class:: ResourceDescription + + A static description of a resource. +*/ +const char LuaResourceDescription::className[] = "ResourceDescription"; +const MethodType<LuaResourceDescription> LuaResourceDescription::Methods[] = { + METHOD(LuaResourceDescription, editor_image), + {nullptr, nullptr}, +}; +const PropertyType<LuaResourceDescription> LuaResourceDescription::Properties[] = { + PROP_RO(LuaResourceDescription, name), + PROP_RO(LuaResourceDescription, descname), + PROP_RO(LuaResourceDescription, is_detectable), + PROP_RO(LuaResourceDescription, max_amount), + PROP_RO(LuaResourceDescription, representative_image), + {nullptr, nullptr, nullptr}, +}; + +void LuaResourceDescription::__persist(lua_State* L) { + const Widelands::ResourceDescription* descr = get(); + PERS_STRING("name", descr->name()); +} + +void LuaResourceDescription::__unpersist(lua_State* L) { + std::string name; + UNPERS_STRING("name", name); + const World& world = get_egbase(L).world(); + const ResourceDescription* descr = world.get_resource(world.safe_resource_index(name.c_str())); + set_description_pointer(descr); +} + +/* + ========================================================== + PROPERTIES + ========================================================== + */ + +/* RST + .. attribute:: name + + (RO) the :class:`string` internal name of this resource +*/ + +int LuaResourceDescription::get_name(lua_State * L) { + lua_pushstring(L, get()->name()); + return 1; +} + +/* RST + .. attribute:: descname + + (RO) the :class:`string` display name of this resource +*/ + +int LuaResourceDescription::get_descname(lua_State * L) { + lua_pushstring(L, get()->descname()); + return 1; +} + +/* RST + .. attribute:: is_detectable + + (RO) true if geologists can find this resource +*/ + +int LuaResourceDescription::get_is_detectable(lua_State * L) { + lua_pushboolean(L, get()->detectable()); + return 1; +} + +/* RST + .. attribute:: max_amount + + (RO) the maximum amount of this resource that a terrain can have +*/ + +int LuaResourceDescription::get_max_amount(lua_State * L) { + lua_pushinteger(L, get()->max_amount()); + return 1; +} + +/* RST + .. attribute:: representative_image + + (RO) the :class:`string` path to the image representing this resource in the GUI +*/ +int LuaResourceDescription::get_representative_image(lua_State * L) { + lua_pushstring(L, get()->representative_image()); + return 1; +} + + +/* + ========================================================== + METHODS + ========================================================== + */ + +/* RST + .. method:: representative_image(amount) + + :arg amount: The amount of the resource what we want an overlay image for + + (RO) the :class:`string` path to the image representing the specified amount of this resource +*/ +int LuaResourceDescription::editor_image(lua_State * L) { + if (lua_gettop(L) != 2) { + report_error(L, "Takes only one argument."); + } + const uint32_t amount = luaL_checkuint32(L, 2); + lua_pushstring(L, get()->editor_image(amount)); + return 1; +} + +/* RST +TerrainDescription +-------------------- +.. class:: TerrainDescription + + A static description of a terrain. +*/ +const char LuaTerrainDescription::className[] = "TerrainDescription"; +const MethodType<LuaTerrainDescription> LuaTerrainDescription::Methods[] = { + METHOD(LuaTerrainDescription, probability_to_grow), + {nullptr, nullptr}, +}; +const PropertyType<LuaTerrainDescription> LuaTerrainDescription::Properties[] = { + PROP_RO(LuaTerrainDescription, name), + PROP_RO(LuaTerrainDescription, descname), + PROP_RO(LuaTerrainDescription, default_resource_name), + PROP_RO(LuaTerrainDescription, default_resource_amount), + PROP_RO(LuaTerrainDescription, editor_category), + PROP_RO(LuaTerrainDescription, fertility), + PROP_RO(LuaTerrainDescription, humidity), + PROP_RO(LuaTerrainDescription, representative_image), + PROP_RO(LuaTerrainDescription, temperature), + PROP_RO(LuaTerrainDescription, valid_resources_names), + {nullptr, nullptr, nullptr}, +}; + +void LuaTerrainDescription::__persist(lua_State* L) { + const Widelands::TerrainDescription* descr = get(); + PERS_STRING("name", descr->name()); +} + +void LuaTerrainDescription::__unpersist(lua_State* L) { + std::string name; + UNPERS_STRING("name", name); + set_description_pointer(get_egbase(L).world().get_ter(name.c_str())); +} + +/* + ========================================================== + PROPERTIES + ========================================================== + */ + +/* RST + .. attribute:: name + + (RO) the :class:`string` internal name of this terrain +*/ + +int LuaTerrainDescription::get_name(lua_State * L) { + lua_pushstring(L, get()->name()); + return 1; +} + +/* RST + .. attribute:: descname + + (RO) the :class:`string` display name of this terrain +*/ + +int LuaTerrainDescription::get_descname(lua_State * L) { + lua_pushstring(L, get()->descname()); + return 1; +} + +/* RST + .. attribute:: get_default_resource_descname + + (RO) the :class:`string` description name of the default resource provided by this terrain, or + nil if the terrain has no default resource. +*/ + +int LuaTerrainDescription::get_default_resource_name(lua_State * L) { + int res_index = get()->get_default_resource(); + const World& world = get_egbase(L).world(); + if (res_index != Widelands::kNoResource && res_index < world.get_nr_resources()) { + lua_pushstring(L, world.get_resource(res_index)->name()); + } else { + lua_pushnil(L); + } + return 1; +} + +/* RST + .. attribute:: default_resource_amount + + (RO) the int amount of the default resource provided by this terrain. +*/ + +int LuaTerrainDescription::get_default_resource_amount(lua_State * L) { + lua_pushinteger(L, get()->get_default_resource_amount()); + return 1; +} + + +/* RST + .. attribute:: the name and descname of the editor category of this terrain + + (RO) a table with "name" and "descname" entries for the editor category, or nil if it has none. +*/ +int LuaTerrainDescription::get_editor_category(lua_State * L) { + const EditorCategory& editor_category = get()->editor_category(); + if (&editor_category) { + lua_newtable(L); + lua_pushstring(L, "name"); + lua_pushstring(L, editor_category.name()); + lua_settable(L, -3); + lua_pushstring(L, "descname"); + lua_pushstring(L, editor_category.descname()); + lua_settable(L, -3); + } else { + lua_pushnil(L); + } + return 1; +} + +/* RST + .. attribute:: fertility + + (RO) the :class:`double` fertility value for this terrain +*/ + +int LuaTerrainDescription::get_fertility(lua_State * L) { + lua_pushnumber(L, get()->fertility()); + return 1; +} + +/* RST + .. attribute:: humidity + + (RO) the :class:`double` humidity value for this terrain +*/ + +int LuaTerrainDescription::get_humidity(lua_State * L) { + lua_pushnumber(L, get()->humidity()); + return 1; +} + +/* RST + .. attribute:: representative_image + + (RO) the :class:`string` file path to a representative image +*/ + +int LuaTerrainDescription::get_representative_image(lua_State * L) { + lua_pushstring(L, *get()->texture_paths().begin()); + return 1; +} + +/* RST + .. attribute:: temperature + + (RO) the :class:`double` temperature value for this terrain +*/ + +int LuaTerrainDescription::get_temperature(lua_State * L) { + lua_pushnumber(L, get()->temperature()); + return 1; +} + + +/* RST + .. attribute:: valid_resources_names + + (RO) a list of the names for all valid resources for this terrain. +*/ + +int LuaTerrainDescription::get_valid_resources_names(lua_State * L) { + const World& world = get_egbase(L).world(); + lua_newtable(L); + int index = 1; + for (uint8_t res_index : get()->valid_resources()) { + if (res_index != Widelands::kNoResource && res_index < world.get_nr_resources()) { + lua_pushint32(L, index++); + lua_pushstring(L, world.get_resource(res_index)->name()); + lua_settable(L, -3); + } + } + return 1; +} + + +/* + ========================================================== + METHODS + ========================================================== + */ + +/* RST + .. method:: probability_to_grow + + :arg treename: The tree that we are checking the probability for. + :type treename: :class:`string` + + (RO) A double describing the probability that the given tree will grow on this terrain. +*/ +int LuaTerrainDescription::probability_to_grow(lua_State * L) { + if (lua_gettop(L) != 2) { + report_error(L, "Takes only one argument."); + } + const std::string tree_name = luaL_checkstring(L, 2); + const World& world = get_egbase(L).world(); + const DescriptionIndex index = world.get_immovable_index(tree_name); + if (index != INVALID_INDEX) { + const ImmovableDescr* tree = world.get_immovable_descr(index); + if (tree->has_terrain_affinity()) { + lua_pushnumber(L, Widelands::probability_to_grow(tree->terrain_affinity(), *get())); + } else { + lua_pushnil(L); + } + } else { + lua_pushnil(L); + } + return 1; +} + + /* RST @@ -4943,6 +5443,10 @@ register_class<LuaTribeDescription>(L, "map"); register_class<LuaMapObjectDescription>(L, "map"); + register_class<LuaImmovableDescription>(L, "map", true); + add_parent<LuaImmovableDescription, LuaMapObjectDescription>(L); + lua_pop(L, 1); // Pop the meta table + register_class<LuaBuildingDescription>(L, "map", true); add_parent<LuaBuildingDescription, LuaMapObjectDescription>(L); lua_pop(L, 1); // Pop the meta table @@ -4986,6 +5490,9 @@ add_parent<LuaWorkerDescription, LuaMapObjectDescription>(L); lua_pop(L, 1); // Pop the meta table + register_class<LuaResourceDescription>(L, "map"); + register_class<LuaTerrainDescription>(L, "map"); + register_class<LuaField>(L, "map"); register_class<LuaPlayerSlot>(L, "map"); register_class<LuaMapObject>(L, "map"); === modified file 'src/scripting/lua_map.h' --- src/scripting/lua_map.h 2016-01-22 19:53:32 +0000 +++ src/scripting/lua_map.h 2016-01-26 07:30:52 +0000 @@ -35,6 +35,7 @@ #include "logic/map_objects/tribes/trainingsite.h" #include "logic/map_objects/tribes/warehouse.h" #include "logic/map_objects/tribes/worker.h" +#include "logic/map_objects/world/terrain_description.h" #include "scripting/lua.h" #include "scripting/luna.h" @@ -43,8 +44,10 @@ class SoldierDescr; class BuildingDescr; class Bob; + class ResourceDescription; class WareDescr; class WorkerDescr; + class TerrainDescription; class TribeDescr; } @@ -203,6 +206,49 @@ return static_cast<const Widelands::klass*>(LuaMapObjectDescription::get()); \ } +class LuaImmovableDescription : public LuaMapObjectDescription { +public: + LUNA_CLASS_HEAD(LuaImmovableDescription); + + virtual ~LuaImmovableDescription() {} + + LuaImmovableDescription() {} + LuaImmovableDescription(const Widelands::ImmovableDescr* const immovabledescr) + : LuaMapObjectDescription(immovabledescr) { + } + LuaImmovableDescription(lua_State* L) : LuaMapObjectDescription(L) { + } + + void __persist(lua_State * L) override; + void __unpersist(lua_State * L) override; + + /* + * Properties + */ + int get_basename(lua_State *); + int get_build_cost(lua_State *); + int get_editor_category(lua_State *); + int get_has_terrain_affinity(lua_State *); + int get_pickiness(lua_State *); + int get_preferred_fertility(lua_State *); + int get_preferred_humidity(lua_State *); + int get_preferred_temperature(lua_State *); + int get_owner_type(lua_State *); + int get_size(lua_State *); + + /* + * Lua methods + */ + + /* + * C methods + */ + +private: + CASTED_GET_DESCRIPTION(ImmovableDescr) +}; + + class LuaBuildingDescription : public LuaMapObjectDescription { public: LUNA_CLASS_HEAD(LuaBuildingDescription); @@ -515,6 +561,110 @@ #undef CASTED_GET_DESCRIPTION + +class LuaResourceDescription : public LuaMapModuleClass { +public: + LUNA_CLASS_HEAD(LuaResourceDescription); + + virtual ~LuaResourceDescription() {} + + LuaResourceDescription() : resourcedescr_(nullptr) {} + LuaResourceDescription(const Widelands::ResourceDescription* const resourcedescr) + : resourcedescr_(resourcedescr) {} + LuaResourceDescription(lua_State* L) : resourcedescr_(nullptr) { + report_error(L, "Cannot instantiate a 'LuaResourceDescription' directly!"); + } + + void __persist(lua_State * L) override; + void __unpersist(lua_State * L) override; + + /* + * Properties + */ + int get_name(lua_State *); + int get_descname(lua_State *); + int get_is_detectable(lua_State *); + int get_max_amount(lua_State *); + int get_representative_image(lua_State *); + + /* + * Lua methods + */ + + int editor_image(lua_State *); + + /* + * C methods + */ +protected: + const Widelands::ResourceDescription* get() const { + assert(resourcedescr_ != nullptr); + return resourcedescr_; + } + // For persistence. + void set_description_pointer(const Widelands::ResourceDescription* pointer) { + resourcedescr_ = pointer; + } + +private: + const Widelands::ResourceDescription* resourcedescr_; +}; + + + +class LuaTerrainDescription : public LuaMapModuleClass { +public: + LUNA_CLASS_HEAD(LuaTerrainDescription); + + virtual ~LuaTerrainDescription() {} + + LuaTerrainDescription() : terraindescr_(nullptr) {} + LuaTerrainDescription(const Widelands::TerrainDescription* const terraindescr) + : terraindescr_(terraindescr) {} + LuaTerrainDescription(lua_State* L) : terraindescr_(nullptr) { + report_error(L, "Cannot instantiate a 'LuaTerrainDescription' directly!"); + } + + void __persist(lua_State * L) override; + void __unpersist(lua_State * L) override; + + /* + * Properties + */ + int get_name(lua_State *); + int get_descname(lua_State *); + int get_default_resource_name(lua_State *); + int get_default_resource_amount(lua_State *); + int get_editor_category(lua_State *); + int get_fertility(lua_State *); + int get_humidity(lua_State *); + int get_representative_image(lua_State *); + int get_temperature(lua_State *); + int get_valid_resources_names(lua_State *); + + /* + * Lua methods + */ + int probability_to_grow(lua_State *); + + /* + * C methods + */ +protected: + const Widelands::TerrainDescription* get() const { + assert(terraindescr_ != nullptr); + return terraindescr_; + } + // For persistence. + void set_description_pointer(const Widelands::TerrainDescription* pointer) { + terraindescr_ = pointer; + } + +private: + const Widelands::TerrainDescription* terraindescr_; +}; + + #define CASTED_GET(klass) \ Widelands:: klass * get(lua_State * L, Widelands::EditorGameBase & egbase) { \ return static_cast<Widelands:: klass *> \ === modified file 'src/scripting/lua_root.cc' --- src/scripting/lua_root.cc 2015-11-28 22:29:26 +0000 +++ src/scripting/lua_root.cc 2016-01-26 07:30:52 +0000 @@ -309,6 +309,8 @@ const char LuaWorld::className[] = "World"; const MethodType<LuaWorld> LuaWorld::Methods[] = { + METHOD(LuaWorld, immovable_descriptions), + METHOD(LuaWorld, terrain_descriptions), METHOD(LuaWorld, new_critter_type), METHOD(LuaWorld, new_editor_immovable_category), METHOD(LuaWorld, new_editor_terrain_category), @@ -345,6 +347,55 @@ */ /* RST + .. method:: immovable_descriptions(attribute_name) + + Returns a list of names with the immovables that have the attribute with the given attribute_name. + + (RO) a list of immovable names, e.g. {"alder_summer_old", "cirrus_wasteland_old", ...} +*/ +int LuaWorld::immovable_descriptions(lua_State* L) { + if (lua_gettop(L) != 2) { + report_error(L, "Takes only one argument."); + } + const World& world = get_egbase(L).world(); + const std::string attribute_name = luaL_checkstring(L, 2); + lua_newtable(L); + int index = 1; + for (DescriptionIndex i = 0; i < world.get_nr_immovables(); ++i) { + const ImmovableDescr* immovable = world.get_immovable_descr(i); + uint32_t attribute_id = immovable->get_attribute_id(attribute_name); + if (immovable->has_attribute(attribute_id)) { + lua_pushint32(L, index++); + lua_pushstring(L, immovable->name()); + lua_settable(L, -3); + } + } + return 1; +} + + +/* RST + .. method:: terrain_descriptions() + + Returns a list of names with the terrains that are available in the worls. + + (RO) a list of terrain names, e.g. {"wiese1", "wiese2", ...} +*/ +int LuaWorld::terrain_descriptions(lua_State* L) { + const World& world = get_egbase(L).world(); + lua_newtable(L); + int index = 1; + for (DescriptionIndex i = 0; i < world.terrains().size(); ++i) { + const TerrainDescription& terrain = world.terrain_descr(i); + lua_pushint32(L, index++); + lua_pushstring(L, terrain.name()); + lua_settable(L, -3); + } + return 1; +} + + +/* RST .. method:: new_resource_type(table) Adds a new resource type that can be in the different maps. Takes a === modified file 'src/scripting/lua_root.h' --- src/scripting/lua_root.h 2015-09-04 11:11:50 +0000 +++ src/scripting/lua_root.h 2016-01-26 07:30:52 +0000 @@ -111,6 +111,8 @@ /* * Lua methods */ + int immovable_descriptions(lua_State* L); + int terrain_descriptions(lua_State* L); int new_critter_type(lua_State* L); int new_editor_immovable_category(lua_State* L); int new_editor_terrain_category(lua_State* L); === modified file 'test/maps/lua_testsuite.wmf/scripting/immovables_descriptions.lua' --- test/maps/lua_testsuite.wmf/scripting/immovables_descriptions.lua 2015-10-31 12:11:44 +0000 +++ test/maps/lua_testsuite.wmf/scripting/immovables_descriptions.lua 2016-01-26 07:30:52 +0000 @@ -13,6 +13,95 @@ end) end + +-- ======================================================= +-- ***************** ImmovableDescription ***************** +-- ======================================================= + +function test_descr:test_immovable_descr() + assert_error("Wrong immovable", function() egbase:get_immovable_description("XXX") end) + assert_error("Wrong number of parameters: 2", function() egbase:get_immovable_description("XXX", "YYY") end) + assert_error("Wrong number of parameters: 3", function() egbase:get_immovable_description("XXX","YYY","ZZZ") end) +end + +function test_descr:test_immovable_basename() + assert_equal("", egbase:get_immovable_description("bush1").basename) + assert_equal("", egbase:get_immovable_description("cornfield_ripe").basename) + assert_equal("", egbase:get_immovable_description("alder_summer_sapling").basename) + assert_equal(_"Alder", egbase:get_immovable_description("alder_summer_old").basename) +end + +function test_descr:test_immovable_build_cost() + local build_cost = egbase:get_immovable_description("atlanteans_shipconstruction").build_cost + assert_equal(10, build_cost["planks"]) + assert_equal(2, build_cost["log"]) + assert_equal(4, build_cost["spidercloth"]) + assert_equal(nil, build_cost["wine"]) +end + +function test_descr:test_immovable_editor_category() + assert_equal("plants", egbase:get_immovable_description("bush1").editor_category.name) + assert_equal(_"Plants", egbase:get_immovable_description("bush1").editor_category.descname) + assert_equal(nil, egbase:get_immovable_description("cornfield_ripe").editor_category) + assert_equal("trees_deciduous", egbase:get_immovable_description("alder_summer_sapling").editor_category.name) + assert_equal(_"Deciduous Trees", egbase:get_immovable_description("alder_summer_sapling").editor_category.descname) + assert_equal("trees_deciduous", egbase:get_immovable_description("alder_summer_old").editor_category.name) +end + +function test_descr:test_immovable_has_terrain_affinity() + assert_equal(false, egbase:get_immovable_description("bush1").has_terrain_affinity) + assert_equal(false, egbase:get_immovable_description("cornfield_ripe").has_terrain_affinity) + assert_equal(true, egbase:get_immovable_description("alder_summer_sapling").has_terrain_affinity) + assert_equal(true, egbase:get_immovable_description("alder_summer_old").has_terrain_affinity) +end + +function test_descr:test_immovable_pickiness() + assert_equal(0, egbase:get_immovable_description("bush1").pickiness) + assert_equal(0, egbase:get_immovable_description("cornfield_ripe").pickiness) + assert_equal(0.6, egbase:get_immovable_description("alder_summer_sapling").pickiness) + assert_equal(0.6, egbase:get_immovable_description("alder_summer_old").pickiness) + assert_equal(0.6, egbase:get_immovable_description("mushroom_red_wasteland_sapling").pickiness) +end + +function test_descr:test_immovable_preferred_fertility() + assert_equal(0, egbase:get_immovable_description("bush1").preferred_fertility) + assert_equal(0, egbase:get_immovable_description("cornfield_ripe").preferred_fertility) + assert_equal(0.6, egbase:get_immovable_description("alder_summer_sapling").preferred_fertility) + assert_equal(0.6, egbase:get_immovable_description("alder_summer_old").preferred_fertility) + assert_equal(0.85, egbase:get_immovable_description("mushroom_red_wasteland_sapling").preferred_fertility) +end + +function test_descr:test_immovable_preferred_humidity() + assert_equal(0, egbase:get_immovable_description("bush1").preferred_humidity) + assert_equal(0, egbase:get_immovable_description("cornfield_ripe").preferred_humidity) + assert_equal(0.65, egbase:get_immovable_description("alder_summer_sapling").preferred_humidity) + assert_equal(0.65, egbase:get_immovable_description("alder_summer_old").preferred_humidity) + assert_equal(0.35, egbase:get_immovable_description("mushroom_red_wasteland_sapling").preferred_humidity) +end + +function test_descr:test_immovable_preferred_temperature() + assert_equal(0, egbase:get_immovable_description("bush1").preferred_temperature) + assert_equal(0, egbase:get_immovable_description("cornfield_ripe").preferred_temperature) + assert_equal(125, egbase:get_immovable_description("alder_summer_sapling").preferred_temperature) + assert_equal(125, egbase:get_immovable_description("alder_summer_old").preferred_temperature) + assert_equal(80, egbase:get_immovable_description("mushroom_red_wasteland_sapling").preferred_temperature) +end + +function test_descr:test_immovable_owner_type() + assert_equal("world", egbase:get_immovable_description("bush1").owner_type) + assert_equal("tribe", egbase:get_immovable_description("cornfield_ripe").owner_type) + assert_equal("world", egbase:get_immovable_description("alder_summer_sapling").owner_type) + assert_equal("world", egbase:get_immovable_description("alder_summer_old").owner_type) +end + +function test_descr:test_immovable_size() + assert_equal(0, egbase:get_immovable_description("bush1").size) + assert_equal(1, egbase:get_immovable_description("cornfield_ripe").size) + assert_equal(1, egbase:get_immovable_description("alder_summer_sapling").size) + assert_equal(1, egbase:get_immovable_description("alder_summer_old").size) +end + + -- ======================================================= -- ***************** BuildingDescription ***************** -- ======================================================= === modified file 'test/maps/lua_testsuite.wmf/scripting/init.lua' --- test/maps/lua_testsuite.wmf/scripting/init.lua 2015-10-31 12:11:44 +0000 +++ test/maps/lua_testsuite.wmf/scripting/init.lua 2016-01-26 07:30:52 +0000 @@ -33,6 +33,7 @@ include "map:scripting/immovables.lua" include "map:scripting/immovables_descriptions.lua" +include "map:scripting/terrains_resources_descriptions.lua" include "map:scripting/tribes_descriptions.lua" if not wl.editor then === added file 'test/maps/lua_testsuite.wmf/scripting/terrains_resources_descriptions.lua' --- test/maps/lua_testsuite.wmf/scripting/terrains_resources_descriptions.lua 1970-01-01 00:00:00 +0000 +++ test/maps/lua_testsuite.wmf/scripting/terrains_resources_descriptions.lua 2016-01-26 07:30:52 +0000 @@ -0,0 +1,154 @@ +set_textdomain("tribes") + +test_terrains_resource_descr = lunit.TestCase("Terrains and resources descriptions test") + +-- ======================================================= +-- ***************** ResourceDescription ***************** +-- ======================================================= + +function test_terrains_resource_descr:test_resource_descr() + assert_error("Wrong terrain", function() egbase:get_resource_description("XXX") end) + assert_error("Wrong number of parameters: 2", function() egbase:get_resource_description("XXX", "YYY") end) + assert_error("Wrong number of parameters: 3", function() egbase:get_resource_description("XXX","YYY","ZZZ") end) +end + +function test_terrains_resource_descr:test_resource_descname() + assert_equal(_"Coal", egbase:get_resource_description("coal").descname) + assert_equal(_"Stones", egbase:get_resource_description("stones").descname) + assert_equal(_"Water", egbase:get_resource_description("water").descname) + assert_equal(_"Fish", egbase:get_resource_description("fish").descname) +end + +function test_terrains_resource_descr:test_resource_name() + assert_equal("coal", egbase:get_resource_description("coal").name) + assert_equal("stones", egbase:get_resource_description("stones").name) + assert_equal("water", egbase:get_resource_description("water").name) + assert_equal("fish", egbase:get_resource_description("fish").name) +end + +function test_terrains_resource_descr:test_resource_is_detectable() + assert_equal(true, egbase:get_resource_description("coal").is_detectable) + assert_equal(true, egbase:get_resource_description("stones").is_detectable) + assert_equal(true, egbase:get_resource_description("water").is_detectable) + assert_equal(false, egbase:get_resource_description("fish").is_detectable) +end + +function test_terrains_resource_descr:test_resource_max_amount() + assert_equal(20, egbase:get_resource_description("coal").max_amount) + assert_equal(20, egbase:get_resource_description("stones").max_amount) + assert_equal(50, egbase:get_resource_description("water").max_amount) + assert_equal(20, egbase:get_resource_description("fish").max_amount) +end + +function test_terrains_resource_descr:test_resource_representative_image() + assert_equal("world/resources/pics/coal4.png", egbase:get_resource_description("coal").representative_image) + assert_equal("world/resources/pics/stones4.png", egbase:get_resource_description("stones").representative_image) + assert_equal("world/resources/pics/water4.png", egbase:get_resource_description("water").representative_image) + assert_equal("world/resources/pics/fish.png", egbase:get_resource_description("fish").representative_image) +end + +function test_terrains_resource_descr:test_resource_editor_image() + assert_equal("world/resources/pics/coal1.png", egbase:get_resource_description("coal"):editor_image(0)) + assert_equal("world/resources/pics/coal1.png", egbase:get_resource_description("coal"):editor_image(5)) + assert_equal("world/resources/pics/coal2.png", egbase:get_resource_description("coal"):editor_image(6)) + assert_equal("world/resources/pics/coal2.png", egbase:get_resource_description("coal"):editor_image(10)) + assert_equal("world/resources/pics/coal3.png", egbase:get_resource_description("coal"):editor_image(15)) + assert_equal("world/resources/pics/coal4.png", egbase:get_resource_description("coal"):editor_image(16)) + assert_equal("world/resources/pics/coal4.png", egbase:get_resource_description("coal"):editor_image(1000)) +end + +-- ======================================================= +-- ***************** TerrainDescription ****************** +-- ======================================================= + +function test_terrains_resource_descr:test_terrain_descr() + assert_error("Wrong terrain", function() egbase:get_terrain_description("XXX") end) + assert_error("Wrong number of parameters: 2", function() egbase:get_terrain_description("XXX", "YYY") end) + assert_error("Wrong number of parameters: 3", function() egbase:get_terrain_description("XXX","YYY","ZZZ") end) +end + +function test_terrains_resource_descr:test_terrain_descname() + assert_equal(_"Meadow", egbase:get_terrain_description("wiese1").descname) + assert_equal(_"Beach", egbase:get_terrain_description("wasteland_beach").descname) + assert_equal(_"Forested Mountain", egbase:get_terrain_description("desert_forested_mountain2").descname) + assert_equal(_"Water", egbase:get_terrain_description("winter_water").descname) +end + +function test_terrains_resource_descr:test_terrain_name() + assert_equal("wiese1", egbase:get_terrain_description("wiese1").name) + assert_equal("wasteland_beach", egbase:get_terrain_description("wasteland_beach").name) + assert_equal("desert_forested_mountain2", egbase:get_terrain_description("desert_forested_mountain2").name) + assert_equal("winter_water", egbase:get_terrain_description("winter_water").name) +end + +function test_terrains_resource_descr:test_terrain_default_resource_name() + assert_equal("water", egbase:get_terrain_description("wiese1").default_resource_name) + assert_equal(nil, egbase:get_terrain_description("wasteland_beach").default_resource_name) + assert_equal(nil, egbase:get_terrain_description("desert_forested_mountain2").default_resource_name) + assert_equal("fish", egbase:get_terrain_description("winter_water").default_resource_name) +end + +function test_terrains_resource_descr:test_terrain_default_resource_amount() + assert_equal(10, egbase:get_terrain_description("wiese1").default_resource_amount) + assert_equal(0, egbase:get_terrain_description("wasteland_beach").default_resource_amount) + assert_equal(0, egbase:get_terrain_description("desert_forested_mountain2").default_resource_amount) + assert_equal(4, egbase:get_terrain_description("winter_water").default_resource_amount) +end + +function test_terrains_resource_descr:test_terrain_editor_category() + assert_equal("green", egbase:get_terrain_description("wiese1").editor_category.name) + assert_equal(_"Summer", egbase:get_terrain_description("wiese1").editor_category.descname) + assert_equal("wasteland", egbase:get_terrain_description("wasteland_beach").editor_category.name) + assert_equal(_"Wasteland", egbase:get_terrain_description("wasteland_beach").editor_category.descname) + assert_equal("desert", egbase:get_terrain_description("desert_forested_mountain2").editor_category.name) + assert_equal(_"Desert", egbase:get_terrain_description("desert_forested_mountain2").editor_category.descname) + assert_equal("winter", egbase:get_terrain_description("winter_water").editor_category.name) + assert_equal(_"Winter", egbase:get_terrain_description("winter_water").editor_category.descname) +end + +function test_terrains_resource_descr:test_terrain_fertility() + assert_equal(0.7, egbase:get_terrain_description("wiese1").fertility) + assert_equal(0.2, egbase:get_terrain_description("wasteland_beach").fertility) + assert_equal(0.5, egbase:get_terrain_description("desert_forested_mountain2").fertility) + assert_equal(0.001, egbase:get_terrain_description("winter_water").fertility) +end + +function test_terrains_resource_descr:test_terrain_humidity() + assert_equal(0.6, egbase:get_terrain_description("wiese1").humidity) + assert_equal(0.4, egbase:get_terrain_description("wasteland_beach").humidity) + assert_equal(0.5, egbase:get_terrain_description("desert_forested_mountain2").humidity) + assert_equal(0.999, egbase:get_terrain_description("winter_water").humidity) +end + +function test_terrains_resource_descr:test_terrain_temperature() + assert_equal(100, egbase:get_terrain_description("wiese1").temperature) + assert_equal(60, egbase:get_terrain_description("wasteland_beach").temperature) + assert_equal(120, egbase:get_terrain_description("desert_forested_mountain2").temperature) + assert_equal(50, egbase:get_terrain_description("winter_water").temperature) +end + +function test_terrains_resource_descr:test_terrain_representative_image() + assert_equal("world/terrains/pics/green/wiese1_00.png", egbase:get_terrain_description("wiese1").representative_image) + assert_equal("world/terrains/pics/wasteland/strand_00.png", egbase:get_terrain_description("wasteland_beach").representative_image) + assert_equal("world/terrains/pics/desert/forested_mountain2_00.png", egbase:get_terrain_description("desert_forested_mountain2").representative_image) + assert_equal("world/terrains/pics/winter/water_00.png", egbase:get_terrain_description("winter_water").representative_image) +end + +function test_terrains_resource_descr:test_valid_resources_names() + assert_equal("water", egbase:get_terrain_description("wiese1").valid_resources_names[1]) + assert_equal(0, #egbase:get_terrain_description("wasteland_beach").valid_resources_names) + assert_equal(4, #egbase:get_terrain_description("desert_forested_mountain2").valid_resources_names) + assert_equal("fish", egbase:get_terrain_description("winter_water").valid_resources_names[1]) +end + +function test_terrains_resource_descr:test_terrain_probability_to_grow() + -- Using comparisons in order to not run into trouble with floating point numbers + assert_true(egbase:get_terrain_description("wiese1"):probability_to_grow("alder_summer_sapling") < 0.6) + assert_true(egbase:get_terrain_description("wiese1"):probability_to_grow("alder_summer_sapling") > 0.4) + assert_true(egbase:get_terrain_description("wasteland_beach"):probability_to_grow("alder_summer_sapling") < 0.003) + assert_true(egbase:get_terrain_description("wasteland_beach"):probability_to_grow("alder_summer_sapling") > 0.002) + assert_true(egbase:get_terrain_description("desert_forested_mountain2"):probability_to_grow("alder_summer_sapling") < 0.662) + assert_true(egbase:get_terrain_description("desert_forested_mountain2"):probability_to_grow("alder_summer_sapling") > 0.660) + assert_true(egbase:get_terrain_description("winter_water"):probability_to_grow("alder_summer_sapling") < 0.000038) + assert_true(egbase:get_terrain_description("winter_water"):probability_to_grow("alder_summer_sapling") > 0.000037) +end === modified file 'tribes/buildings/trainingsites/atlanteans/labyrinth/init.lua' --- tribes/buildings/trainingsites/atlanteans/labyrinth/init.lua 2016-01-02 21:39:43 +0000 +++ tribes/buildings/trainingsites/atlanteans/labyrinth/init.lua 2016-01-26 07:30:52 +0000 @@ -34,8 +34,8 @@ }, aihints = { - prohibited_till=900, - forced_after=1500, + prohibited_till = 900, + forced_after = 1500, trainingsite_type = "basic", very_weak_ai_limit = 1, weak_ai_limit = 2 === modified file 'world/immovables/trees/alder/init.lua' --- world/immovables/trees/alder/init.lua 2015-11-03 18:18:27 +0000 +++ world/immovables/trees/alder/init.lua 2016-01-26 07:30:52 +0000 @@ -87,6 +87,7 @@ world:new_immovable_type{ name = "alder_summer_old", descname = _ "Alder (Old)", + basename = _ "Alder", editor_category = "trees_deciduous", size = "small", attributes = { "tree" }, === modified file 'world/immovables/trees/aspen/init.lua' --- world/immovables/trees/aspen/init.lua 2015-11-20 18:21:27 +0000 +++ world/immovables/trees/aspen/init.lua 2016-01-26 07:30:52 +0000 @@ -82,6 +82,7 @@ world:new_immovable_type{ name = "aspen_summer_old", descname = _ "Aspen (Old)", + basename = _ "Aspen", editor_category = "trees_deciduous", size = "small", attributes = { "tree" }, === modified file 'world/immovables/trees/beech/init.lua' --- world/immovables/trees/beech/init.lua 2015-11-03 18:18:27 +0000 +++ world/immovables/trees/beech/init.lua 2016-01-26 07:30:52 +0000 @@ -79,6 +79,7 @@ world:new_immovable_type{ name = "beech_summer_old", descname = _ "Beech (Old)", + basename = _ "Beech", editor_category = "trees_deciduous", size = "small", attributes = { "tree" }, === modified file 'world/immovables/trees/birch/init.lua' --- world/immovables/trees/birch/init.lua 2015-11-03 18:18:27 +0000 +++ world/immovables/trees/birch/init.lua 2016-01-26 07:30:52 +0000 @@ -82,6 +82,7 @@ world:new_immovable_type{ name = "birch_summer_old", descname = _ "Birch (Old)", + basename = _ "Birch", editor_category = "trees_deciduous", size = "small", attributes = { "tree" }, === modified file 'world/immovables/trees/cirrus/init.lua' --- world/immovables/trees/cirrus/init.lua 2015-11-03 18:18:27 +0000 +++ world/immovables/trees/cirrus/init.lua 2016-01-26 07:30:52 +0000 @@ -83,6 +83,8 @@ name = "cirrus_wasteland_old", -- TRANSLATORS: This is a fictitious tree. Be creative if you want. descname = _ "Cirrus Tree (Old)", + -- TRANSLATORS: This is a fictitious tree. Be creative if you want. + basename = _ "Cirrus Tree", editor_category = "trees_wasteland", size = "small", attributes = { "tree" }, === modified file 'world/immovables/trees/larch/init.lua' --- world/immovables/trees/larch/init.lua 2015-11-03 18:18:27 +0000 +++ world/immovables/trees/larch/init.lua 2016-01-26 07:30:52 +0000 @@ -79,6 +79,7 @@ world:new_immovable_type{ name = "larch_summer_old", descname = _ "Larch (Old)", + basename = _ "Larch", editor_category = "trees_coniferous", size = "small", attributes = { "tree" }, === modified file 'world/immovables/trees/liana/init.lua' --- world/immovables/trees/liana/init.lua 2015-11-03 18:18:27 +0000 +++ world/immovables/trees/liana/init.lua 2016-01-26 07:30:52 +0000 @@ -86,6 +86,8 @@ name = "liana_wasteland_old", -- TRANSLATORS: This is a fictitious tree. Be creative if you want. descname = _ "Liana Tree (Old)", + -- TRANSLATORS: This is a fictitious tree. Be creative if you want. + basename = _ "Liana Tree", editor_category = "trees_wasteland", size = "small", attributes = { "tree" }, === modified file 'world/immovables/trees/maple/init.lua' --- world/immovables/trees/maple/init.lua 2015-11-03 18:18:27 +0000 +++ world/immovables/trees/maple/init.lua 2016-01-26 07:30:52 +0000 @@ -79,6 +79,7 @@ world:new_immovable_type{ name = "maple_winter_old", descname = _ "Maple (Old)", + basename = _ "Maple", editor_category = "trees_deciduous", size = "small", attributes = { "tree" }, === modified file 'world/immovables/trees/mushroom_dark/init.lua' --- world/immovables/trees/mushroom_dark/init.lua 2015-11-03 18:18:27 +0000 +++ world/immovables/trees/mushroom_dark/init.lua 2016-01-26 07:30:52 +0000 @@ -83,6 +83,8 @@ name = "mushroom_dark_wasteland_old", -- TRANSLATORS: This is a fictitious tree. Be creative if you want. descname = _ "Dark Mushroom Tree (Old)", + -- TRANSLATORS: This is a fictitious tree. Be creative if you want. + basename = _ "Dark Mushroom Tree", editor_category = "trees_wasteland", size = "small", attributes = { "tree" }, === modified file 'world/immovables/trees/mushroom_green/init.lua' --- world/immovables/trees/mushroom_green/init.lua 2015-11-03 18:18:27 +0000 +++ world/immovables/trees/mushroom_green/init.lua 2016-01-26 07:30:52 +0000 @@ -83,6 +83,8 @@ name = "mushroom_green_wasteland_old", -- TRANSLATORS: This is a fictitious tree. Be creative if you want. descname = _ "Green Mushroom Tree (Old)", + -- TRANSLATORS: This is a fictitious tree. Be creative if you want. + basename = _ "Green Mushroom Tree", editor_category = "trees_wasteland", size = "small", attributes = { "tree" }, === modified file 'world/immovables/trees/mushroom_red/init.lua' --- world/immovables/trees/mushroom_red/init.lua 2015-11-03 18:18:27 +0000 +++ world/immovables/trees/mushroom_red/init.lua 2016-01-26 07:30:52 +0000 @@ -86,6 +86,8 @@ name = "mushroom_red_wasteland_old", -- TRANSLATORS: This is a fictitious tree. Be creative if you want. descname = _ "Red Mushroom Tree (Old)", + -- TRANSLATORS: This is a fictitious tree. Be creative if you want. + basename = _ "Red Mushroom Tree", editor_category = "trees_wasteland", size = "small", attributes = { "tree" }, === modified file 'world/immovables/trees/oak/init.lua' --- world/immovables/trees/oak/init.lua 2015-11-20 18:21:27 +0000 +++ world/immovables/trees/oak/init.lua 2016-01-26 07:30:52 +0000 @@ -79,6 +79,7 @@ world:new_immovable_type{ name = "oak_summer_old", descname = _ "Oak (Old)", + basename = _ "Oak", editor_category = "trees_deciduous", size = "small", attributes = { "tree" }, === modified file 'world/immovables/trees/palm_borassus/init.lua' --- world/immovables/trees/palm_borassus/init.lua 2016-01-21 10:39:57 +0000 +++ world/immovables/trees/palm_borassus/init.lua 2016-01-26 07:30:52 +0000 @@ -79,6 +79,7 @@ world:new_immovable_type{ name = "palm_borassus_desert_old", descname = _ "Borassus Palm (Old)", + basename = _ "Borassus Palm", editor_category = "trees_palm", size = "small", attributes = { "tree" }, === modified file 'world/immovables/trees/palm_coconut/init.lua' --- world/immovables/trees/palm_coconut/init.lua 2015-11-03 18:18:27 +0000 +++ world/immovables/trees/palm_coconut/init.lua 2016-01-26 07:30:52 +0000 @@ -79,6 +79,7 @@ world:new_immovable_type{ name = "palm_coconut_desert_old", descname = _ "Coconut Palm (Old)", + basename = _ "Coconut Palm", editor_category = "trees_palm", size = "small", attributes = { "tree" }, === modified file 'world/immovables/trees/palm_date/init.lua' --- world/immovables/trees/palm_date/init.lua 2016-01-22 08:01:22 +0000 +++ world/immovables/trees/palm_date/init.lua 2016-01-26 07:30:52 +0000 @@ -82,6 +82,7 @@ world:new_immovable_type{ name = "palm_date_desert_old", descname = _ "Date Palm (Old)", + basename = _ "Date Palm", editor_category = "trees_palm", size = "small", attributes = { "tree" }, === modified file 'world/immovables/trees/palm_oil/init.lua' --- world/immovables/trees/palm_oil/init.lua 2016-01-21 10:39:57 +0000 +++ world/immovables/trees/palm_oil/init.lua 2016-01-26 07:30:52 +0000 @@ -83,6 +83,7 @@ world:new_immovable_type{ name = "palm_oil_desert_old", descname = _ "Oil Palm (Old)", + basename = _ "Oil Palm", editor_category = "trees_palm", size = "small", attributes = { "tree" }, === modified file 'world/immovables/trees/palm_roystonea/init.lua' --- world/immovables/trees/palm_roystonea/init.lua 2016-01-21 10:39:57 +0000 +++ world/immovables/trees/palm_roystonea/init.lua 2016-01-26 07:30:52 +0000 @@ -79,6 +79,7 @@ world:new_immovable_type{ name = "palm_roystonea_desert_old", descname = _ "Roystonea regia Palm (Old)", + basename = _ "Roystonea regia Palm", editor_category = "trees_palm", size = "small", attributes = { "tree" }, === modified file 'world/immovables/trees/rowan/init.lua' --- world/immovables/trees/rowan/init.lua 2015-11-03 18:18:27 +0000 +++ world/immovables/trees/rowan/init.lua 2016-01-26 07:30:52 +0000 @@ -82,6 +82,7 @@ world:new_immovable_type{ name = "rowan_summer_old", descname = _ "Rowan (Old)", + basename = _ "Rowan", editor_category = "trees_deciduous", size = "small", attributes = { "tree" }, === modified file 'world/immovables/trees/spruce/init.lua' --- world/immovables/trees/spruce/init.lua 2015-11-03 18:18:27 +0000 +++ world/immovables/trees/spruce/init.lua 2016-01-26 07:30:52 +0000 @@ -79,6 +79,7 @@ world:new_immovable_type{ name = "spruce_summer_old", descname = _ "Spruce (Old)", + basename = _ "Spruce", editor_category = "trees_coniferous", size = "small", attributes = { "tree" }, === modified file 'world/immovables/trees/twine/init.lua' --- world/immovables/trees/twine/init.lua 2015-11-03 18:18:27 +0000 +++ world/immovables/trees/twine/init.lua 2016-01-26 07:30:52 +0000 @@ -83,6 +83,8 @@ name = "twine_wasteland_old", -- TRANSLATORS: This is a fictitious tree. Be creative if you want. descname = _ "Twine Tree (Old)", + -- TRANSLATORS: This is a fictitious tree. Be creative if you want. + basename = _ "Twine Tree", editor_category = "trees_wasteland", size = "small", attributes = { "tree" }, === modified file 'world/immovables/trees/umbrella_green/init.lua' --- world/immovables/trees/umbrella_green/init.lua 2015-11-03 18:18:27 +0000 +++ world/immovables/trees/umbrella_green/init.lua 2016-01-26 07:30:52 +0000 @@ -83,6 +83,8 @@ name = "umbrella_green_wasteland_old", -- TRANSLATORS: This is a fictitious tree. Be creative if you want. descname = _ "Green Umbrella Tree (Old)", + -- TRANSLATORS: This is a fictitious tree. Be creative if you want. + basename = _ "Green Umbrella Tree", editor_category = "trees_wasteland", size = "small", attributes = { "tree" }, === modified file 'world/immovables/trees/umbrella_red/init.lua' --- world/immovables/trees/umbrella_red/init.lua 2015-11-03 18:18:27 +0000 +++ world/immovables/trees/umbrella_red/init.lua 2016-01-26 07:30:52 +0000 @@ -86,6 +86,8 @@ name = "umbrella_red_wasteland_old", -- TRANSLATORS: This is a fictitious tree. Be creative if you want. descname = _ "Red Umbrella Tree (Old)", + -- TRANSLATORS: This is a fictitious tree. Be creative if you want. + basename = _ "Red Umbrella Tree", editor_category = "trees_wasteland", size = "small", attributes = { "tree" }, === modified file 'world/resources/init.lua' --- world/resources/init.lua 2016-01-11 22:13:36 +0000 +++ world/resources/init.lua 2016-01-26 07:30:52 +0000 @@ -9,6 +9,8 @@ max_amount = 20, -- A geologist can find it, otherwise false (see Fish) detectable = true, + -- This represents the resource in menus etc. + representative_image = pics_dir .. "coal4.png", -- Picture that is used to indicate the amount of resource on the map -- [5] means amount 0 to 5; next line means amount 6 to 10 and so on -- The picture with highest number is additionally used in ui @@ -25,6 +27,7 @@ descname = _ "Gold", max_amount = 20, detectable = true, + representative_image = pics_dir .. "gold4.png", editor_pictures = { [5] = pics_dir .. "gold1.png", [10] = pics_dir .. "gold2.png", @@ -38,6 +41,7 @@ descname = _ "Iron", max_amount = 20, detectable = true, + representative_image = pics_dir .. "iron4.png", editor_pictures = { [5] = pics_dir .. "iron1.png", [10] = pics_dir .. "iron2.png", @@ -51,6 +55,7 @@ descname = _ "Stones", max_amount = 20, detectable = true, + representative_image = pics_dir .. "stones4.png", editor_pictures = { [5] = pics_dir .. "stones1.png", [10] = pics_dir .. "stones2.png", @@ -64,6 +69,7 @@ descname = _ "Water", max_amount = 50, detectable = true, + representative_image = pics_dir .. "water4.png", editor_pictures = { [10] = pics_dir .."water1.png", [20] = pics_dir .."water2.png", @@ -77,14 +83,11 @@ descname = _ "Fish", max_amount = 20, detectable = false, + representative_image = pics_dir .. "fish.png", editor_pictures = { [5] = pics_dir .. "fish1.png", [10] = pics_dir .. "fish2.png", [15] = pics_dir .. "fish3.png", [1000] = pics_dir .. "fish4.png", - -- Clutch: The editor chooses the image with the highest number for the - -- UI. So we keep a nice picture for this purpose at the top of this - -- list. - [1001] = pics_dir .. "fish.png", } }
_______________________________________________ Mailing list: https://launchpad.net/~widelands-dev Post to : widelands-dev@lists.launchpad.net Unsubscribe : https://launchpad.net/~widelands-dev More help : https://help.launchpad.net/ListHelp