Benedikt Straub has proposed merging lp:~widelands-dev/widelands/peaceful into lp:widelands.
Commit message: Allow scripts to override the hostility of each player to each other player. Add an option »Peaceful Mode« to the launch game screen. Requested reviews: Widelands Developers (widelands-dev) Related bugs: Bug #1794959 in widelands: "Implement independent stance from every tribe to every other tribe" https://bugs.launchpad.net/widelands/+bug/1794959 For more details, see: https://code.launchpad.net/~widelands-dev/widelands/peaceful/+merge/365273 -- Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/peaceful into lp:widelands.
=== modified file 'data/campaigns/fri02.wmf/scripting/mission_thread.lua' --- data/campaigns/fri02.wmf/scripting/mission_thread.lua 2018-09-29 08:43:57 +0000 +++ data/campaigns/fri02.wmf/scripting/mission_thread.lua 2019-03-29 10:22:28 +0000 @@ -280,7 +280,8 @@ if not p2.defeated then campaign_message_box(defeat_murilius_1) campaign_message_box(defeat_murilius_2) - p2.team = 2 + p1:set_attack_forbidden(2, false) + p2:set_attack_forbidden(1, false) o = add_campaign_objective(obj_defeat_murilius) local def = false while not def do @@ -299,7 +300,8 @@ end function defeat_murilius() - p2.team = 2 + p1:set_attack_forbidden(2, false) + p2:set_attack_forbidden(1, false) campaign_message_box(defeat_both) local o = add_campaign_objective(obj_defeat_both) while not (p2.defeated and p3.defeated) do sleep(4829) end @@ -333,13 +335,27 @@ include "map:scripting/starting_conditions.lua" sleep(5000) - p1.team = 1 - p2.team = 1 - p3.team = 2 --- TODO: instead of alliances, just forbid certain players to attack each other: --- · Beginning: forbid 1>2, 2>1, 2>3 --- · Refusing alliance: forbid only 2>3 --- · Accepting alliance: first unchanged, after p3 defeated: allow all + p1:set_attack_forbidden(2, true) + p2:set_attack_forbidden(1, true) + p2:set_attack_forbidden(3, true) + run(function() + while true do + local conquered = (#p3:get_buildings("empire_sentry") + + #p3:get_buildings("empire_blockhouse") + + #p3:get_buildings("empire_outpost") + + #p3:get_buildings("empire_barrier") + + #p3:get_buildings("empire_tower") + + #p3:get_buildings("empire_fortress") + + #p3:get_buildings("empire_castle") - + #p2:get_buildings("barbarians_sentry") - + #p2:get_buildings("barbarians_barrier") - + #p2:get_buildings("barbarians_tower") - + #p2:get_buildings("barbarians_fortress") - + #p2:get_buildings("barbarians_citadel")) + p2:set_attack_forbidden(3, conquered <= 0) + sleep(6913) + end + end) campaign_message_box(intro_3) local o = add_campaign_objective(obj_new_home) === modified file 'src/game_io/game_player_info_packet.cc' --- src/game_io/game_player_info_packet.cc 2019-02-23 11:00:49 +0000 +++ src/game_io/game_player_info_packet.cc 2019-03-29 10:22:28 +0000 @@ -30,14 +30,14 @@ namespace Widelands { -constexpr uint16_t kCurrentPacketVersion = 22; +constexpr uint16_t kCurrentPacketVersion = 23; void GamePlayerInfoPacket::read(FileSystem& fs, Game& game, MapObjectLoader*) { try { FileRead fr; fr.open(fs, "binary/player_info"); uint16_t const packet_version = fr.unsigned_16(); - if (packet_version == kCurrentPacketVersion) { + if (packet_version <= kCurrentPacketVersion && packet_version >= 22) { uint32_t const max_players = fr.unsigned_16(); for (uint32_t i = 1; i < max_players + 1; ++i) { game.remove_player(i); @@ -59,6 +59,15 @@ player->set_see_all(see_all); player->set_ai(fr.c_string()); + + if (packet_version == kCurrentPacketVersion) { + player->forbid_attack_.clear(); + uint8_t size = fr.unsigned_8(); + for (uint8_t j = 0; j < size; ++j) { + player->forbid_attack_.emplace(fr.unsigned_8()); + } + } + player->read_statistics(fr, packet_version); player->read_remaining_shipnames(fr); @@ -118,6 +127,11 @@ fw.c_string(plr->name_.c_str()); fw.c_string(plr->ai_.c_str()); + fw.unsigned_8(plr->forbid_attack_.size()); + for (const auto& it : plr->forbid_attack_) { + fw.unsigned_8(it); + } + plr->write_statistics(fw); plr->write_remaining_shipnames(fw); fw.unsigned_32(plr->casualties()); === modified file 'src/logic/game.cc' --- src/logic/game.cc 2019-03-24 11:58:54 +0000 +++ src/logic/game.cc 2019-03-29 10:22:28 +0000 @@ -322,6 +322,19 @@ // Check for win_conditions if (!settings.scenario) { loader_ui->step(_("Initializing game…")); + if (settings.peaceful) { + for (uint32_t i = 1; i < settings.players.size(); ++i) { + if (Player* p1 = get_player(i)) { + for (uint32_t j = i + 1; j <= settings.players.size(); ++j) { + if (Player* p2 = get_player(j)) { + p1->set_attack_forbidden(j, true); + p2->set_attack_forbidden(i, true); + } + } + } + } + } + std::unique_ptr<LuaTable> table(lua().run_script(settings.win_condition_script)); table->do_not_warn_about_unaccessed_keys(); win_condition_displayname_ = table->get_string("name"); === modified file 'src/logic/game_settings.h' --- src/logic/game_settings.h 2019-02-23 11:00:49 +0000 +++ src/logic/game_settings.h 2019-03-29 10:22:28 +0000 @@ -111,7 +111,7 @@ * Think of it as the Model in MVC. */ struct GameSettings { - GameSettings() : playernum(0), usernum(0), scenario(false), multiplayer(false), savegame(false) { + GameSettings() : playernum(0), usernum(0), scenario(false), multiplayer(false), savegame(false), peaceful(false) { std::unique_ptr<LuaInterface> lua(new LuaInterface); std::unique_ptr<LuaTable> win_conditions( lua->run_script("scripting/win_conditions/init.lua")); @@ -156,6 +156,9 @@ /// Is a savegame selected for loading? bool savegame; + // Is all fighting forbidden? + bool peaceful; + /// List of tribes that players are allowed to choose std::vector<Widelands::TribeBasicInfo> tribes; @@ -210,6 +213,9 @@ virtual void set_win_condition_script(const std::string& wc) = 0; virtual std::string get_win_condition_script() = 0; + virtual void set_peaceful_mode(bool peace) = 0; + virtual bool is_peaceful_mode() = 0; + // For retrieving tips texts struct NoTribe {}; const std::string& get_players_tribe() { === modified file 'src/logic/player.cc' --- src/logic/player.cc 2019-03-09 08:58:52 +0000 +++ src/logic/player.cc 2019-03-29 10:22:28 +0000 @@ -250,7 +250,8 @@ * each other. */ bool Player::is_hostile(const Player& other) const { - return &other != this && (!team_number_ || team_number_ != other.team_number_); + return &other != this && (!team_number_ || team_number_ != other.team_number_) && + !is_attack_forbidden(other.player_number()); } bool Player::is_defeated() const { @@ -1326,6 +1327,21 @@ return ai_; } +bool Player::is_attack_forbidden(PlayerNumber who) const { + return forbid_attack_.find(who) != forbid_attack_.end(); +} + +void Player::set_attack_forbidden(PlayerNumber who, bool forbid) { + const auto it = forbid_attack_.find(who); + if (forbid ^ (it == forbid_attack_.end())) { + return; + } else if (forbid) { + forbid_attack_.emplace(who); + } else { + forbid_attack_.erase(it); + } +} + /** * Pick random name from remaining names (if any) */ === modified file 'src/logic/player.h' --- src/logic/player.h 2019-03-09 08:58:52 +0000 +++ src/logic/player.h 2019-03-29 10:22:28 +0000 @@ -603,6 +603,9 @@ further_initializations_.push_back(init); } + void set_attack_forbidden(PlayerNumber who, bool forbid); + bool is_attack_forbidden(PlayerNumber who) const; + const std::string pick_shipname(); private: @@ -680,6 +683,8 @@ */ std::vector<std::vector<uint32_t>> ware_stocks_; + std::set<PlayerNumber> forbid_attack_; + PlayerBuildingStats building_stats_; DISALLOW_COPY_AND_ASSIGN(Player); === modified file 'src/logic/single_player_game_settings_provider.cc' --- src/logic/single_player_game_settings_provider.cc 2019-02-23 11:00:49 +0000 +++ src/logic/single_player_game_settings_provider.cc 2019-03-29 10:22:28 +0000 @@ -69,6 +69,14 @@ return s.mapfilename; } +bool SinglePlayerGameSettingsProvider::is_peaceful_mode() { + return s.peaceful; +} + +void SinglePlayerGameSettingsProvider::set_peaceful_mode(bool peace) { + s.peaceful = peace; +} + void SinglePlayerGameSettingsProvider::set_map(const std::string& mapname, const std::string& mapfilename, uint32_t const maxplayers, === modified file 'src/logic/single_player_game_settings_provider.h' --- src/logic/single_player_game_settings_provider.h 2019-02-23 11:00:49 +0000 +++ src/logic/single_player_game_settings_provider.h 2019-03-29 10:22:28 +0000 @@ -62,6 +62,9 @@ std::string get_win_condition_script() override; void set_win_condition_script(const std::string& wc) override; + void set_peaceful_mode(bool peace) override; + bool is_peaceful_mode() override; + private: GameSettings s; }; === modified file 'src/network/gameclient.cc' --- src/network/gameclient.cc 2019-02-23 11:00:49 +0000 +++ src/network/gameclient.cc 2019-03-29 10:22:28 +0000 @@ -405,6 +405,15 @@ // set_player_number(uint8_t) to the host. } + +void GameClient::set_peaceful_mode(bool peace) { + d->settings.peaceful = peace; +} + +bool GameClient::is_peaceful_mode() { + return d->settings.peaceful; +} + std::string GameClient::get_win_condition_script() { return d->settings.win_condition_script; } @@ -819,6 +828,10 @@ d->settings.win_condition_script = g_fs->FileSystem::fix_cross_file(packet.string()); break; } + case NETCMD_PEACEFUL_MODE: { + d->settings.peaceful = packet.unsigned_8(); + break; + } case NETCMD_LAUNCH: { if (!d->modal || d->game) { === modified file 'src/network/gameclient.h' --- src/network/gameclient.h 2019-02-23 11:00:49 +0000 +++ src/network/gameclient.h 2019-03-29 10:22:28 +0000 @@ -95,6 +95,9 @@ void set_win_condition_script(const std::string&) override; std::string get_win_condition_script() override; + void set_peaceful_mode(bool peace) override; + bool is_peaceful_mode() override; + // ChatProvider interface void send(const std::string& msg) override; const std::vector<ChatMessage>& get_messages() const override; === modified file 'src/network/gamehost.cc' --- src/network/gamehost.cc 2019-02-23 11:00:49 +0000 +++ src/network/gamehost.cc 2019-03-29 10:22:28 +0000 @@ -203,6 +203,13 @@ host_->set_win_condition_script(wc); } + void set_peaceful_mode(bool peace) override { + host_->set_peaceful_mode(peace); + } + bool is_peaceful_mode() override { + return host_->settings().peaceful; + } + private: GameHost* host_; std::vector<std::string> wincondition_scripts_; @@ -1368,6 +1375,16 @@ broadcast(packet); } +void GameHost::set_peaceful_mode(bool peace) { + d->settings.peaceful = peace; + + // Broadcast changes + SendPacket packet; + packet.unsigned_8(NETCMD_PEACEFUL_MODE); + packet.unsigned_8(peace ? 1 : 0); + broadcast(packet); +} + void GameHost::switch_to_player(uint32_t user, uint8_t number) { if (number < d->settings.players.size() && (d->settings.players.at(number).state != PlayerSettings::State::kOpen && @@ -1676,6 +1693,11 @@ packet.string(d->settings.win_condition_script); d->net->send(client.sock_id, packet); + packet.reset(); + packet.unsigned_8(NETCMD_PEACEFUL_MODE); + packet.unsigned_8(d->settings.peaceful ? 1 : 0); + d->net->send(client.sock_id, packet); + // Broadcast new information about the player to everybody packet.reset(); packet.unsigned_8(NETCMD_SETTING_USER); @@ -2143,6 +2165,12 @@ } break; + case NETCMD_PEACEFUL_MODE: + if (!d->game) { + throw DisconnectException("NO_ACCESS_TO_SERVER"); + } + break; + case NETCMD_LAUNCH: if (!d->game) { throw DisconnectException("NO_ACCESS_TO_SERVER"); === modified file 'src/network/gamehost.h' --- src/network/gamehost.h 2019-02-23 11:00:49 +0000 +++ src/network/gamehost.h 2019-03-29 10:22:28 +0000 @@ -80,6 +80,7 @@ void set_player_shared(PlayerSlot number, Widelands::PlayerNumber shared); void switch_to_player(uint32_t user, uint8_t number); void set_win_condition_script(const std::string& wc); + void set_peaceful_mode(bool peace); void replace_client_with_ai(uint8_t playernumber, const std::string& ai); // just visible stuff for the select mapmenu === modified file 'src/network/network_protocol.h' --- src/network/network_protocol.h 2019-02-23 11:00:49 +0000 +++ src/network/network_protocol.h 2019-03-29 10:22:28 +0000 @@ -429,6 +429,14 @@ NETCMD_SYSTEM_MESSAGE_CODE = 32, /** + * Sent by the host to toggle peaceful mode. + * + * Attached data is: + * \li uint8_t: 1 if peaceful mode is enabled, 0 otherwise + */ + NETCMD_PEACEFUL_MODE = 33, + + /** * Sent by the metaserver to a freshly opened game to check connectability */ NETCMD_METASERVER_PING = 64 === modified file 'src/scripting/lua_game.cc' --- src/scripting/lua_game.cc 2019-03-09 08:58:52 +0000 +++ src/scripting/lua_game.cc 2019-03-29 10:22:28 +0000 @@ -101,6 +101,8 @@ METHOD(LuaPlayer, allow_workers), METHOD(LuaPlayer, switchplayer), METHOD(LuaPlayer, get_produced_wares_count), + METHOD(LuaPlayer, set_attack_forbidden), + METHOD(LuaPlayer, is_attack_forbidden), {nullptr, nullptr}, }; const PropertyType<LuaPlayer> LuaPlayer::Properties[] = { @@ -875,6 +877,39 @@ return 1; } +/* RST + .. method:: is_attack_forbidden(who) + + Returns true if this player is currently forbidden to attack the player with the specified + player number. Note that the return value `false` does not necessarily mean that this + player *can* attack the other player, as they might for example be in the same team. + + :arg who: player number of the player to query + :type who: :class:`int` + :rtype: :class:`boolean` +*/ +int LuaPlayer::is_attack_forbidden(lua_State* L) { + lua_pushboolean(L, get(L, get_egbase(L)).is_attack_forbidden(luaL_checkinteger(L, 2))); + return 1; +} + +/* RST + .. method:: set_attack_forbidden(who, forbid) + + Sets whether this player is forbidden to attack the player with the specified + player number. Note that setting this to `false` does not necessarily mean that this + player *can* attack the other player, as they might for example be in the same team. + + :arg who: player number of the player to query + :type who: :class:`int` + :arg forbid: Whether to allow or forbid attacks + :type forbid: :class:`boolean` +*/ +int LuaPlayer::set_attack_forbidden(lua_State* L) { + get(L, get_egbase(L)).set_attack_forbidden(luaL_checkinteger(L, 2), luaL_checkboolean(L, 3)); + return 0; +} + /* ========================================================== C METHODS === modified file 'src/scripting/lua_game.h' --- src/scripting/lua_game.h 2019-02-23 11:00:49 +0000 +++ src/scripting/lua_game.h 2019-03-29 10:22:28 +0000 @@ -97,6 +97,8 @@ int allow_workers(lua_State* L); int switchplayer(lua_State* L); int get_produced_wares_count(lua_State* L); + int set_attack_forbidden(lua_State* L); + int is_attack_forbidden(lua_State* L); /* * C methods === modified file 'src/ui_fsmenu/launch_game.cc' --- src/ui_fsmenu/launch_game.cc 2019-02-23 11:00:49 +0000 +++ src/ui_fsmenu/launch_game.cc 2019-03-29 10:22:28 +0000 @@ -55,6 +55,12 @@ "", UI::DropdownType::kTextual, UI::PanelStyle::kFsMenu), + + peaceful_(this, + Vector2i(get_w() * 7 / 10, + get_h() * 19 / 40 + buth_), + _("Peaceful mode"), + _("Forbid fighting between players")), ok_(this, "ok", 0, 0, butw_, buth_, UI::ButtonStyle::kFsMenuPrimary, _("Start game")), back_(this, "back", 0, 0, butw_, buth_, UI::ButtonStyle::kFsMenuSecondary, _("Back")), // Text labels @@ -65,6 +71,8 @@ nr_players_(0) { win_condition_dropdown_.selected.connect( boost::bind(&FullscreenMenuLaunchGame::win_condition_selected, this)); + peaceful_.changed.connect( + boost::bind(&FullscreenMenuLaunchGame::toggle_peaceful, this)); back_.sigclicked.connect( boost::bind(&FullscreenMenuLaunchGame::clicked_back, boost::ref(*this))); ok_.sigclicked.connect(boost::bind(&FullscreenMenuLaunchGame::clicked_ok, boost::ref(*this))); @@ -78,6 +86,14 @@ delete lua_; } +void FullscreenMenuLaunchGame::update_peaceful_mode() { + bool scenario_or_savegame = settings_->settings().scenario || settings_->settings().savegame; + peaceful_.set_enabled(!scenario_or_savegame && settings_->can_change_map()); + if (scenario_or_savegame) { + peaceful_.set_state(false); + } +} + bool FullscreenMenuLaunchGame::init_win_condition_label() { if (settings_->settings().scenario) { win_condition_dropdown_.set_enabled(false); @@ -190,6 +206,10 @@ return t; } +void FullscreenMenuLaunchGame::toggle_peaceful() { + settings_->set_peaceful_mode(peaceful_.get_state()); +} + // Implemented by subclasses void FullscreenMenuLaunchGame::clicked_ok() { NEVER_HERE(); === modified file 'src/ui_fsmenu/launch_game.h' --- src/ui_fsmenu/launch_game.h 2019-02-23 11:00:49 +0000 +++ src/ui_fsmenu/launch_game.h 2019-03-29 10:22:28 +0000 @@ -26,6 +26,7 @@ #include "graphic/playercolor.h" #include "logic/map.h" #include "ui_basic/button.h" +#include "ui_basic/checkbox.h" #include "ui_basic/dropdown.h" #include "ui_basic/textarea.h" #include "ui_fsmenu/base.h" @@ -56,6 +57,9 @@ /// Creates a blank label/tooltip and returns 'false' otherwise. bool init_win_condition_label(); + /// Enables or disables the peaceful mode checkbox. + void update_peaceful_mode(); + /// Loads all win conditions that can be played with the map into the selection dropdown. /// Disables the dropdown if the map is a scenario. void update_win_conditions(); @@ -71,10 +75,13 @@ std::unique_ptr<LuaTable> win_condition_if_valid(const std::string& win_condition_script, std::set<std::string> tags) const; + void toggle_peaceful(); + uint32_t butw_; uint32_t buth_; UI::Dropdown<std::string> win_condition_dropdown_; + UI::Checkbox peaceful_; std::string last_win_condition_; UI::Button ok_, back_; UI::Textarea title_; === modified file 'src/ui_fsmenu/launch_mpg.cc' --- src/ui_fsmenu/launch_mpg.cc 2019-02-23 11:00:49 +0000 +++ src/ui_fsmenu/launch_mpg.cc 2019-03-29 10:22:28 +0000 @@ -159,7 +159,7 @@ UI::PanelStyle::kFsMenu), client_info_(this, right_column_x_, - get_h() * 13 / 20 - 2 * label_height_, + get_h() * 15 / 20 - 2 * label_height_, butw_, get_h(), UI::PanelStyle::kFsMenu), @@ -167,7 +167,8 @@ // Variables and objects used in the menu chat_(nullptr) { - ok_.set_pos(Vector2i(right_column_x_, get_h() * 12 / 20 - 2 * label_height_)); + peaceful_.set_pos(Vector2i(right_column_x_, get_h() * 25 / 40 - 2 * label_height_)); + ok_.set_pos(Vector2i(right_column_x_, get_h() * 14 / 20 - 2 * label_height_)); back_.set_pos(Vector2i(right_column_x_, get_h() * 218 / 240)); win_condition_dropdown_.set_pos( Vector2i(right_column_x_, get_h() * 11 / 20 - 2 * label_height_)); @@ -422,6 +423,9 @@ change_map_or_save_.set_enabled(settings_->can_change_map()); change_map_or_save_.set_visible(settings_->can_change_map()); + update_peaceful_mode(); + peaceful_.set_state(settings_->is_peaceful_mode()); + if (!settings_->can_change_map() && !init_win_condition_label()) { try { // We do not validate the scripts for the client - it's only a label. === modified file 'src/ui_fsmenu/launch_spg.cc' --- src/ui_fsmenu/launch_spg.cc 2019-02-23 11:00:49 +0000 +++ src/ui_fsmenu/launch_spg.cc 2019-03-29 10:22:28 +0000 @@ -217,6 +217,8 @@ select_map_.set_visible(settings_->can_change_map()); select_map_.set_enabled(settings_->can_change_map()); + peaceful_.set_state(settings_->is_peaceful_mode()); + set_player_names_and_tribes(); } @@ -262,6 +264,7 @@ safe_place_for_host(nr_players_); settings_->set_map(mapdata.name, mapdata.filename, nr_players_); update_win_conditions(); + update_peaceful_mode(); update(true); return true; }
_______________________________________________ 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