fix agentOnCamera to cope with the wrap
[openc2e.git] / qtgui / qtopenc2e.cpp
blobfb4ff25cf0bc3d644f1026f287881e90df0554fd
1 /*
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.
17 #include "World.h"
18 #include "qtopenc2e.h"
19 #include <QtGui>
20 #include "openc2eview.h"
21 #include "Engine.h"
22 #include "AudioBackend.h"
23 #include "MetaRoom.h"
25 #include "AgentInjector.h"
26 #include "BrainViewer.h"
27 #include "CreatureGrapher.h"
29 #include "Creature.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()));
41 // idle timer
42 // TODO: should prbly have a background thread to do this?
43 ourTimer = new QTimer(this);
44 connect(ourTimer, SIGNAL(timeout()), this, SLOT(tick()));
45 ourTimer->start();
47 (void)statusBar();
48 std::string titlebar = engine.getGameName() + " - openc2e (development build)";
49 setWindowTitle(titlebar.c_str());
50 resize(800, 600);
52 /* File menu */
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);
61 /* View menu */
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();
74 /* Control menu */
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);
107 /* Debug menu */
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);
127 /* Tools menu */
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());
153 /* Creatures menu */
155 creaturesMenu = menuBar()->addMenu(tr("&Creatures"));
156 connect(creaturesMenu, SIGNAL(aboutToShow()), this, SLOT(updateCreaturesMenu()));
158 /* Help menu */
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);
175 assert(g);
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);
187 assert(srcaction);
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
195 if (a == p.get()) {
196 world.selectCreature(p);
197 return;
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());
212 if (a) {
213 // TODO: add breed?
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>";
248 else
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();
264 viewport->tick();
265 viewport->horizontalScrollBar()->setValue(x - world.camera.getMetaRoom()->x());
266 viewport->verticalScrollBar()->setValue(y - world.camera.getMetaRoom()->y());
268 if (engine.done) close();
270 if (didtick) {
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)
277 onCreatureChange();
280 // TODO: emit creatureTicked() if necessary
282 emit ticked();
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);
294 // action handlers
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() {
316 brainViewer->show();
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);
328 } else {
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());
359 #undef slots
360 void QtOpenc2e::newNorn() {
361 if (engine.version > 2) return; // TODO: fixme
363 std::string genomefile = "test";
364 shared_ptr<genomeFile> genome;
365 try {
366 genome = world.loadGenome(genomefile);
367 } catch (creaturesException &e) {
368 QMessageBox::warning(this, tr("Couldn't load genome file:"), e.prettyPrint().c_str());
369 return;
372 if (!genome) {
374 return;
377 SkeletalCreature *a = new SkeletalCreature(4);
379 int sex = 1 + (int) (2.0 * (rand() / (RAND_MAX + 1.0)));
380 oldCreature *c;
381 try {
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) {
385 delete a;
386 QMessageBox::warning(this, tr("Couldn't create creature:"), e.prettyPrint().c_str());
387 return;
390 a->setCreature(c);
391 a->finishInit();
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
401 c->born();
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));
414 /* set the gender */
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);
422 if (err.size())
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());
429 if (a) {
430 return a->getCreature();
434 return 0;