5 * Created by Alyssa Milburn on Tue Nov 28 2006.
6 * Copyright (c) 2006-2008 Alyssa Milburn. All rights reserved.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
24 #include "caosVM.h" // for setupCommandPointers()
25 #include "caosScript.h" // for executeNetwork()
26 #include "PointerAgent.h"
27 #include "dialect.h" // registerDelegates
28 #include "NullBackend.h"
29 #include "NullAudioBackend.h"
31 #include <boost/filesystem/path.hpp>
32 #include <boost/filesystem/operations.hpp>
33 #include <boost/filesystem/convenience.hpp>
34 #include <boost/program_options.hpp>
35 #include <boost/format.hpp>
36 namespace fs
= boost::filesystem
;
37 namespace po
= boost::program_options
;
40 #include <sys/types.h> // passwd*
41 #include <pwd.h> // getpwuid
54 refreshdisplay
= false;
59 for (unsigned int i
= 0; i
< 10; i
++) ticktimes
[i
] = 0;
61 version
= 0; // TODO: something something
63 srand(time(NULL
)); // good a place as any :)
65 cmdline_enable_sound
= true;
66 cmdline_norun
= false;
70 addPossibleBackend("null", shared_ptr
<Backend
>(new NullBackend()));
71 addPossibleAudioBackend("null", shared_ptr
<AudioBackend
>(new NullAudioBackend()));
77 void Engine::addPossibleBackend(std::string s
, boost::shared_ptr
<Backend
> b
) {
80 preferred_backend
= s
;
81 possible_backends
[s
] = b
;
84 void Engine::addPossibleAudioBackend(std::string s
, boost::shared_ptr
<AudioBackend
> b
) {
87 preferred_audiobackend
= s
;
88 possible_audiobackends
[s
] = b
;
91 void Engine::setBackend(shared_ptr
<Backend
> b
) {
93 lasttimestamp
= backend
->ticks();
95 // load palette for C1
96 if (world
.gametype
== "c1") {
97 // TODO: case-sensitivity for the lose
98 fs::path
palpath(world
.data_directories
[0] / "/Palettes/palette.dta");
99 if (fs::exists(palpath
) && !fs::is_directory(palpath
)) {
100 palette
= new unsigned char[768];
102 std::ifstream
f(palpath
.native_directory_string().c_str(), std::ios::binary
);
104 f
.read((char *)palette
, 768);
106 for (unsigned int i
= 0; i
< 768; i
++) {
107 palette
[i
] = palette
[i
] * 4;
110 backend
->setPalette((uint8
*)palette
);
112 throw creaturesException("Couldn't find C1 palette data!");
116 std::string
Engine::executeNetwork(std::string in
) {
117 // now parse and execute the CAOS we obtained
118 caosVM
vm(0); // needs to be outside 'try' so we can reset outputstream on exception
120 std::istringstream
s(in
);
121 caosScript
script(world
.gametype
, "<network>"); // XXX
123 script
.installScripts();
124 std::ostringstream o
;
125 vm
.setOutputStream(o
);
126 vm
.runEntirely(script
.installer
);
127 vm
.outputstream
= 0; // otherwise would point to dead stack
129 } catch (std::exception
&e
) {
130 vm
.outputstream
= 0; // otherwise would point to dead stack
131 return std::string("### EXCEPTION: ") + e
.what();
135 bool Engine::needsUpdate() {
136 return (!world
.paused
) && (fastticks
|| (backend
->ticks() > (tickdata
+ world
.ticktime
)));
139 void Engine::update() {
140 tickdata
= backend
->ticks();
146 if (dorendering
|| refreshdisplay
) {
147 refreshdisplay
= false;
149 if (backend
->selfRender()) {
150 // TODO: this makes race/pace hilariously inaccurate, since render time isn't included
151 backend
->requestRender();
158 // TODO: this doesn't seem to actually be every 7 seconds, but actually somewhat random
159 // TODO: this should be linked to 'real' time, so it doesn't go crazy when game speed is modified
160 // TODO: is this the right place for this?
161 if (version
== 1 && (world
.tickcount
% 70) == 0) {
162 int piece
= 1 + (rand() % 28);
163 std::string filename
= boost::str(boost::format("MU%02d") % piece
);
164 boost::shared_ptr
<AudioSource
> s
= world
.playAudio(filename
, AgentRef(), false, false);
165 if (s
) s
->setVolume(0.4f
);
168 // update our data for things like pace, race, ticktime, etc
169 ticktimes
[ticktimeptr
] = backend
->ticks() - tickdata
;
171 if (ticktimeptr
== 10) ticktimeptr
= 0;
173 for (unsigned int i
= 0; i
< 10; i
++) avgtime
+= ((float)ticktimes
[i
] / world
.ticktime
);
174 world
.pace
= avgtime
/ 10;
176 world
.race
= backend
->ticks() - lasttimestamp
;
177 lasttimestamp
= backend
->ticks();
180 bool Engine::tick() {
182 backend
->handleEvents();
184 // tick+draw the world, if necessary
185 bool needupdate
= needsUpdate();
191 handleKeyboardScrolling();
196 void Engine::handleKeyboardScrolling() {
197 // keyboard-based scrolling
198 static float accelspeed
= 8, decelspeed
= .5, maxspeed
= 64;
199 static float velx
= 0;
200 static float vely
= 0;
202 bool wasdMode
= false;
203 caosVar v
= world
.variables
["engine_wasd"];
205 switch (v
.getInt()) {
206 case 1: // enable if CTRL is held
207 wasdMode
= backend
->keyDown(17); // CTRL
209 case 2: // enable unconditionally
210 // (this needs agent support to suppress chat bubbles etc)
217 std::cout
<< "Warning: engine_wasd_scrolling is set to unknown value " << v
.getInt() << std::endl
;
218 world
.variables
["engine_wasd_scrolling"] = caosVar(0);
225 bool leftdown
= backend
->keyDown(37)
226 || (wasdMode
&& a_down
);
227 bool rightdown
= backend
->keyDown(39)
228 || (wasdMode
&& d_down
);
229 bool updown
= backend
->keyDown(38)
230 || (wasdMode
&& w_down
);
231 bool downdown
= backend
->keyDown(40)
232 || (wasdMode
&& s_down
);
238 if (!leftdown
&& !rightdown
) {
240 if (fabs(velx
) < 0.1) velx
= 0;
246 if (!updown
&& !downdown
) {
248 if (fabs(vely
) < 0.1) vely
= 0;
251 // enforced maximum speed
252 if (velx
>= maxspeed
) velx
= maxspeed
;
253 else if (velx
<= -maxspeed
) velx
= -maxspeed
;
254 if (vely
>= maxspeed
) vely
= maxspeed
;
255 else if (vely
<= -maxspeed
) vely
= -maxspeed
;
257 // do the actual movement
259 int adjustx
= world
.camera
.getX(), adjusty
= world
.camera
.getY();
260 int adjustbyx
= (int)velx
, adjustbyy
= (int) vely
;
262 world
.camera
.moveTo(adjustx
+ adjustbyx
, adjusty
+ adjustbyy
, jump
);
266 void Engine::processEvents() {
268 while (backend
->pollEvent(event
)) {
269 switch (event
.type
) {
270 case eventresizewindow
:
271 handleResizedWindow(event
);
275 handleMouseMove(event
);
278 case eventmousebuttonup
:
279 case eventmousebuttondown
:
280 handleMouseButton(event
);
284 handleKeyDown(event
);
287 case eventspecialkeydown
:
288 handleSpecialKeyDown(event
);
291 case eventspecialkeyup
:
292 handleSpecialKeyUp(event
);
305 void Engine::handleResizedWindow(SomeEvent
&event
) {
307 for (std::list
<boost::shared_ptr
<Agent
> >::iterator i
= world
.agents
.begin(); i
!= world
.agents
.end(); i
++) {
309 (*i
)->queueScript(123, 0); // window resized script
313 void Engine::handleMouseMove(SomeEvent
&event
) {
315 world
.hand()->moveTo(event
.x
+ world
.camera
.getX(), event
.y
+ world
.camera
.getY());
316 world
.hand()->velx
.setInt(event
.xrel
* 4);
317 world
.hand()->vely
.setInt(event
.yrel
* 4);
319 // middle mouse button scrolling
320 if (event
.button
& buttonmiddle
)
321 world
.camera
.moveTo(world
.camera
.getX() - event
.xrel
, world
.camera
.getY() - event
.yrel
, jump
);
324 for (std::list
<boost::shared_ptr
<Agent
> >::iterator i
= world
.agents
.begin(); i
!= world
.agents
.end(); i
++) {
326 if ((*i
)->imsk_mouse_move
) {
327 caosVar x
; x
.setFloat(world
.hand()->x
);
328 caosVar y
; y
.setFloat(world
.hand()->y
);
329 (*i
)->queueScript(75, 0, x
, y
); // Raw Mouse Move
334 void Engine::handleMouseButton(SomeEvent
&event
) {
336 for (std::list
<boost::shared_ptr
<Agent
> >::iterator i
= world
.agents
.begin(); i
!= world
.agents
.end(); i
++) {
338 if ((event
.type
== eventmousebuttonup
&& (*i
)->imsk_mouse_up
) ||
339 (event
.type
== eventmousebuttondown
&& (*i
)->imsk_mouse_down
)) {
340 // set the button value as necessary
342 switch (event
.button
) { // Backend guarantees that only one button will be set on a mousebuttondown event.
343 // the values here make fuzzie suspicious that c2e combines these events
344 // nornagon seems to think c2e doesn't
345 case buttonleft
: button
.setInt(1); break;
346 case buttonright
: button
.setInt(2); break;
347 case buttonmiddle
: button
.setInt(4); break;
351 // if it was a mouse button we're interested in, then fire the relevant raw event
352 if (button
.getInt() != 0) {
353 if (event
.type
== eventmousebuttonup
)
354 (*i
)->queueScript(77, 0, button
); // Raw Mouse Up
356 (*i
)->queueScript(76, 0, button
); // Raw Mouse Down
359 if ((event
.type
== eventmousebuttondown
&&
360 (event
.button
== buttonwheelup
|| event
.button
== buttonwheeldown
) &&
361 (*i
)->imsk_mouse_wheel
)) {
362 // fire the mouse wheel event with the relevant delta value
364 if (event
.button
== buttonwheeldown
)
368 (*i
)->queueScript(78, 0, delta
); // Raw Mouse Wheel
372 if (!world
.hand()->handle_events
) return;
373 if (event
.type
!= eventmousebuttondown
) return;
375 // do our custom handling
376 if (event
.button
== buttonleft
) {
377 CompoundPart
*a
= world
.partAt(world
.hand()->x
, world
.hand()->y
);
378 if (a
/* && a->canActivate() */) { // TODO
379 // if the agent isn't paused, tell it to handle a click
380 if (!a
->getParent()->paused
)
381 a
->handleClick(world
.hand()->x
- a
->x
- a
->getParent()->x
, world
.hand()->y
- a
->y
- a
->getParent()->y
);
383 // TODO: not sure how to handle the following properly, needs research..
385 if (engine
.version
< 3) {
390 world
.hand()->firePointerScript(eve
, a
->getParent()); // Pointer Activate 1
391 } else if (engine
.version
> 2)
392 world
.hand()->queueScript(116, 0); // Pointer Clicked Background
393 } else if (event
.button
== buttonright
) {
394 if (world
.paused
) return; // TODO: wrong?
396 // picking up and dropping are implictly handled by the scripts (well, messages) 4 and 5
397 // TODO: check if this is correct behaviour, one issue is that this isn't instant, another
398 // is the messages might only be fired in c2e when you use MESG WRIT, in which case we'll
399 // need to manually set world.hand()->carrying to NULL and a here, respectively - fuzzie
400 if (world
.hand()->carrying
) {
401 // TODO: c1 support - these attributes are invalid for c1
402 if (!world
.hand()->carrying
->suffercollisions() || (world
.hand()->carrying
->validInRoomSystem() || version
== 1)) {
403 world
.hand()->carrying
->queueScript(5, world
.hand()); // drop
405 int eve
; if (engine
.version
< 3) eve
= 54; else eve
= 105;
406 world
.hand()->firePointerScript(eve
, world
.hand()->carrying
); // Pointer Drop
408 // TODO: is this the correct check?
409 if (world
.hand()->carrying
->sufferphysics() && world
.hand()->carrying
->suffercollisions()) {
410 // TODO: do this in the pointer agent?
411 world
.hand()->carrying
->velx
.setFloat(world
.hand()->velx
.getFloat());
412 world
.hand()->carrying
->vely
.setFloat(world
.hand()->vely
.getFloat());
415 // TODO: some kind of "fail to drop" animation/sound?
418 Agent
*a
= world
.agentAt(world
.hand()->x
, world
.hand()->y
, false, true);
420 a
->queueScript(4, world
.hand()); // pickup
422 int eve
; if (engine
.version
< 3) eve
= 53; else eve
= 104;
423 world
.hand()->firePointerScript(eve
, a
); // Pointer Pickup
426 } else if (event
.button
== buttonmiddle
) {
427 std::vector
<shared_ptr
<Room
> > rooms
= world
.map
.roomsAt(event
.x
+ world
.camera
.getX(), event
.y
+ world
.camera
.getY());
428 if (rooms
.size() > 0) std::cout
<< "Room at cursor is " << rooms
[0]->id
<< std::endl
;
429 Agent
*a
= world
.agentAt(event
.x
+ world
.camera
.getX(), event
.y
+ world
.camera
.getY(), true);
431 std::cout
<< "Agent under mouse is " << a
->identify();
433 std::cout
<< "No agent under cursor";
434 std::cout
<< std::endl
;
438 void Engine::handleKeyDown(SomeEvent
&event
) {
440 case 'w': w_down
= true; break;
441 case 'a': a_down
= true; break;
442 case 's': s_down
= true; break;
443 case 'd': d_down
= true; break;
446 // tell the agent with keyboard focus
447 if (world
.focusagent
) {
448 TextEntryPart
*t
= (TextEntryPart
*)((CompoundAgent
*)world
.focusagent
.get())->part(world
.focuspart
);
450 t
->handleKey(event
.key
);
456 for (std::list
<boost::shared_ptr
<Agent
> >::iterator i
= world
.agents
.begin(); i
!= world
.agents
.end(); i
++) {
458 if ((*i
)->imsk_translated_char
)
459 (*i
)->queueScript(79, 0, k
); // translated char script
463 void Engine::handleSpecialKeyUp(SomeEvent
&event
) {
480 void Engine::handleSpecialKeyDown(SomeEvent
&event
) {
497 // handle debug keys, if they're enabled
498 caosVar v
= world
.variables
["engine_debug_keys"];
499 if (v
.hasInt() && v
.getInt() == 1) {
500 if (backend
->keyDown(16)) { // shift down
501 MetaRoom
*n
; // for pageup/pagedown
505 world
.showrooms
= !world
.showrooms
;
509 // TODO: debug pause game
517 // TODO: previous metaroom
518 if ((world
.map
.getMetaRoomCount() - 1) == world
.camera
.getMetaRoom()->id
)
520 n
= world
.map
.getMetaRoom(world
.camera
.getMetaRoom()->id
+ 1);
522 world
.camera
.goToMetaRoom(n
->id
);
526 // TODO: next metaroom
527 if (world
.camera
.getMetaRoom()->id
== 0)
529 n
= world
.map
.getMetaRoom(world
.camera
.getMetaRoom()->id
- 1);
531 world
.camera
.goToMetaRoom(n
->id
);
534 default: break; // to shut up warnings
539 // tell the agent with keyboard focus
540 if (world
.focusagent
) {
541 TextEntryPart
*t
= (TextEntryPart
*)((CompoundAgent
*)world
.focusagent
.get())->part(world
.focuspart
);
543 t
->handleSpecialKey(event
.key
);
549 for (std::list
<boost::shared_ptr
<Agent
> >::iterator i
= world
.agents
.begin(); i
!= world
.agents
.end(); i
++) {
551 if ((*i
)->imsk_key_down
)
552 (*i
)->queueScript(73, 0, k
); // key down script
556 static const char data_default
[] = "./data";
558 static void opt_version() {
559 // We already showed the primary version bit, just throw in some random legalese
561 "This is free software; see the source for copying conditions. There is NO" << std::endl
<<
562 "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." << std::endl
<< std::endl
<<
563 "...please don't sue us." << std::endl
;
566 bool Engine::parseCommandLine(int argc
, char *argv
[]) {
567 // variables for command-line flags
569 std::vector
<std::string
> data_vec
;
571 // generate help for backend options
572 std::string available_backends
;
573 for (std::map
<std::string
, boost::shared_ptr
<Backend
> >::iterator i
= possible_backends
.begin(); i
!= possible_backends
.end(); i
++) {
574 if (available_backends
.empty()) available_backends
= i
->first
;
575 else available_backends
+= ", " + i
->first
;
577 available_backends
= "Select the backend (options: " + available_backends
+ "), default is " + preferred_backend
;
579 std::string available_audiobackends
;
580 for (std::map
<std::string
, boost::shared_ptr
<AudioBackend
> >::iterator i
= possible_audiobackends
.begin(); i
!= possible_audiobackends
.end(); i
++) {
581 if (available_audiobackends
.empty()) available_audiobackends
= i
->first
;
582 else available_audiobackends
+= ", " + i
->first
;
584 available_audiobackends
= "Select the audio backend (options: " + available_audiobackends
+ "), default is " + preferred_audiobackend
;
586 // parse the command-line flags
587 po::options_description desc
;
589 ("help,h", "Display help on command-line options")
590 ("version,V", "Display openc2e version")
591 ("silent,s", "Disable all sounds")
592 ("backend,k", po::value
<std::string
>(&preferred_backend
)->composing(), available_backends
.c_str())
593 ("audiobackend,o", po::value
<std::string
>(&preferred_audiobackend
)->composing(), available_audiobackends
.c_str())
594 ("data-path,d", po::value
< std::vector
<std::string
> >(&data_vec
)->composing(),
595 "Sets or adds a path to a data directory")
596 ("bootstrap,b", po::value
< std::vector
<std::string
> >(&cmdline_bootstrap
)->composing(),
597 "Sets or adds a path or COS file to bootstrap from")
598 ("gametype,g", po::value
< std::string
>(&world
.gametype
), "Set the game type (c1, c2, cv or c3)")
599 ("gamename,m", po::value
< std::string
>(&gamename
), "Set the game name")
600 ("norun,n", "Don't run the game, just execute scripts")
601 ("autokill,a", "Enable autokill")
602 ("autostop", "Enable autostop (or disable it, for CV)")
604 po::variables_map vm
;
605 po::store(po::parse_command_line(argc
, argv
, desc
), vm
);
608 cmdline_enable_sound
= !vm
.count("silent");
609 cmdline_norun
= vm
.count("norun");
611 if (vm
.count("help")) {
612 std::cout
<< desc
<< std::endl
;
616 if (vm
.count("version")) {
621 if (vm
.count("autokill")) {
622 world
.autokill
= true;
625 if (vm
.count("autostop")) {
626 world
.autostop
= true;
629 if (vm
.count("data-path") == 0) {
630 std::cout
<< "Warning: No data path specified, trying default of '" << data_default
<< "', see --help if you need to specify one." << std::endl
;
631 data_vec
.push_back(data_default
);
634 // add all the data directories to the list
635 for (std::vector
<std::string
>::iterator i
= data_vec
.begin(); i
!= data_vec
.end(); i
++) {
636 fs::path
datadir(*i
, fs::native
);
637 if (!fs::exists(datadir
)) {
638 throw creaturesException("data path '" + *i
+ "' doesn't exist");
640 world
.data_directories
.push_back(datadir
);
643 // make a vague attempt at blacklisting some characters inside the gamename
644 // (it's used in directory names, registry keys, etc)
645 std::string invalidchars
= "\\/:*?\"<>|";
646 for (unsigned int i
= 0; i
< invalidchars
.size(); i
++) {
647 if (gamename
.find(invalidchars
[i
]) != gamename
.npos
)
648 throw creaturesException(std::string("The character ") + invalidchars
[i
] + " is not valid in a gamename.");
654 bool Engine::initialSetup() {
655 assert(world
.data_directories
.size() > 0);
657 // autodetect gametype if necessary
658 if (world
.gametype
.empty()) {
659 std::cout
<< "Warning: No gametype specified, ";
660 // TODO: is this sane? especially unsure about about.exe
661 if (!world
.findFile("Creatures.exe").empty()) {
662 std::cout
<< "found Creatures.exe, assuming C1 (c1)";
663 world
.gametype
= "c1";
664 } else if (!world
.findFile("Creatures2.exe").empty()) {
665 std::cout
<< "found Creatures2.exe, assuming C2 (c2)";
666 world
.gametype
= "c2";
667 } else if (!world
.findFile("Sea-Monkeys.ico").empty()) {
668 std::cout
<< "found Sea-Monkeys.ico, assuming Sea-Monkeys (sm)";
669 world
.gametype
= "sm";
670 } else if (!world
.findFile("about.exe").empty()) {
671 std::cout
<< "found about.exe, assuming CA, CP or CV (cv)";
672 world
.gametype
= "cv";
674 std::cout
<< "assuming C3/DS (c3)";
675 world
.gametype
= "c3";
677 std::cout
<< ", see --help if you need to specify one." << std::endl
;
680 // set engine version
681 // TODO: set gamename
682 if (world
.gametype
== "c1") {
683 if (gamename
.empty()) gamename
= "Creatures 1";
685 } else if (world
.gametype
== "c2") {
686 if (gamename
.empty()) gamename
= "Creatures 2";
688 } else if (world
.gametype
== "c3") {
689 if (gamename
.empty()) gamename
= "Creatures 3";
691 } else if (world
.gametype
== "cv") {
692 if (gamename
.empty()) gamename
= "Creatures Village";
694 world
.autostop
= !world
.autostop
;
695 } else if (world
.gametype
== "sm") {
696 if (gamename
.empty()) gamename
= "Sea-Monkeys";
700 throw creaturesException(boost::str(boost::format("unknown gametype '%s'!") % world
.gametype
));
702 // finally, add our cache directory to the end
703 world
.data_directories
.push_back(storageDirectory());
707 std::cout
<< "* Reading catalogue files..." << std::endl
;
708 world
.initCatalogue();
709 std::cout
<< "* Initial setup..." << std::endl
;
710 world
.init(); // just reads mouse cursor (we want this after the catalogue reading so we don't play "guess the filename")
711 if (engine
.version
> 2) {
712 std::cout
<< "* Reading PRAY files..." << std::endl
;
713 world
.praymanager
.update();
716 if (cmdline_norun
) preferred_backend
= "null";
717 if (preferred_backend
!= "null") std::cout
<< "* Initialising backend " << preferred_backend
<< "..." << std::endl
;
718 shared_ptr
<Backend
> b
= possible_backends
[preferred_backend
];
719 if (!b
) throw creaturesException("No such backend " + preferred_backend
);
720 b
->init(); setBackend(b
);
721 possible_backends
.clear();
723 if (cmdline_norun
|| !cmdline_enable_sound
) preferred_audiobackend
= "null";
724 if (preferred_audiobackend
!= "null") std::cout
<< "* Initialising audio backend " << preferred_audiobackend
<< "..." << std::endl
;
725 shared_ptr
<AudioBackend
> a
= possible_audiobackends
[preferred_audiobackend
];
726 if (!a
) throw creaturesException("No such audio backend " + preferred_audiobackend
);
728 a
->init(); audio
= a
;
729 } catch (creaturesException
&e
) {
730 std::cerr
<< "* Couldn't initialize backend " << preferred_audiobackend
<< ": " << e
.what() << std::endl
<< "* Continuing without sound." << std::endl
;
731 audio
= shared_ptr
<AudioBackend
>(new NullAudioBackend());
734 possible_audiobackends
.clear();
736 world
.camera
.setBackend(backend
); // TODO: hrr
738 int listenport
= backend
->networkInit();
739 if (listenport
!= -1) {
740 // inform the user of the port used, and store it in the relevant file
741 std::cout
<< "Listening for connections on port " << listenport
<< "." << std::endl
;
743 fs::path p
= fs::path(homeDirectory().native_directory_string() + "/.creaturesengine", fs::native
);
745 fs::create_directory(p
);
746 if (fs::is_directory(p
)) {
747 std::ofstream
f((p
.native_directory_string() + "/port").c_str(), std::ios::trunc
);
748 f
<< boost::str(boost::format("%d") % listenport
);
753 if (world
.data_directories
.size() < 3) {
754 // TODO: This is a hack for DS, basically. Not sure if it works properly. - fuzzie
755 caosVar name
; name
.setString("engine_no_auxiliary_bootstrap_1");
756 caosVar contents
; contents
.setInt(1);
757 eame_variables
[name
] = contents
;
760 // execute the initial scripts!
761 std::cout
<< "* Executing initial scripts..." << std::endl
;
762 if (cmdline_bootstrap
.size() == 0) {
763 world
.executeBootstrap(false);
765 std::vector
<std::string
> scripts
;
767 for (std::vector
< std::string
>::iterator bsi
= cmdline_bootstrap
.begin(); bsi
!= cmdline_bootstrap
.end(); bsi
++) {
768 fs::path
scriptdir(*bsi
, fs::native
);
769 if (!fs::exists(scriptdir
)) {
770 std::cerr
<< "Warning: Couldn't find a specified script directory (trying " << *bsi
<< ")!\n";
773 world
.executeBootstrap(scriptdir
);
777 // if there aren't any metarooms, we can't run a useful game, the user probably
778 // wanted to execute a CAOS script or something went badly wrong.
779 if (!cmdline_norun
&& world
.map
.getMetaRoomCount() == 0) {
781 throw creaturesException("No metarooms found in given bootstrap directories or files");
784 std::cout
<< "* Done startup." << std::endl
;
787 // TODO: see comment above about avoiding backend when norun is set
788 std::cout
<< "Told not to run the world, so stopping now." << std::endl
;
796 void Engine::shutdown() {
800 freeDelegates(); // does nothing if there are none (ie, no call to initialSetup)
803 fs::path
Engine::homeDirectory() {
807 char *envhome
= getenv("HOME");
809 p
= fs::path(envhome
, fs::native
);
810 if ((!envhome
) || (!fs::is_directory(p
)))
811 p
= fs::path(getpwuid(getuid())->pw_dir
, fs::native
);
812 if (!fs::is_directory(p
)) {
813 std::cerr
<< "Can't work out what your home directory is, giving up and using /tmp for now." << std::endl
;
814 p
= fs::path("/tmp", fs::native
); // sigh
817 TCHAR szPath
[_MAX_PATH
];
818 SHGetSpecialFolderPath(NULL
, szPath
, CSIDL_PERSONAL
, TRUE
);
820 p
= fs::path(szPath
, fs::native
);
821 if (!fs::exists(p
) || !fs::is_directory(p
))
822 throw creaturesException("Windows reported that your My Documents folder is at '" + std::string(szPath
) + "' but there's no directory there!");
828 fs::path
Engine::storageDirectory() {
830 std::string dirname
= "/.openc2e";
832 std::string dirname
= "/My Games";
836 fs::path p
= fs::path(homeDirectory().native_directory_string() + dirname
, fs::native
);
838 fs::create_directory(p
);
839 else if (!fs::is_directory(p
))
840 throw creaturesException("Your openc2e data directory " + p
.native_directory_string() + " is a file, not a directory. That's bad.");
842 // game-specific storage dir
843 p
= fs::path(p
.native_directory_string() + std::string("/" + gamename
), fs::native
);
845 fs::create_directory(p
);
846 else if (!fs::is_directory(p
))
847 throw creaturesException("Your openc2e game data directory " + p
.native_directory_string() + " is a file, not a directory. That's bad.");