xoreos  0.0.5
campaign.cpp
Go to the documentation of this file.
1 /* xoreos - A reimplementation of BioWare's Aurora engine
2  *
3  * xoreos is the legal property of its developers, whose names
4  * can be found in the AUTHORS file distributed with this source
5  * distribution.
6  *
7  * xoreos is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 3
10  * of the License, or (at your option) any later version.
11  *
12  * xoreos is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with xoreos. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
25 #include <cassert>
26 
27 #include <boost/scope_exit.hpp>
28 
29 #include "src/common/util.h"
30 #include "src/common/error.h"
31 #include "src/common/configman.h"
32 #include "src/common/readfile.h"
33 #include "src/common/filepath.h"
34 #include "src/common/filelist.h"
35 
36 #include "src/aurora/resman.h"
37 #include "src/aurora/language.h"
38 #include "src/aurora/gff3file.h"
39 
40 #include "src/graphics/camera.h"
41 
42 #include "src/events/events.h"
43 
47 
51 
52 namespace Engines {
53 
54 namespace NWN2 {
55 
56 Campaign::Campaign(::Engines::Console &console) : _console(&console),
57  _hasCampaign(false), _running(false), _exit(true), _newCampaignStandalone(false) {
58 
59  _module.reset(new Module(*_console));
60 }
61 
63  try {
64  clear();
65  } catch (...) {
66  }
67 }
68 
70  unload(true);
71 }
72 
74  return _name;
75 }
76 
78  return _description;
79 }
80 
82  assert(_module);
83 
84  return *_module;
85 }
86 
87 bool Campaign::isLoaded() const {
88  return _hasCampaign && _module->isLoaded() && _pc;
89 }
90 
91 bool Campaign::isRunning() const {
92  return !EventMan.quitRequested() && _running && !_exit && _module->isRunning();
93 }
94 
95 void Campaign::unload(bool completeUnload) {
96  _module->clear();
97 
98  _hasCampaign = false;
99  _running = false;
100  _exit = true;
101 
102  _name.clear();
104 
105  _modules.clear();
107 
109  _newCampaignStandalone = false;
110 
111  _eventQueue.clear();
112 
113  clearVariables();
114 
115  if (completeUnload)
116  unloadPC();
117 
119 }
120 
122  _pc.reset();
123 }
124 
125 void Campaign::load(const Common::UString &campaign) {
126  if (isRunning()) {
127  // We are currently running a campaign. Schedule a safe change instead
128 
129  changeCampaign(campaign, false);
130  return;
131  }
132 
133  // We are not currently running a campaign. Directly load the new campaign
134  loadCampaign(campaign, false);
135 }
136 
138  if (isRunning()) {
139  // We are currently running a campaign. Schedule a safe change instead
140 
141  changeCampaign(module, true);
142  return;
143  }
144 
145  // We are not currently running a campaign. Directly load the new campaign
146  loadCampaign(module, true);
147 }
148 
149 void Campaign::usePC(const Common::UString &bic, bool local) {
150  unloadPC();
151 
152  if (bic.empty())
153  throw Common::Exception("Tried to load an empty PC");
154 
155  try {
156  _pc.reset(new Creature(bic, local));
157  } catch (Common::Exception &e) {
158  e.add("Can't load PC \"%s\"", bic.c_str());
159  throw e;
160  }
161 
162  LangMan.setCurrentGender(_pc->isFemale() ? Aurora::kLanguageGenderFemale : Aurora::kLanguageGenderMale);
163 }
164 
166  _exit = true;
167 }
168 
170  const Common::UString directory = getDirectory(campaign, true);
171  if (directory.empty())
172  throw Common::Exception("No such campaign \"%s\"", campaign.c_str());
173 
174  bool success = false;
175  BOOST_SCOPE_EXIT( (&success) (this_) ) {
176  if (!success)
177  this_->clear();
178  } BOOST_SCOPE_EXIT_END
179 
180  indexMandatoryDirectory(directory, 0, -1, 1000, &_resCampaign);
181 
183  try {
184  gff.reset(new Aurora::GFF3File("campaign", Aurora::kFileTypeCAM, MKTAG('C', 'A', 'M', ' ')));
185  } catch (Common::Exception &e) {
186  e.add("Failed to load campaign information file");
187  throw;
188  }
189 
190  if (!gff->getTopLevel().hasField("ModNames") || !gff->getTopLevel().hasField("StartModule"))
191  throw Common::Exception("Campaign information file is missing modules");
192 
193  _startModule = gff->getTopLevel().getString("StartModule") + ".mod";
194 
195  const Aurora::GFF3List &modules = gff->getTopLevel().getList("ModNames");
196  for (Aurora::GFF3List::const_iterator m = modules.begin(); m != modules.end(); ++m)
197  _modules.push_back((*m)->getString("ModuleName") + ".mod");
198 
199  _name = gff->getTopLevel().getString("DisplayName");
200  _description = gff->getTopLevel().getString("Description");
201 
202  success = true;
203 }
204 
206  _modules.push_back(module);
207 
208  _startModule = module;
209 }
210 
211 void Campaign::loadCampaign(const Common::UString &campaign, bool standalone) {
212  unload(false);
213 
214  if (!standalone)
215  loadCampaignResource(campaign);
216  else
217  setupStandaloneModule(campaign);
218 
219  try {
220  _module->load(_startModule);
221  } catch (Common::Exception &e) {
222  clear();
223 
224  e.add("Failed to load campaign's starting module");
225  throw;
226  }
227 
228  _hasCampaign = true;
229 }
230 
232  if (!_hasCampaign)
233  throw Common::Exception("Campaign::enter(): Lacking a campaign?!?");
234 
235  if (!_pc)
236  throw Common::Exception("Campaign::enter(): Lacking a PC?!?");
237 
238  _pc->clearVariables();
239  _module->enter(*_pc);
240 
241  _running = true;
242  _exit = false;
243 }
244 
246  _module->leave();
247 
248  _running = false;
249  _exit = true;
250 }
251 
252 void Campaign::addEvent(const Events::Event &event) {
253  _eventQueue.push_back(event);
254 }
255 
257  if (!isRunning())
258  return;
259 
260  replaceCampaign();
261 
262  if (!isRunning())
263  return;
264 
265  handleEvents();
266 }
267 
269  for (EventQueue::const_iterator event = _eventQueue.begin(); event != _eventQueue.end(); ++event) {
270  // Handle console
271  if (_console->isVisible()) {
272  _console->processEvent(*event);
273  continue;
274  }
275 
276  if (event->type == Events::kEventKeyDown) {
277  // Console
278  if ((event->key.keysym.sym == SDLK_d) && (event->key.keysym.mod & KMOD_CTRL)) {
279  _console->show();
280  continue;
281  }
282  }
283 
284  // Camera
285  if (FreeRoamCam.handleCameraInput(*event))
286  continue;
287 
288  _module->addEvent(*event);
289  }
290 
291  _eventQueue.clear();
292 
293  CameraMan.update();
294 
295  _module->processEventQueue();
296 }
297 
298 void Campaign::changeCampaign(const Common::UString &campaign, bool standalone) {
299  _newCampaign = campaign;
300  _newCampaignStandalone = standalone;
301 }
302 
304  if (_newCampaign.empty())
305  return;
306 
307  const Common::UString campaign = _newCampaign;
308  const bool standalone = _newCampaignStandalone;
309 
310  loadCampaign(campaign, standalone);
311  enter();
312 }
313 
314 Common::UString Campaign::getDirectory(const Common::UString &campaign, bool relative) {
315  const Common::UString campaignsDir = ConfigMan.getString("NWN2_campaignDir");
316  const Common::UString campaignDir = Common::FilePath::findSubDirectory(campaignsDir, campaign, true);
317 
318  if (!relative)
319  return campaignDir;
320 
321  return Common::FilePath::relativize(ResMan.getDataBase(), campaignDir);
322 }
323 
325  try {
326  const Common::FileList camFiles(getDirectory(campaign, false));
327  const Common::UString camFile (camFiles.findFirst("campaign.cam", true));
328 
329  Aurora::GFF3File cam(new Common::ReadFile(camFile), MKTAG('C', 'A', 'M', ' '));
330 
331  return cam.getTopLevel().getString("DisplayName");
332 
333  } catch (...) {
334  }
335 
336  return "";
337 }
338 
340  try {
341  const Common::FileList camFiles(getDirectory(campaign, false));
342  const Common::UString camFile (camFiles.findFirst("campaign.cam", true));
343 
344  Aurora::GFF3File cam(new Common::ReadFile(camFile), MKTAG('C', 'A', 'M', ' '));
345 
346  return cam.getTopLevel().getString("Description");
347 
348  } catch (...) {
349  }
350 
351  return "";
352 }
353 
354 } // End of namespace NWN2
355 
356 } // End of namespace Engines
Handling version V3.2/V3.3 of BioWare&#39;s GFFs (generic file format).
#define ResMan
Shortcut for accessing the sound manager.
Definition: resman.h:557
Common::UString _startModule
The module the current campaign starts in.
Definition: campaign.h:118
#define MKTAG(a0, a1, a2, a3)
A wrapper macro used around four character constants, like &#39;DATA&#39;, to ensure portability.
Definition: endianness.h:140
Generic Aurora engines resource utility functions.
void add(const char *s,...) GCC_PRINTF(2
Definition: error.cpp:58
bool isVisible() const
Definition: console.cpp:797
void exit()
Exit the currently running campaign.
Definition: campaign.cpp:165
void unload(bool completeUnload=true)
Unload the whole shebang.
Definition: campaign.cpp:95
A class holding an UTF-8 string.
Definition: ustring.h:48
void reset(PointerType o=0)
Resets the pointer with the new value.
Definition: scopedptr.h:87
The global config manager.
::Engines::Console * _console
Definition: campaign.h:104
bool _hasCampaign
Do we have a campaign?
Definition: campaign.h:106
Common::ScopedPtr< Module > _module
The current module of the current campaign.
Definition: campaign.h:124
The context holding a Neverwinter Nights 2 campaign.
void changeCampaign(const Common::UString &campaign, bool standalone)
Schedule a change to a new campaign.
Definition: campaign.cpp:298
Camera management.
A simple streaming file reading class.
Definition: readfile.h:40
std::list< Common::UString > _modules
All modules used by the current campaign.
Definition: campaign.h:116
void loadCampaign(const Common::UString &campaign, bool standalone)
Load a new campaign.
Definition: campaign.cpp:211
Exception that provides a stack of explanations.
Definition: error.h:36
#define FreeRoamCam
void loadCampaignResource(const Common::UString &campaign)
Load the actual campaign resources.
Definition: campaign.cpp:169
SDL_Event Event
Definition: types.h:42
void deindexResources(Common::ChangeID &changeID)
Remove previously added resources from the ResourceManager.
Definition: resources.cpp:164
UString findFirst(const UString &str, bool caseInsensitive) const
Find the first file ending with the given string.
Definition: filelist.cpp:193
void loadModule(const Common::UString &module)
Load a stand-alone module as a campaign.
Definition: campaign.cpp:137
Keyboard key was pressed.
Definition: types.h:46
The context needed to run a Neverwinter Nights 2 module.
Basic exceptions to throw.
void addEvent(const Events::Event &event)
Add a single event for consideration into the event queue.
Definition: campaign.cpp:252
A creature in a Neverwinter Nights 2 area.
const char * c_str() const
Return the (utf8 encoded) string data.
Definition: ustring.cpp:249
void processEventQueue()
Process the current event queue.
Definition: campaign.cpp:256
#define ConfigMan
Shortcut for accessing the config manager.
Definition: configman.h:176
Campaign information.
Definition: types.h:169
bool _newCampaignStandalone
Is the campaign to change to a stand-alone module?
Definition: campaign.h:132
Utility templates and functions.
const Common::UString & getDescription() const
Return the description of the current campaign.
Definition: campaign.cpp:77
void load(const Common::UString &campaign)
Load a campaign.
Definition: campaign.cpp:125
Campaign(::Engines::Console &console)
Definition: campaign.cpp:56
The global events manager.
Module & getModule()
Return the currently running module.
Definition: campaign.cpp:81
A GFF (generic file format) V3.2/V3.3 file, found in all Aurora games except Sonic Chronicles: The Da...
Definition: gff3file.h:85
Types and functions related to language.
void leave()
Leave the running campaign, quitting it.
Definition: campaign.cpp:245
Common::UString _name
The name of the currently loaded campaign.
Definition: campaign.h:111
const Common::UString & getName() const
Return the name of the current campaign.
Definition: campaign.cpp:73
bool empty() const
Is the string empty?
Definition: ustring.cpp:245
Common::UString _newCampaign
The campaign we should change to.
Definition: campaign.h:130
Generic Aurora engines (debug) console.
void setupStandaloneModule(const Common::UString &module)
Set up the loading of a singular, stand-alone module.
Definition: campaign.cpp:205
StackException Exception
Definition: error.h:59
void replaceCampaign()
Actually replace the currently running campaign.
Definition: campaign.cpp:303
std::vector< const GFF3Struct * > GFF3List
Definition: types.h:449
#define EventMan
Shortcut for accessing the events manager.
Definition: events.h:210
Common::ChangeID _resCampaign
Resources added by the campaign.
Definition: campaign.h:121
Implementing the stream reading interfaces for files.
bool _running
Are we currently running a campaign?
Definition: campaign.h:107
void indexMandatoryDirectory(const Common::UString &dir, const char *glob, int depth, uint32 priority, Common::ChangeID *changeID)
Add a directory to the resource manager, erroring out if it does not exist.
Definition: resources.cpp:112
static Common::UString getDirectory(const Common::UString &campaign, bool relative)
Return the actual real directory for this campaign.
Definition: campaign.cpp:314
#define LangMan
Shortcut for accessing the language manager.
Definition: language.h:275
Common::ScopedPtr< Creature > _pc
The player character we use.
Definition: campaign.h:127
static UString relativize(const UString &basePath, const UString &path)
Return the path relative to the base path.
Definition: filepath.cpp:142
A list of files.
Definition: filelist.h:35
bool _exit
Should we exit the campaign?
Definition: campaign.h:108
bool processEvent(const Events::Event &event)
Definition: console.cpp:817
#define CameraMan
Shortcut for accessing the camera manager.
Definition: camera.h:83
A list of files.
void usePC(const Common::UString &bic, bool local)
Use this character as the player character.
Definition: campaign.cpp:149
void clear()
Clear the whole context.
Definition: campaign.cpp:69
bool isRunning() const
Is a campaign currently running?
Definition: campaign.cpp:91
Engine utility class for free-roam camera handling.
bool isLoaded() const
Is a campaign currently loaded and ready to run?
Definition: campaign.cpp:87
void clear()
Clear the string&#39;s contents.
Definition: ustring.cpp:236
void enter()
Enter the loaded campaign, starting it.
Definition: campaign.cpp:231
Common::UString _description
The description of the currently loaded campaign.
Definition: campaign.h:113
The global resource manager for Aurora resources.
Utility class for manipulating file paths.
static UString findSubDirectory(const UString &directory, const UString &subDirectory, bool caseInsensitive=false)
Find a directory&#39;s subdirectory.
Definition: filepath.cpp:318
EventQueue _eventQueue
Definition: campaign.h:134