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 // TODO: 'engine.version < 3' check for nointerrupt param is a guess and untested - fuzzie
280 if (vm
->fireScript(s
, (event
== 9 || engine
.version
< 3), 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 from
->dropCarried(this); // TODO: correct?
310 bool Agent::queueScript(unsigned short event
, AgentRef from
, caosVar p0
, caosVar p1
) {
311 // Queue a script for execution on the VM of this agent.
313 if (dying
) return false;
315 // only bother firing the event if either it exists, or it's one with engine code attached
316 // TODO: why don't we do the engine checks/etc here?
319 if (!findScript(event
)) return false;
331 world
.queueScript(event
, this, from
, p0
, p1
);
337 void Agent::handleClick(float clickx
, float clicky
) {
338 // Handle a mouse click.
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 queueScript(calculateScriptId(action
), (Agent
*)world
.hand());
355 // new-style click handling (c2e)
358 } else if (clac
[0] != -1) {
359 queueScript(calculateScriptId(clac
[0]), (Agent
*)world
.hand());
363 void Agent::playAudio(std::string filename
, bool controlled
, bool loop
) {
367 world
.playAudio(filename
, this, controlled
, loop
);
370 bool agentOnCamera(Agent
*targ
, bool checkall
= false); // caosVM_camera.cpp
372 void Agent::updateAudio(boost::shared_ptr
<AudioSource
> s
) {
375 s
->setPos(x
+ getWidth() / 2, y
+ getHeight() / 2, zorder
);
376 // TODO: we could do with a nicer 'inrange' check
377 bool inrange
= (x
+ 500 > world
.camera
.getX()) && (x
+ getWidth() - 500 < world
.camera
.getX() + world
.camera
.getWidth()) &&
378 (y
+ 500 > world
.camera
.getY()) && (y
+ getHeight() - 500 < world
.camera
.getY() + world
.camera
.getHeight());
380 world
.camera
.getMetaRoom() != world
.map
.metaRoomAt(x
, y
)
384 // TODO: setVelocity?
387 Point
const Agent::boundingBoxPoint(unsigned int n
) {
388 return boundingBoxPoint(n
, Point(x
, y
), getWidth(), getHeight());
391 Point
const Agent::boundingBoxPoint(unsigned int n
, Point in
, unsigned int w
, unsigned int h
) {
397 p
.y
= in
.y
+ (h
/ 2.0f
);
402 p
.y
= in
.y
+ (h
/ 2.0f
);
406 p
.x
= in
.x
+ (w
/ 2.0f
);
411 p
.x
= in
.x
+ (w
/ 2.0f
);
416 throw creaturesException("Agent::boundingBoxPoint got unknown direction");
422 bool Agent::validInRoomSystem() {
423 // Return true if this agent is inside the world room system at present, or false if it isn't.
426 if (engine
.version
== 1) return true;
428 return validInRoomSystem(Point(x
, y
), getWidth(), getHeight(), perm
);
431 bool const Agent::validInRoomSystem(Point p
, unsigned int w
, unsigned int h
, int testperm
) {
432 // Return true if this agent is inside the world room system at the specified point, or false if it isn't.
434 for (unsigned int i
= 0; i
< 4; i
++) {
437 case 0: src
= boundingBoxPoint(0, p
, w
, h
); dest
= boundingBoxPoint(3, p
, w
, h
); break; // left to bottom
438 case 1: src
= boundingBoxPoint(1, p
, w
, h
); dest
= boundingBoxPoint(3, p
, w
, h
); break; // right to bottom
439 case 2: src
= boundingBoxPoint(2, p
, w
, h
); dest
= boundingBoxPoint(0, p
, w
, h
); break; // top to left
440 case 3: src
= boundingBoxPoint(2, p
, w
, h
); dest
= boundingBoxPoint(1, p
, w
, h
); break; // top to right
443 if (engine
.version
== 2) {
444 // Creatures 2 physics
446 int dx
= dest
.x
- src
.x
;
447 int dy
= dest
.y
- src
.y
;
450 double delta
= 1000000000;
451 bool collided
= false;
453 // TODO: check suffercollisions?
455 // TODO: muh, provided direction here is kinda a hack
456 findCollisionInDirection((i
< 2) ? 3 : 2, src
, dx
, dy
, deltapt
, delta
, collided
, true);
457 if (collided
) return false;
459 // Creatures 3 physics
461 float srcx
= src
.x
, srcy
= src
.y
;
463 shared_ptr
<Room
> ourRoom
= world
.map
.roomAt(srcx
, srcy
);
464 if (!ourRoom
) return false;
466 unsigned int dir
; Line wall
;
467 world
.map
.collideLineWithRoomSystem(src
, dest
, ourRoom
, src
, wall
, dir
, testperm
);
469 if (src
!= dest
) return false;
476 void Agent::physicsTick() {
477 if (engine
.version
== 1) return; // C1 has no physics, and different attributes.
479 if (carriedby
) return; // We don't move when carried, so what's the point?
481 if (engine
.version
== 2) {
482 // Creatures 2 physics is different.
487 if (!wasmoved
) return; // some agents are created outside INST and get autokilled if we try physics on them before they move
489 // set destination point based on velocities
490 float destx
= x
+ velx
.getFloat();
491 float desty
= y
+ vely
.getFloat();
493 if (sufferphysics()) {
494 // TODO: falling behaviour needs looking at more closely..
495 // .. but it shouldn't be 'false' by default on non-physics agents, so..
497 // increase speed according to accg
498 // TODO: should we be changing vely first, instead of after a successful move (below)?
499 desty
+= accg
.getFloat();
502 if (suffercollisions()) {
503 float lastdistance
= 1000000.0f
;
504 bool collided
= false;
505 Line wall
; // only valid when collided
506 unsigned int collidedirection
= 0; // only valid when collided
509 // iterate through all four points of the bounding box
510 for (unsigned int i
= 0; i
< 4; i
++) {
511 // this mess is because we want to start with the bottom point - DOWN (3) - before the others, for efficiency
512 Point src
= boundingBoxPoint((i
== 0) ? 3 : i
- 1);
515 float srcx
= src
.x
, srcy
= src
.y
;
517 shared_ptr
<Room
> ourRoom
= world
.map
.roomAt(srcx
, srcy
);
519 ourRoom
= world
.map
.roomAt(srcx
, srcy
);
522 if (!displaycore
) { // TODO: ugh, displaycore is a horrible thing to use for this
523 // we're out of the room system, physics bug, but let's try MVSFing back in to cover for fuzzie's poor programming skills
524 static bool tryingmove
; tryingmove
= false; // avoid infinite loop
525 if (!tryingmove
&& tryMoveToPlaceAround(x
, y
)) {
526 //std::cout << identify() << " was out of room system due to a physics bug but we hopefully moved it back in.." << std::endl;
533 unhandledException(boost::str(boost::format("out of room system at (%f, %f)") % srcx
% srcy
), false);
536 return; // out of room system
539 Point
dest(destx
+ (srcx
- x
), desty
+ (srcy
- y
));
540 unsigned int local_collidedirection
;
543 // this changes src to the point at which we end up
544 bool local_collided
= world
.map
.collideLineWithRoomSystem(src
, dest
, ourRoom
, src
, local_wall
, local_collidedirection
, perm
);
547 if (src
.x
== srcx
&& src
.y
== srcy
)
550 float xdiff
= src
.x
- srcx
;
551 float ydiff
= src
.y
- srcy
;
552 dist
= xdiff
*xdiff
+ ydiff
*ydiff
;
555 if (dist
>= lastdistance
) {
556 assert(i
!= 0); // this had better not be our first collision!
557 continue; // further away than a previous collision
561 bestmove
.x
= x
+ (src
.x
- srcx
);
562 bestmove
.y
= y
+ (src
.y
- srcy
);
563 collidedirection
= local_collidedirection
;
565 collided
= local_collided
;
568 break; // no point checking any more, is there?
571 // *** do actual movement
572 if (lastdistance
!= 0.0f
) {
573 moveTo(bestmove
.x
, bestmove
.y
);
576 lastcollidedirection
= collidedirection
;
577 queueScript(6, 0, velx
, vely
); // TODO: include this? .. we need to include SOMETHING, c3 ball checks for <3
580 if (wall
.getType() == HORIZONTAL
) {
581 vely
.setFloat(-vely
.getFloat());
582 } else if (wall
.getType() == VERTICAL
) {
583 velx
.setFloat(-velx
.getFloat());
585 // line starts always have a lower x value than the end
586 float xdiff
= wall
.getEnd().x
- wall
.getStart().x
;
587 float ydiff
= wall
.getEnd().y
- wall
.getStart().y
;
588 float fvelx
= velx
.getFloat(), fvely
= vely
.getFloat();
590 // calculate input/slope angles
594 inputangle
= M_PI
/ 2.0;
596 inputangle
= 3 * (M_PI
/ 2.0);
598 inputangle
= atan(fvely
/ fvelx
);
600 double slopeangle
= atan(-ydiff
/ xdiff
); // xdiff != 0 because wall isn't vertical
602 // calculate output angle
603 double outputangle
= slopeangle
+ (slopeangle
- inputangle
) + M_PI
;
605 // turn back into component velocities
606 double vectorlength
= sqrt(fvelx
*fvelx
+ fvely
*fvely
);
607 float xoutput
= cos(outputangle
) * vectorlength
;
608 float youtput
= sin(outputangle
) * vectorlength
;
610 velx
.setFloat(xoutput
);
611 vely
.setFloat(-youtput
);
614 if (elas
!= 100.0f
) {
615 velx
.setFloat(velx
.getFloat() * (elas
/ 100.0f
));
616 vely
.setFloat(vely
.getFloat() * (elas
/ 100.0f
));
620 } else if (sufferphysics() && accg
!= 0) {
621 falling
= true; // TODO: icky
622 vely
.setFloat(vely
.getFloat() + accg
.getFloat());
624 } else { velx
.setFloat(0); vely
.setFloat(0); } // TODO: correct?
626 if (vely
.hasDecimal() || velx
.hasDecimal())
627 moveTo(destx
, desty
);
629 vely
.setFloat(vely
.getFloat() + accg
.getFloat());
632 if (sufferphysics() && (aero
!= 0)) {
633 // reduce speed according to AERO
634 // TODO: aero should be an integer!
635 velx
.setFloat(velx
.getFloat() - (velx
.getFloat() * (aero
.getFloat() / 100.0f
)));
636 vely
.setFloat(vely
.getFloat() - (vely
.getFloat() * (aero
.getFloat() / 100.0f
)));
640 shared_ptr
<Room
> const Agent::bestRoomAt(unsigned int tryx
, unsigned int tryy
, unsigned int direction
, shared_ptr
<Room
> exclude
) {
641 std::vector
<shared_ptr
<Room
> > rooms
= world
.map
.roomsAt(tryx
, tryy
);
645 if (rooms
.size() == 0) return shared_ptr
<Room
>();
646 if (rooms
.size() == 1) r
= rooms
[0];
647 else if (rooms
.size() > 1) {
649 for (j
= 0; j
< rooms
.size(); j
++) {
650 if (rooms
[j
] == exclude
) continue;
652 if (direction
== 0) { // left
653 if (rooms
[j
]->x_left
== tryx
) break;
654 } else if (direction
== 1) { // right
655 if (rooms
[j
]->x_right
== tryx
) break;
656 } else if (direction
== 2) { // top
657 if (rooms
[j
]->y_left_ceiling
== tryy
) break;
659 if (rooms
[j
]->y_left_floor
== tryy
) break;
662 if (j
== rooms
.size()) j
= 0;
666 if (r
== exclude
) return shared_ptr
<Room
>();
670 // Creatures 2 collision finding
671 void const Agent::findCollisionInDirection(unsigned int i
, Point src
, int &dx
, int &dy
, Point
&deltapt
, double &delta
, bool &collided
, bool followrooms
) {
675 shared_ptr
<Room
> room
= bestRoomAt(src
.x
, src
.y
, i
, shared_ptr
<Room
>());
677 if (!room
) { // out of room system
679 unhandledException(boost::str(boost::format("out of room system at (%f, %f)") % src
.x
% src
.y
), false);
687 bool steep
= abs(dy
) > abs(dx
);
689 int signdx
= dx
< 0 ? -1 : 1;
690 int signdy
= dy
< 0 ? -1 : 1;
692 Line
l(Point(0,0),Point(dx
,dy
));
694 Point
lastpoint(0,0);
696 for (int loc
= 0; loc
<= abs(steep
? dy
: dx
); loc
++) {
697 Point p
= steep
? l
.pointAtY(loc
*signdy
) : l
.pointAtX(loc
*signdx
);
701 bool trycollisions
= false;
703 bool endofroom
= false;
705 if (src
.x
+ p
.x
< room
->x_left
) {
706 if (i
!= 1 && dx
< 0) { trycollisions
= true; lastdirection
= 0; }
709 if (src
.x
+ p
.x
> room
->x_right
) {
710 if (i
!= 0 && dx
> 0) { trycollisions
= true; lastdirection
= 1; }
713 if (src
.y
+ p
.y
< room
->y_left_ceiling
) {
714 if (i
!= 3 && dy
< 0) { trycollisions
= true; lastdirection
= 2; }
717 if (src
.y
+ p
.y
> room
->y_left_floor
) {
718 if (i
!= 2 && dy
> 0) { trycollisions
= true; lastdirection
= 3; }
722 // find the next room, if necessary, and work out whether we should move into it or break out
724 shared_ptr
<Room
> newroom
= bestRoomAt(src
.x
+ p
.x
, src
.y
+ p
.y
, i
, room
);
726 bool collision
= false;
728 // collide if we're out of the room system
729 if (!newroom
) { collision
= true; }
730 // collide if there's no new room connected to this one
731 else if (room
->doors
.find(newroom
) == room
->doors
.end()) { collision
= true; }
732 // collide if the PERM between this room and the new room is smaller than or equal to our size
733 else if (size
.getInt() > room
->doors
[newroom
]->perm
) { collision
= true; }
735 if (collision
&& (trycollisions
|| (!newroom
))) {
740 // move to the new room and keep going!
744 if (room
->floorpoints
.size() && i
== 3 && dy
>= 0 && size
.getInt() > room
->floorvalue
.getInt()) { // TODO: Hack!
745 // TODO: we don't check floorYatX isn't returning a 'real' room floor, but floorpoints should cover the whole floor anyway
746 int floory
= room
->floorYatX(src
.x
+ p
.x
);
748 // never collide when the top point of an object is below the floor.
749 Point top
= boundingBoxPoint(2);
751 // TODO: consider steep floors
752 if (src
.y
+ p
.y
> floory
&& top
.y
< floory
) {
759 if ((!followrooms
) && endofroom
) break;
764 double length2
= (lastpoint
.x
* lastpoint
.x
) + (lastpoint
.y
* lastpoint
.y
);
765 if (length2
< delta
) {
766 // TODO: !followrooms is a horrible way to detect a non-physics call
767 if (collided
&& followrooms
) lastcollidedirection
= lastdirection
;
773 void Agent::physicsTickC2() {
774 int dx
= velx
.getInt(), dy
= vely
.getInt();
776 if (dx
!= 0 || dy
!= 0) grav
.setInt(1);
778 if (grav
.getInt() != 0 && sufferphysics()) {
784 if (dy
== 0 && dx
== 0) { // nothing to do
785 if (vely
.getInt() == 0) {
789 // y motion cancelled by gravity
797 double delta
= 1000000000;
799 bool collided
= false;
801 if (suffercollisions()) {
802 for (unsigned int i
= 0; i
< 4; i
++) {
803 Point src
= boundingBoxPoint(i
);
804 findCollisionInDirection(i
, src
, dx
, dy
, deltapt
, delta
, collided
, true);
811 if (collided
&& (velx
.getInt() != 0 || vely
.getInt() != 0)) {
812 if (lastcollidedirection
>= 2) // up and down
813 vely
.setInt(-(vely
.getInt() - (rest
.getInt() * vely
.getInt()) / 100));
815 velx
.setInt(-(velx
.getInt() - (rest
.getInt() * velx
.getInt()) / 100));
818 if ((int)deltapt
.x
== 0 && (int)deltapt
.y
== 0) {
823 moveTo(x
+ (int)deltapt
.x
, y
+ (int)deltapt
.y
);
824 if (sufferphysics()) {
825 int fricx
= (aero
.getInt() * velx
.getInt()) / 100;
826 int fricy
= (aero
.getInt() * vely
.getInt()) / 100;
827 if (abs(velx
.getInt()) > 0 && fricx
== 0) fricx
= (velx
.getInt() < 0) ? -1 : 1;
828 if (abs(vely
.getInt()) > 0 && fricy
== 0) fricy
= (vely
.getInt() < 0) ? -1 : 1;
829 velx
.setInt(velx
.getInt() - fricx
);
830 vely
.setInt(vely
.getInt() - fricy
);
836 // sanity checks to stop ticks on dead agents
841 // if the sound is no longer playing...
842 if (sound
->getState() != SS_PLAY
) {
843 // ...release our reference to it
846 // otherwise, reposition audio
851 // don't tick paused agents
855 if (emitca_index
!= -1 && emitca_amount
!= 0.0f
) {
856 assert(0 <= emitca_index
&& emitca_index
<= 19);
857 shared_ptr
<Room
> r
= world
.map
.roomAt(x
, y
);
859 r
->catemp
[emitca_index
] += emitca_amount
;
860 /*if (r->catemp[emitca_index] <= 0.0f) r->catemp[emitca_index] = 0.0f;
861 else if (r->catemp[emitca_index] >= 1.0f) r->catemp[emitca_index] = 1.0f;*/
865 // tick the physics engine
867 if (dying
) return; // in case we were autokilled
869 // update the timer if needed, and then queue a timer event if necessary
871 tickssincelasttimer
++;
872 if (tickssincelasttimer
== timerrate
) {
873 queueScript(9); // TODO: include this?
874 tickssincelasttimer
= 0;
876 assert(timerrate
> tickssincelasttimer
);
883 void Agent::unhandledException(std::string info
, bool wasscript
) {
884 // TODO: do something with this? empty the stack?
885 if (world
.autostop
) {
886 // autostop is mostly for Creatures Village, which is full of buggy scripts
888 } else if (world
.autokill
&& !dynamic_cast<CreatureAgent
*>(this)) { // don't autokill creatures! TODO: someday we probably should :)
891 std::cerr
<< identify() << " was autokilled during script " << lastScript
<< " due to: " << info
<< std::endl
;
893 std::cerr
<< identify() << " was autokilled due to: " << info
<< std::endl
;
897 std::cerr
<< identify() << " caused an exception during script " << lastScript
<< ": " << info
<< std::endl
;
899 std::cerr
<< identify() << " caused an exception: " << info
<< std::endl
;
903 void Agent::vmTick() {
904 // Tick the VM associated with this agent.
906 assert(vm
); // There must *be* a VM to tick.
907 LifeAssert
la(this); // sanity check
909 // If we're out of timeslice, give ourselves some more (depending on the game type).
910 if (!vm
->timeslice
) {
911 vm
->timeslice
= (engine
.version
< 3) ? 1 : 5;
914 // Keep trying to run VMs while we don't run out of timeslice, end up with a blocked VM, or a VM stops.
915 while (vm
&& vm
->timeslice
&& !vm
->isBlocking() && !vm
->stopped()) {
916 assert(vm
->timeslice
> 0);
918 // Tell the VM to tick (using all available timeslice), catching exceptions as necessary.
921 } catch (invalidAgentException
&e
) {
922 // try letting the exception script handle it
923 if (!queueScript(255))
924 unhandledException(e
.prettyPrint(), true);
926 stopScript(); // we still want current script to die
927 } catch (creaturesException
&e
) {
928 unhandledException(e
.prettyPrint(), true);
929 } catch (std::exception
&e
) {
930 unhandledException(e
.what(), true);
933 // If the VM stopped, it's done.
934 if (vm
&& vm
->stopped()) {
940 // Zot any remaining timeslice, since we're done now.
941 if (vm
) vm
->timeslice
= 0;
943 // If there's no current VM but there's one on the call stack, a previous VM must have finished,
944 // pop one from the call stack to run next time.
945 if (!vm
&& !vmstack
.empty()) {
946 vm
= vmstack
.front();
952 assert(lifecount
== 0);
954 if (!initialized
) return;
956 // we can't do kill() here because we can't do anything which might try using our shared_ptr
957 // (since this could be during world destruction)
976 if (floatable()) floatRelease();
977 if (carrying
) dropCarried(carrying
);
978 // TODO: should the carried agent really be responsible for dropping from vehicle?
979 if (invehicle
) invehicle
->drop(this);
981 dying
= true; // what a world, what a world...
990 agents_iter
->reset();
998 unsigned int Agent::getZOrder() const {
1000 // TODO: take notice of cabp in c2e, at least. also, stacking .. ?
1001 Vehicle
*v
= dynamic_cast<Vehicle
*>(invehicle
.get());
1003 if (engine
.version
< 3)
1004 return v
->cabinplane
; // TODO: correct?
1006 return v
->getZOrder() + v
->cabinplane
;
1007 // TODO: Vehicle should probably rearrange zorder of passengers if ever moved
1008 } else if (carriedby
) {
1009 // TODO: check for overflow
1010 // TODO: is adding our own zorder here correct behaviour? someone should check
1011 if (engine
.version
> 1)
1012 return carriedby
->getZOrder() - 100;
1014 return carriedby
->getZOrder();
1020 void Agent::setZOrder(unsigned int z
) {
1025 int Agent::getUNID() const {
1028 return unid
= world
.newUNID(const_cast<Agent
*>(this));
1031 #include "Catalogue.h"
1033 std::string
Agent::identify() const {
1034 std::ostringstream o
;
1035 o
<< (int)family
<< " " << (int)genus
<< " " << species
;
1036 const std::string n
= catalogue
.getAgentName(family
, genus
, species
);
1038 o
<< " (" + n
+ ")";
1040 o << " unid " << unid;
1042 o << " (no unid assigned)"; */
1046 bool agentzorder::operator ()(const Agent
*s1
, const Agent
*s2
) const {
1047 return s1
->getZOrder() < s2
->getZOrder();
1050 void Agent::pushVM(caosVM
*newvm
) {
1053 vmstack
.push_front(vm
);
1057 bool Agent::vmStopped() {
1058 return (!vm
|| vm
->stopped());
1061 void Agent::stopScript() {
1067 void Agent::addCarried(AgentRef a
) {
1070 // TODO: muh, vehicle drop needs more thought
1072 Vehicle
*v
= dynamic_cast<Vehicle
*>(a
->invehicle
.get());
1079 // TODO: this doesn't reorder children or anything..
1080 a
->setZOrder(a
->zorder
);
1082 // fire 'Got Carried Agent'
1083 if (engine
.version
>= 3)
1084 queueScript(124, a
); // TODO: is this the correct param?
1087 void Agent::carry(AgentRef a
) {
1090 // TODO: check for infinite loops (eg, us carrying an agent which is carrying us) :)
1093 dropCarried(carrying
);
1097 a
->carriedby
= AgentRef(this);
1098 // TODO: move carrying agent to the right position
1099 adjustCarried(0, 0);
1102 bool Agent::beDropped() {
1103 carriedby
= AgentRef(0);
1104 bool wasinvehicle
= invehicle
;
1105 invehicle
= AgentRef(0);
1107 // TODO: this doesn't reorder children or anything..
1110 // TODO: no idea if this is right, it tries to re-enable gravity when dropping C2 agents
1111 if (engine
.version
== 2) grav
.setInt(1);
1112 if (engine
.version
== 3) falling
= true;
1114 if (!wasinvehicle
) { // ie, we're not being dropped by a vehicle
1115 // TODO: check for vehicles in a saner manner?
1116 for (std::list
<boost::shared_ptr
<Agent
> >::iterator i
= world
.agents
.begin(); i
!= world
.agents
.end(); i
++) {
1117 boost::shared_ptr
<Agent
> a
= (*i
);
1119 Vehicle
*v
= dynamic_cast<Vehicle
*>(a
.get());
1122 if (agentsTouching(this, v
)) {
1123 v
->addCarried(this);
1124 // TODO: how to handle not-invehicle case, where vehicle has failed to pick us up?
1125 if (invehicle
) return true;
1130 if (engine
.version
== 1) {
1131 MetaRoom
* m
= world
.map
.metaRoomAt(x
, y
);
1132 if (!m
) return false;
1133 shared_ptr
<Room
> r
= m
->nextFloorFromPoint(x
, y
);
1134 if (!r
) return false;
1135 moveTo(x
, r
->bot
.pointAtX(x
).y
- getHeight());
1137 // TODO: maybe think about this some more
1138 tryMoveToPlaceAround(x
, y
);
1141 // TODO: return value is not used anywhere yet?
1145 void Agent::dropCarried(AgentRef a
) {
1148 // TODO: this doesn't reorder children or anything..
1149 a
->setZOrder(a
->zorder
);
1151 // fire 'Lost Carried Agent'
1152 if (engine
.version
>= 3)
1153 queueScript(125, a
); // TODO: is this the correct param?
1156 void Agent::drop(AgentRef a
) {
1157 if (!carrying
) return;
1158 assert(carrying
== a
);
1161 carrying
= AgentRef(0);
1164 std::pair
<int, int> Agent::getCarryPoint() {
1165 unsigned int ourpose
= 0;
1168 if ((s
= dynamic_cast<SpritePart
*>(part(0))))
1169 ourpose
= s
->getBase() + s
->getPose();
1171 std::pair
<int, int> pos(0, 0);
1173 std::map
<unsigned int, std::pair
<int, int> >::iterator i
= carry_points
.find(ourpose
);
1174 if (i
!= carry_points
.end())
1180 std::pair
<int, int> Agent::getCarriedPoint() {
1181 unsigned int theirpose
= 0;
1184 if ((s
= dynamic_cast<SpritePart
*>(part(0))))
1185 theirpose
= s
->getBase() + s
->getPose();
1187 std::pair
<int, int> pos(0, 0);
1189 std::map
<unsigned int, std::pair
<int, int> >::iterator i
= carried_points
.find(theirpose
);
1190 if (i
!= carried_points
.end()) {
1192 } else if (s
&& engine
.version
> 1) {
1193 // TODO: why fudge pickup location here and not in default carried points or something?
1194 // (this is nornagon's code which i moved - fuzzie)
1195 pos
.first
= s
->getSprite()->width(s
->getCurrentSprite()) / 2;
1201 void Agent::adjustCarried(float unusedxoffset
, float unusedyoffset
) {
1202 // Adjust the position of the agent we're carrying.
1203 // TODO: this doesn't actually position the carried agent correctly, sigh
1205 if (!carrying
) return;
1207 unsigned int ourpose
= 0;
1209 int xoffset
= 0, yoffset
= 0;
1210 if (engine
.version
< 3 && world
.hand() == this) {
1211 // this appears to produce correct behaviour in the respective games, don't ask me -nornagon
1212 if (engine
.version
== 2) {
1213 xoffset
= world
.hand()->getWidth() / 2;
1214 yoffset
= world
.hand()->getHeight() / 2 - 2;
1216 yoffset
= world
.hand()->getHeight() / 2 - 3;
1219 std::pair
<int, int> carrypoint
= getCarryPoint();
1220 xoffset
+= carrypoint
.first
;
1221 yoffset
+= carrypoint
.second
;
1223 std::pair
<int, int> carriedpoint
= carrying
->getCarriedPoint();
1224 xoffset
-= carriedpoint
.first
;
1225 yoffset
-= carriedpoint
.second
;
1227 if (xoffset
!= 0 || yoffset
!= 0)
1228 carrying
->moveTo(x
+ xoffset
, y
+ yoffset
, true);
1231 void Agent::setClassifier(unsigned char f
, unsigned char g
, unsigned short s
) {
1236 if (engine
.version
< 3) {
1237 // TODO: categories for other game versions
1240 category
= world
.findCategory(family
, genus
, species
);
1244 bool Agent::tryMoveToPlaceAround(float x
, float y
) {
1245 if (!suffercollisions()) {
1250 // second hacky attempt, move from side to side (+/- width) and up (- height) a little
1251 unsigned int trywidth
= getWidth() * 2; if (trywidth
< 100) trywidth
= 100;
1252 unsigned int tryheight
= getHeight() * 2; if (tryheight
< 100) tryheight
= 100;
1253 for (unsigned int xadjust
= 0; xadjust
< trywidth
; xadjust
++) {
1254 for (unsigned int yadjust
= 0; yadjust
< tryheight
; yadjust
++) {
1255 if (validInRoomSystem(Point(x
- xadjust
, y
- yadjust
), getWidth(), getHeight(), perm
))
1256 moveTo(x
- xadjust
, y
- yadjust
);
1257 else if ((xadjust
!= 0) && validInRoomSystem(Point(x
+ xadjust
, y
- yadjust
), getWidth(), getHeight(), perm
))
1258 moveTo(x
+ xadjust
, y
- yadjust
);
1268 /* vim: set noet: */