2 This program is free software; you can redistribute it and/or modify
3 it under the terms of the GNU General Public License as published by
4 the Free Software Foundation; either version 2 of the License, or
5 (at your option) any later version.
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
12 You should have received a copy of the GNU General Public License along
13 with this program; if not, write to the Free Software Foundation, Inc.,
14 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 #include "qtopenc2e.h"
20 #include "openc2eview.h"
22 #include "AudioBackend.h"
26 #include "AgentInjector.h"
27 #include "BrainViewer.h"
28 #include "CreatureGrapher.h"
31 #include "SkeletalCreature.h"
32 #include "PointerAgent.h"
36 // Constructor which creates the main window.
38 QtOpenc2e::QtOpenc2e(boost::shared_ptr
<QtBackend
> backend
) {
39 viewport
= new openc2eView(this, backend
);
40 setCentralWidget(viewport
);
42 connect(this, SIGNAL(creatureChanged()), this, SLOT(onCreatureChange()));
45 // TODO: should prbly have a background thread to do this?
46 ourTimer
= new QTimer(this);
47 connect(ourTimer
, SIGNAL(timeout()), this, SLOT(tick()));
51 std::string titlebar
= engine
.getGameName() + " - openc2e ";
53 titlebar
+= "(development build)";
55 titlebar
+= "(" RELEASE_VERSION
")";
57 setWindowTitle(titlebar
.c_str());
62 exitAct
= new QAction(tr("&Exit"), this);
63 exitAct
->setStatusTip(tr("Exit openc2e"));
64 connect(exitAct
, SIGNAL(triggered()), this, SLOT(close()));
66 fileMenu
= menuBar()->addMenu(tr("&File"));
67 fileMenu
->addAction(exitAct
);
71 viewMenu
= menuBar()->addMenu(tr("&View"));
73 toggleScrollbarsAct
= new QAction(tr("Show &Scrollbars"), this);
74 toggleScrollbarsAct
->setCheckable(true);
75 connect(toggleScrollbarsAct
, SIGNAL(triggered()), this, SLOT(toggleShowScrollbars()));
76 viewMenu
->addAction(toggleScrollbarsAct
);
78 // only enable scrollbars for c1/c2, by default
79 toggleScrollbarsAct
->setChecked(engine
.version
< 3);
80 toggleShowScrollbars();
84 controlMenu
= menuBar()->addMenu(tr("C&ontrol"));
85 connect(controlMenu
, SIGNAL(aboutToShow()), this, SLOT(updateMenus()));
87 pauseAct
= new QAction(tr("&Pause"), this);
88 connect(pauseAct
, SIGNAL(triggered()), this, SLOT(togglePause()));
89 controlMenu
->addAction(pauseAct
);
91 muteAct
= new QAction(tr("&Mute"), this);
92 muteAct
->setCheckable(true);
93 connect(muteAct
, SIGNAL(triggered()), this, SLOT(toggleMute()));
94 controlMenu
->addAction(muteAct
);
96 controlMenu
->addSeparator();
98 fastSpeedAct
= new QAction(tr("&Fast speed"), this);
99 fastSpeedAct
->setCheckable(true);
100 connect(fastSpeedAct
, SIGNAL(triggered()), this, SLOT(toggleFastSpeed()));
101 controlMenu
->addAction(fastSpeedAct
);
103 displayUpdatesAct
= new QAction(tr("Slow &display updates"), this);
104 displayUpdatesAct
->setCheckable(true);
105 displayUpdatesAct
->setEnabled(false);
106 connect(displayUpdatesAct
, SIGNAL(triggered()), this, SLOT(toggleDisplayUpdates()));
107 controlMenu
->addAction(displayUpdatesAct
);
109 autokillAct
= new QAction(tr("&Autokill"), this);
110 autokillAct
->setCheckable(true);
111 autokillAct
->setChecked(world
.autokill
);
112 connect(autokillAct
, SIGNAL(triggered()), this, SLOT(toggleAutokill()));
113 controlMenu
->addAction(autokillAct
);
117 debugMenu
= menuBar()->addMenu(tr("&Debug"));
118 connect(debugMenu
, SIGNAL(aboutToShow()), this, SLOT(updateMenus()));
120 showMapAct
= new QAction(tr("Show &Map"), this);
121 showMapAct
->setCheckable(true);
122 connect(showMapAct
, SIGNAL(triggered()), this, SLOT(toggleShowMap()));
123 debugMenu
->addAction(showMapAct
);
125 newNornAct
= new QAction(tr("Create a new (debug) &Norn"), this);
126 if (engine
.version
> 2) newNornAct
->setEnabled(false); // TODO
127 connect(newNornAct
, SIGNAL(triggered()), this, SLOT(newNorn()));
128 debugMenu
->addAction(newNornAct
);
130 newEggAct
= new QAction(tr("Create a random &egg"), this);
131 if (engine
.version
> 1) newEggAct
->setEnabled(false); // TODO
132 connect(newEggAct
, SIGNAL(triggered()), this, SLOT(newEgg()));
133 debugMenu
->addAction(newEggAct
);
137 toolsMenu
= menuBar()->addMenu(tr("&Tools"));
139 hatcheryAct
= new QAction(tr("&Hatchery"), this);
140 connect(hatcheryAct
, SIGNAL(triggered()), this, SLOT(showHatchery()));
141 toolsMenu
->addAction(hatcheryAct
);
142 if (engine
.version
> 1) hatcheryAct
->setEnabled(false);
144 agentInjectorAct
= new QAction(tr("&Agent Injector"), this);
145 connect(agentInjectorAct
, SIGNAL(triggered()), this, SLOT(showAgentInjector()));
146 toolsMenu
->addAction(agentInjectorAct
);
148 brainViewerAct
= new QAction(tr("&Brain Viewer"), this);
149 connect(brainViewerAct
, SIGNAL(triggered()), this, SLOT(showBrainViewer()));
150 toolsMenu
->addAction(brainViewerAct
);
152 if (engine
.version
== 1)
153 hatchery
= new Hatchery(this);
154 agentInjector
= new AgentInjector(this);
155 brainViewer
= new BrainViewer(this);
156 connect(this, SIGNAL(ticked()), brainViewer
, SLOT(onTick()));
158 creatureGrapher
= new CreatureGrapher(this);
159 connect(this, SIGNAL(ticked()), creatureGrapher
, SLOT(onCreatureTick())); // TODO
160 creatureGrapherDock
= new QDockWidget(this);
161 creatureGrapherDock
->hide();
162 creatureGrapherDock
->setWidget(creatureGrapher
);
163 creatureGrapherDock
->setFloating(true);
164 creatureGrapherDock
->resize(QSize(300, 300));
165 creatureGrapherDock
->setWindowTitle(tr("Creature Grapher"));
166 toolsMenu
->addAction(creatureGrapherDock
->toggleViewAction());
170 creaturesMenu
= menuBar()->addMenu(tr("&Creatures"));
171 connect(creaturesMenu
, SIGNAL(aboutToShow()), this, SLOT(updateCreaturesMenu()));
175 menuBar()->addSeparator();
177 aboutAct
= new QAction(tr("&About"), this);
178 aboutAct
->setStatusTip(tr("Find out about openc2e"));
179 connect(aboutAct
, SIGNAL(triggered()), this, SLOT(about()));
181 helpMenu
= menuBar()->addMenu(tr("&Help"));
182 helpMenu
->addAction(aboutAct
);
185 QtOpenc2e::~QtOpenc2e() {
188 monikerData
&monikerDataFor(AgentRef a
) {
189 shared_ptr
<class genomeFile
> g
= a
->getSlot(0);
191 std::string moniker
= world
.history
.findMoniker(g
);
192 return world
.history
.getMoniker(moniker
);
195 std::string
creatureNameFor(AgentRef a
) {
196 return monikerDataFor(a
).name
;
199 void QtOpenc2e::selectCreature() {
200 QObject
*src
= sender();
201 QAction
*srcaction
= dynamic_cast<QAction
*>(src
);
204 Agent
*a
= (Agent
*)srcaction
->data().value
<void *>();
206 for (std::list
<boost::shared_ptr
<Agent
> >::iterator i
= world
.agents
.begin(); i
!= world
.agents
.end(); i
++) {
207 boost::shared_ptr
<Agent
> p
= *i
;
208 if (!p
) continue; // grr, but needed
211 world
.selectCreature(p
);
216 // looks like the creature disappeared from under us..
219 void QtOpenc2e::updateCreaturesMenu() {
220 creaturesMenu
->clear();
222 for (std::list
<boost::shared_ptr
<Agent
> >::iterator i
= world
.agents
.begin(); i
!= world
.agents
.end(); i
++) {
223 boost::shared_ptr
<Agent
> p
= *i
;
224 if (!p
) continue; // grr, but needed
226 CreatureAgent
*a
= dynamic_cast<CreatureAgent
*>(p
.get());
229 std::string creaturename
= creatureNameFor(a
);
230 if (creaturename
.empty()) creaturename
= "<Unnamed>";
231 creaturename
+= std::string(" (") + (a
->getCreature()->isFemale() ? "Female" : "Male") + ")";
233 // create a new action with menu as parent, so it'll be destroyed on clear()
234 QAction
*creatureSelectAct
= new QAction(creaturename
.c_str(), creaturesMenu
);
235 creatureSelectAct
->setData(QVariant::fromValue((void *)a
));
237 creatureSelectAct
->setCheckable(true);
238 if (world
.selectedcreature
== p
) creatureSelectAct
->setChecked(true);
239 connect(creatureSelectAct
, SIGNAL(triggered()), this, SLOT(selectCreature()));
241 if (monikerDataFor(a
).getStatus() != borncreature
)
242 creatureSelectAct
->setDisabled(true);
244 creaturesMenu
->addAction(creatureSelectAct
);
248 if (creaturesMenu
->isEmpty()) {
249 QAction
*dummyAct
= new QAction("<none available>", creaturesMenu
);
250 dummyAct
->setEnabled(false);
251 creaturesMenu
->addAction(dummyAct
);
255 void QtOpenc2e::onCreatureChange() {
256 std::string titlebar
= engine
.getGameName() + " - openc2e ";
258 titlebar
+= "(development build)";
260 titlebar
+= "(" RELEASE_VERSION
")";
262 if (world
.selectedcreature
) {
263 oldcreaturename
= creatureNameFor(world
.selectedcreature
);
265 if (oldcreaturename
.empty())
266 titlebar
+= " - <Unnamed>";
268 titlebar
+= " - " + oldcreaturename
;
271 setWindowTitle(titlebar
.c_str());
274 void QtOpenc2e::tick() {
275 // set refreshdisplay occasionally, for updates when dorendering is false
276 if (world
.worldtickcount
% world
.ticktime
== 0) // every 10 in-world seconds, with default times
277 engine
.refreshdisplay
= true;
279 bool didtick
= engine
.tick();
281 int y
= world
.camera
.getY();
282 int x
= world
.camera
.getX();
284 viewport
->horizontalScrollBar()->setValue(x
- world
.camera
.getMetaRoom()->x());
285 viewport
->verticalScrollBar()->setValue(y
- world
.camera
.getMetaRoom()->y());
287 if (engine
.done
) close();
290 if (world
.selectedcreature
!= selectedcreature
) {
291 selectedcreature
= world
.selectedcreature
;
292 emit
creatureChanged();
293 } else if (world
.selectedcreature
) {
294 // pick up name changes
295 if (creatureNameFor(world
.selectedcreature
) != oldcreaturename
)
299 // TODO: emit creatureTicked() if necessary
304 if (viewport
->needsRender()) {
305 viewport
->viewport()->repaint();
308 unsigned int i
= engine
.msUntilTick();
309 if (i
!= 0 || ourTimer
->interval() != 0) // TODO: things get a bit annoyingly unresponsive without this
310 ourTimer
->setInterval(i
);
315 void QtOpenc2e::updateMenus() {
316 showMapAct
->setChecked(world
.showrooms
);
317 fastSpeedAct
->setChecked(engine
.fastticks
);
318 displayUpdatesAct
->setChecked(!engine
.dorendering
);
319 autokillAct
->setChecked(world
.autokill
);
320 muteAct
->setChecked(engine
.audio
->isMuted());
321 if (world
.paused
) pauseAct
->setText("&Play");
322 else pauseAct
->setText("&Pause");
325 void QtOpenc2e::about() {
326 std::string abouttxt
= "An open-source game engine to run the Creatures "
327 "series of games.\nVersion: ";
329 abouttxt
+= "development build";
331 abouttxt
+= RELEASE_VERSION
;
333 abouttxt
+= " (built " __DATE__
" " __TIME__
")";
334 QMessageBox::about(this, tr("openc2e"), abouttxt
.c_str());
337 void QtOpenc2e::showHatchery() {
339 hatchery
->activateWindow();
342 void QtOpenc2e::showAgentInjector() {
343 agentInjector
->show();
344 agentInjector
->activateWindow();
347 void QtOpenc2e::showBrainViewer() {
349 brainViewer
->activateWindow();
352 void QtOpenc2e::toggleShowMap() {
353 world
.showrooms
= !world
.showrooms
;
356 void QtOpenc2e::toggleShowScrollbars() {
357 if (toggleScrollbarsAct
->isChecked()) {
358 viewport
->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded
);
359 viewport
->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded
);
361 viewport
->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
362 viewport
->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
366 void QtOpenc2e::toggleFastSpeed() {
367 engine
.fastticks
= !engine
.fastticks
;
368 displayUpdatesAct
->setEnabled(engine
.fastticks
);
369 if (!engine
.fastticks
) {
370 engine
.dorendering
= true;
371 displayUpdatesAct
->setChecked(false);
375 void QtOpenc2e::toggleDisplayUpdates() {
376 engine
.dorendering
= !engine
.dorendering
;
379 void QtOpenc2e::toggleAutokill() {
380 world
.autokill
= !world
.autokill
;
383 void QtOpenc2e::togglePause() {
384 world
.paused
= !world
.paused
;
387 void QtOpenc2e::toggleMute() {
388 engine
.audio
->setMute(!engine
.audio
->isMuted());
392 void QtOpenc2e::newNorn() {
393 if (engine
.version
> 2) return; // TODO: fixme
395 std::string genomefile
= "test";
396 shared_ptr
<genomeFile
> genome
;
398 genome
= world
.loadGenome(genomefile
);
399 } catch (creaturesException
&e
) {
400 QMessageBox::warning(this, tr("Couldn't load genome file:"), e
.prettyPrint().c_str());
409 SkeletalCreature
*a
= new SkeletalCreature(4);
411 int sex
= 1 + (int) (2.0 * (rand() / (RAND_MAX
+ 1.0)));
414 if (engine
.version
== 1) c
= new c1Creature(genome
, (sex
== 2), 0, a
);
415 else c
= new c2Creature(genome
, (sex
== 2), 0, a
);
416 } catch (creaturesException
&e
) {
418 QMessageBox::warning(this, tr("Couldn't create creature:"), e
.prettyPrint().c_str());
425 // if you make this work for c2e, you should probably set sane attributes here?
427 a
->slots
[0] = genome
;
428 world
.newMoniker(genome
, genomefile
, a
);
429 world
.history
.getMoniker(world
.history
.findMoniker(genome
)).moveToCreature(a
);
431 // TODO: set it dreaming
435 world
.hand()->addCarried(a
);
438 void QtOpenc2e::newEgg() {
439 std::string eggscript
;
440 /* create the egg obj */
441 eggscript
= boost::str(boost::format("new: simp eggs 8 %d 2000 0\n") % ((rand() % 6) * 8));
442 /* set the pose and the correct class/attributes */
443 eggscript
+= "pose 3\nsetv clas 33882624\nsetv attr 67\n";
444 /* create the genome */
445 eggscript
+= boost::str(boost::format("new: gene tokn dad%d tokn mum%d obv0\n") % (1 + rand() % 6) % (1 + rand() % 6));
447 eggscript
+= "setv obv1 0\n";
448 /* start the clock */
449 eggscript
+= "tick 2400\n";
450 /* move it into place */
451 eggscript
+= boost::str(boost::format("mvto %d 870\n") % (2600 + rand() % 200));
453 std::string err
= engine
.executeNetwork(eggscript
);
455 QMessageBox::warning(this, tr("Couldn't create egg:"), err
.c_str());
458 Creature
*QtOpenc2e::getSelectedCreature() {
459 if (world
.selectedcreature
) {
460 CreatureAgent
*a
= dynamic_cast<CreatureAgent
*>(world
.selectedcreature
.get());
462 return a
->getCreature();