1 /*
2  * Copyright (C) 2008-2020 by the Widelands Development Team
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17  *
18  */
19 
20 #ifndef WL_LOGIC_GAME_SETTINGS_H
21 #define WL_LOGIC_GAME_SETTINGS_H
22 
23 #include <memory>
24 #include <string>
25 
26 #include "io/filesystem/layered_filesystem.h"
27 #include "logic/map_objects/tribes/tribe_basic_info.h"
28 #include "logic/player_end_result.h"
29 #include "logic/widelands.h"
30 #include "notifications/note_ids.h"
31 #include "notifications/notifications.h"
32 #include "scripting/lua_interface.h"
33 #include "scripting/lua_table.h"
34 
35 // PlayerSlot 0 will give us Widelands::PlayerNumber 1 etc., so we rename it to avoid confusion.
36 // TODO(GunChleoc): Rename all uint8_t to PlayerSlot or Widelands::PlayerNumber
37 using PlayerSlot = Widelands::PlayerNumber;
38 
39 struct PlayerSettings {
40 	enum class State { kOpen, kHuman, kComputer, kClosed, kShared };
41 
42 	/// Returns whether the given state allows sharing a slot at all
can_be_sharedPlayerSettings43 	static bool can_be_shared(PlayerSettings::State state) {
44 		return state != PlayerSettings::State::kClosed && state != PlayerSettings::State::kShared;
45 	}
46 
47 	State state;
48 	uint8_t initialization_index;
49 	std::string name;
50 	std::string tribe;
51 	bool random_tribe;
52 	std::string ai; /**< Preferred AI provider for this player */
53 	bool random_ai;
54 	Widelands::TeamNumber team;
55 	bool closeable;     // only used in multiplayer scenario maps
56 	uint8_t shared_in;  // the number of the player that uses this player's starting position
57 };
58 
59 struct UserSettings {
60 	// TODO(k.halfman): make this some const instead of calculating this every time
noneUserSettings61 	static uint8_t none() {
62 		return std::numeric_limits<uint8_t>::max();
63 	}
not_connectedUserSettings64 	static uint8_t not_connected() {
65 		return none() - 1;
66 	}
highest_playernumUserSettings67 	static uint8_t highest_playernum() {
68 		return not_connected() - 1;
69 	}
70 
UserSettingsUserSettings71 	UserSettings(Widelands::PlayerEndResult init_result, bool init_ready)
72 	   : result(init_result), ready(init_ready) {
73 	}
UserSettingsUserSettings74 	UserSettings() : UserSettings(Widelands::PlayerEndResult::kUndefined, false) {
75 	}
76 
77 	uint8_t position = 0;
78 	std::string name;
79 	Widelands::PlayerEndResult result;
80 	std::string win_condition_string;
81 	bool ready;  // until now only used as a check for whether user is currently receiving a file or
82 	             // not
83 };
84 
85 /// The gamehost/gameclient are sending those to notify about status changes, which are then picked
86 /// up by the UI.
87 struct NoteGameSettings {
88 	CAN_BE_SENT_AS_NOTE(NoteId::GameSettings)
89 
90 	enum class Action {
91 		kUser,    // A client has picked a different player slot / become an observer
92 		kPlayer,  // A player slot has changed its status (type, tribe etc.)
93 		kMap      // A new map/savegame was selected
94 	};
95 
96 	Action action;
97 	PlayerSlot position;
98 	uint8_t usernum;
99 
100 	explicit NoteGameSettings(Action init_action,
101 	                          PlayerSlot init_position = std::numeric_limits<uint8_t>::max(),
102 	                          uint8_t init_usernum = UserSettings::none())
actionNoteGameSettings103 	   : action(init_action), position(init_position), usernum(init_usernum) {
104 	}
105 };
106 
107 /**
108  * Holds all settings about a game that can be configured before the
109  * game actually starts.
110  *
111  * Think of it as the Model in MVC.
112  */
113 struct GameSettings {
GameSettingsGameSettings114 	GameSettings()
115 	   : playernum(0),
116 	     usernum(0),
117 	     scenario(false),
118 	     multiplayer(false),
119 	     savegame(false),
120 	     peaceful(false) {
121 		std::unique_ptr<LuaInterface> lua(new LuaInterface);
122 		std::unique_ptr<LuaTable> win_conditions(
123 		   lua->run_script("scripting/win_conditions/init.lua"));
124 		for (const int key : win_conditions->keys<int>()) {
125 			std::string filename = win_conditions->get_string(key);
126 			if (g_fs->file_exists(filename)) {
127 				win_condition_scripts.push_back(filename);
128 			} else {
129 				throw wexception("Win condition file \"%s\" does not exist", filename.c_str());
130 			}
131 		}
132 	}
133 
134 	/// Find a player number that the slot could share in. Does not guarantee that a viable slot was
135 	/// actually found.
136 	Widelands::PlayerNumber find_shared(PlayerSlot slot) const;
137 	/// Check if the player number returned by find_shared is usable
138 	bool is_shared_usable(PlayerSlot slot, Widelands::PlayerNumber shared) const;
139 	/// Savegame slots and certain scenario slots can't be closed
140 	bool uncloseable(PlayerSlot slot) const;
141 
142 	/// Number of player position
143 	int16_t playernum;
144 	/// Number of users entry
145 	int8_t usernum;
146 
147 	/// Name of the selected map
148 	std::string mapname;
149 	std::string mapfilename;
150 
151 	/// Lua file defining the win condition to use.
152 	std::string win_condition_script;
153 	/// An ordered list of all win condition script files.
154 	std::vector<std::string> win_condition_scripts;
155 
156 	/// Is map a scenario
157 	bool scenario;
158 
159 	/// Is this a multiplayer game
160 	bool multiplayer;
161 
162 	/// Is a savegame selected for loading?
163 	bool savegame;
164 
165 	// Is all fighting forbidden?
166 	bool peaceful;
167 
168 	/// List of tribes that players are allowed to choose
169 	std::vector<Widelands::TribeBasicInfo> tribes;
170 
171 	/// Player configuration, with 0-based indices for players
172 	std::vector<PlayerSettings> players;
173 
174 	/// Users connected to the game (0-based indices) - only used in multiplayer
175 	std::vector<UserSettings> users;
176 };
177 
178 /**
179  * UI classes are given a GameSettingsProvider instead of direct
180  * access to \ref GameSettings. This allows pluggable behaviour in menus,
181  * depending on whether the menu was called for a singleplayer game or
182  * multiplayer game.
183  *
184  * Think of it as a mix of Model and Controller in MVC.
185  */
186 struct GameSettingsProvider {
~GameSettingsProviderGameSettingsProvider187 	virtual ~GameSettingsProvider() {
188 	}
189 
190 	virtual const GameSettings& settings() = 0;
191 
192 	virtual void set_scenario(bool set) = 0;
193 	virtual bool can_change_map() = 0;
194 	virtual bool can_change_player_state(uint8_t number) = 0;
195 	virtual bool can_change_player_tribe(uint8_t number) = 0;
196 	virtual bool can_change_player_init(uint8_t number) = 0;
197 	virtual bool can_change_player_team(uint8_t number) = 0;
198 
199 	virtual bool can_launch() = 0;
200 
201 	virtual void set_map(const std::string& mapname,
202 	                     const std::string& mapfilename,
203 	                     uint32_t maxplayers,
204 	                     bool savegame = false) = 0;
205 	virtual void set_player_state(uint8_t number, PlayerSettings::State) = 0;
206 	virtual void set_player_ai(uint8_t number, const std::string&, bool const random_ai = false) = 0;
207 	// Multiplayer no longer toggles per button
next_player_stateGameSettingsProvider208 	virtual void next_player_state(uint8_t /* number */) {
209 	}
210 	virtual void
211 	set_player_tribe(uint8_t number, const std::string&, bool const random_tribe = false) = 0;
212 	virtual void set_player_init(uint8_t number, uint8_t index) = 0;
213 	virtual void set_player_name(uint8_t number, const std::string&) = 0;
214 	virtual void set_player(uint8_t number, const PlayerSettings&) = 0;
215 	virtual void set_player_number(uint8_t number) = 0;
216 	virtual void set_player_team(uint8_t number, Widelands::TeamNumber team) = 0;
217 	virtual void set_player_closeable(uint8_t number, bool closeable) = 0;
218 	virtual void set_player_shared(PlayerSlot number, Widelands::PlayerNumber shared) = 0;
219 	virtual void set_win_condition_script(const std::string& wc) = 0;
220 	virtual std::string get_win_condition_script() = 0;
221 
222 	virtual void set_peaceful_mode(bool peace) = 0;
223 	virtual bool is_peaceful_mode() = 0;
224 
has_players_tribeGameSettingsProvider225 	bool has_players_tribe() {
226 		return UserSettings::highest_playernum() >= settings().playernum;
227 	}
228 	// For retrieving tips texts
229 	struct NoTribe {};
get_players_tribeGameSettingsProvider230 	const std::string& get_players_tribe() {
231 		if (!has_players_tribe())
232 			throw NoTribe();
233 		return settings().players[settings().playernum].tribe;
234 	}
235 };
236 
237 #endif  // end of include guard: WL_LOGIC_GAME_SETTINGS_H
238