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