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;
248 from
->dropCarried(this); // TODO: correct?
251 if (c
&& !cr_can_eat
) return false;
253 case 13: // hold hands with pointer
258 case 14: // stop holding hands with pointer
263 case 92: // TODO: hack for 'UI Mouse Down' event - we need a real event system!
264 std::cout
<< "faking event 92 on " << identify() << std::endl
;
265 CompoundPart
*p
= world
.partAt(world
.hand()->x
, world
.hand()->y
);
266 if (!p
|| p
->getParent() != this) // if something is horridly broken here, return
267 return false; // was caos_assert(p && p->getParent() == this);
268 p
->handleClick(world
.hand()->x
- p
->x
- p
->getParent()->x
, world
.hand()->y
- p
->y
- p
->getParent()->y
);
269 // TODO: we're [obviously] missing firing the pointer script here, but it's a hack for now
273 bool ranscript
= false;
275 shared_ptr
<script
> s
= findScript(event
);
278 if (!vm
) { madevm
= true; vm
= world
.getVM(this); }
280 if (vm
->fireScript(s
, event
== 9, from
)) {
283 vm
->setVariables(one
, two
);
285 // TODO: we should set _it_ in a more sensible way
286 CreatureAgent
*a
= dynamic_cast<CreatureAgent
*>(this);
288 Creature
*c
= a
->getCreature();
290 vm
->_it_
= c
->getAttentionFocus();
303 if (invehicle
) break;
304 if (engine
.version
> 1) break;
306 // Creatures 1 drop snapping
307 // TODO: this probably doesn't belong here, but it has to be run after the
308 // drop script starts (see for instance C1 carrots, which change pose)
309 MetaRoom
* m
= world
.map
.metaRoomAt(x
, y
);
311 shared_ptr
<Room
> r
= m
->nextFloorFromPoint(x
, y
);
313 moveTo(x
, r
->bot
.pointAtX(x
).y
- getHeight());
321 bool Agent::queueScript(unsigned short event
, AgentRef from
, caosVar p0
, caosVar p1
) {
322 // Queue a script for execution on the VM of this agent.
324 if (dying
) return false;
326 // only bother firing the event if either it exists, or it's one with engine code attached
327 // TODO: why don't we do the engine checks/etc here?
330 if (!findScript(event
)) return false;
342 world
.queueScript(event
, this, from
, p0
, p1
);
348 int Agent::handleClick(float clickx
, float clicky
) {
349 // Handle a mouse click.
350 if (!activateable()) return -1;
352 // old-style click handling (c1/c2)
353 if (engine
.version
< 3) {
356 // look up the relevant action for our ACTV state from the clac table
357 if ((unsigned int)actv
.getInt() < 3)
358 action
= clac
[actv
.getInt()];
361 return calculateScriptId(action
);
367 // new-style click handling (c2e)
369 return -1; // TODO: handle CLIK
370 } else if (clac
[0] != -1) {
371 return calculateScriptId(clac
[0]);
377 void Agent::playAudio(std::string filename
, bool controlled
, bool loop
) {
381 world
.playAudio(filename
, this, controlled
, loop
);
384 bool agentOnCamera(Agent
*targ
, bool checkall
= false); // caosVM_camera.cpp
386 void Agent::updateAudio(boost::shared_ptr
<AudioSource
> s
) {
389 s
->setPos(x
+ getWidth() / 2, y
+ getHeight() / 2, zorder
);
390 // TODO: we could do with a nicer 'inrange' check
391 bool inrange
= (x
+ 500 > world
.camera
.getX()) && (x
+ getWidth() - 500 < world
.camera
.getX() + world
.camera
.getWidth()) &&
392 (y
+ 500 > world
.camera
.getY()) && (y
+ getHeight() - 500 < world
.camera
.getY() + world
.camera
.getHeight());
394 world
.camera
.getMetaRoom() != world
.map
.metaRoomAt(x
, y
)
398 // TODO: setVelocity?
401 Point
const Agent::boundingBoxPoint(unsigned int n
) {
402 return boundingBoxPoint(n
, Point(x
, y
), getWidth(), getHeight());
405 Point
const Agent::boundingBoxPoint(unsigned int n
, Point in
, unsigned int w
, unsigned int h
) {
411 p
.y
= in
.y
+ (h
/ 2.0f
);
416 p
.y
= in
.y
+ (h
/ 2.0f
);
420 p
.x
= in
.x
+ (w
/ 2.0f
);
425 p
.x
= in
.x
+ (w
/ 2.0f
);
430 throw creaturesException("Agent::boundingBoxPoint got unknown direction");
436 bool Agent::validInRoomSystem() {
437 // Return true if this agent is inside the world room system at present, or false if it isn't.
440 if (engine
.version
== 1) return true;
442 return validInRoomSystem(Point(x
, y
), getWidth(), getHeight(), perm
);
445 bool const Agent::validInRoomSystem(Point p
, unsigned int w
, unsigned int h
, int testperm
) {
446 // Return true if this agent is inside the world room system at the specified point, or false if it isn't.
448 for (unsigned int i
= 0; i
< 4; i
++) {
451 case 0: src
= boundingBoxPoint(0, p
, w
, h
); dest
= boundingBoxPoint(3, p
, w
, h
); break; // left to bottom
452 case 1: src
= boundingBoxPoint(1, p
, w
, h
); dest
= boundingBoxPoint(3, p
, w
, h
); break; // right to bottom
453 case 2: src
= boundingBoxPoint(2, p
, w
, h
); dest
= boundingBoxPoint(0, p
, w
, h
); break; // top to left
454 case 3: src
= boundingBoxPoint(2, p
, w
, h
); dest
= boundingBoxPoint(1, p
, w
, h
); break; // top to right
457 if (engine
.version
== 2) {
458 // Creatures 2 physics
460 int dx
= dest
.x
- src
.x
;
461 int dy
= dest
.y
- src
.y
;
464 double delta
= 1000000000;
465 bool collided
= false;
467 // TODO: check suffercollisions?
469 // TODO: muh, provided direction here is kinda a hack
470 findCollisionInDirection((i
< 2) ? 3 : 2, src
, dx
, dy
, deltapt
, delta
, collided
, true);
471 if (collided
) return false;
473 // Creatures 3 physics
475 float srcx
= src
.x
, srcy
= src
.y
;
477 shared_ptr
<Room
> ourRoom
= world
.map
.roomAt(srcx
, srcy
);
478 if (!ourRoom
) return false;
480 unsigned int dir
; Line wall
;
481 world
.map
.collideLineWithRoomSystem(src
, dest
, ourRoom
, src
, wall
, dir
, testperm
);
483 if (src
!= dest
) return false;
490 void Agent::physicsTick() {
491 if (engine
.version
== 1) return; // C1 has no physics, and different attributes.
493 if (carriedby
) return; // We don't move when carried, so what's the point?
495 if (engine
.version
== 2) {
496 // Creatures 2 physics is different.
501 if (!wasmoved
) return; // some agents are created outside INST and get autokilled if we try physics on them before they move
503 // set destination point based on velocities
504 float destx
= x
+ velx
.getFloat();
505 float desty
= y
+ vely
.getFloat();
507 if (sufferphysics()) {
508 // TODO: falling behaviour needs looking at more closely..
509 // .. but it shouldn't be 'false' by default on non-physics agents, so..
511 // increase speed according to accg
512 // TODO: should we be changing vely first, instead of after a successful move (below)?
513 desty
+= accg
.getFloat();
516 if (suffercollisions()) {
517 float lastdistance
= 1000000.0f
;
518 bool collided
= false;
519 Line wall
; // only valid when collided
520 unsigned int collidedirection
= 0; // only valid when collided
523 // iterate through all four points of the bounding box
524 for (unsigned int i
= 0; i
< 4; i
++) {
525 // this mess is because we want to start with the bottom point - DOWN (3) - before the others, for efficiency
526 Point src
= boundingBoxPoint((i
== 0) ? 3 : i
- 1);
529 float srcx
= src
.x
, srcy
= src
.y
;
531 shared_ptr
<Room
> ourRoom
= world
.map
.roomAt(srcx
, srcy
);
533 ourRoom
= world
.map
.roomAt(srcx
, srcy
);
536 if (!displaycore
) { // TODO: ugh, displaycore is a horrible thing to use for this
537 // we're out of the room system, physics bug, but let's try MVSFing back in to cover for fuzzie's poor programming skills
538 static bool tryingmove
; tryingmove
= false; // avoid infinite loop
539 if (!tryingmove
&& tryMoveToPlaceAround(x
, y
)) {
540 //std::cout << identify() << " was out of room system due to a physics bug but we hopefully moved it back in.." << std::endl;
547 unhandledException(boost::str(boost::format("out of room system at (%f, %f)") % srcx
% srcy
), false);
550 return; // out of room system
553 Point
dest(destx
+ (srcx
- x
), desty
+ (srcy
- y
));
554 unsigned int local_collidedirection
;
557 // this changes src to the point at which we end up
558 bool local_collided
= world
.map
.collideLineWithRoomSystem(src
, dest
, ourRoom
, src
, local_wall
, local_collidedirection
, perm
);
561 if (src
.x
== srcx
&& src
.y
== srcy
)
564 float xdiff
= src
.x
- srcx
;
565 float ydiff
= src
.y
- srcy
;
566 dist
= xdiff
*xdiff
+ ydiff
*ydiff
;
569 if (dist
>= lastdistance
) {
570 assert(i
!= 0); // this had better not be our first collision!
571 continue; // further away than a previous collision
575 bestmove
.x
= x
+ (src
.x
- srcx
);
576 bestmove
.y
= y
+ (src
.y
- srcy
);
577 collidedirection
= local_collidedirection
;
579 collided
= local_collided
;
582 break; // no point checking any more, is there?
585 // *** do actual movement
586 if (lastdistance
!= 0.0f
) {
587 moveTo(bestmove
.x
, bestmove
.y
);
590 lastcollidedirection
= collidedirection
;
591 queueScript(6, 0, velx
, vely
); // TODO: include this? .. we need to include SOMETHING, c3 ball checks for <3
594 if (wall
.getType() == HORIZONTAL
) {
595 vely
.setFloat(-vely
.getFloat());
596 } else if (wall
.getType() == VERTICAL
) {
597 velx
.setFloat(-velx
.getFloat());
599 // line starts always have a lower x value than the end
600 float xdiff
= wall
.getEnd().x
- wall
.getStart().x
;
601 float ydiff
= wall
.getEnd().y
- wall
.getStart().y
;
602 float fvelx
= velx
.getFloat(), fvely
= vely
.getFloat();
604 // calculate input/slope angles
608 inputangle
= M_PI
/ 2.0;
610 inputangle
= 3 * (M_PI
/ 2.0);
612 inputangle
= atan(fvely
/ fvelx
);
614 double slopeangle
= atan(-ydiff
/ xdiff
); // xdiff != 0 because wall isn't vertical
616 // calculate output angle
617 double outputangle
= slopeangle
+ (slopeangle
- inputangle
) + M_PI
;
619 // turn back into component velocities
620 double vectorlength
= sqrt(fvelx
*fvelx
+ fvely
*fvely
);
621 float xoutput
= cos(outputangle
) * vectorlength
;
622 float youtput
= sin(outputangle
) * vectorlength
;
624 velx
.setFloat(xoutput
);
625 vely
.setFloat(-youtput
);
628 if (elas
!= 100.0f
) {
629 velx
.setFloat(velx
.getFloat() * (elas
/ 100.0f
));
630 vely
.setFloat(vely
.getFloat() * (elas
/ 100.0f
));
634 } else if (sufferphysics() && accg
!= 0) {
635 falling
= true; // TODO: icky
636 vely
.setFloat(vely
.getFloat() + accg
.getFloat());
638 } else { velx
.setFloat(0); vely
.setFloat(0); } // TODO: correct?
640 if (vely
.hasDecimal() || velx
.hasDecimal())
641 moveTo(destx
, desty
);
643 vely
.setFloat(vely
.getFloat() + accg
.getFloat());
646 if (sufferphysics() && (aero
!= 0)) {
647 // reduce speed according to AERO
648 // TODO: aero should be an integer!
649 velx
.setFloat(velx
.getFloat() - (velx
.getFloat() * (aero
.getFloat() / 100.0f
)));
650 vely
.setFloat(vely
.getFloat() - (vely
.getFloat() * (aero
.getFloat() / 100.0f
)));
654 shared_ptr
<Room
> const Agent::bestRoomAt(unsigned int tryx
, unsigned int tryy
, unsigned int direction
, shared_ptr
<Room
> exclude
) {
655 std::vector
<shared_ptr
<Room
> > rooms
= world
.map
.roomsAt(tryx
, tryy
);
659 if (rooms
.size() == 0) return shared_ptr
<Room
>();
660 if (rooms
.size() == 1) r
= rooms
[0];
661 else if (rooms
.size() > 1) {
663 for (j
= 0; j
< rooms
.size(); j
++) {
664 if (rooms
[j
] == exclude
) continue;
666 if (direction
== 0) { // left
667 if (rooms
[j
]->x_left
== tryx
) break;
668 } else if (direction
== 1) { // right
669 if (rooms
[j
]->x_right
== tryx
) break;
670 } else if (direction
== 2) { // top
671 if (rooms
[j
]->y_left_ceiling
== tryy
) break;
673 if (rooms
[j
]->y_left_floor
== tryy
) break;
676 if (j
== rooms
.size()) j
= 0;
680 if (r
== exclude
) return shared_ptr
<Room
>();
684 // Creatures 2 collision finding
685 void const Agent::findCollisionInDirection(unsigned int i
, Point src
, int &dx
, int &dy
, Point
&deltapt
, double &delta
, bool &collided
, bool followrooms
) {
689 shared_ptr
<Room
> room
= bestRoomAt(src
.x
, src
.y
, i
, shared_ptr
<Room
>());
691 if (!room
) { // out of room system
693 unhandledException(boost::str(boost::format("out of room system at (%f, %f)") % src
.x
% src
.y
), false);
701 bool steep
= abs(dy
) > abs(dx
);
703 int signdx
= dx
< 0 ? -1 : 1;
704 int signdy
= dy
< 0 ? -1 : 1;
706 Line
l(Point(0,0),Point(dx
,dy
));
708 Point
lastpoint(0,0);
710 for (int loc
= 0; loc
<= abs(steep
? dy
: dx
); loc
++) {
711 Point p
= steep
? l
.pointAtY(loc
*signdy
) : l
.pointAtX(loc
*signdx
);
715 bool trycollisions
= false;
717 bool endofroom
= false;
719 if (src
.x
+ p
.x
< room
->x_left
) {
720 if (i
!= 1 && dx
< 0) { trycollisions
= true; lastdirection
= 0; }
723 if (src
.x
+ p
.x
> room
->x_right
) {
724 if (i
!= 0 && dx
> 0) { trycollisions
= true; lastdirection
= 1; }
727 if (src
.y
+ p
.y
< room
->y_left_ceiling
) {
728 if (i
!= 3 && dy
< 0) { trycollisions
= true; lastdirection
= 2; }
731 if (src
.y
+ p
.y
> room
->y_left_floor
) {
732 if (i
!= 2 && dy
> 0) { trycollisions
= true; lastdirection
= 3; }
736 // find the next room, if necessary, and work out whether we should move into it or break out
738 shared_ptr
<Room
> newroom
= bestRoomAt(src
.x
+ p
.x
, src
.y
+ p
.y
, i
, room
);
740 bool collision
= false;
742 // collide if we're out of the room system
743 if (!newroom
) { collision
= true; }
744 // collide if there's no new room connected to this one
745 else if (room
->doors
.find(newroom
) == room
->doors
.end()) { collision
= true; }
746 // collide if the PERM between this room and the new room is smaller than or equal to our size
747 else if (size
.getInt() > room
->doors
[newroom
]->perm
) { collision
= true; }
749 if (collision
&& (trycollisions
|| (!newroom
))) {
754 // move to the new room and keep going!
758 if (room
->floorpoints
.size() && i
== 3 && dy
>= 0 && size
.getInt() > room
->floorvalue
.getInt()) { // TODO: Hack!
759 // TODO: we don't check floorYatX isn't returning a 'real' room floor, but floorpoints should cover the whole floor anyway
760 int floory
= room
->floorYatX(src
.x
+ p
.x
);
762 // never collide when the top point of an object is below the floor.
763 Point top
= boundingBoxPoint(2);
765 // TODO: consider steep floors
766 if (src
.y
+ p
.y
> floory
&& top
.y
< floory
) {
773 if ((!followrooms
) && endofroom
) break;
778 double length2
= (lastpoint
.x
* lastpoint
.x
) + (lastpoint
.y
* lastpoint
.y
);
779 if (length2
< delta
) {
780 // TODO: !followrooms is a horrible way to detect a non-physics call
781 if (collided
&& followrooms
) lastcollidedirection
= lastdirection
;
787 void Agent::physicsTickC2() {
788 int dx
= velx
.getInt(), dy
= vely
.getInt();
790 if (dx
!= 0 || dy
!= 0) grav
.setInt(1);
792 if (grav
.getInt() != 0 && sufferphysics()) {
798 if (dy
== 0 && dx
== 0) { // nothing to do
799 if (vely
.getInt() == 0) {
803 // y motion cancelled by gravity
811 double delta
= 1000000000;
813 bool collided
= false;
815 if (suffercollisions()) {
816 for (unsigned int i
= 0; i
< 4; i
++) {
817 Point src
= boundingBoxPoint(i
);
818 findCollisionInDirection(i
, src
, dx
, dy
, deltapt
, delta
, collided
, true);
825 if (collided
&& (velx
.getInt() != 0 || vely
.getInt() != 0)) {
826 if (lastcollidedirection
>= 2) // up and down
827 vely
.setInt(-(vely
.getInt() - (rest
.getInt() * vely
.getInt()) / 100));
829 velx
.setInt(-(velx
.getInt() - (rest
.getInt() * velx
.getInt()) / 100));
832 if ((int)deltapt
.x
== 0 && (int)deltapt
.y
== 0) {
837 moveTo(x
+ (int)deltapt
.x
, y
+ (int)deltapt
.y
);
838 if (sufferphysics()) {
839 int fricx
= (aero
.getInt() * velx
.getInt()) / 100;
840 int fricy
= (aero
.getInt() * vely
.getInt()) / 100;
841 if (abs(velx
.getInt()) > 0 && fricx
== 0) fricx
= (velx
.getInt() < 0) ? -1 : 1;
842 if (abs(vely
.getInt()) > 0 && fricy
== 0) fricy
= (vely
.getInt() < 0) ? -1 : 1;
843 velx
.setInt(velx
.getInt() - fricx
);
844 vely
.setInt(vely
.getInt() - fricy
);
850 // sanity checks to stop ticks on dead agents
855 // if the sound is no longer playing...
856 if (sound
->getState() != SS_PLAY
) {
857 // ...release our reference to it
860 // otherwise, reposition audio
865 // don't tick paused agents
869 if (emitca_index
!= -1 && emitca_amount
!= 0.0f
) {
870 assert(0 <= emitca_index
&& emitca_index
<= 19);
871 shared_ptr
<Room
> r
= world
.map
.roomAt(x
, y
);
873 r
->catemp
[emitca_index
] += emitca_amount
;
874 /*if (r->catemp[emitca_index] <= 0.0f) r->catemp[emitca_index] = 0.0f;
875 else if (r->catemp[emitca_index] >= 1.0f) r->catemp[emitca_index] = 1.0f;*/
879 // tick the physics engine
881 if (dying
) return; // in case we were autokilled
883 // update the timer if needed, and then queue a timer event if necessary
885 tickssincelasttimer
++;
886 if (tickssincelasttimer
== timerrate
) {
887 queueScript(9); // TODO: include this?
888 tickssincelasttimer
= 0;
890 assert(timerrate
> tickssincelasttimer
);
897 void Agent::unhandledException(std::string info
, bool wasscript
) {
898 // TODO: do something with this? empty the stack?
899 if (world
.autostop
) {
900 // autostop is mostly for Creatures Village, which is full of buggy scripts
902 } else if (world
.autokill
&& !dynamic_cast<CreatureAgent
*>(this)) { // don't autokill creatures! TODO: someday we probably should :)
905 std::cerr
<< identify() << " was autokilled during script " << lastScript
<< " due to: " << info
<< std::endl
;
907 std::cerr
<< identify() << " was autokilled due to: " << info
<< std::endl
;
911 std::cerr
<< identify() << " caused an exception during script " << lastScript
<< ": " << info
<< std::endl
;
913 std::cerr
<< identify() << " caused an exception: " << info
<< std::endl
;
917 void Agent::vmTick() {
918 // Tick the VM associated with this agent.
920 assert(vm
); // There must *be* a VM to tick.
921 LifeAssert
la(this); // sanity check
923 // If we're out of timeslice, give ourselves some more (depending on the game type).
924 if (!vm
->timeslice
) {
925 vm
->timeslice
= (engine
.version
< 3) ? 1 : 5;
928 // Keep trying to run VMs while we don't run out of timeslice, end up with a blocked VM, or a VM stops.
929 while (vm
&& vm
->timeslice
&& !vm
->isBlocking() && !vm
->stopped()) {
930 assert(vm
->timeslice
> 0);
932 // Tell the VM to tick (using all available timeslice), catching exceptions as necessary.
935 } catch (invalidAgentException
&e
) {
936 // try letting the exception script handle it
937 if (!queueScript(255))
938 unhandledException(e
.prettyPrint(), true);
940 stopScript(); // we still want current script to die
941 } catch (creaturesException
&e
) {
942 unhandledException(e
.prettyPrint(), true);
943 } catch (std::exception
&e
) {
944 unhandledException(e
.what(), true);
947 // If the VM stopped, it's done.
948 if (vm
&& vm
->stopped()) {
954 // Zot any remaining timeslice, since we're done now.
955 if (vm
) vm
->timeslice
= 0;
957 // If there's no current VM but there's one on the call stack, a previous VM must have finished,
958 // pop one from the call stack to run next time.
959 if (!vm
&& !vmstack
.empty()) {
960 vm
= vmstack
.front();
966 assert(lifecount
== 0);
968 if (!initialized
) return;
970 // we can't do kill() here because we can't do anything which might try using our shared_ptr
971 // (since this could be during world destruction)
990 if (floatable()) floatRelease();
991 if (carrying
) dropCarried(carrying
);
992 // TODO: should the carried agent really be responsible for dropping from vehicle?
993 if (invehicle
) invehicle
->drop(this);
995 dying
= true; // what a world, what a world...
1004 agents_iter
->reset();
1012 unsigned int Agent::getZOrder() const {
1014 // TODO: take notice of cabp in c2e, at least. also, stacking .. ?
1015 Vehicle
*v
= dynamic_cast<Vehicle
*>(invehicle
.get());
1017 if (engine
.version
< 3)
1018 return v
->cabinplane
; // TODO: correct?
1020 return v
->getZOrder() + v
->cabinplane
;
1021 // TODO: Vehicle should probably rearrange zorder of passengers if ever moved
1022 } else if (carriedby
) {
1023 // TODO: check for overflow
1024 // TODO: is adding our own zorder here correct behaviour? someone should check
1025 if (engine
.version
> 1)
1026 return carriedby
->getZOrder() - 100;
1028 return carriedby
->getZOrder();
1034 void Agent::setZOrder(unsigned int z
) {
1039 int Agent::getUNID() const {
1042 return unid
= world
.newUNID(const_cast<Agent
*>(this));
1045 #include "Catalogue.h"
1047 std::string
Agent::identify() const {
1048 std::ostringstream o
;
1049 o
<< (int)family
<< " " << (int)genus
<< " " << species
;
1050 const std::string n
= catalogue
.getAgentName(family
, genus
, species
);
1052 o
<< " (" + n
+ ")";
1054 o << " unid " << unid;
1056 o << " (no unid assigned)"; */
1060 bool agentzorder::operator ()(const Agent
*s1
, const Agent
*s2
) const {
1061 return s1
->getZOrder() < s2
->getZOrder();
1064 void Agent::pushVM(caosVM
*newvm
) {
1067 vmstack
.push_front(vm
);
1071 bool Agent::vmStopped() {
1072 return (!vm
|| vm
->stopped());
1075 void Agent::stopScript() {
1081 void Agent::addCarried(AgentRef a
) {
1084 // TODO: muh, vehicle drop needs more thought
1086 Vehicle
*v
= dynamic_cast<Vehicle
*>(a
->invehicle
.get());
1093 // TODO: this doesn't reorder children or anything..
1094 a
->setZOrder(a
->zorder
);
1096 // fire 'Got Carried Agent'
1097 if (engine
.version
>= 3)
1098 queueScript(124, a
); // TODO: is this the correct param?
1101 void Agent::carry(AgentRef a
) {
1104 // TODO: check for infinite loops (eg, us carrying an agent which is carrying us) :)
1107 dropCarried(carrying
);
1111 a
->carriedby
= AgentRef(this);
1112 // TODO: move carrying agent to the right position
1113 adjustCarried(0, 0);
1116 bool Agent::beDropped() {
1117 carriedby
= AgentRef(0);
1118 bool wasinvehicle
= invehicle
;
1119 invehicle
= AgentRef(0);
1121 // TODO: this doesn't reorder children or anything..
1124 // TODO: no idea if this is right, it tries to re-enable gravity when dropping C2 agents
1125 if (engine
.version
== 2) grav
.setInt(1);
1126 if (engine
.version
== 3) falling
= true;
1128 if (!wasinvehicle
) { // ie, we're not being dropped by a vehicle
1129 // TODO: check for vehicles in a saner manner?
1130 for (std::list
<boost::shared_ptr
<Agent
> >::iterator i
= world
.agents
.begin(); i
!= world
.agents
.end(); i
++) {
1131 boost::shared_ptr
<Agent
> a
= (*i
);
1133 Vehicle
*v
= dynamic_cast<Vehicle
*>(a
.get());
1136 if (agentsTouching(this, v
)) {
1137 v
->addCarried(this);
1138 // TODO: how to handle not-invehicle case, where vehicle has failed to pick us up?
1139 if (invehicle
) return true;
1144 if (engine
.version
> 1) {
1145 // TODO: maybe think about this some more - does this belong here?
1146 tryMoveToPlaceAround(x
, y
);
1149 // TODO: return value is not used anywhere yet?
1153 void Agent::dropCarried(AgentRef a
) {
1156 // TODO: this doesn't reorder children or anything..
1157 a
->setZOrder(a
->zorder
);
1159 // fire 'Lost Carried Agent'
1160 if (engine
.version
>= 3)
1161 queueScript(125, a
); // TODO: is this the correct param?
1164 void Agent::drop(AgentRef a
) {
1165 if (!carrying
) return;
1166 assert(carrying
== a
);
1169 carrying
= AgentRef(0);
1172 std::pair
<int, int> Agent::getCarryPoint() {
1173 unsigned int ourpose
= 0;
1176 if ((s
= dynamic_cast<SpritePart
*>(part(0))))
1177 ourpose
= s
->getBase() + s
->getPose();
1179 std::pair
<int, int> pos(0, 0);
1181 std::map
<unsigned int, std::pair
<int, int> >::iterator i
= carry_points
.find(ourpose
);
1182 if (i
!= carry_points
.end())
1188 std::pair
<int, int> Agent::getCarriedPoint() {
1189 unsigned int theirpose
= 0;
1192 if ((s
= dynamic_cast<SpritePart
*>(part(0))))
1193 theirpose
= s
->getBase() + s
->getPose();
1195 std::pair
<int, int> pos(0, 0);
1197 std::map
<unsigned int, std::pair
<int, int> >::iterator i
= carried_points
.find(theirpose
);
1198 if (i
!= carried_points
.end()) {
1200 } else if (s
&& engine
.version
> 1) {
1201 // TODO: why fudge pickup location here and not in default carried points or something?
1202 // (this is nornagon's code which i moved - fuzzie)
1203 pos
.first
= s
->getSprite()->width(s
->getCurrentSprite()) / 2;
1209 void Agent::adjustCarried(float unusedxoffset
, float unusedyoffset
) {
1210 // Adjust the position of the agent we're carrying.
1211 // TODO: this doesn't actually position the carried agent correctly, sigh
1213 if (!carrying
) return;
1215 unsigned int ourpose
= 0;
1217 int xoffset
= 0, yoffset
= 0;
1218 if (engine
.version
< 3 && world
.hand() == this) {
1219 // this appears to produce correct behaviour in the respective games, don't ask me -nornagon
1220 if (engine
.version
== 2) {
1221 xoffset
= world
.hand()->getWidth() / 2;
1222 yoffset
= world
.hand()->getHeight() / 2 - 2;
1224 yoffset
= world
.hand()->getHeight() / 2 - 3;
1227 std::pair
<int, int> carrypoint
= getCarryPoint();
1228 xoffset
+= carrypoint
.first
;
1229 yoffset
+= carrypoint
.second
;
1231 std::pair
<int, int> carriedpoint
= carrying
->getCarriedPoint();
1232 xoffset
-= carriedpoint
.first
;
1233 yoffset
-= carriedpoint
.second
;
1235 if (xoffset
!= 0 || yoffset
!= 0)
1236 carrying
->moveTo(x
+ xoffset
, y
+ yoffset
, true);
1239 void Agent::setClassifier(unsigned char f
, unsigned char g
, unsigned short s
) {
1244 if (engine
.version
< 3) {
1245 // TODO: categories for other game versions
1248 category
= world
.findCategory(family
, genus
, species
);
1252 bool Agent::tryMoveToPlaceAround(float x
, float y
) {
1253 if (!suffercollisions()) {
1258 // second hacky attempt, move from side to side (+/- width) and up (- height) a little
1259 unsigned int trywidth
= getWidth() * 2; if (trywidth
< 100) trywidth
= 100;
1260 unsigned int tryheight
= getHeight() * 2; if (tryheight
< 100) tryheight
= 100;
1261 for (unsigned int xadjust
= 0; xadjust
< trywidth
; xadjust
++) {
1262 for (unsigned int yadjust
= 0; yadjust
< tryheight
; yadjust
++) {
1263 if (validInRoomSystem(Point(x
- xadjust
, y
- yadjust
), getWidth(), getHeight(), perm
))
1264 moveTo(x
- xadjust
, y
- yadjust
);
1265 else if ((xadjust
!= 0) && validInRoomSystem(Point(x
+ xadjust
, y
- yadjust
), getWidth(), getHeight(), perm
))
1266 moveTo(x
+ xadjust
, y
- yadjust
);
1276 void Agent::join(unsigned int outid
, AgentRef dest
, unsigned int inid
) {
1278 assert(outports
.find(outid
) != outports
.end());
1279 assert(dest
->inports
.find(inid
) != dest
->inports
.end());
1280 outports
[outid
]->dests
.push_back(std::pair
<AgentRef
, unsigned int>(dest
, inid
));
1281 dest
->inports
[inid
]->source
= this;
1282 dest
->inports
[inid
]->sourceid
= outid
;
1285 /* vim: set noet: */