add more hax to CallButton
[openc2e.git] / World.cpp
bloba3a614ce3ddfe9a46d6f0481e8abd7c23e5b944b
1 /*
2 * World.cpp
3 * openc2e
5 * Created by Alyssa Milburn on Tue May 25 2004.
6 * Copyright (c) 2004-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.
20 #include "World.h"
21 #include "Engine.h"
22 #include "caosVM.h" // for setupCommandPointers()
23 #include "caosScript.h"
24 #include "PointerAgent.h"
25 #include "CompoundAgent.h" // for setFocus
26 #include <limits.h> // for MAXINT
27 #include "creaturesImage.h"
28 #include "CreatureAgent.h"
29 #include "Backend.h"
30 #include "AudioBackend.h"
31 #include "SFCFile.h"
32 #include "Room.h"
33 #include "MetaRoom.h"
34 #include "Catalogue.h"
36 #include <boost/format.hpp>
37 #include <boost/filesystem/convenience.hpp>
38 namespace fs = boost::filesystem;
40 World world;
42 World::World() {
43 ticktime = 50;
44 tickcount = 0;
45 worldtickcount = 0;
46 race = 50; // sensible default?
47 pace = 0.0f; // sensible default?
48 quitting = saving = false;
49 theHand = 0;
50 showrooms = false;
51 autokill = false;
52 autostop = false;
55 World::~World() {
56 agents.clear();
57 for (std::vector<caosVM *>::iterator i = vmpool.begin(); i != vmpool.end(); i++)
58 delete *i;
61 // annoyingly, if we put this in the constructor, the catalogue isn't available yet
62 void World::init() {
63 // First, try initialising the mouse cursor from the catalogue tag.
64 if (engine.version > 2 && catalogue.hasTag("Pointer Information")) {
65 const std::vector<std::string> &pointerinfo = catalogue.getTag("Pointer Information");
66 if (pointerinfo.size() >= 3) {
67 shared_ptr<creaturesImage> img = gallery.getImage(pointerinfo[2]);
68 if (img) {
69 theHand = new PointerAgent(pointerinfo[2]);
70 int family, genus, species;
71 if (sscanf(pointerinfo[0].c_str(), "%d %d %d", &family, &genus, &species) == 3)
72 theHand->setClassifier(family, genus, species);
73 int hotspotx, hotspoty;
74 if (sscanf(pointerinfo[1].c_str(), "%d %d", &hotspotx, &hotspoty) == 2)
75 theHand->setHotspot(hotspotx, hotspoty);
76 // TODO: seamonkeys has 'numImages baseImage' 'blockwidth blockheight' on the end of this tag
77 theHand->finishInit();
78 } else {
79 std::cout << "There was a seemingly-useful \"Pointer Information\" catalogue tag provided, but sprite file '" << pointerinfo[2] << " ' doesn't exist!" << std::endl;
84 // If for some reason we failed to do that (missing/bad catalogue tag? missing file?), try falling back to a sane default.
85 if (!theHand) {
86 shared_ptr<creaturesImage> img;
87 if (gametype == "c3")
88 img = gallery.getImage("hand"); // as used in C3 and DS
89 else
90 img = gallery.getImage("syst"); // as used in C1, C2 and CV
91 if (img) {
92 theHand = new PointerAgent(img->getName());
93 if (engine.version > 2) {
94 theHand->finishInit(); // SFCFile handles this for c1/c2
95 std::cout << "Warning: No valid \"Pointer Information\" catalogue tag, defaulting to '" << img->getName() << "'." << std::endl;
97 } else {
98 if (engine.version > 2)
99 std::cout << "Couldn't find a valid \"Pointer Information\" catalogue tag, and c";
100 else
101 std::cout << "C";
102 std::cout << "ouldn't find a pointer sprite, so not creating the pointer agent." << std::endl;
106 // *** set defaults for non-zero GAME engine variables
107 // TODO: this should be doing during world init, rather than global init
108 // TODO: not complete
109 caosVar v;
110 variables.clear();
112 // core engine bits
113 v.setInt(1); variables["engine_debug_keys"] = v;
114 v.setInt(1); variables["engine_full_screen_toggle"] = v;
115 v.setInt(9998); variables["engine_plane_for_lines"] = v;
116 v.setInt(6); variables["engine_zlib_compression"] = v;
118 // creature pregnancy
119 v.setInt(1); variables["engine_multiple_birth_maximum"] = v;
120 v.setFloat(0.5f); variables["engine_multiple_birth_identical_chance"] = v;
122 // port lines
123 v.setFloat(600.0f); variables["engine_distance_before_port_line_warns"] = v;
124 v.setFloat(800.0f); variables["engine_distance_before_port_line_snaps"] = v;
126 // adjust to default tick rate for C1/C2 if necessary
127 if (engine.version < 3)
128 ticktime = 100;
130 timeofday = dayofseason = season = year = 0;
133 void World::shutdown() {
134 map.Reset();
137 caosVM *World::getVM(Agent *a) {
138 if (vmpool.empty()) {
139 return new caosVM(a);
140 } else {
141 caosVM *x = vmpool.back();
142 vmpool.pop_back();
143 x->setOwner(a);
144 return x;
148 void World::freeVM(caosVM *v) {
149 v->setOwner(0);
150 vmpool.push_back(v);
153 void World::queueScript(unsigned short event, AgentRef agent, AgentRef from, caosVar p0, caosVar p1) {
154 scriptevent e;
156 assert(agent);
158 e.scriptno = event;
159 e.agent = agent;
160 e.from = from;
161 e.p[0] = p0;
162 e.p[1] = p1;
164 scriptqueue.push_back(e);
167 // TODO: eventually, the part should be referenced via a weak_ptr, maaaaybe?
168 void World::setFocus(TextEntryPart *p) {
169 // Unfocus the current agent. Not sure if c2e always does this (what if the agent/part is bad?).
170 if (focusagent) {
171 CompoundAgent *c = dynamic_cast<CompoundAgent *>(focusagent.get());
172 assert(c);
173 TextEntryPart *p = dynamic_cast<TextEntryPart *>(c->part(focuspart));
174 if (p)
175 p->loseFocus();
178 if (!p)
179 focusagent.clear();
180 else {
181 p->gainFocus();
182 focusagent = p->getParent();
183 focuspart = p->id;
187 void World::tick() {
188 if (saving) {} // TODO: save
189 if (quitting) {
190 // due to destruction ordering we must explicitly destroy all agents here
191 agents.clear();
192 exit(0);
195 // Notify the audio backend about our current viewpoint center.
196 engine.audio->setViewpointCenter(world.camera.getXCentre(), world.camera.getYCentre());
198 std::list<std::pair<boost::shared_ptr<AudioSource>, bool> >::iterator si = uncontrolled_sounds.begin();
199 while (si != uncontrolled_sounds.end()) {
200 std::list<std::pair<boost::shared_ptr<AudioSource>, bool> >::iterator next = si; next++;
201 if (si->first->getState() != SS_PLAY) {
202 // sound is stopped, so release our reference
203 uncontrolled_sounds.erase(si);
204 } else {
205 if (si->second) {
206 // follow viewport
207 si->first->setPos(world.camera.getXCentre(), world.camera.getYCentre(), 0);
208 } else {
209 // mute/unmute off-screen uncontrolled audio if necessary
210 float x, y, z;
211 si->first->getPos(x, y, z);
212 if (engine.version > 2) // TODO: this is because of wrap issues, but we need a better fix
213 si->first->setMute(world.camera.getMetaRoom() != world.map.metaRoomAt(x, y));
217 si = next;
220 // Tick all agents, deleting as necessary.
221 std::list<boost::shared_ptr<Agent> >::iterator i = agents.begin();
222 while (i != agents.end()) {
223 boost::shared_ptr<Agent> a = *i;
224 if (!a) {
225 std::list<boost::shared_ptr<Agent> >::iterator i2 = i;
226 i2++;
227 agents.erase(i);
228 i = i2;
229 continue;
231 i++;
232 a->tick();
235 // Process the script queue.
236 std::list<scriptevent> newqueue;
237 for (std::list<scriptevent>::iterator i = scriptqueue.begin(); i != scriptqueue.end(); i++) {
238 boost::shared_ptr<Agent> agent = i->agent.lock();
239 if (agent) {
240 if (engine.version < 3) {
241 // only try running a collision script if the agent doesn't have a running script
242 // TODO: we don't really understand how script interruption in c1/c2 works
243 if (agent->vm && !agent->vm->stopped() && i->scriptno == 6) {
244 continue;
247 agent->fireScript(i->scriptno, i->from, i->p[0], i->p[1]);
250 scriptqueue.clear();
251 scriptqueue = newqueue;
253 tickcount++;
254 worldtickcount++;
256 if (engine.version == 2) {
257 if (worldtickcount % 3600 == 0) {
258 timeofday++;
259 if (timeofday == 5) { // 5 parts of the day
260 timeofday = 0;
261 dayofseason++;
263 if (dayofseason == 4) { // 4 days per season
264 dayofseason = 0;
265 season++;
267 if (season == 4) { // 4 seasons per year
268 season = 0;
269 year++;
274 world.map.tick();
276 // TODO: correct behaviour? hrm :/
277 world.hand()->velx.setFloat(world.hand()->velx.getFloat() / 2.0f);
278 world.hand()->vely.setFloat(world.hand()->vely.getFloat() / 2.0f);
281 Agent *World::agentAt(unsigned int x, unsigned int y, bool obey_all_transparency, bool needs_mouseable) {
282 CompoundPart *p = partAt(x, y, obey_all_transparency, needs_mouseable);
283 if (p)
284 return p->getParent();
285 else
286 return 0;
289 CompoundPart *World::partAt(unsigned int x, unsigned int y, bool obey_all_transparency, bool needs_mouseable) {
290 Agent *transagent = 0;
291 if (!obey_all_transparency)
292 transagent = agentAt(x, y, true, needs_mouseable);
294 for (std::multiset<CompoundPart *, partzorder>::iterator i = zorder.begin(); i != zorder.end(); i++) {
295 int ax = (int)(x - (*i)->getParent()->x);
296 int ay = (int)(y - (*i)->getParent()->y);
297 if ((*i)->x <= ax) if ((*i)->y <= ay) if (((*i) -> x + (int)(*i)->getWidth()) >= ax) if (((*i) -> y + (int)(*i)->getHeight()) >= ay)
298 if ((*i)->getParent() != theHand) {
299 SpritePart *s = dynamic_cast<SpritePart *>(*i);
300 if (s && s->isTransparent() && obey_all_transparency)
301 // transparent parts in C1/C2 are scenery
302 // TODO: always true? you can't sekritly set parts to be transparent in C2?
303 if (engine.version < 3 || s->transparentAt(ax - s->x, ay - s->y))
304 continue;
305 if (needs_mouseable && !((*i)->getParent()->mouseable()))
306 continue;
307 if (!obey_all_transparency)
308 if ((*i)->getParent() != transagent)
309 continue;
310 return *i;
314 return 0;
317 void World::setUNID(Agent *whofor, int unid) {
318 assert(whofor->shared_from_this() == unidmap[unid].lock() || unidmap[unid].expired());
319 whofor->unid = unid;
320 unidmap[unid] = whofor->shared_from_this();
323 int World::newUNID(Agent *whofor) {
324 do {
325 int unid = rand();
326 if (unid && unidmap[unid].expired()) {
327 setUNID(whofor, unid);
328 return unid;
330 } while (1);
333 void World::freeUNID(int unid) {
334 unidmap.erase(unid);
337 shared_ptr<Agent> World::lookupUNID(int unid) {
338 if (unid == 0) return shared_ptr<Agent>();
339 return unidmap[unid].lock();
342 void World::drawWorld() {
343 drawWorld(&camera, engine.backend->getMainSurface());
346 void World::drawWorld(Camera *cam, Surface *surface) {
347 assert(surface);
349 MetaRoom *m = cam->getMetaRoom();
350 if (!m) {
351 // Whoops - the room we're in vanished, or maybe we were never in one?
352 // Try to get a new one ...
353 m = map.getFallbackMetaroom();
354 if (!m)
355 throw creaturesException("drawWorld() couldn't find any metarooms");
356 cam->goToMetaRoom(m->id);
358 int adjustx = cam->getX();
359 int adjusty = cam->getY();
360 shared_ptr<creaturesImage> bkgd = m->getBackground(""); // TODO
362 // TODO: work out what c2e does when it doesn't have a background..
363 if (!bkgd) return;
365 assert(bkgd->numframes() > 0);
367 int sprwidth = bkgd->width(0);
368 int sprheight = bkgd->height(0);
370 // draw the blk
371 for (unsigned int i = 0; i < (m->fullheight() / sprheight); i++) {
372 for (unsigned int j = 0; j < (m->fullwidth() / sprwidth); j++) {
373 // figure out which block number to use
374 unsigned int whereweare = j * (m->fullheight() / sprheight) + i;
376 // make one pass for non-wraparound rooms, or two passes for wraparound ones
377 // TODO: implement this in a more sensible way, or at least optimise it
378 for (unsigned int z = 0; z < (m->wraparound() ? 2 : 1); z++) {
379 int destx = (j * sprwidth) - adjustx + m->x();
380 int desty = (i * sprheight) - adjusty + m->y();
382 // if we're on the second pass, render to the *right* of the normal area
383 if (z == 1) destx += m->width();
385 // if the block's on screen, render it.
386 if ((destx >= -sprwidth) && (desty >= -sprheight) &&
387 (destx - sprwidth <= (int)surface->getWidth()) &&
388 (desty - sprheight <= (int)surface->getHeight()))
389 surface->render(bkgd, whereweare, destx, desty, false, 0, false, true);
394 // render all the agents
395 for (std::multiset<renderable *, renderablezorder>::iterator i = renders.begin(); i != renders.end(); i++) {
396 if ((*i)->showOnRemoteCameras() || cam == &camera) {
397 // three-pass for wraparound rooms, the third since agents often straddle the boundary
398 // TODO: same as above with background rendering
399 for (unsigned int z = 0; z < (m->wraparound() ? 3 : 1); z++) {
400 int newx = -adjustx, newy = -adjusty;
401 if (z == 1) newx += m->width();
402 else if (z == 2) newx -= m->width();
403 (*i)->render(surface, newx, newy);
408 // render port connection lines. TODO: these should be rendered as some kind
409 // of renderable, not directly like this.
410 for (std::list<boost::shared_ptr<Agent> >::iterator i = world.agents.begin(); i != world.agents.end(); i++) {
411 boost::shared_ptr<Agent> a = *i;
412 if (!a) continue;
413 for (std::map<unsigned int, boost::shared_ptr<OutputPort> >::iterator p = a->outports.begin();
414 p != a->outports.end(); p++) {
415 for (PortConnectionList::iterator c = p->second->dests.begin(); c != p->second->dests.end(); c++) {
416 if (!c->first) continue;
417 InputPort *target = c->first->inports[c->second].get();
418 surface->renderLine(a->x + p->second->x - adjustx, a->y + p->second->y - adjusty,
419 c->first->x + target->x - adjustx, c->first->y + target->y - adjusty, 0x00ff00ff);
424 if (showrooms) {
425 shared_ptr<Room> r = map.roomAt(hand()->x, hand()->y);
426 for (std::vector<shared_ptr<Room> >::iterator i = cam->getMetaRoom()->rooms.begin();
427 i != cam->getMetaRoom()->rooms.end(); i++) {
428 unsigned int col = 0xFFFF00CC;
429 if (*i == r) col = 0xFF00FFCC;
430 else if (r) {
431 if ((**i).doors.find(r) != (**i).doors.end())
432 col = 0x00FFFFCC;
435 // rooms don't wrap over the boundary, so just draw twice
436 for (unsigned int z = 0; z < (m->wraparound() ? 2 : 1); z++) {
437 int newx = adjustx;
438 if (z == 1)
439 newx -= m->width();
441 (*i)->renderBorders(surface, newx, adjusty, col);
446 if (hand()->holdingWire) {
447 if (!hand()->wireOriginAgent) {
448 hand()->holdingWire = 0;
449 } else {
450 int x, y;
451 if (hand()->holdingWire == 1) {
452 // holding from outport
453 OutputPort *out = hand()->wireOriginAgent->outports[hand()->wireOriginID].get();
454 x = out->x; y = out->y;
455 } else {
456 // holding from inport
457 InputPort *in = hand()->wireOriginAgent->inports[hand()->wireOriginID].get();
458 x = in->x; y = in->y;
460 surface->renderLine(x + hand()->wireOriginAgent->x - adjustx,
461 y + hand()->wireOriginAgent->y - adjusty, hand()->x - adjustx, hand()->y - adjusty, 0x00ff00ff);
465 surface->renderDone();
468 void World::executeInitScript(fs::path p) {
469 assert(fs::exists(p));
470 assert(!fs::is_directory(p));
472 std::string x = p.native_file_string();
473 std::ifstream s(x.c_str());
474 assert(s.is_open());
475 //std::cout << "executing script " << x << "...\n";
476 //std::cout.flush(); std::cerr.flush();
477 try {
478 caosScript script(gametype, x);
479 script.parse(s);
480 caosVM vm(0);
481 script.installScripts();
482 vm.runEntirely(script.installer);
483 } catch (creaturesException &e) {
484 std::cerr << "exec of \"" << p.leaf() << "\" failed due to exception " << e.prettyPrint() << std::endl;
485 } catch (std::exception &e) {
486 std::cerr << "exec of \"" << p.leaf() << "\" failed due to exception " << e.what() << std::endl;
488 std::cout.flush(); std::cerr.flush();
491 void World::executeBootstrap(fs::path p) {
492 if (!fs::is_directory(p)) {
493 executeInitScript(p);
494 return;
497 std::vector<fs::path> scripts;
499 fs::directory_iterator fsend;
500 for (fs::directory_iterator d(p); d != fsend; ++d) {
501 if ((!fs::is_directory(*d)) && (fs::extension(*d) == ".cos"))
502 scripts.push_back(*d);
505 std::sort(scripts.begin(), scripts.end());
506 for (std::vector<fs::path>::iterator i = scripts.begin(); i != scripts.end(); i++)
507 executeInitScript(*i);
510 void World::executeBootstrap(bool switcher) {
511 if (engine.version < 3) {
512 // read from Eden.sfc
514 if (data_directories.size() == 0)
515 throw creaturesException("C1/2 can't run without data directories!");
517 // TODO: case-sensitivity for the lose
518 fs::path edenpath(data_directories[0] / "/Eden.sfc");
519 if (fs::exists(edenpath) && !fs::is_directory(edenpath)) {
520 SFCFile sfc;
521 std::ifstream f(edenpath.native_directory_string().c_str(), std::ios::binary);
522 f >> std::noskipws;
523 sfc.read(&f);
524 sfc.copyToWorld();
525 return;
526 } else
527 throw creaturesException("couldn't find file Eden.sfc, required for C1/2");
530 // TODO: this code is possibly wrong with multiple bootstrap directories
531 std::multimap<std::string, fs::path> bootstraps;
533 for (std::vector<fs::path>::iterator i = data_directories.begin(); i != data_directories.end(); i++) {
534 assert(fs::exists(*i));
535 assert(fs::is_directory(*i));
536 fs::path b(*i / "/Bootstrap/");
537 if (fs::exists(b) && fs::is_directory(b)) {
538 fs::directory_iterator fsend;
539 // iterate through each bootstrap directory
540 for (fs::directory_iterator d(b); d != fsend; ++d) {
541 if (fs::exists(*d) && fs::is_directory(*d)) {
542 std::string s = (*d).leaf();
543 // TODO: cvillage has switcher code in 'Startup', so i included it here too
544 if (s == "000 Switcher" || s == "Startup") {
545 if (!switcher) continue;
546 } else {
547 if (switcher) continue;
550 bootstraps.insert(std::pair<std::string, fs::path>(s, *d));
556 for (std::multimap<std::string, fs::path>::iterator i = bootstraps.begin(); i != bootstraps.end(); i++) {
557 executeBootstrap(i->second);
561 void World::initCatalogue() {
562 for (std::vector<fs::path>::iterator i = data_directories.begin(); i != data_directories.end(); i++) {
563 assert(fs::exists(*i));
564 assert(fs::is_directory(*i));
566 fs::path c(*i / "/Catalogue/");
567 if (fs::exists(c) && fs::is_directory(c))
568 catalogue.initFrom(c);
572 #include "PathResolver.h"
573 std::string World::findFile(std::string name) {
574 // Go backwards, so we find files in more 'modern' directories first..
575 for (int i = data_directories.size() - 1; i != -1; i--) {
576 fs::path p = data_directories[i];
577 std::string r = (p / fs::path(name, fs::native)).native_directory_string();
578 if (resolveFile(r))
579 return r;
582 return "";
585 std::vector<std::string> World::findFiles(std::string dir, std::string wild) {
586 std::vector<std::string> possibles;
588 // Go backwards, so we find files in more 'modern' directories first..
589 for (int i = data_directories.size() - 1; i != -1; i--) {
590 fs::path p = data_directories[i];
591 std::string r = (p / fs::path(dir, fs::native)).native_directory_string();
592 std::vector<std::string> results = findByWildcard(r, wild);
593 possibles.insert(possibles.end(), results.begin(), results.end()); // merge results
596 return possibles;
599 std::string World::getUserDataDir() {
600 return (data_directories.end() - 1)->native_directory_string();
603 void World::selectCreature(boost::shared_ptr<Agent> a) {
604 if (a) {
605 CreatureAgent *c = dynamic_cast<CreatureAgent *>(a.get());
606 caos_assert(c);
609 if (selectedcreature != a) {
610 for (std::list<boost::shared_ptr<Agent> >::iterator i = world.agents.begin(); i != world.agents.end(); i++) {
611 if (!*i) continue;
612 (*i)->queueScript(120, 0, caosVar(a), caosVar(selectedcreature)); // selected creature changed
615 selectedcreature = a;
619 shared_ptr<genomeFile> World::loadGenome(std::string &genefile) {
620 std::vector<std::string> possibles = findFiles("/Genetics/", genefile + ".gen");
621 if (possibles.empty()) return shared_ptr<genomeFile>();
622 genefile = possibles[(int)((float)possibles.size() * (rand() / (RAND_MAX + 1.0)))];
624 shared_ptr<genomeFile> p(new genomeFile());
625 std::ifstream gfile(genefile.c_str(), std::ios::binary);
626 caos_assert(gfile.is_open());
627 gfile >> std::noskipws;
628 gfile >> *(p.get());
630 return p;
633 void World::newMoniker(shared_ptr<genomeFile> g, std::string genefile, AgentRef agent) {
634 std::string d = history.newMoniker(g);
635 world.history.getMoniker(d).addEvent(2, "", genefile);
636 world.history.getMoniker(d).moveToAgent(agent);
639 std::string World::generateMoniker(std::string basename) {
640 if (engine.version < 3) {
641 /* old-style monikers are four characters in a format like 9GVC */
642 unsigned int n = 1 + (unsigned int)(9.0 * (rand() / (RAND_MAX + 1.0)));
643 std::string moniker = boost::str(boost::format("%d") % n);
644 for (unsigned int i = 0; i < 3; i++) {
645 unsigned int n = (unsigned int)(26.0 * (rand() / (RAND_MAX + 1.0)));
646 moniker += boost::str(boost::format("%c") % (char)('A' + n));
648 return moniker;
651 // TODO: is there a better way to handle this? incoming basename is from catalogue files..
652 if (basename.size() != 4) {
653 std::cout << "World::generateMoniker got passed '" << basename << "' as a basename which isn't 4 characters, so ignoring it" << std::endl;
654 basename = "xxxx";
657 std::string x = basename;
658 for (unsigned int i = 0; i < 4; i++) {
659 unsigned int n = (unsigned int) (0xfffff * (rand() / (RAND_MAX + 1.0)));
660 x = x + "-" + boost::str(boost::format("%05x") % n);
663 return x;
666 boost::shared_ptr<AudioSource> World::playAudio(std::string filename, AgentRef agent, bool controlled, bool loop, bool followviewport) {
667 if (filename.size() == 0) return boost::shared_ptr<AudioSource>();
669 boost::shared_ptr<AudioSource> sound = engine.audio->newSource();
670 if (!sound) return boost::shared_ptr<AudioSource>();
672 AudioClip clip = engine.audio->loadClip(filename);
673 if (!clip) {
674 // note that more specific error messages can be thrown by implementations of loadClip
675 if (engine.version < 3) return boost::shared_ptr<AudioSource>(); // creatures 1 and 2 ignore non-existent audio clips
676 throw creaturesException("failed to load audio clip " + filename);
679 sound->setClip(clip);
681 if (loop) {
682 assert(controlled);
683 sound->setLooping(true);
686 if (agent) {
687 assert(!followviewport);
689 agent->updateAudio(sound);
690 if (controlled)
691 agent->sound = sound;
692 else
693 uncontrolled_sounds.push_back(std::pair<boost::shared_ptr<class AudioSource>, bool>(sound, false));
694 } else {
695 assert(!controlled);
697 // TODO: handle non-agent sounds
698 sound->setPos(world.camera.getXCentre(), world.camera.getYCentre(), 0);
699 uncontrolled_sounds.push_back(std::pair<boost::shared_ptr<class AudioSource>, bool>(sound, followviewport));
702 sound->play();
704 return sound;
707 int World::findCategory(unsigned char family, unsigned char genus, unsigned short species) {
708 if (!catalogue.hasTag("Agent Classifiers")) return -1;
710 const std::vector<std::string> &t = catalogue.getTag("Agent Classifiers");
712 for (unsigned int i = 0; i < t.size(); i++) {
713 std::string buffer = boost::str(boost::format("%d %d %d") % (int)family % (int)genus % (int)species);
714 if (t[i] == buffer) return i;
715 buffer = boost::str(boost::format("%d %d 0") % (int)family % (int)genus);
716 if (t[i] == buffer) return i;
717 buffer = boost::str(boost::format("%d 0 0") % (int)family);
718 if (t[i] == buffer) return i;
719 // leave it here: 0 0 0 would be silly to have in Agent Classifiers.
722 return -1;
725 /* vim: set noet: */