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()->pointerX(), world
.hand()->pointerY());
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()->pointerX() - p
->x
- p
->getParent()->x
, world
.hand()->pointerY() - 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 static bool inrange_at(const MetaRoom
*room
, float x
, float y
, unsigned int width
, unsigned int height
) {
387 const static unsigned int buffer
= 500;
389 if (world
.camera
.getMetaRoom() != room
)
391 if (x
+ buffer
< world
.camera
.getX() || x
+ width
- buffer
> world
.camera
.getX() + world
.camera
.getWidth())
393 if (y
+ buffer
< world
.camera
.getY() || y
+ height
- buffer
> world
.camera
.getY() + world
.camera
.getHeight())
398 void Agent::updateAudio(boost::shared_ptr
<AudioSource
> s
) {
400 MetaRoom
*room
= world
.map
.metaRoomAt(x
, y
);
403 bool inrange
= false;
404 if (inrange_at(room
, x
, y
, getWidth(), getHeight())) {
407 } else if (room
->wraparound()) {
408 if (inrange_at(room
, x
- room
->width(), y
, getWidth(), getHeight())) {
409 xc
= x
- room
->width();
411 } else if (inrange_at(room
, x
+ room
->width(), y
, getWidth(), getHeight())) {
412 xc
= x
+ room
->width();
416 s
->setMute(!inrange
);
418 s
->setPos(xc
+ getWidth() / 2, y
+ getHeight() / 2, zorder
);
419 // TODO: setVelocity?
422 Point
const Agent::boundingBoxPoint(unsigned int n
) {
423 return boundingBoxPoint(n
, Point(x
, y
), getWidth(), getHeight());
426 Point
const Agent::boundingBoxPoint(unsigned int n
, Point in
, unsigned int w
, unsigned int h
) {
432 p
.y
= in
.y
+ (h
/ 2.0f
);
437 p
.y
= in
.y
+ (h
/ 2.0f
);
441 p
.x
= in
.x
+ (w
/ 2.0f
);
446 p
.x
= in
.x
+ (w
/ 2.0f
);
451 throw creaturesException("Agent::boundingBoxPoint got unknown direction");
457 bool Agent::validInRoomSystem() {
458 // Return true if this agent is inside the world room system at present, or false if it isn't.
461 if (engine
.version
== 1) return true;
463 return validInRoomSystem(Point(x
, y
), getWidth(), getHeight(), perm
);
466 bool const Agent::validInRoomSystem(Point p
, unsigned int w
, unsigned int h
, int testperm
) {
467 // Return true if this agent is inside the world room system at the specified point, or false if it isn't.
469 for (unsigned int i
= 0; i
< 4; i
++) {
472 case 0: src
= boundingBoxPoint(0, p
, w
, h
); dest
= boundingBoxPoint(3, p
, w
, h
); break; // left to bottom
473 case 1: src
= boundingBoxPoint(1, p
, w
, h
); dest
= boundingBoxPoint(3, p
, w
, h
); break; // right to bottom
474 case 2: src
= boundingBoxPoint(2, p
, w
, h
); dest
= boundingBoxPoint(0, p
, w
, h
); break; // top to left
475 case 3: src
= boundingBoxPoint(2, p
, w
, h
); dest
= boundingBoxPoint(1, p
, w
, h
); break; // top to right
478 if (engine
.version
== 2) {
479 // Creatures 2 physics
481 int dx
= dest
.x
- src
.x
;
482 int dy
= dest
.y
- src
.y
;
485 double delta
= 1000000000;
486 bool collided
= false;
488 // TODO: check suffercollisions?
490 // TODO: muh, provided direction here is kinda a hack
491 findCollisionInDirection((i
< 2) ? 3 : 2, src
, dx
, dy
, deltapt
, delta
, collided
, true);
492 if (collided
) return false;
494 // Creatures 3 physics
496 float srcx
= src
.x
, srcy
= src
.y
;
498 shared_ptr
<Room
> ourRoom
= world
.map
.roomAt(srcx
, srcy
);
499 if (!ourRoom
) return false;
501 unsigned int dir
; Line wall
;
502 world
.map
.collideLineWithRoomSystem(src
, dest
, ourRoom
, src
, wall
, dir
, testperm
);
504 if (src
!= dest
) return false;
511 void Agent::physicsTick() {
512 if (engine
.version
== 1) return; // C1 has no physics, and different attributes.
514 if (carriedby
) return; // We don't move when carried, so what's the point?
516 if (engine
.version
== 2) {
517 // Creatures 2 physics is different.
522 if (!wasmoved
) return; // some agents are created outside INST and get autokilled if we try physics on them before they move
524 // set destination point based on velocities
525 float destx
= x
+ velx
.getFloat();
526 float desty
= y
+ vely
.getFloat();
528 if (sufferphysics()) {
529 // TODO: falling behaviour needs looking at more closely..
530 // .. but it shouldn't be 'false' by default on non-physics agents, so..
532 // increase speed according to accg
533 // TODO: should we be changing vely first, instead of after a successful move (below)?
534 desty
+= accg
.getFloat();
537 if (suffercollisions()) {
538 float lastdistance
= 1000000.0f
;
539 bool collided
= false;
540 Line wall
; // only valid when collided
541 unsigned int collidedirection
= 0; // only valid when collided
544 // iterate through all four points of the bounding box
545 for (unsigned int i
= 0; i
< 4; i
++) {
546 // this mess is because we want to start with the bottom point - DOWN (3) - before the others, for efficiency
547 Point src
= boundingBoxPoint((i
== 0) ? 3 : i
- 1);
550 float srcx
= src
.x
, srcy
= src
.y
;
552 shared_ptr
<Room
> ourRoom
= world
.map
.roomAt(srcx
, srcy
);
554 ourRoom
= world
.map
.roomAt(srcx
, srcy
);
557 if (!displaycore
) { // TODO: ugh, displaycore is a horrible thing to use for this
558 // we're out of the room system, physics bug, but let's try MVSFing back in to cover for fuzzie's poor programming skills
559 static bool tryingmove
; tryingmove
= false; // avoid infinite loop
560 if (!tryingmove
&& tryMoveToPlaceAround(x
, y
)) {
561 //std::cout << identify() << " was out of room system due to a physics bug but we hopefully moved it back in.." << std::endl;
568 unhandledException(boost::str(boost::format("out of room system at (%f, %f)") % srcx
% srcy
), false);
571 return; // out of room system
574 Point
dest(destx
+ (srcx
- x
), desty
+ (srcy
- y
));
575 unsigned int local_collidedirection
;
578 // this changes src to the point at which we end up
579 bool local_collided
= world
.map
.collideLineWithRoomSystem(src
, dest
, ourRoom
, src
, local_wall
, local_collidedirection
, perm
);
582 if (src
.x
== srcx
&& src
.y
== srcy
)
585 float xdiff
= src
.x
- srcx
;
586 float ydiff
= src
.y
- srcy
;
587 dist
= xdiff
*xdiff
+ ydiff
*ydiff
;
590 if (dist
>= lastdistance
) {
591 assert(i
!= 0); // this had better not be our first collision!
592 continue; // further away than a previous collision
596 bestmove
.x
= x
+ (src
.x
- srcx
);
597 bestmove
.y
= y
+ (src
.y
- srcy
);
598 collidedirection
= local_collidedirection
;
600 collided
= local_collided
;
603 break; // no point checking any more, is there?
606 // *** do actual movement
607 if (lastdistance
!= 0.0f
) {
608 moveTo(bestmove
.x
, bestmove
.y
);
611 lastcollidedirection
= collidedirection
;
612 queueScript(6, 0, velx
, vely
); // TODO: include this? .. we need to include SOMETHING, c3 ball checks for <3
615 if (wall
.getType() == HORIZONTAL
) {
616 vely
.setFloat(-vely
.getFloat());
617 } else if (wall
.getType() == VERTICAL
) {
618 velx
.setFloat(-velx
.getFloat());
620 // line starts always have a lower x value than the end
621 float xdiff
= wall
.getEnd().x
- wall
.getStart().x
;
622 float ydiff
= wall
.getEnd().y
- wall
.getStart().y
;
623 float fvelx
= velx
.getFloat(), fvely
= vely
.getFloat();
625 // calculate input/slope angles
629 inputangle
= M_PI
/ 2.0;
631 inputangle
= 3 * (M_PI
/ 2.0);
633 inputangle
= atan(fvely
/ fvelx
);
635 double slopeangle
= atan(-ydiff
/ xdiff
); // xdiff != 0 because wall isn't vertical
637 // calculate output angle
638 double outputangle
= slopeangle
+ (slopeangle
- inputangle
) + M_PI
;
640 // turn back into component velocities
641 double vectorlength
= sqrt(fvelx
*fvelx
+ fvely
*fvely
);
642 float xoutput
= cos(outputangle
) * vectorlength
;
643 float youtput
= sin(outputangle
) * vectorlength
;
645 velx
.setFloat(xoutput
);
646 vely
.setFloat(-youtput
);
649 if (elas
!= 100.0f
) {
650 velx
.setFloat(velx
.getFloat() * (elas
/ 100.0f
));
651 vely
.setFloat(vely
.getFloat() * (elas
/ 100.0f
));
655 } else if (sufferphysics() && accg
!= 0) {
656 falling
= true; // TODO: icky
657 vely
.setFloat(vely
.getFloat() + accg
.getFloat());
659 } else { velx
.setFloat(0); vely
.setFloat(0); } // TODO: correct?
661 if (vely
.hasDecimal() || velx
.hasDecimal())
662 moveTo(destx
, desty
);
664 vely
.setFloat(vely
.getFloat() + accg
.getFloat());
667 if (sufferphysics() && (aero
!= 0)) {
668 // reduce speed according to AERO
669 // TODO: aero should be an integer!
670 velx
.setFloat(velx
.getFloat() - (velx
.getFloat() * (aero
.getFloat() / 100.0f
)));
671 vely
.setFloat(vely
.getFloat() - (vely
.getFloat() * (aero
.getFloat() / 100.0f
)));
675 shared_ptr
<Room
> const Agent::bestRoomAt(unsigned int tryx
, unsigned int tryy
, unsigned int direction
, shared_ptr
<Room
> exclude
) {
676 std::vector
<shared_ptr
<Room
> > rooms
= world
.map
.roomsAt(tryx
, tryy
);
680 if (rooms
.size() == 0) return shared_ptr
<Room
>();
681 if (rooms
.size() == 1) r
= rooms
[0];
682 else if (rooms
.size() > 1) {
684 for (j
= 0; j
< rooms
.size(); j
++) {
685 if (rooms
[j
] == exclude
) continue;
687 if (direction
== 0) { // left
688 if (rooms
[j
]->x_left
== tryx
) break;
689 } else if (direction
== 1) { // right
690 if (rooms
[j
]->x_right
== tryx
) break;
691 } else if (direction
== 2) { // top
692 if (rooms
[j
]->y_left_ceiling
== tryy
) break;
694 if (rooms
[j
]->y_left_floor
== tryy
) break;
697 if (j
== rooms
.size()) j
= 0;
701 if (r
== exclude
) return shared_ptr
<Room
>();
705 // Creatures 2 collision finding
706 void const Agent::findCollisionInDirection(unsigned int i
, Point src
, int &dx
, int &dy
, Point
&deltapt
, double &delta
, bool &collided
, bool followrooms
) {
710 shared_ptr
<Room
> room
= bestRoomAt(src
.x
, src
.y
, i
, shared_ptr
<Room
>());
712 if (!room
) { // out of room system
714 unhandledException(boost::str(boost::format("out of room system at (%f, %f)") % src
.x
% src
.y
), false);
722 bool steep
= abs(dy
) > abs(dx
);
724 int signdx
= dx
< 0 ? -1 : 1;
725 int signdy
= dy
< 0 ? -1 : 1;
727 Line
l(Point(0,0),Point(dx
,dy
));
729 Point
lastpoint(0,0);
731 for (int loc
= 0; loc
<= abs(steep
? dy
: dx
); loc
++) {
732 Point p
= steep
? l
.pointAtY(loc
*signdy
) : l
.pointAtX(loc
*signdx
);
736 bool trycollisions
= false;
738 bool endofroom
= false;
740 if (src
.x
+ p
.x
< room
->x_left
) {
741 if (i
!= 1 && dx
< 0) { trycollisions
= true; lastdirection
= 0; }
744 if (src
.x
+ p
.x
> room
->x_right
) {
745 if (i
!= 0 && dx
> 0) { trycollisions
= true; lastdirection
= 1; }
748 if (src
.y
+ p
.y
< room
->y_left_ceiling
) {
749 if (i
!= 3 && dy
< 0) { trycollisions
= true; lastdirection
= 2; }
752 if (src
.y
+ p
.y
> room
->y_left_floor
) {
753 if (i
!= 2 && dy
> 0) { trycollisions
= true; lastdirection
= 3; }
757 // find the next room, if necessary, and work out whether we should move into it or break out
759 shared_ptr
<Room
> newroom
= bestRoomAt(src
.x
+ p
.x
, src
.y
+ p
.y
, i
, room
);
761 bool collision
= false;
763 // collide if we're out of the room system
764 if (!newroom
) { collision
= true; }
765 // collide if there's no new room connected to this one
766 else if (room
->doors
.find(newroom
) == room
->doors
.end()) { collision
= true; }
767 // collide if the PERM between this room and the new room is smaller than or equal to our size
768 else if (size
.getInt() > room
->doors
[newroom
]->perm
) { collision
= true; }
770 if (collision
&& (trycollisions
|| (!newroom
))) {
775 // move to the new room and keep going!
779 if (room
->floorpoints
.size() && i
== 3 && dy
>= 0 && size
.getInt() > room
->floorvalue
.getInt()) { // TODO: Hack!
780 // TODO: we don't check floorYatX isn't returning a 'real' room floor, but floorpoints should cover the whole floor anyway
781 int floory
= room
->floorYatX(src
.x
+ p
.x
);
783 // never collide when the top point of an object is below the floor.
784 Point top
= boundingBoxPoint(2);
786 // TODO: consider steep floors
787 if (src
.y
+ p
.y
> floory
&& top
.y
< floory
) {
794 if ((!followrooms
) && endofroom
) break;
799 double length2
= (lastpoint
.x
* lastpoint
.x
) + (lastpoint
.y
* lastpoint
.y
);
800 if (length2
< delta
) {
801 // TODO: !followrooms is a horrible way to detect a non-physics call
802 if (collided
&& followrooms
) lastcollidedirection
= lastdirection
;
808 void Agent::physicsTickC2() {
809 int dx
= velx
.getInt(), dy
= vely
.getInt();
811 if (dx
!= 0 || dy
!= 0) grav
.setInt(1);
813 if (grav
.getInt() != 0 && sufferphysics()) {
819 if (dy
== 0 && dx
== 0) { // nothing to do
820 if (vely
.getInt() == 0) {
824 // y motion cancelled by gravity
832 double delta
= 1000000000;
834 bool collided
= false;
836 if (suffercollisions()) {
837 for (unsigned int i
= 0; i
< 4; i
++) {
838 Point src
= boundingBoxPoint(i
);
839 findCollisionInDirection(i
, src
, dx
, dy
, deltapt
, delta
, collided
, true);
846 if (collided
&& (velx
.getInt() != 0 || vely
.getInt() != 0)) {
847 if (lastcollidedirection
>= 2) // up and down
848 vely
.setInt(-(vely
.getInt() - (rest
.getInt() * vely
.getInt()) / 100));
850 velx
.setInt(-(velx
.getInt() - (rest
.getInt() * velx
.getInt()) / 100));
853 if ((int)deltapt
.x
== 0 && (int)deltapt
.y
== 0) {
858 moveTo(x
+ (int)deltapt
.x
, y
+ (int)deltapt
.y
);
859 if (sufferphysics()) {
860 int fricx
= (aero
.getInt() * velx
.getInt()) / 100;
861 int fricy
= (aero
.getInt() * vely
.getInt()) / 100;
862 if (abs(velx
.getInt()) > 0 && fricx
== 0) fricx
= (velx
.getInt() < 0) ? -1 : 1;
863 if (abs(vely
.getInt()) > 0 && fricy
== 0) fricy
= (vely
.getInt() < 0) ? -1 : 1;
864 velx
.setInt(velx
.getInt() - fricx
);
865 vely
.setInt(vely
.getInt() - fricy
);
871 // sanity checks to stop ticks on dead agents
876 // if the sound is no longer playing...
877 if (sound
->getState() != SS_PLAY
) {
878 // ...release our reference to it
881 // otherwise, reposition audio
886 // don't tick paused agents
890 if (emitca_index
!= -1 && emitca_amount
!= 0.0f
) {
891 assert(0 <= emitca_index
&& emitca_index
<= 19);
892 shared_ptr
<Room
> r
= world
.map
.roomAt(x
, y
);
894 r
->catemp
[emitca_index
] += emitca_amount
;
895 /*if (r->catemp[emitca_index] <= 0.0f) r->catemp[emitca_index] = 0.0f;
896 else if (r->catemp[emitca_index] >= 1.0f) r->catemp[emitca_index] = 1.0f;*/
900 // tick the physics engine
902 if (dying
) return; // in case we were autokilled
904 // update the timer if needed, and then queue a timer event if necessary
906 tickssincelasttimer
++;
907 if (tickssincelasttimer
== timerrate
) {
908 queueScript(9); // TODO: include this?
909 tickssincelasttimer
= 0;
911 assert(timerrate
> tickssincelasttimer
);
918 void Agent::unhandledException(std::string info
, bool wasscript
) {
919 // TODO: do something with this? empty the stack?
920 if (world
.autostop
) {
921 // autostop is mostly for Creatures Village, which is full of buggy scripts
923 } else if (world
.autokill
&& !dynamic_cast<CreatureAgent
*>(this)) { // don't autokill creatures! TODO: someday we probably should :)
926 std::cerr
<< identify() << " was autokilled during script " << lastScript
<< " due to: " << info
<< std::endl
;
928 std::cerr
<< identify() << " was autokilled due to: " << info
<< std::endl
;
932 std::cerr
<< identify() << " caused an exception during script " << lastScript
<< ": " << info
<< std::endl
;
934 std::cerr
<< identify() << " caused an exception: " << info
<< std::endl
;
938 void Agent::vmTick() {
939 // Tick the VM associated with this agent.
941 assert(vm
); // There must *be* a VM to tick.
942 LifeAssert
la(this); // sanity check
944 // If we're out of timeslice, give ourselves some more (depending on the game type).
945 if (!vm
->timeslice
) {
946 vm
->timeslice
= (engine
.version
< 3) ? 1 : 5;
949 // Keep trying to run VMs while we don't run out of timeslice, end up with a blocked VM, or a VM stops.
950 while (vm
&& vm
->timeslice
&& !vm
->isBlocking() && !vm
->stopped()) {
951 assert(vm
->timeslice
> 0);
953 // Tell the VM to tick (using all available timeslice), catching exceptions as necessary.
956 } catch (invalidAgentException
&e
) {
957 // try letting the exception script handle it
958 if (!queueScript(255))
959 unhandledException(e
.prettyPrint(), true);
961 stopScript(); // we still want current script to die
962 } catch (creaturesException
&e
) {
963 unhandledException(e
.prettyPrint(), true);
964 } catch (std::exception
&e
) {
965 unhandledException(e
.what(), true);
968 // If the VM stopped, it's done.
969 if (vm
&& vm
->stopped()) {
975 // Zot any remaining timeslice, since we're done now.
976 if (vm
) vm
->timeslice
= 0;
978 // If there's no current VM but there's one on the call stack, a previous VM must have finished,
979 // pop one from the call stack to run next time.
980 if (!vm
&& !vmstack
.empty()) {
981 vm
= vmstack
.front();
987 assert(lifecount
== 0);
989 if (!initialized
) return;
991 // we can't do kill() here because we can't do anything which might try using our shared_ptr
992 // (since this could be during world destruction)
1009 void Agent::kill() {
1011 if (floatable()) floatRelease();
1012 if (carrying
) dropCarried(carrying
);
1013 if (carriedby
) carriedby
->drop(this);
1014 // TODO: should the carried agent really be responsible for dropping from vehicle?
1015 if (invehicle
) invehicle
->drop(this);
1017 dying
= true; // what a world, what a world...
1026 agents_iter
->reset();
1034 unsigned int Agent::getZOrder() const {
1036 // TODO: take notice of cabp in c2e, at least. also, stacking .. ?
1037 Vehicle
*v
= dynamic_cast<Vehicle
*>(invehicle
.get());
1039 if (engine
.version
< 3)
1040 return v
->cabinplane
; // TODO: correct?
1042 return v
->getZOrder() + v
->cabinplane
;
1043 // TODO: Vehicle should probably rearrange zorder of passengers if ever moved
1044 } else if (carriedby
) {
1045 // TODO: check for overflow
1046 // TODO: is adding our own zorder here correct behaviour? someone should check
1047 if (engine
.version
> 1)
1048 return carriedby
->getZOrder() - 100;
1050 return carriedby
->getZOrder();
1056 void Agent::setZOrder(unsigned int z
) {
1061 int Agent::getUNID() const {
1064 return unid
= world
.newUNID(const_cast<Agent
*>(this));
1067 #include "Catalogue.h"
1069 std::string
Agent::identify() const {
1070 std::ostringstream o
;
1071 o
<< (int)family
<< " " << (int)genus
<< " " << species
;
1072 const std::string n
= catalogue
.getAgentName(family
, genus
, species
);
1074 o
<< " (" + n
+ ")";
1076 o << " unid " << unid;
1078 o << " (no unid assigned)"; */
1082 bool agentzorder::operator ()(const Agent
*s1
, const Agent
*s2
) const {
1083 return s1
->getZOrder() < s2
->getZOrder();
1086 void Agent::pushVM(caosVM
*newvm
) {
1089 vmstack
.push_front(vm
);
1093 bool Agent::vmStopped() {
1094 return (!vm
|| vm
->stopped());
1097 void Agent::stopScript() {
1103 void Agent::addCarried(AgentRef a
) {
1106 // TODO: muh, vehicle drop needs more thought
1108 Vehicle
*v
= dynamic_cast<Vehicle
*>(a
->invehicle
.get());
1115 // TODO: this doesn't reorder children or anything..
1116 a
->setZOrder(a
->zorder
);
1118 // fire 'Got Carried Agent'
1119 if (engine
.version
>= 3)
1120 queueScript(124, a
); // TODO: is this the correct param?
1123 void Agent::carry(AgentRef a
) {
1126 // TODO: check for infinite loops (eg, us carrying an agent which is carrying us) :)
1129 dropCarried(carrying
);
1133 a
->carriedby
= AgentRef(this);
1134 // TODO: move carrying agent to the right position
1135 adjustCarried(0, 0);
1138 bool Agent::beDropped() {
1139 carriedby
= AgentRef(0);
1140 bool wasinvehicle
= invehicle
;
1141 invehicle
= AgentRef(0);
1143 // TODO: this doesn't reorder children or anything..
1146 // TODO: no idea if this is right, it tries to re-enable gravity when dropping C2 agents
1147 if (engine
.version
== 2) grav
.setInt(1);
1148 if (engine
.version
== 3) falling
= true;
1150 if (!wasinvehicle
) { // ie, we're not being dropped by a vehicle
1151 // TODO: check for vehicles in a saner manner?
1152 for (std::list
<boost::shared_ptr
<Agent
> >::iterator i
= world
.agents
.begin(); i
!= world
.agents
.end(); i
++) {
1153 boost::shared_ptr
<Agent
> a
= (*i
);
1155 Vehicle
*v
= dynamic_cast<Vehicle
*>(a
.get());
1158 if (agentsTouching(this, v
)) {
1159 v
->addCarried(this);
1160 // TODO: how to handle not-invehicle case, where vehicle has failed to pick us up?
1161 if (invehicle
) return true;
1166 if (engine
.version
> 1) {
1167 // TODO: maybe think about this some more - does this belong here?
1168 tryMoveToPlaceAround(x
, y
);
1171 // TODO: return value is not used anywhere yet?
1175 void Agent::dropCarried(AgentRef a
) {
1178 // TODO: this doesn't reorder children or anything..
1179 a
->setZOrder(a
->zorder
);
1181 // fire 'Lost Carried Agent'
1182 if (engine
.version
>= 3)
1183 queueScript(125, a
); // TODO: is this the correct param?
1186 void Agent::drop(AgentRef a
) {
1187 if (!carrying
) return;
1188 assert(carrying
== a
);
1191 carrying
= AgentRef(0);
1194 std::pair
<int, int> Agent::getCarryPoint() {
1195 unsigned int ourpose
= 0;
1198 if ((s
= dynamic_cast<SpritePart
*>(part(0))))
1199 ourpose
= s
->getBase() + s
->getPose();
1201 std::pair
<int, int> pos(0, 0);
1203 std::map
<unsigned int, std::pair
<int, int> >::iterator i
= carry_points
.find(ourpose
);
1204 if (i
!= carry_points
.end())
1210 std::pair
<int, int> Agent::getCarriedPoint() {
1211 unsigned int theirpose
= 0;
1214 if ((s
= dynamic_cast<SpritePart
*>(part(0))))
1215 theirpose
= s
->getBase() + s
->getPose();
1217 std::pair
<int, int> pos(0, 0);
1219 std::map
<unsigned int, std::pair
<int, int> >::iterator i
= carried_points
.find(theirpose
);
1220 if (i
!= carried_points
.end()) {
1222 } else if (s
&& engine
.version
> 2) {
1223 // TODO: why fudge pickup location here and not in default carried points or something?
1224 // (this is nornagon's code which i moved - fuzzie)
1225 pos
.first
= s
->getSprite()->width(s
->getCurrentSprite()) / 2;
1231 void Agent::adjustCarried(float unusedxoffset
, float unusedyoffset
) {
1232 // Adjust the position of the agent we're carrying.
1233 // TODO: this doesn't actually position the carried agent correctly, sigh
1235 if (!carrying
) return;
1237 unsigned int ourpose
= 0;
1239 int xoffset
= 0, yoffset
= 0;
1240 if (engine
.version
< 3 && world
.hand() == this) {
1241 // this appears to produce correct behaviour in the respective games, don't ask me -nornagon
1242 if (engine
.version
== 2)
1243 yoffset
= world
.hand()->getHeight() / 2 - 2;
1245 yoffset
= world
.hand()->getHeight() / 2 - 3;
1248 std::pair
<int, int> carrypoint
= getCarryPoint();
1249 xoffset
+= carrypoint
.first
;
1250 yoffset
+= carrypoint
.second
;
1252 std::pair
<int, int> carriedpoint
= carrying
->getCarriedPoint();
1253 xoffset
-= carriedpoint
.first
;
1254 yoffset
-= carriedpoint
.second
;
1256 if (xoffset
!= 0 || yoffset
!= 0)
1257 carrying
->moveTo(x
+ xoffset
, y
+ yoffset
, true);
1260 void Agent::setClassifier(unsigned char f
, unsigned char g
, unsigned short s
) {
1265 if (engine
.version
< 3) {
1266 // TODO: categories for other game versions
1269 category
= world
.findCategory(family
, genus
, species
);
1273 bool Agent::tryMoveToPlaceAround(float x
, float y
) {
1274 if (!suffercollisions()) {
1279 // second hacky attempt, move from side to side (+/- width) and up (- height) a little
1280 unsigned int trywidth
= getWidth() * 2; if (trywidth
< 100) trywidth
= 100;
1281 unsigned int tryheight
= getHeight() * 2; if (tryheight
< 100) tryheight
= 100;
1282 for (unsigned int xadjust
= 0; xadjust
< trywidth
; xadjust
++) {
1283 for (unsigned int yadjust
= 0; yadjust
< tryheight
; yadjust
++) {
1284 if (validInRoomSystem(Point(x
- xadjust
, y
- yadjust
), getWidth(), getHeight(), perm
))
1285 moveTo(x
- xadjust
, y
- yadjust
);
1286 else if ((xadjust
!= 0) && validInRoomSystem(Point(x
+ xadjust
, y
- yadjust
), getWidth(), getHeight(), perm
))
1287 moveTo(x
+ xadjust
, y
- yadjust
);
1297 void Agent::join(unsigned int outid
, AgentRef dest
, unsigned int inid
) {
1299 assert(outports
.find(outid
) != outports
.end());
1300 assert(dest
->inports
.find(inid
) != dest
->inports
.end());
1301 outports
[outid
]->dests
.push_back(std::pair
<AgentRef
, unsigned int>(dest
, inid
));
1302 dest
->inports
[inid
]->source
= this;
1303 dest
->inports
[inid
]->sourceid
= outid
;
1306 /* vim: set noet: */