1 /******************************************************************************
2 * Warmux is a convivial mass murder game.
3 * Copyright (C) 2001-2011 Warmux Team.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18 ******************************************************************************
19 * Warmux configuration : all main configuration variables have there default
20 * value here. They should all be modifiable using the xml config file
21 *****************************************************************************/
22
23 #include <iostream>
24 #include <cstdio>
25 #include <WARMUX_file_tools.h>
26 #include "game/config.h"
27 #include "game/game.h"
28 #include "game/game_mode.h"
29 #include "object/medkit.h"
30 #include "object/bonus_box.h"
31 #include "weapon/weapons_list.h"
32
GameMode()33 GameMode::GameMode():
34 doc_objects(NULL),
35 weapons_xml(NULL)
36 {
37 m_current = "classic";
38
39 LoadDefaultValues();
40 }
41
LoadDefaultValues()42 void GameMode::LoadDefaultValues()
43 {
44 rules = "none";
45 nb_characters = 6;
46 max_teams = 8;
47 duration_turn = 60;
48 duration_move_player = 3;
49 duration_exchange_player = 2;
50 duration_before_death_mode = 20 * 60;
51 damage_per_turn_during_death_mode = 5;
52 gravity = 9.81;
53 safe_fall = 10;
54 damage_per_fall_unit = 7;
55
56 character.init_energy = 100;
57 character.max_energy = 100;
58 character.mass = 100;
59 character.air_resist_factor = 1.0;
60 character.jump_strength = 8;
61 character.jump_angle = -60;
62 character.super_jump_strength = 11;
63 character.super_jump_angle = -80;
64 character.back_jump_strength = 9;
65 character.back_jump_angle = -100;
66 character.walking_pause = 50;
67
68 auto_change_character = true;
69
70 allow_character_selection = BEFORE_FIRST_ACTION;
71
72 if (doc_objects)
73 delete doc_objects;
74
75 doc_objects = new XmlReader();
76 }
77
~GameMode()78 GameMode::~GameMode()
79 {
80 delete doc_objects;
81 }
82
83 // Load data options from the selected game_mode
LoadXml(const xmlNode * xml)84 bool GameMode::LoadXml(const xmlNode* xml)
85 {
86 bool r;
87
88 r = XmlReader::ReadString(xml, "rules", rules);
89 if (!r) {
90 fprintf(stderr, "Game mode: missing <rules>\n");
91 return false;
92 }
93
94 XmlReader::ReadBool(xml, "auto_change_character", auto_change_character);
95
96 std::string txt;
97 if (XmlReader::ReadString(xml, "allow_character_selection", txt))
98 {
99 if (txt == "always")
100 allow_character_selection = ALWAYS;
101 else if (txt == "never")
102 allow_character_selection = NEVER;
103 else if (txt == "before_first_action")
104 allow_character_selection = BEFORE_FIRST_ACTION;
105 else
106 fprintf(stderr, "%s is not a valid option for \"allow_character_selection\"\n", txt.c_str());
107 }
108
109 XmlReader::ReadUint(xml, "duration_turn", duration_turn);
110 XmlReader::ReadUint(xml, "duration_move_player", duration_move_player);
111 XmlReader::ReadUint(xml, "duration_exchange_player", duration_exchange_player);
112 XmlReader::ReadUint(xml, "duration_before_death_mode", duration_before_death_mode);
113 XmlReader::ReadUint(xml, "damage_per_turn_during_death_mode", damage_per_turn_during_death_mode);
114 XmlReader::ReadUint(xml, "max_teams", max_teams);
115 XmlReader::ReadUint(xml, "nb_characters", nb_characters);
116 XmlReader::ReadDouble(xml, "gravity", gravity);
117 XmlReader::ReadDouble(xml, "safe_fall", safe_fall);
118 XmlReader::ReadDouble(xml, "damage_per_fall_unit", damage_per_fall_unit);
119
120 // Character options
121 const xmlNode* character_xml = XmlReader::GetMarker(xml, "character");
122 if (character_xml) {
123 const xmlNode* item = XmlReader::GetMarker(character_xml, "energy");
124 if (item) {
125 XmlReader::ReadUintAttr(item, "initial", character.init_energy);
126 XmlReader::ReadUintAttr(item, "maximum", character.max_energy);
127 if (character.init_energy==0) character.init_energy = 1;
128 if (character.max_energy==0) character.max_energy = 1;
129 }
130 XmlReader::ReadUint(character_xml, "mass", character.mass);
131 XmlReader::ReadDouble(character_xml, "air_resist_factor", character.air_resist_factor);
132 item = XmlReader::GetMarker(character_xml, "jump");
133 if (item) {
134 Double angle_deg;
135 XmlReader::ReadDoubleAttr(item, "strength", character.jump_strength);
136 XmlReader::ReadDoubleAttr(item, "angle", angle_deg);
137 character.jump_angle = static_cast<Double>(angle_deg) * PI / 180;
138 }
139
140 item = XmlReader::GetMarker(character_xml, "super_jump");
141 if (item) {
142 Double angle_deg;
143 XmlReader::ReadDoubleAttr(item, "strength", character.super_jump_strength);
144 XmlReader::ReadDoubleAttr(item, "angle", angle_deg);
145 character.super_jump_angle = static_cast<Double>(angle_deg) * PI / 180;
146 }
147 item = XmlReader::GetMarker(character_xml, "back_jump");
148 if (item) {
149 Double angle_deg;
150 XmlReader::ReadDoubleAttr(item, "strength", character.back_jump_strength);
151 XmlReader::ReadDoubleAttr(item, "angle", angle_deg);
152 character.back_jump_angle = static_cast<Double>(angle_deg) * PI / 180;
153 }
154 XmlReader::ReadUint(character_xml, "walking_pause", character.walking_pause);
155 const xmlNode* explosion = XmlReader::GetMarker(character_xml, "death_explosion");
156 if (explosion)
157 death_explosion_cfg.LoadXml(explosion);
158 }
159
160 // Barrel explosion
161 const xmlNode* barrel_xml = XmlReader::GetMarker(xml, "barrel");
162 if (barrel_xml) {
163 const xmlNode* barrel_explosion = XmlReader::GetMarker(barrel_xml, "explosion");
164 if (barrel_explosion)
165 barrel_explosion_cfg.LoadXml(barrel_explosion);
166 }
167
168 //=== Weapons ===
169 weapons_xml = XmlReader::GetMarker(xml, "weapons");
170
171 // Bonus box explosion - must be loaded after the weapons.
172 const xmlNode* bonus_box_xml = XmlReader::GetMarker(xml, "bonus_box");
173 if (bonus_box_xml) {
174 BonusBox::LoadXml(bonus_box_xml);
175
176 const xmlNode* bonus_box_explosion = XmlReader::GetMarker(bonus_box_xml, "explosion");
177 if (bonus_box_explosion)
178 bonus_box_explosion_cfg.LoadXml(bonus_box_explosion);
179 }
180
181 // Medkit - reuses the bonus_box explosion.
182 const xmlNode* medkit_xml = XmlReader::GetMarker(xml, "medkit");
183 if (medkit_xml) {
184 Medkit::LoadXml(medkit_xml);
185 }
186
187 return true;
188 }
189
Load(void)190 bool GameMode::Load(void)
191 {
192 Config * config = Config::GetInstance();
193 m_current = config->GetGameMode();
194
195 LoadDefaultValues();
196
197 // Game mode objects configuration file
198 if (!doc_objects->Load(GetObjectsFilename()))
199 return false;
200
201
202 if (!doc.Load(GetFilename()))
203 return false;
204 if (!LoadXml(doc.GetRoot()))
205 return false;
206
207 return true;
208 }
209
210 // Load the game mode from strings (probably from network)
LoadFromString(const std::string & game_mode_name,const std::string & game_mode_contents,const std::string & game_mode_objects_contents)211 bool GameMode::LoadFromString(const std::string& game_mode_name,
212 const std::string& game_mode_contents,
213 const std::string& game_mode_objects_contents)
214 {
215 m_current = game_mode_name;
216 MSG_DEBUG("game_mode", "Loading %s from network: ", m_current.c_str());
217
218 if (!doc_objects->LoadFromString(game_mode_objects_contents))
219 return false;
220
221 if (!doc.LoadFromString(game_mode_contents))
222 return false;
223 if (!LoadXml(doc.GetRoot()))
224 return false;
225
226 MSG_DEBUG("game_mode", "OK\n");
227 return true;
228 }
229
ExportFileToString(const std::string & filename,std::string & contents) const230 bool GameMode::ExportFileToString(const std::string& filename, std::string& contents) const
231 {
232 contents = "";
233
234 XmlReader doc;
235 if (!doc.Load(filename))
236 return false;
237
238 contents = doc.ExportToString();
239
240 return true;
241 }
242
ExportToString(std::string & mode,std::string & mode_objects) const243 bool GameMode::ExportToString(std::string& mode,
244 std::string& mode_objects) const
245 {
246 #if 0
247 bool r;
248
249 r = ExportFileToString(GetFilename(), mode);
250 if (r) {
251 r = ExportFileToString(GetObjectsFilename(), mode_objects);
252 }
253
254 return r;
255 #else
256 mode_objects = doc_objects->ExportToString();
257 mode = doc.ExportToString();
258 return !mode_objects.empty() && mode.empty();
259 #endif
260 }
261
AllowCharacterSelection() const262 bool GameMode::AllowCharacterSelection() const
263 {
264 switch (allow_character_selection)
265 {
266 case GameMode::ALWAYS:
267 break;
268
269 case GameMode::BEFORE_FIRST_ACTION:
270 return (Game::GetInstance()->ReadState() == Game::PLAYING) && !Game::GetInstance()->IsCharacterAlreadyChosen();
271
272 case GameMode::NEVER:
273 return false;
274 }
275
276 return true;
277 }
278
GetFilename() const279 std::string GameMode::GetFilename() const
280 {
281 Config * config = Config::GetInstance();
282 std::string filename = std::string("game_mode" PATH_SEPARATOR)
283 + m_current
284 + std::string(".xml");
285
286 std::string fullname = config->GetPersonalDataDir() + filename;
287
288 if (!DoesFileExist(fullname))
289 fullname = config->GetDataDir() + filename;
290
291 if (!DoesFileExist(fullname)) {
292 Error(Format("Can not find file %s", fullname.c_str()));
293 }
294
295 return fullname;
296 }
297
GetDefaultObjectsFilename() const298 std::string GameMode::GetDefaultObjectsFilename() const
299 {
300 std::string filename("game_mode" PATH_SEPARATOR "default_objects.xml");
301
302 return filename;
303 }
304
GetObjectsFilename() const305 std::string GameMode::GetObjectsFilename() const
306 {
307 Config * config = Config::GetInstance();
308 std::string filename = std::string("game_mode" PATH_SEPARATOR)
309 + m_current
310 + std::string("_objects.xml");
311
312 std::string fullname = config->GetPersonalDataDir() + filename;
313
314 if (!DoesFileExist(fullname))
315 fullname = config->GetDataDir() + filename;
316
317 if (!DoesFileExist(fullname)) {
318 std::cerr << "Game mode: File " << fullname
319 << " does not exist, use the default one instead." << std::endl;
320 }
321
322 fullname = config->GetDataDir() + GetDefaultObjectsFilename();
323 if (!DoesFileExist(fullname)) {
324 Error(Format("Can not find file %s", fullname.c_str()));
325 }
326
327 return fullname;
328 }
329
330 // Static method
ListGameModes()331 std::vector<std::pair<std::string, std::string> > GameMode::ListGameModes()
332 {
333 std::vector<std::pair<std::string, std::string> > game_modes;
334 game_modes.push_back(std::pair<std::string, std::string>("classic", _("Classic")));
335 game_modes.push_back(std::pair<std::string, std::string>("unlimited", _("Unlimited")));
336 game_modes.push_back(std::pair<std::string, std::string>("blitz", _("Blitz")));
337 #ifdef DEBUG
338 game_modes.push_back(std::pair<std::string, std::string>("skin_viewer", "Skin Viewer"));
339 #endif
340
341 std::string personal_dir = Config::GetInstance()->GetPersonalDataDir() +
342 std::string("game_mode" PATH_SEPARATOR);
343
344 FolderSearch *f = OpenFolder(personal_dir);
345 if (f) {
346 bool is_file = true;
347 const char *name;
348 while ((name = FolderSearchNext(f, is_file)) != NULL) {
349
350 // Only check files
351 if (is_file) {
352 std::string filename(name);
353
354 if (filename.size() >= 5
355 && filename.compare(filename.size()-4, 4, ".xml") == 0
356 && (filename.size() < 12
357 || filename.compare(filename.size()-12, 12, "_objects.xml") != 0)) {
358
359 std::string game_mode_name = filename.substr(0, filename.size()-4);
360 game_modes.push_back(std::pair<std::string, std::string>(game_mode_name, game_mode_name));
361 }
362 }
363
364 // Prepare again for searching files
365 is_file = true;
366 }
367 CloseFolder(f);
368 }
369
370 return game_modes;
371 }
372