scummvm/actor.cpp

419 lines
11 KiB
C++
Raw Normal View History

2003-08-15 18:00:22 +00:00
// Residual - Virtual machine to run LucasArts' 3D adventure games
2005-01-01 10:23:18 +00:00
// Copyright (C) 2003-2005 The ScummVM-Residual Team (www.scummvm.org)
2003-08-15 18:00:22 +00:00
//
// 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "stdafx.h"
#include "actor.h"
#include "engine.h"
#include "costume.h"
#include "lipsynch.h"
2004-03-20 09:38:33 +00:00
#include "localize.h"
#include "driver_gl.h"
2005-01-01 12:27:57 +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) :
2004-12-09 23:55:43 +00:00
_name(name), _talkColor(255, 255, 255), _pos(0, 0, 0),
_pitch(0), _yaw(0), _roll(0), _walkRate(0), _turnRate(0),
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),
_mumbleCostume(NULL), _mumbleChore(-1) {
g_engine->registerActor(this);
2004-12-09 23:55:43 +00:00
_lookingMode = false;
_constrain = false;
for (int i = 0; i < 10; i++) {
2004-12-09 23:55:43 +00:00
_talkCostume[i] = NULL;
_talkChore[i] = -1;
}
2003-08-15 18:00:22 +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;
} else
2004-12-09 23:55:43 +00:00
_turning = false;
}
void Actor::walkTo(Vector3d p) {
// 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;
else {
2004-12-09 23:55:43 +00:00
_walking = true;
_destPos = p;
2004-12-09 23:55:43 +00:00
if (p.x() != _pos.x() || p.y() != _pos.y())
turnTo(_pitch, yawTo(p), _roll);
}
}
bool Actor::isWalking() const {
2004-12-09 23:55:43 +00:00
return _walkedLast || _walkedCur || _walking;
}
bool Actor::isTurning() const {
2004-12-09 23:55:43 +00:00
if (_turning)
return true;
2004-12-09 23:55:43 +00:00
if (_lastTurnDir != 0 || _currTurnDir != 0)
return true;
2004-12-09 23:55:43 +00:00
return false;
}
2003-08-15 18:00:22 +00:00
void Actor::walkForward() {
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);
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-12-09 23:55:43 +00:00
if (!_constrain) {
_pos = destPos;
_walkedCur = true;
} else {
Sector *sector = g_engine->currScene()->findPointSector(destPos, 0x1000);
if (sector != NULL) {
2004-12-09 23:55:43 +00:00
_pos = sector->projectToPlane(destPos);
_walkedCur = true;
}
}
}
Vector3d Actor::puckVector() const {
2004-12-09 23:55:43 +00:00
float yaw_rad = _yaw * (M_PI / 180);
Vector3d forwardVec(-std::sin(yaw_rad), std::cos(yaw_rad), 0);
Sector *sector = g_engine->currScene()->findPointSector(_pos, 0x1000);
if (sector == NULL)
return forwardVec;
else
return sector->projectToPuckVector(forwardVec);
}
void Actor::setRestChore(int chore, Costume *cost) {
2004-12-09 23:55:43 +00:00
if (_restCostume == cost && _restChore == chore)
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);
}
void Actor::setWalkChore(int chore, Costume *cost) {
2004-12-09 23:55:43 +00:00
if (_walkCostume == cost && _walkChore == chore)
return;
2004-12-09 23:55:43 +00:00
if (_walkChore >= 0)
_walkCostume->stopChore(_walkChore);
_walkCostume = cost;
_walkChore = chore;
}
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)
return;
2004-12-09 23:55:43 +00:00
if (_leftTurnChore >= 0) {
_turnCostume->stopChore(_leftTurnChore);
_turnCostume->stopChore(_rightTurnChore);
}
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))
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
index--;
2004-12-09 23:55:43 +00:00
if (_talkCostume[index] == cost && _talkChore[index] == chore)
return;
2004-12-09 23:55:43 +00:00
if (_talkChore[index] >= 0)
_talkCostume[index]->stopChore(_talkChore[index]);
_talkCostume[index] = cost;
_talkChore[index] = chore;
}
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) {
float delta = g_engine->perSecond(_turnRate) * dir;
2004-12-09 23:55:43 +00:00
_yaw += delta;
_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);
Vector3d forwardVec(-std::sin(yaw_rad), std::cos(yaw_rad), 0);
2004-12-09 23:55:43 +00:00
Vector3d delta = a.pos() - _pos;
delta.z() = 0;
2004-12-09 23:55:43 +00:00
return angle(forwardVec, delta) * (180 / M_PI);
2003-08-15 18:00:22 +00:00
}
float Actor::yawTo(Vector3d p) const {
2004-12-09 23:55:43 +00:00
Vector3d dpos = p - _pos;
if (dpos.x() == 0 && dpos.y() == 0)
return 0;
else
return std::atan2(-dpos.x(), dpos.y()) * (180 / M_PI);
}
2005-01-02 13:34:50 +00:00
void Actor::sayLine(const char *msg, const char *msgId) {
assert(msg);
assert(msgId);
2005-01-02 13:34:50 +00:00
if (msg[0] == '/' || msg[0] == 0 || msgId[0] == 0)
return;
2004-12-09 23:55:43 +00:00
2005-01-02 13:34:50 +00:00
std::string soundName = msgId;
std::string soundLip = msgId;
soundName += ".wav";
soundLip += ".lip";
2004-12-09 23:55:43 +00:00
2005-01-02 13:34:50 +00:00
if (_talkSoundName == soundName)
2005-01-01 21:07:52 +00:00
return;
2005-01-01 21:07:52 +00:00
if (g_imuse->getSoundStatus(_talkSoundName.c_str()))
shutUp();
2005-01-02 13:34:50 +00:00
_lipSynch = g_resourceloader->loadLipSynch(soundLip.c_str());
_talkSoundName = soundName;
2005-01-01 21:07:52 +00:00
g_imuse->startVoice(_talkSoundName.c_str());
_talkAnim = -1;
}
2003-08-15 18:00:22 +00:00
bool Actor::talking() {
2005-01-01 21:07:52 +00:00
return g_imuse->getSoundStatus(_talkSoundName.c_str());
2003-08-15 18:00:22 +00:00
}
void Actor::shutUp() {
2005-01-01 21:07:52 +00:00
if (_lipSynch != NULL) {
g_imuse->stopSound(_talkSoundName.c_str());
if (_talkChore[_talkAnim] >= 0)
_talkCostume[_talkAnim]->stopChore(_talkChore[_talkAnim]);
_lipSynch = NULL;
} else if (_mumbleChore >= 0)
_mumbleCostume->stopChore(_mumbleChore);
2005-01-02 13:34:50 +00:00
_talkSoundName = "";
}
void Actor::pushCostume(const char *name) {
Costume *newCost = g_resourceloader->loadCostume(name, currentCostume());
2004-12-09 23:55:43 +00:00
_costumeStack.push_back(newCost);
2003-08-15 18:00:22 +00:00
}
void Actor::setCostume(const char *name) {
2004-12-09 23:55:43 +00:00
if (!_costumeStack.empty())
popCostume();
2004-12-09 23:55:43 +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-12-09 23:55:43 +00:00
freeCostumeChore(_costumeStack.back(), _mumbleCostume, _mumbleChore);
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();
}
2003-08-15 18:00:22 +00:00
}
void Actor::clearCostumes() {
// Make sure to destroy costume copies in reverse order
2004-12-09 23:55:43 +00:00
while (!_costumeStack.empty())
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++)
if (std::strcmp((*i)->filename(), name) == 0)
return *i;
2004-12-09 23:55:43 +00:00
return NULL;
2003-08-15 18:00:22 +00:00
}
void Actor::update() {
// 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) {
g_engine->currScene()->findClosestSector(_pos, NULL, &_pos);
}
2004-12-09 23:55:43 +00:00
if (_turning) {
float turnAmt = g_engine->perSecond(_turnRate);
2004-12-09 23:55:43 +00:00
float dyaw = _destYaw - _yaw;
while (dyaw > 180)
dyaw -= 360;
while (dyaw < -180)
dyaw += 360;
if (turnAmt >= std::abs(dyaw)) {
2004-12-09 23:55:43 +00:00
_yaw = _destYaw;
_turning = false;
}
else if (dyaw > 0)
2004-12-09 23:55:43 +00:00
_yaw += turnAmt;
else
2004-12-09 23:55:43 +00:00
_yaw -= turnAmt;
_currTurnDir = (dyaw > 0 ? 1 : -1);
}
2004-12-09 23:55:43 +00:00
if (_walking) {
Vector3d dir = _destPos - _pos;
float dist = dir.magnitude();
if (dist > 0)
dir /= dist;
float walkAmt = g_engine->perSecond(_walkRate);
if (walkAmt >= dist) {
2004-12-09 23:55:43 +00:00
_pos = _destPos;
_walking = false;
_turning = false;
} else
_pos += dir * walkAmt;
2004-12-09 23:55:43 +00:00
_walkedCur = true;
}
// 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-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-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)
_turnCostume->playChoreLooping(getTurnChore(_currTurnDir));
} else
_currTurnDir = 0;
_walkedLast = _walkedCur;
_walkedCur = false;
_lastTurnDir = _currTurnDir;
_currTurnDir = 0;
// Update lip synching
2005-01-01 21:07:52 +00:00
if (_lipSynch != NULL) {
int posSound = g_imuse->getPosIn60HzTicks(_talkSoundName.c_str());
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
}
2005-01-01 21:07:52 +00:00
if (!g_imuse->isVoicePlaying())
shutUp();
2005-01-01 21:07:52 +00:00
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);
(*i)->update();
}
2004-12-09 23:55:43 +00:00
if (_lookingMode) {
float lookAtAmt = g_engine->perSecond(_lookAtRate);
}
2003-08-15 18:00:22 +00:00
}
void Actor::draw() {
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()) {
g_driver->startActorDraw(_pos, _yaw, _pitch, _roll);
_costumeStack.back()->draw();
g_driver->finishActorDraw();
}
2004-12-09 23:55:43 +00:00
}