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"
25 #include "AgentInjector.h"
26 #include "BrainViewer.h"
27 #include "CreatureGrapher.h"
30 #include "SkeletalCreature.h"
31 #include "PointerAgent.h"
33 // Constructor which creates the main window.
35 QtOpenc2e::QtOpenc2e(boost::shared_ptr
<QtBackend
> backend
) {
36 viewport
= new openc2eView(this, backend
);
37 setCentralWidget(viewport
);
39 connect(this, SIGNAL(creatureChanged()), this, SLOT(onCreatureChange()));
42 // TODO: should prbly have a background thread to do this?
43 ourTimer
= new QTimer(this);
44 connect(ourTimer
, SIGNAL(timeout()), this, SLOT(tick()));
48 std::string titlebar
= engine
.getGameName() + " - openc2e (development build)";
49 setWindowTitle(titlebar
.c_str());
54 exitAct
= new QAction(tr("&Exit"), this);
55 exitAct
->setStatusTip(tr("Exit openc2e"));
56 connect(exitAct
, SIGNAL(triggered()), this, SLOT(close()));
58 fileMenu
= menuBar()->addMenu(tr("&File"));
59 fileMenu
->addAction(exitAct
);
63 viewMenu
= menuBar()->addMenu(tr("&View"));
65 toggleScrollbarsAct
= new QAction(tr("Show &Scrollbars"), this);
66 toggleScrollbarsAct
->setCheckable(true);
67 connect(toggleScrollbarsAct
, SIGNAL(triggered()), this, SLOT(toggleShowScrollbars()));
68 viewMenu
->addAction(toggleScrollbarsAct
);
70 // only enable scrollbars for c1/c2, by default
71 toggleScrollbarsAct
->setChecked(engine
.version
< 3);
72 toggleShowScrollbars();
76 controlMenu
= menuBar()->addMenu(tr("C&ontrol"));
77 connect(controlMenu
, SIGNAL(aboutToShow()), this, SLOT(updateMenus()));
79 pauseAct
= new QAction(tr("&Pause"), this);
80 connect(pauseAct
, SIGNAL(triggered()), this, SLOT(togglePause()));
81 controlMenu
->addAction(pauseAct
);
83 muteAct
= new QAction(tr("&Mute"), this);
84 muteAct
->setCheckable(true);
85 connect(muteAct
, SIGNAL(triggered()), this, SLOT(toggleMute()));
86 controlMenu
->addAction(muteAct
);
88 controlMenu
->addSeparator();
90 fastSpeedAct
= new QAction(tr("&Fast speed"), this);
91 fastSpeedAct
->setCheckable(true);
92 connect(fastSpeedAct
, SIGNAL(triggered()), this, SLOT(toggleFastSpeed()));
93 controlMenu
->addAction(fastSpeedAct
);
95 displayUpdatesAct
= new QAction(tr("Slow &display updates"), this);
96 displayUpdatesAct
->setCheckable(true);
97 displayUpdatesAct
->setEnabled(false);
98 connect(displayUpdatesAct
, SIGNAL(triggered()), this, SLOT(toggleDisplayUpdates()));
99 controlMenu
->addAction(displayUpdatesAct
);
101 autokillAct
= new QAction(tr("&Autokill"), this);
102 autokillAct
->setCheckable(true);
103 autokillAct
->setChecked(world
.autokill
);
104 connect(autokillAct
, SIGNAL(triggered()), this, SLOT(toggleAutokill()));
105 controlMenu
->addAction(autokillAct
);
109 debugMenu
= menuBar()->addMenu(tr("&Debug"));
110 connect(debugMenu
, SIGNAL(aboutToShow()), this, SLOT(updateMenus()));
112 showMapAct
= new QAction(tr("Show &Map"), this);
113 showMapAct
->setCheckable(true);
114 connect(showMapAct
, SIGNAL(triggered()), this, SLOT(toggleShowMap()));
115 debugMenu
->addAction(showMapAct
);
117 newNornAct
= new QAction(tr("Create a new (debug) &Norn"), this);
118 if (engine
.version
> 2) newNornAct
->setEnabled(false); // TODO
119 connect(newNornAct
, SIGNAL(triggered()), this, SLOT(newNorn()));
120 debugMenu
->addAction(newNornAct
);
122 newEggAct
= new QAction(tr("Create a random &egg"), this);
123 if (engine
.version
> 1) newEggAct
->setEnabled(false); // TODO
124 connect(newEggAct
, SIGNAL(triggered()), this, SLOT(newEgg()));
125 debugMenu
->addAction(newEggAct
);
129 toolsMenu
= menuBar()->addMenu(tr("&Tools"));
131 agentInjectorAct
= new QAction(tr("&Agent Injector"), this);
132 connect(agentInjectorAct
, SIGNAL(triggered()), this, SLOT(showAgentInjector()));
133 toolsMenu
->addAction(agentInjectorAct
);
135 brainViewerAct
= new QAction(tr("&Brain Viewer"), this);
136 connect(brainViewerAct
, SIGNAL(triggered()), this, SLOT(showBrainViewer()));
137 toolsMenu
->addAction(brainViewerAct
);
139 agentInjector
= new AgentInjector(this);
140 brainViewer
= new BrainViewer(this);
141 connect(this, SIGNAL(ticked()), brainViewer
, SLOT(onTick()));
143 creatureGrapher
= new CreatureGrapher(this);
144 connect(this, SIGNAL(ticked()), creatureGrapher
, SLOT(onCreatureTick())); // TODO
145 creatureGrapherDock
= new QDockWidget(this);
146 creatureGrapherDock
->hide();
147 creatureGrapherDock
->setWidget(creatureGrapher
);
148 creatureGrapherDock
->setFloating(true);
149 creatureGrapherDock
->resize(QSize(300, 300));
150 creatureGrapherDock
->setWindowTitle(tr("Creature Grapher"));
151 toolsMenu
->addAction(creatureGrapherDock
->toggleViewAction());
155 creaturesMenu
= menuBar()->addMenu(tr("&Creatures"));
156 connect(creaturesMenu
, SIGNAL(aboutToShow()), this, SLOT(updateCreaturesMenu()));
160 menuBar()->addSeparator();
162 aboutAct
= new QAction(tr("&About"), this);
163 aboutAct
->setStatusTip(tr("Find out about openc2e"));
164 connect(aboutAct
, SIGNAL(triggered()), this, SLOT(about()));
166 helpMenu
= menuBar()->addMenu(tr("&Help"));
167 helpMenu
->addAction(aboutAct
);
170 QtOpenc2e::~QtOpenc2e() {
173 monikerData
&monikerDataFor(AgentRef a
) {
174 shared_ptr
<class genomeFile
> g
= a
->getSlot(0);
176 std::string moniker
= world
.history
.findMoniker(g
);
177 return world
.history
.getMoniker(moniker
);
180 std::string
creatureNameFor(AgentRef a
) {
181 return monikerDataFor(a
).name
;
184 void QtOpenc2e::selectCreature() {
185 QObject
*src
= sender();
186 QAction
*srcaction
= dynamic_cast<QAction
*>(src
);
189 Agent
*a
= (Agent
*)srcaction
->data().value
<void *>();
191 for (std::list
<boost::shared_ptr
<Agent
> >::iterator i
= world
.agents
.begin(); i
!= world
.agents
.end(); i
++) {
192 boost::shared_ptr
<Agent
> p
= *i
;
193 if (!p
) continue; // grr, but needed
196 world
.selectCreature(p
);
201 // looks like the creature disappeared from under us..
204 void QtOpenc2e::updateCreaturesMenu() {
205 creaturesMenu
->clear();
207 for (std::list
<boost::shared_ptr
<Agent
> >::iterator i
= world
.agents
.begin(); i
!= world
.agents
.end(); i
++) {
208 boost::shared_ptr
<Agent
> p
= *i
;
209 if (!p
) continue; // grr, but needed
211 CreatureAgent
*a
= dynamic_cast<CreatureAgent
*>(p
.get());
214 std::string creaturename
= creatureNameFor(a
);
215 if (creaturename
.empty()) creaturename
= "<Unnamed>";
216 creaturename
+= std::string(" (") + (a
->getCreature()->isFemale() ? "Female" : "Male") + ")";
218 // create a new action with menu as parent, so it'll be destroyed on clear()
219 QAction
*creatureSelectAct
= new QAction(creaturename
.c_str(), creaturesMenu
);
220 creatureSelectAct
->setData(QVariant::fromValue((void *)a
));
222 creatureSelectAct
->setCheckable(true);
223 if (world
.selectedcreature
== p
) creatureSelectAct
->setChecked(true);
224 connect(creatureSelectAct
, SIGNAL(triggered()), this, SLOT(selectCreature()));
226 if (monikerDataFor(a
).getStatus() != borncreature
)
227 creatureSelectAct
->setDisabled(true);
229 creaturesMenu
->addAction(creatureSelectAct
);
233 if (creaturesMenu
->isEmpty()) {
234 QAction
*dummyAct
= new QAction("<none available>", creaturesMenu
);
235 dummyAct
->setEnabled(false);
236 creaturesMenu
->addAction(dummyAct
);
240 void QtOpenc2e::onCreatureChange() {
241 std::string titlebar
= engine
.getGameName() + " - openc2e (development build)";
243 if (world
.selectedcreature
) {
244 oldcreaturename
= creatureNameFor(world
.selectedcreature
);
246 if (oldcreaturename
.empty())
247 titlebar
+= " - <Unnamed>";
249 titlebar
+= " - " + oldcreaturename
;
252 setWindowTitle(titlebar
.c_str());
255 void QtOpenc2e::tick() {
256 // set refreshdisplay occasionally, for updates when dorendering is false
257 if (world
.worldtickcount
% world
.ticktime
== 0) // every 10 in-world seconds, with default times
258 engine
.refreshdisplay
= true;
260 bool didtick
= engine
.tick();
262 int y
= world
.camera
.getY();
263 int x
= world
.camera
.getX();
265 viewport
->horizontalScrollBar()->setValue(x
- world
.camera
.getMetaRoom()->x());
266 viewport
->verticalScrollBar()->setValue(y
- world
.camera
.getMetaRoom()->y());
268 if (engine
.done
) close();
271 if (world
.selectedcreature
!= selectedcreature
) {
272 selectedcreature
= world
.selectedcreature
;
273 emit
creatureChanged();
274 } else if (world
.selectedcreature
) {
275 // pick up name changes
276 if (creatureNameFor(world
.selectedcreature
) != oldcreaturename
)
280 // TODO: emit creatureTicked() if necessary
285 if (viewport
->needsRender()) {
286 viewport
->viewport()->repaint();
289 unsigned int i
= engine
.msUntilTick();
290 if (i
!= 0 || ourTimer
->interval() != 0) // TODO: things get a bit annoyingly unresponsive without this
291 ourTimer
->setInterval(i
);
296 void QtOpenc2e::updateMenus() {
297 showMapAct
->setChecked(world
.showrooms
);
298 fastSpeedAct
->setChecked(engine
.fastticks
);
299 displayUpdatesAct
->setChecked(!engine
.dorendering
);
300 autokillAct
->setChecked(world
.autokill
);
301 muteAct
->setChecked(engine
.audio
->isMuted());
302 if (world
.paused
) pauseAct
->setText("&Play");
303 else pauseAct
->setText("&Pause");
306 void QtOpenc2e::about() {
307 QMessageBox::about(this, tr("openc2e"), tr("An open-source game engine to run the Creatures series of games."));
310 void QtOpenc2e::showAgentInjector() {
311 agentInjector
->show();
312 agentInjector
->activateWindow();
315 void QtOpenc2e::showBrainViewer() {
317 brainViewer
->activateWindow();
320 void QtOpenc2e::toggleShowMap() {
321 world
.showrooms
= !world
.showrooms
;
324 void QtOpenc2e::toggleShowScrollbars() {
325 if (toggleScrollbarsAct
->isChecked()) {
326 viewport
->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded
);
327 viewport
->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded
);
329 viewport
->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
330 viewport
->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff
);
334 void QtOpenc2e::toggleFastSpeed() {
335 engine
.fastticks
= !engine
.fastticks
;
336 displayUpdatesAct
->setEnabled(engine
.fastticks
);
337 if (!engine
.fastticks
) {
338 engine
.dorendering
= true;
339 displayUpdatesAct
->setChecked(false);
343 void QtOpenc2e::toggleDisplayUpdates() {
344 engine
.dorendering
= !engine
.dorendering
;
347 void QtOpenc2e::toggleAutokill() {
348 world
.autokill
= !world
.autokill
;
351 void QtOpenc2e::togglePause() {
352 world
.paused
= !world
.paused
;
355 void QtOpenc2e::toggleMute() {
356 engine
.audio
->setMute(!engine
.audio
->isMuted());
360 void QtOpenc2e::newNorn() {
361 if (engine
.version
> 2) return; // TODO: fixme
363 std::string genomefile
= "test";
364 shared_ptr
<genomeFile
> genome
;
366 genome
= world
.loadGenome(genomefile
);
367 } catch (creaturesException
&e
) {
368 QMessageBox::warning(this, tr("Couldn't load genome file:"), e
.prettyPrint().c_str());
377 SkeletalCreature
*a
= new SkeletalCreature(4);
379 int sex
= 1 + (int) (2.0 * (rand() / (RAND_MAX
+ 1.0)));
382 if (engine
.version
== 1) c
= new c1Creature(genome
, (sex
== 2), 0, a
);
383 else c
= new c2Creature(genome
, (sex
== 2), 0, a
);
384 } catch (creaturesException
&e
) {
386 QMessageBox::warning(this, tr("Couldn't create creature:"), e
.prettyPrint().c_str());
393 // if you make this work for c2e, you should probably set sane attributes here?
395 a
->slots
[0] = genome
;
396 world
.newMoniker(genome
, genomefile
, a
);
397 world
.history
.getMoniker(world
.history
.findMoniker(genome
)).moveToCreature(a
);
399 // TODO: set it dreaming
403 world
.hand()->addCarried(a
);
406 void QtOpenc2e::newEgg() {
407 std::string eggscript
;
408 /* create the egg obj */
409 eggscript
= boost::str(boost::format("new: simp eggs 8 %d 2000 0\n") % ((rand() % 6) * 8));
410 /* set the pose and the correct class/attributes */
411 eggscript
+= "pose 3\nsetv clas 33882624\nsetv attr 67\n";
412 /* create the genome */
413 eggscript
+= boost::str(boost::format("new: gene tokn dad%d tokn mum%d obv0\n") % (1 + rand() % 6) % (1 + rand() % 6));
415 eggscript
+= "setv obv1 0\n";
416 /* start the clock */
417 eggscript
+= "tick 2400\n";
418 /* move it into place */
419 eggscript
+= boost::str(boost::format("mvto %d 870\n") % (2600 + rand() % 200));
421 std::string err
= engine
.executeNetwork(eggscript
);
423 QMessageBox::warning(this, tr("Couldn't create egg:"), err
.c_str());
426 Creature
*QtOpenc2e::getSelectedCreature() {
427 if (world
.selectedcreature
) {
428 CreatureAgent
*a
= dynamic_cast<CreatureAgent
*>(world
.selectedcreature
.get());
430 return a
->getCreature();