5 * Created by Alyssa Milburn on Tue May 25 2004.
6 * Copyright (c) 2004 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.
26 #include "AudioBackend.h"
27 #include <boost/format.hpp>
30 #include "AgentHelpers.h"
31 #include "creaturesImage.h"
33 void Agent::core_init() {
38 Agent::Agent(unsigned char f
, unsigned char g
, unsigned short s
, unsigned int p
) :
39 vm(0), zorder(p
), timerrate(0), visible(true) {
42 setClassifier(f
, g
, s
);
51 if (engine
.version
== 2) {
56 size
= 127; // TODO: correct default?
58 // TODO: it looks like grav should be 0, but make sure!
59 } else if (engine
.version
> 2) {
64 perm
= 50; // TODO: correct default?
70 // TODO: is this the correct default?
71 clac
[0] = 0; // message# for activate 1
72 if (engine
.version
< 3) {
73 // TODO: is this the correct default? (this is equivalent to bhvr click == 0)
74 clac
[0] = -1; clac
[1] = -1; clac
[2] = -1;
81 paused
= displaycore
= false;
83 cr_can_push
= cr_can_pull
= cr_can_stop
= cr_can_hit
= cr_can_eat
= cr_can_pickup
= false; // TODO: check this
84 imsk_key_down
= imsk_key_up
= imsk_mouse_move
= imsk_mouse_down
= imsk_mouse_up
= imsk_mouse_wheel
= imsk_translated_char
= false;
86 emitca_index
= -1; emitca_amount
= 0.0f
;
88 objp
.setAgent(0); // not strictly necessary
91 void Agent::finishInit() {
92 // lc2e, at least, seems to position agents centered on (-9876,-9876) to begin with
93 // TODO: where should we place agents in other games? is this code right at all anyway?
94 // (bear in mind that there are no parts present for some C1/C2 agents when finishInit is called, atm)
95 if (engine
.version
> 2 && !engine
.bmprenderer
) { // TODO: need to think about bmp-specific code some more
96 x
= -9876.0f
+ (getWidth() / 2.0f
); y
= -9876.0f
+ (getHeight() / 2.0f
);
99 // shared_from_this() can only be used if these is at least one extant
100 // shared_ptr which owns this
101 world
.agents
.push_front(boost::shared_ptr
<Agent
>(this));
102 agents_iter
= world
.agents
.begin();
105 queueScript(10); // constructor
110 void Agent::zotstack() {
112 for (std::list
<caosVM
*>::iterator i
= vmstack
.begin(); i
!= vmstack
.end(); i
++) {
118 void Agent::moveTo(float _x
, float _y
, bool force
) {
119 // Move ourselves to the specified location.
122 // if we're being carried and aren't being forced to move (prbly by our carrier), forget it
123 if (carriedby
&& !force
) return;
125 // TODO: what if we move while being carried? doomy explosions ensue, that's what!
126 float xoffset
= _x
- x
;
127 float yoffset
= _y
- y
;
132 // TODO: this is perhaps non-ideal
133 if (engine
.version
< 3 && xoffset
!= 0.0f
) {
134 // TODO: it'd be nice to handle multiple metarooms
135 MetaRoom
*m
= world
.map
.getFallbackMetaroom();
140 } else if (x
> m
->x() + m
->width()) {
145 for (std::vector
<AgentRef
>::iterator i
= floated
.begin(); i
!= floated
.end(); i
++) {
147 (*i
)->moveTo((*i
)->x
+ xoffset
, (*i
)->y
+ yoffset
);
150 adjustCarried(xoffset
, yoffset
);
153 void Agent::floatTo(AgentRef a
) {
154 std::vector
<AgentRef
>::iterator i
= std::find(floated
.begin(), floated
.end(), a
);
155 assert(i
== floated
.end()); // loops are bad, mmkay
157 if (floatable()) floatRelease();
159 if (floatable()) floatSetup();
162 void Agent::floatTo(float x
, float y
) {
164 moveTo(floatingagent
->x
+ x
, floatingagent
->y
+ y
);
166 moveTo(world
.camera
.getX() + x
, world
.camera
.getY() + y
);
170 void Agent::floatSetup() {
172 floatingagent
->addFloated(this);
174 world
.camera
.addFloated(this);
177 void Agent::floatRelease() {
179 floatingagent
->delFloated(this);
181 world
.camera
.delFloated(this);
184 void Agent::addFloated(AgentRef a
) {
186 assert(a
!= floatingagent
); // loops are bad, mmkay
187 floated
.push_back(a
);
190 void Agent::delFloated(AgentRef a
) {
192 std::vector
<AgentRef
>::iterator i
= std::find(floated
.begin(), floated
.end(), a
);
193 //if (i == floated.end()) return;
194 assert(i
!= floated
.end());
198 shared_ptr
<script
> Agent::findScript(unsigned short event
) {
199 return world
.scriptorium
.getScript(family
, genus
, species
, event
);
202 #include "PointerAgent.h"
203 #include "CreatureAgent.h"
204 #include "Creature.h"
205 bool Agent::fireScript(unsigned short event
, Agent
*from
, caosVar one
, caosVar two
) {
206 // Start running the specified script on the VM of this agent, with FROM set to the provided agent.
208 if (dying
) return false;
210 CreatureAgent
*c
= 0;
211 if (event
<= 3 || event
== 4 || event
== 12 || event
== 13 || event
== 14)
212 c
= dynamic_cast<CreatureAgent
*>(from
);
215 case 0: // deactivate
216 if (c
&& !cr_can_stop
) return false;
217 // TODO: not sure if this is the right place to do this.
220 case 1: // activate 1
221 if (c
&& !cr_can_push
) return false;
222 // TODO: not sure if this is the right place to do this.
225 case 2: // activate 2
226 if (c
&& !cr_can_pull
) return false;
227 // TODO: not sure if this is the right place to do this.
231 if (c
&& !cr_can_hit
) return false;
234 if (c
&& !cr_can_pickup
) return false;
235 if (!from
) return false;
236 if (from
== world
.hand()) {
237 if (!mouseable()) return false;
239 // TODO: valid check for vehicles?
240 if (!carryable()) return false;
242 from
->addCarried(this); // TODO: correct behaviour?
245 if (!from
) return false;
246 // TODO: this check isn't very good for vehicles ;p
247 // if (from != carriedby) return false;
250 if (c
&& !cr_can_eat
) return false;
252 case 13: // hold hands with pointer
257 case 14: // stop holding hands with pointer
262 case 92: // TODO: hack for 'UI Mouse Down' event - we need a real event system!
263 std::cout
<< "faking event 92 on " << identify() << std::endl
;
264 CompoundPart
*p
= world
.partAt(world
.hand()->x
, world
.hand()->y
);
265 if (!p
|| p
->getParent() != this) // if something is horridly broken here, return
266 return false; // was caos_assert(p && p->getParent() == this);
267 p
->handleClick(world
.hand()->x
- p
->x
- p
->getParent()->x
, world
.hand()->y
- p
->y
- p
->getParent()->y
);
268 // TODO: we're [obviously] missing firing the pointer script here, but it's a hack for now
272 bool ranscript
= false;
274 shared_ptr
<script
> s
= findScript(event
);
277 if (!vm
) { madevm
= true; vm
= world
.getVM(this); }
279 if (vm
->fireScript(s
, event
== 9, from
)) {
282 vm
->setVariables(one
, two
);
284 // TODO: we should set _it_ in a more sensible way
285 CreatureAgent
*a
= dynamic_cast<CreatureAgent
*>(this);
287 Creature
*c
= a
->getCreature();
289 vm
->_it_
= c
->getAttentionFocus();
302 from
->dropCarried(this); // TODO: correct?
309 bool Agent::queueScript(unsigned short event
, AgentRef from
, caosVar p0
, caosVar p1
) {
310 // Queue a script for execution on the VM of this agent.
312 if (dying
) return false;
314 // only bother firing the event if either it exists, or it's one with engine code attached
315 // TODO: why don't we do the engine checks/etc here?
318 if (!findScript(event
)) return false;
330 world
.queueScript(event
, this, from
, p0
, p1
);
336 int Agent::handleClick(float clickx
, float clicky
) {
337 // Handle a mouse click.
338 if (!activateable()) return -1;
340 // old-style click handling (c1/c2)
341 if (engine
.version
< 3) {
344 // look up the relevant action for our ACTV state from the clac table
345 if ((unsigned int)actv
.getInt() < 3)
346 action
= clac
[actv
.getInt()];
349 return calculateScriptId(action
);
355 // new-style click handling (c2e)
357 return -1; // TODO: handle CLIK
358 } else if (clac
[0] != -1) {
359 return calculateScriptId(clac
[0]);
365 void Agent::playAudio(std::string filename
, bool controlled
, bool loop
) {
369 world
.playAudio(filename
, this, controlled
, loop
);
372 bool agentOnCamera(Agent
*targ
, bool checkall
= false); // caosVM_camera.cpp
374 void Agent::updateAudio(boost::shared_ptr
<AudioSource
> s
) {
377 s
->setPos(x
+ getWidth() / 2, y
+ getHeight() / 2, zorder
);
378 // TODO: we could do with a nicer 'inrange' check
379 bool inrange
= (x
+ 500 > world
.camera
.getX()) && (x
+ getWidth() - 500 < world
.camera
.getX() + world
.camera
.getWidth()) &&
380 (y
+ 500 > world
.camera
.getY()) && (y
+ getHeight() - 500 < world
.camera
.getY() + world
.camera
.getHeight());
382 world
.camera
.getMetaRoom() != world
.map
.metaRoomAt(x
, y
)
386 // TODO: setVelocity?
389 Point
const Agent::boundingBoxPoint(unsigned int n
) {
390 return boundingBoxPoint(n
, Point(x
, y
), getWidth(), getHeight());
393 Point
const Agent::boundingBoxPoint(unsigned int n
, Point in
, unsigned int w
, unsigned int h
) {
399 p
.y
= in
.y
+ (h
/ 2.0f
);
404 p
.y
= in
.y
+ (h
/ 2.0f
);
408 p
.x
= in
.x
+ (w
/ 2.0f
);
413 p
.x
= in
.x
+ (w
/ 2.0f
);
418 throw creaturesException("Agent::boundingBoxPoint got unknown direction");
424 bool Agent::validInRoomSystem() {
425 // Return true if this agent is inside the world room system at present, or false if it isn't.
428 if (engine
.version
== 1) return true;
430 return validInRoomSystem(Point(x
, y
), getWidth(), getHeight(), perm
);
433 bool const Agent::validInRoomSystem(Point p
, unsigned int w
, unsigned int h
, int testperm
) {
434 // Return true if this agent is inside the world room system at the specified point, or false if it isn't.
436 for (unsigned int i
= 0; i
< 4; i
++) {
439 case 0: src
= boundingBoxPoint(0, p
, w
, h
); dest
= boundingBoxPoint(3, p
, w
, h
); break; // left to bottom
440 case 1: src
= boundingBoxPoint(1, p
, w
, h
); dest
= boundingBoxPoint(3, p
, w
, h
); break; // right to bottom
441 case 2: src
= boundingBoxPoint(2, p
, w
, h
); dest
= boundingBoxPoint(0, p
, w
, h
); break; // top to left
442 case 3: src
= boundingBoxPoint(2, p
, w
, h
); dest
= boundingBoxPoint(1, p
, w
, h
); break; // top to right
445 if (engine
.version
== 2) {
446 // Creatures 2 physics
448 int dx
= dest
.x
- src
.x
;
449 int dy
= dest
.y
- src
.y
;
452 double delta
= 1000000000;
453 bool collided
= false;
455 // TODO: check suffercollisions?
457 // TODO: muh, provided direction here is kinda a hack
458 findCollisionInDirection((i
< 2) ? 3 : 2, src
, dx
, dy
, deltapt
, delta
, collided
, true);
459 if (collided
) return false;
461 // Creatures 3 physics
463 float srcx
= src
.x
, srcy
= src
.y
;
465 shared_ptr
<Room
> ourRoom
= world
.map
.roomAt(srcx
, srcy
);
466 if (!ourRoom
) return false;
468 unsigned int dir
; Line wall
;
469 world
.map
.collideLineWithRoomSystem(src
, dest
, ourRoom
, src
, wall
, dir
, testperm
);
471 if (src
!= dest
) return false;
478 void Agent::physicsTick() {
479 if (engine
.version
== 1) return; // C1 has no physics, and different attributes.
481 if (carriedby
) return; // We don't move when carried, so what's the point?
483 if (engine
.version
== 2) {
484 // Creatures 2 physics is different.
489 if (!wasmoved
) return; // some agents are created outside INST and get autokilled if we try physics on them before they move
491 // set destination point based on velocities
492 float destx
= x
+ velx
.getFloat();
493 float desty
= y
+ vely
.getFloat();
495 if (sufferphysics()) {
496 // TODO: falling behaviour needs looking at more closely..
497 // .. but it shouldn't be 'false' by default on non-physics agents, so..
499 // increase speed according to accg
500 // TODO: should we be changing vely first, instead of after a successful move (below)?
501 desty
+= accg
.getFloat();
504 if (suffercollisions()) {
505 float lastdistance
= 1000000.0f
;
506 bool collided
= false;
507 Line wall
; // only valid when collided
508 unsigned int collidedirection
= 0; // only valid when collided
511 // iterate through all four points of the bounding box
512 for (unsigned int i
= 0; i
< 4; i
++) {
513 // this mess is because we want to start with the bottom point - DOWN (3) - before the others, for efficiency
514 Point src
= boundingBoxPoint((i
== 0) ? 3 : i
- 1);
517 float srcx
= src
.x
, srcy
= src
.y
;
519 shared_ptr
<Room
> ourRoom
= world
.map
.roomAt(srcx
, srcy
);
521 ourRoom
= world
.map
.roomAt(srcx
, srcy
);
524 if (!displaycore
) { // TODO: ugh, displaycore is a horrible thing to use for this
525 // we're out of the room system, physics bug, but let's try MVSFing back in to cover for fuzzie's poor programming skills
526 static bool tryingmove
; tryingmove
= false; // avoid infinite loop
527 if (!tryingmove
&& tryMoveToPlaceAround(x
, y
)) {
528 //std::cout << identify() << " was out of room system due to a physics bug but we hopefully moved it back in.." << std::endl;
535 unhandledException(boost::str(boost::format("out of room system at (%f, %f)") % srcx
% srcy
), false);
538 return; // out of room system
541 Point
dest(destx
+ (srcx
- x
), desty
+ (srcy
- y
));
542 unsigned int local_collidedirection
;
545 // this changes src to the point at which we end up
546 bool local_collided
= world
.map
.collideLineWithRoomSystem(src
, dest
, ourRoom
, src
, local_wall
, local_collidedirection
, perm
);
549 if (src
.x
== srcx
&& src
.y
== srcy
)
552 float xdiff
= src
.x
- srcx
;
553 float ydiff
= src
.y
- srcy
;
554 dist
= xdiff
*xdiff
+ ydiff
*ydiff
;
557 if (dist
>= lastdistance
) {
558 assert(i
!= 0); // this had better not be our first collision!
559 continue; // further away than a previous collision
563 bestmove
.x
= x
+ (src
.x
- srcx
);
564 bestmove
.y
= y
+ (src
.y
- srcy
);
565 collidedirection
= local_collidedirection
;
567 collided
= local_collided
;
570 break; // no point checking any more, is there?
573 // *** do actual movement
574 if (lastdistance
!= 0.0f
) {
575 moveTo(bestmove
.x
, bestmove
.y
);
578 lastcollidedirection
= collidedirection
;
579 queueScript(6, 0, velx
, vely
); // TODO: include this? .. we need to include SOMETHING, c3 ball checks for <3
582 if (wall
.getType() == HORIZONTAL
) {
583 vely
.setFloat(-vely
.getFloat());
584 } else if (wall
.getType() == VERTICAL
) {
585 velx
.setFloat(-velx
.getFloat());
587 // line starts always have a lower x value than the end
588 float xdiff
= wall
.getEnd().x
- wall
.getStart().x
;
589 float ydiff
= wall
.getEnd().y
- wall
.getStart().y
;
590 float fvelx
= velx
.getFloat(), fvely
= vely
.getFloat();
592 // calculate input/slope angles
596 inputangle
= M_PI
/ 2.0;
598 inputangle
= 3 * (M_PI
/ 2.0);
600 inputangle
= atan(fvely
/ fvelx
);
602 double slopeangle
= atan(-ydiff
/ xdiff
); // xdiff != 0 because wall isn't vertical
604 // calculate output angle
605 double outputangle
= slopeangle
+ (slopeangle
- inputangle
) + M_PI
;
607 // turn back into component velocities
608 double vectorlength
= sqrt(fvelx
*fvelx
+ fvely
*fvely
);
609 float xoutput
= cos(outputangle
) * vectorlength
;
610 float youtput
= sin(outputangle
) * vectorlength
;
612 velx
.setFloat(xoutput
);
613 vely
.setFloat(-youtput
);
616 if (elas
!= 100.0f
) {
617 velx
.setFloat(velx
.getFloat() * (elas
/ 100.0f
));
618 vely
.setFloat(vely
.getFloat() * (elas
/ 100.0f
));
622 } else if (sufferphysics() && accg
!= 0) {
623 falling
= true; // TODO: icky
624 vely
.setFloat(vely
.getFloat() + accg
.getFloat());
626 } else { velx
.setFloat(0); vely
.setFloat(0); } // TODO: correct?
628 if (vely
.hasDecimal() || velx
.hasDecimal())
629 moveTo(destx
, desty
);
631 vely
.setFloat(vely
.getFloat() + accg
.getFloat());
634 if (sufferphysics() && (aero
!= 0)) {
635 // reduce speed according to AERO
636 // TODO: aero should be an integer!
637 velx
.setFloat(velx
.getFloat() - (velx
.getFloat() * (aero
.getFloat() / 100.0f
)));
638 vely
.setFloat(vely
.getFloat() - (vely
.getFloat() * (aero
.getFloat() / 100.0f
)));
642 shared_ptr
<Room
> const Agent::bestRoomAt(unsigned int tryx
, unsigned int tryy
, unsigned int direction
, shared_ptr
<Room
> exclude
) {
643 std::vector
<shared_ptr
<Room
> > rooms
= world
.map
.roomsAt(tryx
, tryy
);
647 if (rooms
.size() == 0) return shared_ptr
<Room
>();
648 if (rooms
.size() == 1) r
= rooms
[0];
649 else if (rooms
.size() > 1) {
651 for (j
= 0; j
< rooms
.size(); j
++) {
652 if (rooms
[j
] == exclude
) continue;
654 if (direction
== 0) { // left
655 if (rooms
[j
]->x_left
== tryx
) break;
656 } else if (direction
== 1) { // right
657 if (rooms
[j
]->x_right
== tryx
) break;
658 } else if (direction
== 2) { // top
659 if (rooms
[j
]->y_left_ceiling
== tryy
) break;
661 if (rooms
[j
]->y_left_floor
== tryy
) break;
664 if (j
== rooms
.size()) j
= 0;
668 if (r
== exclude
) return shared_ptr
<Room
>();
672 // Creatures 2 collision finding
673 void const Agent::findCollisionInDirection(unsigned int i
, Point src
, int &dx
, int &dy
, Point
&deltapt
, double &delta
, bool &collided
, bool followrooms
) {
677 shared_ptr
<Room
> room
= bestRoomAt(src
.x
, src
.y
, i
, shared_ptr
<Room
>());
679 if (!room
) { // out of room system
681 unhandledException(boost::str(boost::format("out of room system at (%f, %f)") % src
.x
% src
.y
), false);
689 bool steep
= abs(dy
) > abs(dx
);
691 int signdx
= dx
< 0 ? -1 : 1;
692 int signdy
= dy
< 0 ? -1 : 1;
694 Line
l(Point(0,0),Point(dx
,dy
));
696 Point
lastpoint(0,0);
698 for (int loc
= 0; loc
<= abs(steep
? dy
: dx
); loc
++) {
699 Point p
= steep
? l
.pointAtY(loc
*signdy
) : l
.pointAtX(loc
*signdx
);
703 bool trycollisions
= false;
705 bool endofroom
= false;
707 if (src
.x
+ p
.x
< room
->x_left
) {
708 if (i
!= 1 && dx
< 0) { trycollisions
= true; lastdirection
= 0; }
711 if (src
.x
+ p
.x
> room
->x_right
) {
712 if (i
!= 0 && dx
> 0) { trycollisions
= true; lastdirection
= 1; }
715 if (src
.y
+ p
.y
< room
->y_left_ceiling
) {
716 if (i
!= 3 && dy
< 0) { trycollisions
= true; lastdirection
= 2; }
719 if (src
.y
+ p
.y
> room
->y_left_floor
) {
720 if (i
!= 2 && dy
> 0) { trycollisions
= true; lastdirection
= 3; }
724 // find the next room, if necessary, and work out whether we should move into it or break out
726 shared_ptr
<Room
> newroom
= bestRoomAt(src
.x
+ p
.x
, src
.y
+ p
.y
, i
, room
);
728 bool collision
= false;
730 // collide if we're out of the room system
731 if (!newroom
) { collision
= true; }
732 // collide if there's no new room connected to this one
733 else if (room
->doors
.find(newroom
) == room
->doors
.end()) { collision
= true; }
734 // collide if the PERM between this room and the new room is smaller than or equal to our size
735 else if (size
.getInt() > room
->doors
[newroom
]->perm
) { collision
= true; }
737 if (collision
&& (trycollisions
|| (!newroom
))) {
742 // move to the new room and keep going!
746 if (room
->floorpoints
.size() && i
== 3 && dy
>= 0 && size
.getInt() > room
->floorvalue
.getInt()) { // TODO: Hack!
747 // TODO: we don't check floorYatX isn't returning a 'real' room floor, but floorpoints should cover the whole floor anyway
748 int floory
= room
->floorYatX(src
.x
+ p
.x
);
750 // never collide when the top point of an object is below the floor.
751 Point top
= boundingBoxPoint(2);
753 // TODO: consider steep floors
754 if (src
.y
+ p
.y
> floory
&& top
.y
< floory
) {
761 if ((!followrooms
) && endofroom
) break;
766 double length2
= (lastpoint
.x
* lastpoint
.x
) + (lastpoint
.y
* lastpoint
.y
);
767 if (length2
< delta
) {
768 // TODO: !followrooms is a horrible way to detect a non-physics call
769 if (collided
&& followrooms
) lastcollidedirection
= lastdirection
;
775 void Agent::physicsTickC2() {
776 int dx
= velx
.getInt(), dy
= vely
.getInt();
778 if (dx
!= 0 || dy
!= 0) grav
.setInt(1);
780 if (grav
.getInt() != 0 && sufferphysics()) {
786 if (dy
== 0 && dx
== 0) { // nothing to do
787 if (vely
.getInt() == 0) {
791 // y motion cancelled by gravity
799 double delta
= 1000000000;
801 bool collided
= false;
803 if (suffercollisions()) {
804 for (unsigned int i
= 0; i
< 4; i
++) {
805 Point src
= boundingBoxPoint(i
);
806 findCollisionInDirection(i
, src
, dx
, dy
, deltapt
, delta
, collided
, true);
813 if (collided
&& (velx
.getInt() != 0 || vely
.getInt() != 0)) {
814 if (lastcollidedirection
>= 2) // up and down
815 vely
.setInt(-(vely
.getInt() - (rest
.getInt() * vely
.getInt()) / 100));
817 velx
.setInt(-(velx
.getInt() - (rest
.getInt() * velx
.getInt()) / 100));
820 if ((int)deltapt
.x
== 0 && (int)deltapt
.y
== 0) {
825 moveTo(x
+ (int)deltapt
.x
, y
+ (int)deltapt
.y
);
826 if (sufferphysics()) {
827 int fricx
= (aero
.getInt() * velx
.getInt()) / 100;
828 int fricy
= (aero
.getInt() * vely
.getInt()) / 100;
829 if (abs(velx
.getInt()) > 0 && fricx
== 0) fricx
= (velx
.getInt() < 0) ? -1 : 1;
830 if (abs(vely
.getInt()) > 0 && fricy
== 0) fricy
= (vely
.getInt() < 0) ? -1 : 1;
831 velx
.setInt(velx
.getInt() - fricx
);
832 vely
.setInt(vely
.getInt() - fricy
);
838 // sanity checks to stop ticks on dead agents
843 // if the sound is no longer playing...
844 if (sound
->getState() != SS_PLAY
) {
845 // ...release our reference to it
848 // otherwise, reposition audio
853 // don't tick paused agents
857 if (emitca_index
!= -1 && emitca_amount
!= 0.0f
) {
858 assert(0 <= emitca_index
&& emitca_index
<= 19);
859 shared_ptr
<Room
> r
= world
.map
.roomAt(x
, y
);
861 r
->catemp
[emitca_index
] += emitca_amount
;
862 /*if (r->catemp[emitca_index] <= 0.0f) r->catemp[emitca_index] = 0.0f;
863 else if (r->catemp[emitca_index] >= 1.0f) r->catemp[emitca_index] = 1.0f;*/
867 // tick the physics engine
869 if (dying
) return; // in case we were autokilled
871 // update the timer if needed, and then queue a timer event if necessary
873 tickssincelasttimer
++;
874 if (tickssincelasttimer
== timerrate
) {
875 queueScript(9); // TODO: include this?
876 tickssincelasttimer
= 0;
878 assert(timerrate
> tickssincelasttimer
);
885 void Agent::unhandledException(std::string info
, bool wasscript
) {
886 // TODO: do something with this? empty the stack?
887 if (world
.autostop
) {
888 // autostop is mostly for Creatures Village, which is full of buggy scripts
890 } else if (world
.autokill
&& !dynamic_cast<CreatureAgent
*>(this)) { // don't autokill creatures! TODO: someday we probably should :)
893 std::cerr
<< identify() << " was autokilled during script " << lastScript
<< " due to: " << info
<< std::endl
;
895 std::cerr
<< identify() << " was autokilled due to: " << info
<< std::endl
;
899 std::cerr
<< identify() << " caused an exception during script " << lastScript
<< ": " << info
<< std::endl
;
901 std::cerr
<< identify() << " caused an exception: " << info
<< std::endl
;
905 void Agent::vmTick() {
906 // Tick the VM associated with this agent.
908 assert(vm
); // There must *be* a VM to tick.
909 LifeAssert
la(this); // sanity check
911 // If we're out of timeslice, give ourselves some more (depending on the game type).
912 if (!vm
->timeslice
) {
913 vm
->timeslice
= (engine
.version
< 3) ? 1 : 5;
916 // Keep trying to run VMs while we don't run out of timeslice, end up with a blocked VM, or a VM stops.
917 while (vm
&& vm
->timeslice
&& !vm
->isBlocking() && !vm
->stopped()) {
918 assert(vm
->timeslice
> 0);
920 // Tell the VM to tick (using all available timeslice), catching exceptions as necessary.
923 } catch (invalidAgentException
&e
) {
924 // try letting the exception script handle it
925 if (!queueScript(255))
926 unhandledException(e
.prettyPrint(), true);
928 stopScript(); // we still want current script to die
929 } catch (creaturesException
&e
) {
930 unhandledException(e
.prettyPrint(), true);
931 } catch (std::exception
&e
) {
932 unhandledException(e
.what(), true);
935 // If the VM stopped, it's done.
936 if (vm
&& vm
->stopped()) {
942 // Zot any remaining timeslice, since we're done now.
943 if (vm
) vm
->timeslice
= 0;
945 // If there's no current VM but there's one on the call stack, a previous VM must have finished,
946 // pop one from the call stack to run next time.
947 if (!vm
&& !vmstack
.empty()) {
948 vm
= vmstack
.front();
954 assert(lifecount
== 0);
956 if (!initialized
) return;
958 // we can't do kill() here because we can't do anything which might try using our shared_ptr
959 // (since this could be during world destruction)
978 if (floatable()) floatRelease();
979 if (carrying
) dropCarried(carrying
);
980 // TODO: should the carried agent really be responsible for dropping from vehicle?
981 if (invehicle
) invehicle
->drop(this);
983 dying
= true; // what a world, what a world...
992 agents_iter
->reset();
1000 unsigned int Agent::getZOrder() const {
1002 // TODO: take notice of cabp in c2e, at least. also, stacking .. ?
1003 Vehicle
*v
= dynamic_cast<Vehicle
*>(invehicle
.get());
1005 if (engine
.version
< 3)
1006 return v
->cabinplane
; // TODO: correct?
1008 return v
->getZOrder() + v
->cabinplane
;
1009 // TODO: Vehicle should probably rearrange zorder of passengers if ever moved
1010 } else if (carriedby
) {
1011 // TODO: check for overflow
1012 // TODO: is adding our own zorder here correct behaviour? someone should check
1013 if (engine
.version
> 1)
1014 return carriedby
->getZOrder() - 100;
1016 return carriedby
->getZOrder();
1022 void Agent::setZOrder(unsigned int z
) {
1027 int Agent::getUNID() const {
1030 return unid
= world
.newUNID(const_cast<Agent
*>(this));
1033 #include "Catalogue.h"
1035 std::string
Agent::identify() const {
1036 std::ostringstream o
;
1037 o
<< (int)family
<< " " << (int)genus
<< " " << species
;
1038 const std::string n
= catalogue
.getAgentName(family
, genus
, species
);
1040 o
<< " (" + n
+ ")";
1042 o << " unid " << unid;
1044 o << " (no unid assigned)"; */
1048 bool agentzorder::operator ()(const Agent
*s1
, const Agent
*s2
) const {
1049 return s1
->getZOrder() < s2
->getZOrder();
1052 void Agent::pushVM(caosVM
*newvm
) {
1055 vmstack
.push_front(vm
);
1059 bool Agent::vmStopped() {
1060 return (!vm
|| vm
->stopped());
1063 void Agent::stopScript() {
1069 void Agent::addCarried(AgentRef a
) {
1072 // TODO: muh, vehicle drop needs more thought
1074 Vehicle
*v
= dynamic_cast<Vehicle
*>(a
->invehicle
.get());
1081 // TODO: this doesn't reorder children or anything..
1082 a
->setZOrder(a
->zorder
);
1084 // fire 'Got Carried Agent'
1085 if (engine
.version
>= 3)
1086 queueScript(124, a
); // TODO: is this the correct param?
1089 void Agent::carry(AgentRef a
) {
1092 // TODO: check for infinite loops (eg, us carrying an agent which is carrying us) :)
1095 dropCarried(carrying
);
1099 a
->carriedby
= AgentRef(this);
1100 // TODO: move carrying agent to the right position
1101 adjustCarried(0, 0);
1104 bool Agent::beDropped() {
1105 carriedby
= AgentRef(0);
1106 bool wasinvehicle
= invehicle
;
1107 invehicle
= AgentRef(0);
1109 // TODO: this doesn't reorder children or anything..
1112 // TODO: no idea if this is right, it tries to re-enable gravity when dropping C2 agents
1113 if (engine
.version
== 2) grav
.setInt(1);
1114 if (engine
.version
== 3) falling
= true;
1116 if (!wasinvehicle
) { // ie, we're not being dropped by a vehicle
1117 // TODO: check for vehicles in a saner manner?
1118 for (std::list
<boost::shared_ptr
<Agent
> >::iterator i
= world
.agents
.begin(); i
!= world
.agents
.end(); i
++) {
1119 boost::shared_ptr
<Agent
> a
= (*i
);
1121 Vehicle
*v
= dynamic_cast<Vehicle
*>(a
.get());
1124 if (agentsTouching(this, v
)) {
1125 v
->addCarried(this);
1126 // TODO: how to handle not-invehicle case, where vehicle has failed to pick us up?
1127 if (invehicle
) return true;
1132 if (engine
.version
== 1) {
1133 MetaRoom
* m
= world
.map
.metaRoomAt(x
, y
);
1134 if (!m
) return false;
1135 shared_ptr
<Room
> r
= m
->nextFloorFromPoint(x
, y
);
1136 if (!r
) return false;
1137 moveTo(x
, r
->bot
.pointAtX(x
).y
- getHeight());
1139 // TODO: maybe think about this some more
1140 tryMoveToPlaceAround(x
, y
);
1143 // TODO: return value is not used anywhere yet?
1147 void Agent::dropCarried(AgentRef a
) {
1150 // TODO: this doesn't reorder children or anything..
1151 a
->setZOrder(a
->zorder
);
1153 // fire 'Lost Carried Agent'
1154 if (engine
.version
>= 3)
1155 queueScript(125, a
); // TODO: is this the correct param?
1158 void Agent::drop(AgentRef a
) {
1159 if (!carrying
) return;
1160 assert(carrying
== a
);
1163 carrying
= AgentRef(0);
1166 std::pair
<int, int> Agent::getCarryPoint() {
1167 unsigned int ourpose
= 0;
1170 if ((s
= dynamic_cast<SpritePart
*>(part(0))))
1171 ourpose
= s
->getBase() + s
->getPose();
1173 std::pair
<int, int> pos(0, 0);
1175 std::map
<unsigned int, std::pair
<int, int> >::iterator i
= carry_points
.find(ourpose
);
1176 if (i
!= carry_points
.end())
1182 std::pair
<int, int> Agent::getCarriedPoint() {
1183 unsigned int theirpose
= 0;
1186 if ((s
= dynamic_cast<SpritePart
*>(part(0))))
1187 theirpose
= s
->getBase() + s
->getPose();
1189 std::pair
<int, int> pos(0, 0);
1191 std::map
<unsigned int, std::pair
<int, int> >::iterator i
= carried_points
.find(theirpose
);
1192 if (i
!= carried_points
.end()) {
1194 } else if (s
&& engine
.version
> 1) {
1195 // TODO: why fudge pickup location here and not in default carried points or something?
1196 // (this is nornagon's code which i moved - fuzzie)
1197 pos
.first
= s
->getSprite()->width(s
->getCurrentSprite()) / 2;
1203 void Agent::adjustCarried(float unusedxoffset
, float unusedyoffset
) {
1204 // Adjust the position of the agent we're carrying.
1205 // TODO: this doesn't actually position the carried agent correctly, sigh
1207 if (!carrying
) return;
1209 unsigned int ourpose
= 0;
1211 int xoffset
= 0, yoffset
= 0;
1212 if (engine
.version
< 3 && world
.hand() == this) {
1213 // this appears to produce correct behaviour in the respective games, don't ask me -nornagon
1214 if (engine
.version
== 2) {
1215 xoffset
= world
.hand()->getWidth() / 2;
1216 yoffset
= world
.hand()->getHeight() / 2 - 2;
1218 yoffset
= world
.hand()->getHeight() / 2 - 3;
1221 std::pair
<int, int> carrypoint
= getCarryPoint();
1222 xoffset
+= carrypoint
.first
;
1223 yoffset
+= carrypoint
.second
;
1225 std::pair
<int, int> carriedpoint
= carrying
->getCarriedPoint();
1226 xoffset
-= carriedpoint
.first
;
1227 yoffset
-= carriedpoint
.second
;
1229 if (xoffset
!= 0 || yoffset
!= 0)
1230 carrying
->moveTo(x
+ xoffset
, y
+ yoffset
, true);
1233 void Agent::setClassifier(unsigned char f
, unsigned char g
, unsigned short s
) {
1238 if (engine
.version
< 3) {
1239 // TODO: categories for other game versions
1242 category
= world
.findCategory(family
, genus
, species
);
1246 bool Agent::tryMoveToPlaceAround(float x
, float y
) {
1247 if (!suffercollisions()) {
1252 // second hacky attempt, move from side to side (+/- width) and up (- height) a little
1253 unsigned int trywidth
= getWidth() * 2; if (trywidth
< 100) trywidth
= 100;
1254 unsigned int tryheight
= getHeight() * 2; if (tryheight
< 100) tryheight
= 100;
1255 for (unsigned int xadjust
= 0; xadjust
< trywidth
; xadjust
++) {
1256 for (unsigned int yadjust
= 0; yadjust
< tryheight
; yadjust
++) {
1257 if (validInRoomSystem(Point(x
- xadjust
, y
- yadjust
), getWidth(), getHeight(), perm
))
1258 moveTo(x
- xadjust
, y
- yadjust
);
1259 else if ((xadjust
!= 0) && validInRoomSystem(Point(x
+ xadjust
, y
- yadjust
), getWidth(), getHeight(), perm
))
1260 moveTo(x
+ xadjust
, y
- yadjust
);
1270 void Agent::join(unsigned int outid
, AgentRef dest
, unsigned int inid
) {
1272 assert(outports
.find(outid
) != outports
.end());
1273 assert(dest
->inports
.find(inid
) != dest
->inports
.end());
1274 outports
[outid
]->dests
.push_back(std::pair
<AgentRef
, unsigned int>(dest
, inid
));
1275 dest
->inports
[inid
]->source
= this;
1276 dest
->inports
[inid
]->sourceid
= outid
;
1279 /* vim: set noet: */