2006-04-02 14:20:45 +00:00
|
|
|
/* Residual - Virtual machine to run LucasArts' 3D adventure games
|
|
|
|
* Copyright (C) 2003-2006 The ScummVM-Residual Team (www.scummvm.org)
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
|
|
*
|
|
|
|
* $URL$
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
*/
|
2003-08-15 18:00:22 +00:00
|
|
|
|
2003-08-24 17:56:03 +00:00
|
|
|
#include "stdafx.h"
|
2003-08-15 19:41:26 +00:00
|
|
|
#include "actor.h"
|
|
|
|
#include "engine.h"
|
|
|
|
#include "costume.h"
|
2004-09-11 14:09:43 +00:00
|
|
|
#include "lipsynch.h"
|
2004-03-20 09:38:33 +00:00
|
|
|
#include "localize.h"
|
2005-01-12 18:06:43 +00:00
|
|
|
#include "driver.h"
|
2005-03-19 21:48:23 +00:00
|
|
|
#include "smush.h"
|
2007-02-05 13:49:32 +00:00
|
|
|
#include "walkplane.h"
|
2005-01-01 12:27:57 +00:00
|
|
|
|
2004-09-11 14:09:43 +00:00
|
|
|
#include "mixer/mixer.h"
|
|
|
|
|
2005-01-01 21:07:52 +00:00
|
|
|
#include "imuse/imuse.h"
|
|
|
|
|
2005-01-01 12:27:57 +00:00
|
|
|
#include <cmath>
|
|
|
|
#include <cstring>
|
|
|
|
|
2003-08-15 18:00:22 +00:00
|
|
|
Actor::Actor(const char *name) :
|
2005-08-10 08:33:45 +00:00
|
|
|
_name(name), _setName(""), _talkColor(255, 255, 255), _pos(0, 0, 0),
|
2005-08-01 03:49:02 +00:00
|
|
|
// Some actors don't set walk and turn rates, so we default the
|
|
|
|
// _turnRate so Doug at the cat races can turn and we set the
|
|
|
|
// _walkRate so Glottis at the demon beaver entrance can walk
|
|
|
|
_pitch(0), _yaw(0), _roll(0), _walkRate(1.0f), _turnRate(100.0f),
|
2005-08-10 08:33:45 +00:00
|
|
|
_reflectionAngle(80),
|
2005-01-01 21:07:52 +00:00
|
|
|
_visible(true), _lipSynch(NULL), _turning(false), _walking(false),
|
2004-12-09 23:55:43 +00:00
|
|
|
_restCostume(NULL), _restChore(-1),
|
|
|
|
_walkCostume(NULL), _walkChore(-1), _walkedLast(false), _walkedCur(false),
|
|
|
|
_turnCostume(NULL), _leftTurnChore(-1), _rightTurnChore(-1),
|
|
|
|
_lastTurnDir(0), _currTurnDir(0),
|
2005-03-19 21:48:23 +00:00
|
|
|
_mumbleCostume(NULL), _mumbleChore(-1), _sayLineText(NULL) {
|
2004-12-31 21:35:04 +00:00
|
|
|
g_engine->registerActor(this);
|
2004-12-09 23:55:43 +00:00
|
|
|
_lookingMode = false;
|
|
|
|
_constrain = false;
|
2005-01-14 09:43:09 +00:00
|
|
|
_talkSoundName = "";
|
2007-02-05 13:49:32 +00:00
|
|
|
_activeShadowSlot = -1;
|
2007-03-12 21:50:44 +00:00
|
|
|
_shadowArray = new Shadow[5];
|
|
|
|
memset(_shadowArray, 0, sizeof(Shadow) * 5);
|
2004-03-24 01:36:05 +00:00
|
|
|
|
|
|
|
for (int i = 0; i < 10; i++) {
|
2004-12-09 23:55:43 +00:00
|
|
|
_talkCostume[i] = NULL;
|
|
|
|
_talkChore[i] = -1;
|
2004-03-24 01:36:05 +00:00
|
|
|
}
|
2003-08-15 18:00:22 +00:00
|
|
|
}
|
|
|
|
|
2005-07-26 01:46:53 +00:00
|
|
|
void Actor::setYaw(float yaw) {
|
|
|
|
// While the program correctly handle yaw angles outside
|
|
|
|
// of the range [0, 360), proper convention is to roll
|
|
|
|
// these values over correctly
|
|
|
|
if (yaw >= 360.0)
|
|
|
|
_yaw = yaw - 360;
|
|
|
|
else if (yaw < 0.0)
|
|
|
|
_yaw = yaw + 360;
|
|
|
|
else
|
|
|
|
_yaw = yaw;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Actor::setRot(float pitch, float yaw, float roll) {
|
|
|
|
_pitch = pitch;
|
|
|
|
setYaw(yaw);
|
|
|
|
_roll = roll;
|
|
|
|
}
|
|
|
|
|
2003-08-22 11:37:39 +00:00
|
|
|
void Actor::turnTo(float pitch, float yaw, float roll) {
|
2004-12-09 23:55:43 +00:00
|
|
|
_pitch = pitch;
|
|
|
|
_roll = roll;
|
|
|
|
if (_yaw != yaw) {
|
|
|
|
_turning = true;
|
|
|
|
_destYaw = yaw;
|
2004-02-24 08:20:45 +00:00
|
|
|
} else
|
2004-12-09 23:55:43 +00:00
|
|
|
_turning = false;
|
2003-08-22 11:37:39 +00:00
|
|
|
}
|
|
|
|
|
2003-08-22 12:28:33 +00:00
|
|
|
void Actor::walkTo(Vector3d p) {
|
2004-02-24 08:20:45 +00:00
|
|
|
// For now, this is just the ignoring-boxes version (which afaict
|
|
|
|
// isn't even in the original). This will eventually need a
|
|
|
|
// following-boxes version also.
|
2004-12-09 23:55:43 +00:00
|
|
|
if (p == _pos)
|
|
|
|
_walking = false;
|
2004-02-24 08:20:45 +00:00
|
|
|
else {
|
2004-12-09 23:55:43 +00:00
|
|
|
_walking = true;
|
|
|
|
_destPos = p;
|
2004-02-24 08:20:45 +00:00
|
|
|
|
2004-12-09 23:55:43 +00:00
|
|
|
if (p.x() != _pos.x() || p.y() != _pos.y())
|
|
|
|
turnTo(_pitch, yawTo(p), _roll);
|
2004-02-24 08:20:45 +00:00
|
|
|
}
|
2003-08-22 12:28:33 +00:00
|
|
|
}
|
|
|
|
|
2004-03-25 15:43:05 +00:00
|
|
|
bool Actor::isWalking() const {
|
2004-12-09 23:55:43 +00:00
|
|
|
return _walkedLast || _walkedCur || _walking;
|
2004-03-25 15:43:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Actor::isTurning() const {
|
2004-12-09 23:55:43 +00:00
|
|
|
if (_turning)
|
2004-03-25 15:43:05 +00:00
|
|
|
return true;
|
2004-12-09 23:55:43 +00:00
|
|
|
|
|
|
|
if (_lastTurnDir != 0 || _currTurnDir != 0)
|
2004-03-25 15:43:05 +00:00
|
|
|
return true;
|
2004-12-09 23:55:43 +00:00
|
|
|
|
2004-03-25 15:43:05 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2003-08-15 18:00:22 +00:00
|
|
|
void Actor::walkForward() {
|
2004-12-31 21:35:04 +00:00
|
|
|
float dist = g_engine->perSecond(_walkRate);
|
2004-12-09 23:55:43 +00:00
|
|
|
float yaw_rad = _yaw * (M_PI / 180), pitch_rad = _pitch * (M_PI / 180);
|
2005-08-08 07:05:58 +00:00
|
|
|
//float yaw;
|
2004-02-24 08:20:45 +00:00
|
|
|
Vector3d forwardVec(-std::sin(yaw_rad) * std::cos(pitch_rad),
|
|
|
|
std::cos(yaw_rad) * std::cos(pitch_rad),
|
|
|
|
std::sin(pitch_rad));
|
2004-12-09 23:55:43 +00:00
|
|
|
Vector3d destPos = _pos + forwardVec * dist;
|
2004-03-25 09:33:17 +00:00
|
|
|
|
2005-03-28 01:56:40 +00:00
|
|
|
if (! _constrain) {
|
|
|
|
_pos += forwardVec * dist;
|
2004-12-09 23:55:43 +00:00
|
|
|
_walkedCur = true;
|
2005-03-28 01:56:40 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dist < 0) {
|
|
|
|
dist = -dist;
|
|
|
|
forwardVec = -forwardVec;
|
|
|
|
}
|
|
|
|
|
|
|
|
Sector *currSector = NULL, *prevSector = NULL;
|
|
|
|
Sector::ExitInfo ei;
|
|
|
|
|
|
|
|
g_engine->currScene()->findClosestSector(_pos, &currSector, &_pos);
|
|
|
|
if (currSector == NULL) { // Shouldn't happen...
|
|
|
|
_pos += forwardVec * dist;
|
|
|
|
_walkedCur = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (currSector != NULL) {
|
|
|
|
prevSector = currSector;
|
|
|
|
Vector3d puckVector = currSector->projectToPuckVector(forwardVec);
|
|
|
|
puckVector /= puckVector.magnitude();
|
|
|
|
currSector->getExitInfo(_pos, puckVector, &ei);
|
|
|
|
float exitDist = (ei.exitPoint - _pos).magnitude();
|
|
|
|
if (dist < exitDist) {
|
|
|
|
_pos += puckVector * dist;
|
2004-12-09 23:55:43 +00:00
|
|
|
_walkedCur = true;
|
2005-03-28 01:56:40 +00:00
|
|
|
return;
|
2004-03-25 09:33:17 +00:00
|
|
|
}
|
2005-03-28 01:56:40 +00:00
|
|
|
_pos = ei.exitPoint;
|
|
|
|
dist -= exitDist;
|
|
|
|
if (exitDist > 0.0001)
|
|
|
|
_walkedCur = true;
|
|
|
|
|
|
|
|
// Check for an adjacent sector which can continue
|
|
|
|
// the path
|
2005-04-03 10:41:57 +00:00
|
|
|
currSector = g_engine->currScene()->findPointSector(ei.exitPoint + (float)0.0001 * puckVector, 0x1000);
|
2005-03-28 01:56:40 +00:00
|
|
|
if (currSector == prevSector)
|
|
|
|
break;
|
2004-03-25 09:33:17 +00:00
|
|
|
}
|
2005-03-28 01:56:40 +00:00
|
|
|
|
2005-04-03 10:41:57 +00:00
|
|
|
ei.angleWithEdge *= (float)(180.0 / M_PI);
|
2005-03-28 01:56:40 +00:00
|
|
|
int turnDir = 1;
|
|
|
|
if (ei.angleWithEdge > 90) {
|
|
|
|
ei.angleWithEdge = 180 - ei.angleWithEdge;
|
|
|
|
ei.edgeDir = -ei.edgeDir;
|
|
|
|
turnDir = -1;
|
|
|
|
}
|
|
|
|
if (ei.angleWithEdge > _reflectionAngle)
|
|
|
|
return;
|
|
|
|
|
2005-04-03 10:41:57 +00:00
|
|
|
ei.angleWithEdge += (float)0.1;
|
2005-03-28 01:56:40 +00:00
|
|
|
float turnAmt = g_engine->perSecond(_turnRate);
|
|
|
|
if (turnAmt > ei.angleWithEdge)
|
|
|
|
turnAmt = ei.angleWithEdge;
|
2005-07-26 01:46:53 +00:00
|
|
|
setYaw(_yaw + turnAmt * turnDir);
|
2004-03-24 01:36:05 +00:00
|
|
|
}
|
|
|
|
|
2004-03-25 15:43:05 +00:00
|
|
|
Vector3d Actor::puckVector() const {
|
2004-12-09 23:55:43 +00:00
|
|
|
float yaw_rad = _yaw * (M_PI / 180);
|
2004-03-25 15:43:05 +00:00
|
|
|
Vector3d forwardVec(-std::sin(yaw_rad), std::cos(yaw_rad), 0);
|
|
|
|
|
2004-12-31 21:35:04 +00:00
|
|
|
Sector *sector = g_engine->currScene()->findPointSector(_pos, 0x1000);
|
2004-03-25 15:43:05 +00:00
|
|
|
if (sector == NULL)
|
|
|
|
return forwardVec;
|
|
|
|
else
|
|
|
|
return sector->projectToPuckVector(forwardVec);
|
|
|
|
}
|
|
|
|
|
2004-03-24 01:36:05 +00:00
|
|
|
void Actor::setRestChore(int chore, Costume *cost) {
|
2004-12-09 23:55:43 +00:00
|
|
|
if (_restCostume == cost && _restChore == chore)
|
2004-03-25 15:43:05 +00:00
|
|
|
return;
|
2004-12-09 23:55:43 +00:00
|
|
|
|
|
|
|
if (_restChore >= 0)
|
|
|
|
_restCostume->stopChore(_restChore);
|
|
|
|
|
|
|
|
_restCostume = cost;
|
|
|
|
_restChore = chore;
|
|
|
|
|
|
|
|
if (_restChore >= 0)
|
|
|
|
_restCostume->playChoreLooping(_restChore);
|
2004-03-24 01:36:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Actor::setWalkChore(int chore, Costume *cost) {
|
2004-12-09 23:55:43 +00:00
|
|
|
if (_walkCostume == cost && _walkChore == chore)
|
2004-03-25 15:43:05 +00:00
|
|
|
return;
|
2004-12-09 23:55:43 +00:00
|
|
|
|
|
|
|
if (_walkChore >= 0)
|
|
|
|
_walkCostume->stopChore(_walkChore);
|
|
|
|
|
|
|
|
_walkCostume = cost;
|
|
|
|
_walkChore = chore;
|
2004-03-24 01:36:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Actor::setTurnChores(int left_chore, int right_chore, Costume *cost) {
|
2004-12-09 23:55:43 +00:00
|
|
|
if (_turnCostume == cost && _leftTurnChore == left_chore &&
|
|
|
|
_rightTurnChore == right_chore)
|
2004-03-25 15:43:05 +00:00
|
|
|
return;
|
2004-12-09 23:55:43 +00:00
|
|
|
|
|
|
|
if (_leftTurnChore >= 0) {
|
|
|
|
_turnCostume->stopChore(_leftTurnChore);
|
|
|
|
_turnCostume->stopChore(_rightTurnChore);
|
2004-03-24 01:36:05 +00:00
|
|
|
}
|
|
|
|
|
2004-12-09 23:55:43 +00:00
|
|
|
_turnCostume = cost;
|
|
|
|
_leftTurnChore = left_chore;
|
|
|
|
_rightTurnChore = right_chore;
|
|
|
|
|
|
|
|
if ((left_chore >= 0 && right_chore < 0) || (left_chore < 0 && right_chore >= 0))
|
2004-03-24 01:36:05 +00:00
|
|
|
error("Unexpectedly got only one turn chore\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
void Actor::setTalkChore(int index, int chore, Costume *cost) {
|
|
|
|
if (index < 1 || index > 10)
|
|
|
|
error("Got talk chore index out of range (%d)\n", index);
|
2004-12-09 23:55:43 +00:00
|
|
|
|
2004-03-24 01:36:05 +00:00
|
|
|
index--;
|
2004-12-09 23:55:43 +00:00
|
|
|
|
|
|
|
if (_talkCostume[index] == cost && _talkChore[index] == chore)
|
2004-03-25 15:43:05 +00:00
|
|
|
return;
|
2004-12-09 23:55:43 +00:00
|
|
|
|
|
|
|
if (_talkChore[index] >= 0)
|
|
|
|
_talkCostume[index]->stopChore(_talkChore[index]);
|
|
|
|
|
|
|
|
_talkCostume[index] = cost;
|
|
|
|
_talkChore[index] = chore;
|
2004-03-24 01:36:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Actor::setMumbleChore(int chore, Costume *cost) {
|
2004-12-09 23:55:43 +00:00
|
|
|
if (_mumbleChore >= 0)
|
|
|
|
_mumbleCostume->stopChore(_mumbleChore);
|
|
|
|
|
|
|
|
_mumbleCostume = cost;
|
|
|
|
_mumbleChore = chore;
|
2003-08-15 18:00:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Actor::turn(int dir) {
|
2004-12-31 21:35:04 +00:00
|
|
|
float delta = g_engine->perSecond(_turnRate) * dir;
|
2005-07-26 01:46:53 +00:00
|
|
|
setYaw(_yaw + delta);
|
2004-12-09 23:55:43 +00:00
|
|
|
_currTurnDir = dir;
|
2003-08-15 18:00:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
float Actor::angleTo(const Actor &a) const {
|
2004-12-09 23:55:43 +00:00
|
|
|
float yaw_rad = _yaw * (M_PI / 180);
|
2004-02-24 08:20:45 +00:00
|
|
|
Vector3d forwardVec(-std::sin(yaw_rad), std::cos(yaw_rad), 0);
|
2004-12-09 23:55:43 +00:00
|
|
|
Vector3d delta = a.pos() - _pos;
|
2004-02-24 08:20:45 +00:00
|
|
|
delta.z() = 0;
|
2004-12-09 23:55:43 +00:00
|
|
|
|
2004-02-24 08:20:45 +00:00
|
|
|
return angle(forwardVec, delta) * (180 / M_PI);
|
2003-08-15 18:00:22 +00:00
|
|
|
}
|
|
|
|
|
2003-08-22 12:28:33 +00:00
|
|
|
float Actor::yawTo(Vector3d p) const {
|
2004-12-09 23:55:43 +00:00
|
|
|
Vector3d dpos = p - _pos;
|
|
|
|
|
2004-02-24 08:20:45 +00:00
|
|
|
if (dpos.x() == 0 && dpos.y() == 0)
|
|
|
|
return 0;
|
|
|
|
else
|
|
|
|
return std::atan2(-dpos.x(), dpos.y()) * (180 / M_PI);
|
2003-08-22 12:28:33 +00:00
|
|
|
}
|
|
|
|
|
2005-01-02 13:34:50 +00:00
|
|
|
void Actor::sayLine(const char *msg, const char *msgId) {
|
|
|
|
assert(msg);
|
|
|
|
assert(msgId);
|
2004-02-24 08:20:45 +00:00
|
|
|
|
2005-04-03 11:33:28 +00:00
|
|
|
std::string textName = msgId;
|
|
|
|
textName += ".txt";
|
|
|
|
|
|
|
|
if (msgId[0] == 0) {
|
|
|
|
error("Actor::sayLine: No message ID for text!");
|
2004-02-24 08:20:45 +00:00
|
|
|
return;
|
2005-04-03 11:33:28 +00:00
|
|
|
}
|
2004-12-09 23:55:43 +00:00
|
|
|
|
2005-07-17 23:40:22 +00:00
|
|
|
// During Fullscreen movies SayLine is called for text display only
|
|
|
|
// However, normal SMUSH movies may call SayLine, for example:
|
|
|
|
// When Domino yells at Manny (a SMUSH movie) he does it with
|
|
|
|
// a SayLine request rather than as part of the movie!
|
|
|
|
if (!g_smush->isPlaying() || g_engine->getMode() == ENGINE_MODE_NORMAL) {
|
2005-03-19 21:48:23 +00:00
|
|
|
std::string soundName = msgId;
|
|
|
|
std::string soundLip = msgId;
|
|
|
|
soundName += ".wav";
|
|
|
|
soundLip += ".lip";
|
|
|
|
|
|
|
|
if (_talkSoundName == soundName)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (g_imuse->getSoundStatus(_talkSoundName.c_str()))
|
|
|
|
shutUp();
|
|
|
|
|
|
|
|
_talkSoundName = soundName;
|
|
|
|
g_imuse->startVoice(_talkSoundName.c_str());
|
|
|
|
if (g_engine->currScene()) {
|
|
|
|
g_engine->currScene()->setSoundPosition(_talkSoundName.c_str(), pos());
|
|
|
|
}
|
2005-04-03 11:33:28 +00:00
|
|
|
|
|
|
|
// If the actor is clearly not visible then don't try to play the lip synch
|
|
|
|
if (visible()) {
|
|
|
|
// Sometimes actors speak offscreen before they, including their
|
|
|
|
// talk chores are initialized.
|
|
|
|
// For example, when reading the work order (a LIP file exists for no reason).
|
|
|
|
// Also, some lip synch files have no entries
|
|
|
|
// In these cases, revert to using the mumble chore.
|
|
|
|
_lipSynch = g_resourceloader->loadLipSynch(soundLip.c_str());
|
2005-07-17 23:40:22 +00:00
|
|
|
// If there's no lip synch file then load the mumble chore if it exists
|
|
|
|
// (the mumble chore doesn't exist with the cat races announcer)
|
|
|
|
if (_lipSynch == NULL && _mumbleChore != -1)
|
|
|
|
_mumbleCostume->playChoreLooping(_mumbleChore);
|
|
|
|
|
2005-04-03 11:33:28 +00:00
|
|
|
_talkAnim = -1;
|
|
|
|
}
|
2005-01-05 18:28:50 +00:00
|
|
|
}
|
|
|
|
|
2005-03-20 16:48:26 +00:00
|
|
|
if (_sayLineText) {
|
2005-03-19 21:48:23 +00:00
|
|
|
g_engine->killTextObject(_sayLineText);
|
2005-03-20 16:48:26 +00:00
|
|
|
_sayLineText = NULL;
|
2005-03-19 21:48:23 +00:00
|
|
|
}
|
2005-03-20 16:48:26 +00:00
|
|
|
|
|
|
|
_sayLineText = new TextObject();
|
|
|
|
_sayLineText->setDefaults(&sayLineDefaults);
|
|
|
|
_sayLineText->setText((char *)msg);
|
|
|
|
_sayLineText->setFGColor(&_talkColor);
|
2005-05-05 21:23:17 +00:00
|
|
|
// if the actor isn't visible render their text at the bottom
|
|
|
|
// of the screen
|
|
|
|
if (!visible() || !inSet(g_engine->currScene()->name())) {
|
|
|
|
_sayLineText->setX(640 / 2);
|
2005-07-17 23:40:22 +00:00
|
|
|
_sayLineText->setY(420);
|
2005-05-05 21:23:17 +00:00
|
|
|
} else {
|
|
|
|
// render at the top for active actors for now
|
|
|
|
_sayLineText->setX(640 / 2);
|
|
|
|
_sayLineText->setY(0);
|
|
|
|
}
|
2005-03-20 16:48:26 +00:00
|
|
|
_sayLineText->createBitmap();
|
|
|
|
g_engine->registerTextObject(_sayLineText);
|
2004-03-24 01:36:05 +00:00
|
|
|
}
|
2003-08-15 18:00:22 +00:00
|
|
|
|
|
|
|
bool Actor::talking() {
|
2005-07-17 23:40:22 +00:00
|
|
|
// If there's no sound file then we're obviously not talking
|
|
|
|
if (strlen(_talkSoundName.c_str()) == 0)
|
|
|
|
return false;
|
|
|
|
|
2005-01-01 21:07:52 +00:00
|
|
|
return g_imuse->getSoundStatus(_talkSoundName.c_str());
|
2003-08-15 18:00:22 +00:00
|
|
|
}
|
|
|
|
|
2003-08-21 06:51:02 +00:00
|
|
|
void Actor::shutUp() {
|
2005-08-01 03:49:02 +00:00
|
|
|
// While the call to stop the sound is usually made by the game,
|
|
|
|
// we also need to handle when the user terminates the dialog.
|
|
|
|
// Some warning messages will occur when the user terminates the
|
|
|
|
// actor dialog but the game will continue alright.
|
|
|
|
if (_talkSoundName != "") {
|
|
|
|
g_imuse->stopSound(_talkSoundName.c_str());
|
|
|
|
_talkSoundName = "";
|
|
|
|
}
|
2005-01-01 21:07:52 +00:00
|
|
|
if (_lipSynch != NULL) {
|
2005-01-02 13:57:16 +00:00
|
|
|
if ((_talkAnim != -1) && (_talkChore[_talkAnim] >= 0))
|
2005-01-01 21:07:52 +00:00
|
|
|
_talkCostume[_talkAnim]->stopChore(_talkChore[_talkAnim]);
|
|
|
|
_lipSynch = NULL;
|
2005-01-14 09:29:44 +00:00
|
|
|
} else if (_mumbleChore >= 0) {
|
2005-01-01 21:07:52 +00:00
|
|
|
_mumbleCostume->stopChore(_mumbleChore);
|
2005-01-14 09:29:44 +00:00
|
|
|
}
|
2005-03-20 16:48:26 +00:00
|
|
|
|
|
|
|
if (_sayLineText != NULL) {
|
|
|
|
g_engine->killTextObject(_sayLineText);
|
|
|
|
_sayLineText = NULL;
|
|
|
|
}
|
2003-08-21 06:51:02 +00:00
|
|
|
}
|
|
|
|
|
2003-08-20 14:29:59 +00:00
|
|
|
void Actor::pushCostume(const char *name) {
|
2004-12-31 21:35:04 +00:00
|
|
|
Costume *newCost = g_resourceloader->loadCostume(name, currentCostume());
|
2005-07-10 18:57:27 +00:00
|
|
|
|
2006-01-04 06:00:47 +00:00
|
|
|
newCost->setColormap(NULL);
|
2004-12-09 23:55:43 +00:00
|
|
|
_costumeStack.push_back(newCost);
|
2003-08-15 18:00:22 +00:00
|
|
|
}
|
|
|
|
|
2006-01-04 06:00:47 +00:00
|
|
|
void Actor::setColormap(const char *map) {
|
2006-05-16 22:16:31 +00:00
|
|
|
if (!_costumeStack.empty()) {
|
|
|
|
Costume *cost = _costumeStack.back();
|
|
|
|
cost->setColormap((char *) map);
|
|
|
|
} else {
|
|
|
|
warning("Actor::setColormap: No costumes");
|
|
|
|
}
|
2006-01-04 06:00:47 +00:00
|
|
|
}
|
|
|
|
|
2003-08-20 14:29:59 +00:00
|
|
|
void Actor::setCostume(const char *name) {
|
2004-12-09 23:55:43 +00:00
|
|
|
if (!_costumeStack.empty())
|
2004-02-24 08:20:45 +00:00
|
|
|
popCostume();
|
2004-12-09 23:55:43 +00:00
|
|
|
|
2004-02-24 08:20:45 +00:00
|
|
|
pushCostume(name);
|
2003-08-15 18:00:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Actor::popCostume() {
|
2004-12-09 23:55:43 +00:00
|
|
|
if (!_costumeStack.empty()) {
|
|
|
|
freeCostumeChore(_costumeStack.back(), _restCostume, _restChore);
|
|
|
|
freeCostumeChore(_costumeStack.back(), _walkCostume, _walkChore);
|
|
|
|
|
|
|
|
if (_turnCostume == _costumeStack.back()) {
|
|
|
|
_turnCostume = NULL;
|
|
|
|
_leftTurnChore = -1;
|
|
|
|
_rightTurnChore = -1;
|
2004-03-24 01:36:05 +00:00
|
|
|
}
|
2004-12-09 23:55:43 +00:00
|
|
|
|
|
|
|
freeCostumeChore(_costumeStack.back(), _mumbleCostume, _mumbleChore);
|
2004-03-24 01:36:05 +00:00
|
|
|
for (int i = 0; i < 10; i++)
|
2004-12-09 23:55:43 +00:00
|
|
|
freeCostumeChore(_costumeStack.back(), _talkCostume[i], _talkChore[i]);
|
|
|
|
delete _costumeStack.back();
|
|
|
|
_costumeStack.pop_back();
|
2005-07-17 23:40:22 +00:00
|
|
|
Costume *newCost;
|
|
|
|
if (_costumeStack.empty())
|
|
|
|
newCost = NULL;
|
|
|
|
else
|
|
|
|
newCost = _costumeStack.back();
|
|
|
|
if (newCost == NULL) {
|
|
|
|
if (debugLevel == DEBUG_NORMAL || debugLevel == DEBUG_ALL)
|
|
|
|
printf("Popped (freed) the last costume for an actor.\n");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (debugLevel == DEBUG_WARN || debugLevel == DEBUG_ALL)
|
|
|
|
warning("Attempted to pop (free) a costume when the stack is empty!");
|
2004-02-24 08:20:45 +00:00
|
|
|
}
|
2003-08-15 18:00:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Actor::clearCostumes() {
|
2004-02-24 08:20:45 +00:00
|
|
|
// Make sure to destroy costume copies in reverse order
|
2004-12-09 23:55:43 +00:00
|
|
|
while (!_costumeStack.empty())
|
2004-03-24 01:36:05 +00:00
|
|
|
popCostume();
|
2003-08-15 18:00:22 +00:00
|
|
|
}
|
|
|
|
|
2003-08-26 09:36:47 +00:00
|
|
|
void Actor::setHead( int joint1, int joint2, int joint3, float maxRoll, float maxPitch, float maxYaw ) {
|
2004-12-09 23:55:43 +00:00
|
|
|
if (!_costumeStack.empty()) {
|
|
|
|
_costumeStack.back()->setHead(joint1, joint2, joint3, maxRoll, maxPitch, maxYaw);
|
2003-08-26 09:36:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-08-15 18:00:22 +00:00
|
|
|
Costume *Actor::findCostume(const char *name) {
|
2004-12-09 23:55:43 +00:00
|
|
|
for (std::list<Costume *>::iterator i = _costumeStack.begin(); i != _costumeStack.end(); i++)
|
2004-02-24 08:20:45 +00:00
|
|
|
if (std::strcmp((*i)->filename(), name) == 0)
|
|
|
|
return *i;
|
2004-12-09 23:55:43 +00:00
|
|
|
|
2004-02-24 08:20:45 +00:00
|
|
|
return NULL;
|
2003-08-15 18:00:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Actor::update() {
|
2004-03-26 09:28:13 +00:00
|
|
|
// Snap actor to walkboxes if following them. This might be
|
|
|
|
// necessary for example after activating/deactivating
|
|
|
|
// walkboxes, etc.
|
2004-12-09 23:55:43 +00:00
|
|
|
if (_constrain && !_walking) {
|
2004-12-31 21:35:04 +00:00
|
|
|
g_engine->currScene()->findClosestSector(_pos, NULL, &_pos);
|
2004-03-26 09:28:13 +00:00
|
|
|
}
|
|
|
|
|
2004-12-09 23:55:43 +00:00
|
|
|
if (_turning) {
|
2004-12-31 21:35:04 +00:00
|
|
|
float turnAmt = g_engine->perSecond(_turnRate);
|
2004-12-09 23:55:43 +00:00
|
|
|
float dyaw = _destYaw - _yaw;
|
2004-02-24 08:20:45 +00:00
|
|
|
while (dyaw > 180)
|
|
|
|
dyaw -= 360;
|
|
|
|
while (dyaw < -180)
|
|
|
|
dyaw += 360;
|
2005-08-01 03:49:02 +00:00
|
|
|
// If the actor won't turn because the rate is set to zero then
|
|
|
|
// have the actor turn all the way to the destination yaw.
|
|
|
|
// Without this some actors will lock the interface on changing
|
|
|
|
// scenes, this affects the Bone Wagon in particular.
|
|
|
|
if (turnAmt == 0 || turnAmt >= std::abs(dyaw)) {
|
2005-07-26 01:46:53 +00:00
|
|
|
setYaw(_destYaw);
|
2004-12-09 23:55:43 +00:00
|
|
|
_turning = false;
|
2004-02-24 08:20:45 +00:00
|
|
|
}
|
|
|
|
else if (dyaw > 0)
|
2005-07-26 01:46:53 +00:00
|
|
|
setYaw(_yaw + turnAmt);
|
2004-02-24 08:20:45 +00:00
|
|
|
else
|
2005-07-26 01:46:53 +00:00
|
|
|
setYaw(_yaw -= turnAmt);
|
2004-12-09 23:55:43 +00:00
|
|
|
_currTurnDir = (dyaw > 0 ? 1 : -1);
|
2004-02-24 08:20:45 +00:00
|
|
|
}
|
|
|
|
|
2004-12-09 23:55:43 +00:00
|
|
|
if (_walking) {
|
|
|
|
Vector3d dir = _destPos - _pos;
|
2004-02-24 08:20:45 +00:00
|
|
|
float dist = dir.magnitude();
|
|
|
|
|
|
|
|
if (dist > 0)
|
|
|
|
dir /= dist;
|
|
|
|
|
2004-12-31 21:35:04 +00:00
|
|
|
float walkAmt = g_engine->perSecond(_walkRate);
|
2004-02-24 08:20:45 +00:00
|
|
|
|
|
|
|
if (walkAmt >= dist) {
|
2004-12-09 23:55:43 +00:00
|
|
|
_pos = _destPos;
|
|
|
|
_walking = false;
|
2005-08-01 03:49:02 +00:00
|
|
|
// It seems that we need to allow an already active turning motion to
|
|
|
|
// continue or else turning actors away from barriers won't work right
|
|
|
|
// _turning = false;
|
2004-12-09 23:55:43 +00:00
|
|
|
} else
|
|
|
|
_pos += dir * walkAmt;
|
2004-03-25 15:43:05 +00:00
|
|
|
|
2004-12-09 23:55:43 +00:00
|
|
|
_walkedCur = true;
|
2004-02-24 08:20:45 +00:00
|
|
|
}
|
|
|
|
|
2004-03-24 01:36:05 +00:00
|
|
|
// The rest chore might have been stopped because of a
|
|
|
|
// StopActorChore(nil). Restart it if so.
|
2004-12-09 23:55:43 +00:00
|
|
|
if (_restChore >= 0 && _restCostume->isChoring(_restChore, false) < 0)
|
|
|
|
_restCostume->playChoreLooping(_restChore);
|
2004-03-24 01:36:05 +00:00
|
|
|
|
2004-12-09 23:55:43 +00:00
|
|
|
if (_walkChore >= 0) {
|
|
|
|
if (_walkedCur) {
|
|
|
|
if (_walkCostume->isChoring(_walkChore, false) < 0)
|
|
|
|
_walkCostume->playChoreLooping(_walkChore);
|
|
|
|
} else {
|
|
|
|
if (_walkCostume->isChoring(_walkChore, false) >= 0)
|
|
|
|
_walkCostume->stopChore(_walkChore);
|
2004-03-24 01:36:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-12-09 23:55:43 +00:00
|
|
|
if (_leftTurnChore >= 0) {
|
|
|
|
if (_walkedCur)
|
|
|
|
_currTurnDir = 0;
|
|
|
|
if (_lastTurnDir != 0 && _lastTurnDir != _currTurnDir)
|
|
|
|
_turnCostume->stopChore(getTurnChore(_lastTurnDir));
|
|
|
|
if (_currTurnDir != 0 && _currTurnDir != _lastTurnDir)
|
2005-03-19 21:48:23 +00:00
|
|
|
_turnCostume->playChore(getTurnChore(_currTurnDir));
|
2004-12-09 23:55:43 +00:00
|
|
|
} else
|
|
|
|
_currTurnDir = 0;
|
|
|
|
|
|
|
|
_walkedLast = _walkedCur;
|
|
|
|
_walkedCur = false;
|
|
|
|
_lastTurnDir = _currTurnDir;
|
|
|
|
_currTurnDir = 0;
|
2004-03-24 01:36:05 +00:00
|
|
|
|
2004-09-11 14:09:43 +00:00
|
|
|
// Update lip synching
|
2005-01-01 21:07:52 +00:00
|
|
|
if (_lipSynch != NULL) {
|
2005-07-17 23:40:22 +00:00
|
|
|
int posSound;
|
|
|
|
|
|
|
|
// While getPosIn60HzTicks will return "-1" to indicate that the
|
|
|
|
// sound is no longer playing, it is more appropriate to check first
|
|
|
|
if(g_imuse->getSoundStatus(_talkSoundName.c_str()))
|
|
|
|
posSound = g_imuse->getPosIn60HzTicks(_talkSoundName.c_str());
|
|
|
|
else
|
|
|
|
posSound = -1;
|
2005-01-01 21:07:52 +00:00
|
|
|
if (posSound != -1) {
|
|
|
|
int anim = _lipSynch->getAnim(posSound);
|
|
|
|
if (_talkAnim != anim) {
|
|
|
|
if (_talkAnim != -1 && _talkChore[_talkAnim] >= 0)
|
|
|
|
_talkCostume[_talkAnim]->stopChore(_talkChore[_talkAnim]);
|
|
|
|
if (anim != -1) {
|
|
|
|
_talkAnim = anim;
|
|
|
|
if (_talkChore[_talkAnim] >= 0) {
|
|
|
|
_talkCostume[_talkAnim]->playChoreLooping(_talkChore[_talkAnim]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2004-12-09 23:55:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (std::list<Costume *>::iterator i = _costumeStack.begin(); i != _costumeStack.end(); i++) {
|
|
|
|
(*i)->setPosRotate(_pos, _pitch, _yaw, _roll);
|
2004-02-24 08:20:45 +00:00
|
|
|
(*i)->update();
|
|
|
|
}
|
|
|
|
|
2004-12-09 23:55:43 +00:00
|
|
|
if (_lookingMode) {
|
2005-01-12 23:28:47 +00:00
|
|
|
/*float lookAtAmt = */g_engine->perSecond(_lookAtRate);
|
2004-02-24 08:20:45 +00:00
|
|
|
}
|
2003-08-15 18:00:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Actor::draw() {
|
2007-03-12 21:50:44 +00:00
|
|
|
setupDrawShadow();
|
2004-12-09 23:55:43 +00:00
|
|
|
for (std::list<Costume *>::iterator i = _costumeStack.begin(); i != _costumeStack.end(); i++)
|
2004-03-23 13:05:08 +00:00
|
|
|
(*i)->setupTextures();
|
|
|
|
|
2004-12-09 23:55:43 +00:00
|
|
|
if (!_costumeStack.empty()) {
|
2007-03-12 21:50:44 +00:00
|
|
|
setupDrawShadow();
|
2004-12-09 23:55:43 +00:00
|
|
|
g_driver->startActorDraw(_pos, _yaw, _pitch, _roll);
|
|
|
|
_costumeStack.back()->draw();
|
2004-02-24 08:20:45 +00:00
|
|
|
g_driver->finishActorDraw();
|
2007-03-12 21:50:44 +00:00
|
|
|
finishDrawShadow();
|
2004-02-24 08:20:45 +00:00
|
|
|
}
|
2004-12-09 23:55:43 +00:00
|
|
|
}
|
2005-04-03 11:33:28 +00:00
|
|
|
|
|
|
|
// "Undraw objects" (handle objects for actors that may not be on screen)
|
2005-08-10 08:33:45 +00:00
|
|
|
void Actor::undraw(bool /*visible*/) {
|
2005-04-03 11:33:28 +00:00
|
|
|
if (!talking() || !g_imuse->isVoicePlaying())
|
|
|
|
shutUp();
|
|
|
|
}
|
2007-02-05 13:49:32 +00:00
|
|
|
|
|
|
|
#define strmatch(src, dst) (strlen(src) == strlen(dst) && strcmp(src, dst) == 0)
|
|
|
|
|
|
|
|
void Actor::setShadowPlane(const char *name) {
|
|
|
|
assert(_activeShadowSlot != -1);
|
|
|
|
|
|
|
|
_shadowArray[_activeShadowSlot].name = name;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Actor::addShadowPlane(const char *name) {
|
|
|
|
assert(_activeShadowSlot != -1);
|
|
|
|
|
|
|
|
int numSectors = g_engine->currScene()->getSectorCount();
|
|
|
|
|
|
|
|
for (int i = 0; i < numSectors; i++) {
|
|
|
|
Sector *sector = g_engine->currScene()->getSectorBase(i);
|
|
|
|
if (strmatch(sector->name(), name)) {
|
|
|
|
_shadowArray[_activeShadowSlot].planeList.push_back(sector);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Actor::setActiveShadow(int shadowId) {
|
|
|
|
assert(shadowId >= 0 && shadowId <= 4);
|
|
|
|
|
|
|
|
_activeShadowSlot = shadowId;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Actor::setShadowPoint(Vector3d pos) {
|
|
|
|
assert(_activeShadowSlot != -1);
|
|
|
|
|
|
|
|
_shadowArray[_activeShadowSlot].pos = pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Actor::clearShadowPlanes() {
|
|
|
|
for (int i = 0; i < 5; i++) {
|
|
|
|
Shadow *shadow = &_shadowArray[i];
|
|
|
|
while (!shadow->planeList.empty()) {
|
|
|
|
shadow->planeList.pop_back();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2007-03-12 21:50:44 +00:00
|
|
|
|
|
|
|
void Actor::setupDrawShadow() {
|
|
|
|
if (_activeShadowSlot == -1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
g_driver->setupShadower(_shadowArray);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Actor::finishDrawShadow() {
|
|
|
|
g_driver->setupShadower(NULL);
|
|
|
|
}
|