ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

grim/actor.cpp

Go to the documentation of this file.
00001 /* ResidualVM - A 3D game interpreter
00002  *
00003  * ResidualVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "math/line3d.h"
00024 #include "math/rect2d.h"
00025 
00026 #include "engines/grim/debug.h"
00027 #include "engines/grim/actor.h"
00028 #include "engines/grim/grim.h"
00029 #include "engines/grim/costume.h"
00030 #include "engines/grim/lipsync.h"
00031 #include "engines/grim/movie/movie.h"
00032 #include "engines/grim/sound.h"
00033 #include "engines/grim/lua.h"
00034 #include "engines/grim/resource.h"
00035 #include "engines/grim/savegame.h"
00036 #include "engines/grim/set.h"
00037 #include "engines/grim/gfx_base.h"
00038 #include "engines/grim/model.h"
00039 
00040 #include "engines/grim/emi/emi.h"
00041 #include "engines/grim/emi/costumeemi.h"
00042 #include "engines/grim/emi/skeleton.h"
00043 #include "engines/grim/emi/costume/emiskel_component.h"
00044 #include "engines/grim/emi/modelemi.h"
00045 
00046 #include "common/foreach.h"
00047 
00048 namespace Grim {
00049 
00050 Shadow::Shadow() :
00051         shadowMask(nullptr), shadowMaskSize(0), active(false), dontNegate(false), userData(nullptr) {
00052 }
00053 
00054 static int animTurn(float turnAmt, const Math::Angle &dest, Math::Angle *cur) {
00055     Math::Angle d = dest - *cur;
00056     d.normalize(-180);
00057     // If the actor won't turn because the rate is set to zero then
00058     // have the actor turn all the way to the destination yaw.
00059     // Without this some actors will lock the interface on changing
00060     // scenes, this affects the Bone Wagon in particular.
00061     if (turnAmt == 0 || turnAmt >= fabsf(d.getDegrees())) {
00062         *cur = dest;
00063     } else if (d > 0) {
00064         *cur += turnAmt;
00065     } else {
00066         *cur -= turnAmt;
00067     }
00068     if (d != 0) {
00069         return (d > 0 ? 1 : -1);
00070     }
00071     return 0;
00072 }
00073 
00074 bool Actor::_isTalkingBackground = false;
00075 
00076 void Actor::saveStaticState(SaveGame *state) {
00077     state->writeBool(_isTalkingBackground);
00078 }
00079 
00080 void Actor::restoreStaticState(SaveGame *state) {
00081     _isTalkingBackground = state->readBool();
00082 }
00083 
00084 Actor::Actor() :
00085         _talkColor(255, 255, 255), _pos(0, 0, 0),
00086         _lookingMode(false), _followBoxes(false), _running(false), 
00087         _pitch(0), _yaw(0), _roll(0), _walkRate(0.3f),
00088         _turnRateMultiplier(0.f), _talkAnim(0),
00089         _reflectionAngle(80), _scale(1.f), _timeScale(1.f),
00090         _visible(true), _lipSync(nullptr), _turning(false), _singleTurning(false), _walking(false),
00091         _walkedLast(false), _walkedCur(false),
00092         _collisionMode(CollisionOff), _collisionScale(1.f),
00093         _lastTurnDir(0), _currTurnDir(0),
00094         _sayLineText(0), _talkDelay(0),
00095         _attachedActor(0), _attachedJoint(""),
00096         _globalAlpha(1.f), _alphaMode(AlphaOff),
00097          _mustPlaceText(false), 
00098         _puckOrient(false), _talking(false), 
00099         _inOverworld(false), _drawnToClean(false), _backgroundTalk(false),
00100         _sortOrder(0), _useParentSortOrder(false),
00101         _sectorSortOrder(-1), _fakeUnbound(false), _lightMode(LightFastDyn),
00102         _hasFollowedBoxes(false), _lookAtActor(0) {
00103 
00104     // Some actors don't set walk and turn rates, so we default the
00105     // _turnRate so Doug at the cat races can turn and we set the
00106     // _walkRate so Glottis at the demon beaver entrance can walk and
00107     // so Chepito in su.set
00108     _turnRate = 100.0f;
00109 
00110     _activeShadowSlot = -1;
00111     _shadowArray = new Shadow[MAX_SHADOWS];
00112 }
00113 
00114 Actor::~Actor() {
00115     if (_shadowArray) {
00116         clearShadowPlanes();
00117         delete[] _shadowArray;
00118     }
00119     while (!_costumeStack.empty()) {
00120         delete _costumeStack.back();
00121         _costumeStack.pop_back();
00122     }
00123     g_grim->immediatelyRemoveActor(this);
00124 }
00125 
00126 void Actor::saveState(SaveGame *savedState) const {
00127     // store actor name
00128     savedState->writeString(_name);
00129     savedState->writeString(_setName);
00130 
00131     savedState->writeColor(_talkColor);
00132 
00133     savedState->writeVector3d(_pos);
00134 
00135     savedState->writeFloat(_pitch.getDegrees());
00136     savedState->writeFloat(_yaw.getDegrees());
00137     savedState->writeFloat(_roll.getDegrees());
00138     savedState->writeFloat(_walkRate);
00139     savedState->writeFloat(_turnRate);
00140     savedState->writeFloat(_turnRateMultiplier);
00141     savedState->writeBool(_followBoxes);
00142     savedState->writeFloat(_reflectionAngle);
00143     savedState->writeBool(_visible);
00144     savedState->writeBool(_lookingMode);
00145     savedState->writeFloat(_scale);
00146     savedState->writeFloat(_timeScale);
00147     savedState->writeBool(_puckOrient);
00148 
00149     savedState->writeString(_talkSoundName);
00150     savedState->writeBool(_talking);
00151     savedState->writeBool(_backgroundTalk);
00152 
00153     savedState->writeLEUint32((uint32)_collisionMode);
00154     savedState->writeFloat(_collisionScale);
00155 
00156     if (_lipSync) {
00157         savedState->writeBool(true);
00158         savedState->writeString(_lipSync->getFilename());
00159     } else {
00160         savedState->writeBool(false);
00161     }
00162 
00163     savedState->writeLEUint32(_costumeStack.size());
00164     for (Common::List<Costume *>::const_iterator i = _costumeStack.begin(); i != _costumeStack.end(); ++i) {
00165         Costume *c = *i;
00166         savedState->writeString(c->getFilename());
00167         Costume *pc = c->getPreviousCostume();
00168         int depth = 0;
00169         while (pc) {
00170             ++depth;
00171             pc = pc->getPreviousCostume();
00172         }
00173         savedState->writeLESint32(depth);
00174         pc = c->getPreviousCostume();
00175         for (int j = 0; j < depth; ++j) { //save the previousCostume hierarchy
00176             savedState->writeString(pc->getFilename());
00177             pc = pc->getPreviousCostume();
00178         }
00179         c->saveState(savedState);
00180     }
00181 
00182     savedState->writeBool(_turning);
00183     savedState->writeBool(_singleTurning);
00184     savedState->writeFloat(_moveYaw.getDegrees());
00185     savedState->writeFloat(_movePitch.getDegrees());
00186     savedState->writeFloat(_moveRoll.getDegrees());
00187 
00188     savedState->writeBool(_walking);
00189     savedState->writeVector3d(_destPos);
00190 
00191     _restChore.saveState(savedState);
00192 
00193     _walkChore.saveState(savedState);
00194     savedState->writeBool(_walkedLast);
00195     savedState->writeBool(_walkedCur);
00196 
00197     _leftTurnChore.saveState(savedState);
00198     _rightTurnChore.saveState(savedState);
00199     savedState->writeLESint32(_lastTurnDir);
00200     savedState->writeLESint32(_currTurnDir);
00201 
00202     for (int i = 0; i < 10; ++i) {
00203         _talkChore[i].saveState(savedState);
00204     }
00205     savedState->writeLESint32(_talkAnim);
00206 
00207     _mumbleChore.saveState(savedState);
00208 
00209     for (int i = 0; i < MAX_SHADOWS; ++i) {
00210         Shadow &shadow = _shadowArray[i];
00211         savedState->writeString(shadow.name);
00212 
00213         savedState->writeVector3d(shadow.pos);
00214 
00215         savedState->writeLEUint32(shadow.planeList.size());
00216         // Cannot use g_grim->getCurrSet() here because an actor can have walk planes
00217         // from other scenes. It happens e.g. when Membrillo calls Velasco to tell him
00218         // Naranja is dead.
00219         for (SectorListType::iterator j = shadow.planeList.begin(); j != shadow.planeList.end(); ++j) {
00220             Plane &p = *j;
00221             savedState->writeString(p.setName);
00222             savedState->writeString(p.sector->getName());
00223         }
00224 
00225         savedState->writeLESint32(shadow.shadowMaskSize);
00226         savedState->write(shadow.shadowMask, shadow.shadowMaskSize);
00227         savedState->writeBool(shadow.active);
00228         savedState->writeBool(shadow.dontNegate);
00229     }
00230     savedState->writeLESint32(_activeShadowSlot);
00231 
00232     savedState->writeLESint32(_sayLineText);
00233 
00234     savedState->writeVector3d(_lookAtVector);
00235 
00236     savedState->writeLEUint32(_path.size());
00237     for (Common::List<Math::Vector3d>::const_iterator i = _path.begin(); i != _path.end(); ++i) {
00238         savedState->writeVector3d(*i);
00239     }
00240 
00241     if (g_grim->getGameType() == GType_MONKEY4) {
00242         savedState->writeLEUint32(_alphaMode);
00243         savedState->writeFloat(_globalAlpha);
00244 
00245         savedState->writeBool(_inOverworld);
00246         savedState->writeLESint32(_sortOrder);
00247         savedState->writeBool(_useParentSortOrder);
00248 
00249         savedState->writeLESint32(_attachedActor);
00250         savedState->writeString(_attachedJoint);
00251 
00252         Common::List<MaterialPtr>::const_iterator it = _materials.begin();
00253         for (; it != _materials.end(); ++it) {
00254             if (*it) {
00255                 warning("%s", (*it)->getFilename().c_str());
00256                 savedState->writeLESint32((*it)->getActiveTexture());
00257             }
00258         }
00259 
00260         savedState->writeLESint32(_lookAtActor);
00261 
00262         savedState->writeLEUint32(_localAlpha.size());
00263         for (unsigned int i = 0; i < _localAlpha.size(); i++) {
00264             savedState->writeFloat(_localAlpha[i]);
00265         }
00266         savedState->writeLEUint32(_localAlphaMode.size());
00267         for (unsigned int i = 0; i < _localAlphaMode.size(); i++) {
00268             savedState->writeLESint32(_localAlphaMode[i]);
00269         }
00270     }
00271 
00272     savedState->writeBool(_drawnToClean);
00273     savedState->writeBool(_fakeUnbound);
00274 }
00275 
00276 bool Actor::restoreState(SaveGame *savedState) {
00277     for (Common::List<Costume *>::const_iterator i = _costumeStack.begin(); i != _costumeStack.end(); ++i) {
00278         delete *i;
00279     }
00280     _costumeStack.clear();
00281     _materials.clear();
00282 
00283     // load actor name
00284     _name = savedState->readString();
00285     _setName = savedState->readString();
00286 
00287     _talkColor = savedState->readColor();
00288 
00289     _pos                = savedState->readVector3d();
00290     _pitch              = savedState->readFloat();
00291     _yaw                = savedState->readFloat();
00292     _roll               = savedState->readFloat();
00293     _walkRate           = savedState->readFloat();
00294     _turnRate           = savedState->readFloat();
00295     if (savedState->saveMinorVersion() > 6) {
00296         _turnRateMultiplier = savedState->readFloat();
00297     } else {
00298         _turnRateMultiplier = 1.f;
00299     }
00300     _followBoxes        = savedState->readBool();
00301     _reflectionAngle    = savedState->readFloat();
00302     _visible            = savedState->readBool();
00303     _lookingMode        = savedState->readBool();
00304     _scale              = savedState->readFloat();
00305     _timeScale          = savedState->readFloat();
00306     _puckOrient         = savedState->readBool();
00307 
00308     _talkSoundName      = savedState->readString();
00309     _talking = savedState->readBool();
00310     _backgroundTalk = savedState->readBool();
00311     if (isTalking()) {
00312         g_grim->addTalkingActor(this);
00313     }
00314 
00315     _collisionMode      = (CollisionMode)savedState->readLEUint32();
00316     _collisionScale     = savedState->readFloat();
00317 
00318     if (savedState->readBool()) {
00319         Common::String fn = savedState->readString();
00320         _lipSync = g_resourceloader->getLipSync(fn);
00321     } else {
00322         _lipSync = nullptr;
00323     }
00324 
00325     uint32 size = savedState->readLEUint32();
00326     for (uint32 i = 0; i < size; ++i) {
00327         Common::String fname = savedState->readString();
00328         const int depth = savedState->readLESint32();
00329         Costume *pc = nullptr;
00330         if (depth > 0) {    //build all the previousCostume hierarchy
00331             Common::String *names = new Common::String[depth];
00332             for (int j = 0; j < depth; ++j) {
00333                 names[j] = savedState->readString();
00334             }
00335             for (int j = depth - 1; j >= 0; --j) {
00336                 pc = findCostume(names[j]);
00337                 if (!pc) {
00338                     pc = g_resourceloader->loadCostume(names[j], this, pc);
00339                 }
00340             }
00341             delete[] names;
00342         }
00343 
00344         Costume *c = g_resourceloader->loadCostume(fname, this, pc);
00345         c->restoreState(savedState);
00346         _costumeStack.push_back(c);
00347     }
00348 
00349     _turning = savedState->readBool();
00350     if (savedState->saveMinorVersion() > 25) {
00351         _singleTurning = savedState->readBool();
00352     }
00353     _moveYaw = savedState->readFloat();
00354     if (savedState->saveMinorVersion() > 6) {
00355         _movePitch = savedState->readFloat();
00356         _moveRoll = savedState->readFloat();
00357     } else {
00358         _movePitch = _pitch;
00359         _moveRoll = _roll;
00360     }
00361 
00362     _walking = savedState->readBool();
00363     _destPos = savedState->readVector3d();
00364 
00365     _restChore.restoreState(savedState, this);
00366 
00367     _walkChore.restoreState(savedState, this);
00368     _walkedLast = savedState->readBool();
00369     _walkedCur = savedState->readBool();
00370 
00371     _leftTurnChore.restoreState(savedState, this);
00372     _rightTurnChore.restoreState(savedState, this);
00373     _lastTurnDir = savedState->readLESint32();
00374     _currTurnDir = savedState->readLESint32();
00375 
00376     for (int i = 0; i < 10; ++i) {
00377         _talkChore[i].restoreState(savedState, this);
00378     }
00379     _talkAnim = savedState->readLESint32();
00380 
00381     _mumbleChore.restoreState(savedState, this);
00382 
00383     clearShadowPlanes();
00384 
00385     int maxShadows = MAX_SHADOWS;
00386     if (savedState->saveMinorVersion() < 24)
00387         maxShadows = 5;
00388     for (int i = 0; i < maxShadows; ++i) {
00389         Shadow &shadow = _shadowArray[i];
00390         shadow.name = savedState->readString();
00391 
00392         shadow.pos = savedState->readVector3d();
00393 
00394         size = savedState->readLEUint32();
00395         Set *scene = nullptr;
00396         for (uint32 j = 0; j < size; ++j) {
00397             Common::String actorSetName = savedState->readString();
00398             Common::String secName = savedState->readString();
00399             if (!scene || scene->getName() != actorSetName) {
00400                 scene = g_grim->findSet(actorSetName);
00401             }
00402             if (scene) {
00403                 addShadowPlane(secName.c_str(), scene, i);
00404             } else {
00405                 warning("%s: No scene \"%s\" found, cannot restore shadow on sector \"%s\"", getName().c_str(), actorSetName.c_str(), secName.c_str());
00406             }
00407         }
00408 
00409         shadow.shadowMaskSize = savedState->readLESint32();
00410         delete[] shadow.shadowMask;
00411         if (shadow.shadowMaskSize > 0) {
00412             shadow.shadowMask = new byte[shadow.shadowMaskSize];
00413             savedState->read(shadow.shadowMask, shadow.shadowMaskSize);
00414         } else {
00415             shadow.shadowMask = nullptr;
00416         }
00417         shadow.active = savedState->readBool();
00418         shadow.dontNegate = savedState->readBool();
00419     }
00420     _activeShadowSlot = savedState->readLESint32();
00421 
00422     _sayLineText = savedState->readLESint32();
00423 
00424     _lookAtVector = savedState->readVector3d();
00425 
00426     size = savedState->readLEUint32();
00427     _path.clear();
00428     for (uint32 i = 0; i < size; ++i) {
00429         _path.push_back(savedState->readVector3d());
00430     }
00431 
00432     if (g_grim->getGameType() == GType_MONKEY4) {
00433         _alphaMode = (AlphaMode) savedState->readLEUint32();
00434         _globalAlpha = savedState->readFloat();
00435 
00436         _inOverworld  = savedState->readBool();
00437         _sortOrder    = savedState->readLESint32();
00438         if (savedState->saveMinorVersion() >= 22)
00439             _useParentSortOrder = savedState->readBool();
00440 
00441         if (savedState->saveMinorVersion() < 18)
00442             savedState->readBool(); // Used to be _shadowActive.
00443 
00444         _attachedActor = savedState->readLESint32();
00445         _attachedJoint = savedState->readString();
00446 
00447         // will be recalculated in next update()
00448         _sectorSortOrder = -1;
00449 
00450         if (savedState->saveMinorVersion() < 24) {
00451             // Used to be the wear chore.
00452             if (savedState->readBool()) {
00453                 savedState->readString();
00454             }
00455             savedState->readLESint32();
00456         }
00457 
00458         if (savedState->saveMinorVersion() >= 13) {
00459             Common::List<MaterialPtr>::const_iterator it = _materials.begin();
00460             for (; it != _materials.end(); ++it) {
00461                 if (*it) {
00462                     (*it)->setActiveTexture(savedState->readLESint32());
00463                 }
00464             }
00465         }
00466 
00467         if (savedState->saveMinorVersion() >= 17)
00468             _lookAtActor = savedState->readLESint32();
00469 
00470         if (savedState->saveMinorVersion() >= 25) {
00471             _localAlpha.resize(savedState->readLEUint32());
00472             for (unsigned int i = 0; i < _localAlpha.size(); i++) {
00473                 _localAlpha[i] = savedState->readFloat();
00474             }
00475 
00476             _localAlphaMode.resize(savedState->readLEUint32());
00477             for (unsigned int i = 0; i < _localAlphaMode.size(); i++) {
00478                 _localAlphaMode[i] = savedState->readLESint32();
00479             }
00480         }
00481     }
00482 
00483     if (savedState->saveMinorVersion() >= 4) {
00484         _drawnToClean = savedState->readBool();
00485     } else {
00486         _drawnToClean = false;
00487     }
00488     if (savedState->saveMinorVersion() >= 27) {
00489         _fakeUnbound = savedState->readBool();
00490     } else {
00491         // Note: actor will stay invisible until re-bound to set
00492         // by game scripts.
00493         _fakeUnbound = false;
00494     }
00495 
00496     return true;
00497 }
00498 
00499 void Actor::setRot(const Math::Vector3d &pos) {
00500     Math::Angle y, p, r;
00501     calculateOrientation(pos, &p, &y, &r);
00502     setRot(p, y, r);
00503 }
00504 
00505 void Actor::setRot(const Math::Angle &pitchParam, const Math::Angle &yawParam, const Math::Angle &rollParam) {
00506     _pitch = pitchParam;
00507     _yaw = yawParam;
00508     _moveYaw = _yaw;
00509     _roll = rollParam;
00510     _turning = false;
00511 }
00512 
00513 void Actor::setPos(const Math::Vector3d &position) {
00514     _walking = false;
00515     _pos = position;
00516 
00517     // Don't allow positions outside the sectors.
00518     // This is necessary after solving the tree pump puzzle, when the bone
00519     // wagon returns to the signopost set.
00520     if (_followBoxes) {
00521         g_grim->getCurrSet()->findClosestSector(_pos, nullptr, &_pos);
00522     }
00523 
00524     if (g_grim->getGameType() == GType_MONKEY4) {
00525         Math::Vector3d moveVec = position - _pos;
00526         foreach (Actor *a, g_grim->getActiveActors()) {
00527             handleCollisionWith(a, _collisionMode, &moveVec);
00528         }
00529     }
00530 }
00531 
00532 void Actor::calculateOrientation(const Math::Vector3d &pos, Math::Angle *pitch, Math::Angle *yaw, Math::Angle *roll) {
00533     Math::Vector3d actorForward(0.f, 1.f, 0.f);
00534     Math::Vector3d actorUp(0.f, 0.f, 1.f);
00535     Math::Vector3d lookVector = pos - _pos;
00536     lookVector.normalize();
00537 
00538     // EMI: Y is up-down, actors use an X-Z plane for movement
00539     if (g_grim->getGameType() == GType_MONKEY4) {
00540         float temp = lookVector.z();
00541         lookVector.x() = -lookVector.x();
00542         lookVector.z() = lookVector.y();
00543         lookVector.y() = temp;
00544     }
00545 
00546     Math::Vector3d up = actorUp;
00547     if (_puckOrient) {
00548         Sector *s = nullptr;
00549         g_grim->getCurrSet()->findClosestSector(_pos, &s, nullptr);
00550         if (s) {
00551             up = s->getNormal();
00552         }
00553     }
00554 
00555     Math::Matrix3 m;
00556     m.buildFromTargetDir(actorForward, lookVector, actorUp, up);
00557 
00558     if (_puckOrient) {
00559         m.getEuler(yaw, pitch, roll, Math::EO_ZXY);
00560     } else {
00561         *pitch = _movePitch;
00562         m.getEuler(yaw, nullptr, nullptr, Math::EO_ZXY);
00563         *roll = _moveRoll;
00564     }
00565 }
00566 
00567 void Actor::turnTo(const Math::Vector3d &pos, bool snap) {
00568     Math::Angle y, p, r;
00569     calculateOrientation(pos, &p, &y, &r);
00570     turnTo(p, y, r, snap);
00571 }
00572 
00573 bool Actor::singleTurnTo(const Math::Vector3d &pos) {
00574     Math::Angle y, p, r;
00575     calculateOrientation(pos, &p, &y, &r);
00576 
00577     float turnAmt = g_grim->getPerSecond(_turnRate);
00578     bool done = animTurn(turnAmt, y, &_yaw) == 0;
00579     done = animTurn(turnAmt, p, &_pitch) == 0 && done;
00580     done = animTurn(turnAmt, r, &_roll) == 0 && done;
00581     _moveYaw = _yaw;
00582     _movePitch = _pitch;
00583     _moveRoll = _roll;
00584     _singleTurning = !done;
00585 
00586     return done;
00587 }
00588 
00589 void Actor::turnTo(const Math::Angle &pitchParam, const Math::Angle &yawParam, const Math::Angle &rollParam, bool snap) {
00590     _movePitch = pitchParam;
00591     _moveRoll = rollParam;
00592     _moveYaw = yawParam;
00593     _turnRateMultiplier = (snap ? 5.f : 1.f);
00594     if (_yaw != yawParam || _pitch != pitchParam || _roll != rollParam) {
00595         _turning = true;
00596     } else
00597         _turning = false;
00598 }
00599 
00600 void Actor::walkTo(const Math::Vector3d &p) {
00601     if (p == _pos)
00602         _walking = false;
00603     else {
00604         _walking = true;
00605         _destPos = p;
00606         _path.clear();
00607 
00608         if (_followBoxes) {
00609             bool pathFound = false;
00610 
00611             Set *currSet = g_grim->getCurrSet();
00612             currSet->findClosestSector(p, nullptr, &_destPos);
00613 
00614             Common::List<PathNode *> openList;
00615             Common::List<PathNode *> closedList;
00616 
00617             PathNode *start = new PathNode;
00618             start->parent = nullptr;
00619             start->pos = _pos;
00620             start->dist = 0.f;
00621             start->cost = 0.f;
00622             openList.push_back(start);
00623             currSet->findClosestSector(_pos, &start->sect, nullptr);
00624 
00625             Common::List<Sector *> sectors;
00626             for (int i = 0; i < currSet->getSectorCount(); ++i) {
00627                 Sector *s = currSet->getSectorBase(i);
00628                 int type = s->getType();
00629                 if ((type == Sector::WalkType || type == Sector::HotType || type == Sector::FunnelType) && s->isVisible()) {
00630                     sectors.push_back(s);
00631                 }
00632             }
00633 
00634             do {
00635                 PathNode *node = nullptr;
00636                 float cost = -1.f;
00637                 for (Common::List<PathNode *>::iterator j = openList.begin(); j != openList.end(); ++j) {
00638                     PathNode *n = *j;
00639                     float c = n->dist + n->cost;
00640                     if (cost < 0.f || c < cost) {
00641                         cost = c;
00642                         node = n;
00643                     }
00644                 }
00645                 closedList.push_back(node);
00646                 openList.remove(node);
00647                 Sector *sector = node->sect;
00648 
00649                 if (sector && sector->isPointInSector(_destPos)) {
00650                     PathNode *n = closedList.back();
00651                     // Don't put the start position in the list, or else
00652                     // the first angle calculated in updateWalk() will be
00653                     // meaningless. The only node without parent is the start
00654                     // one.
00655                     while (n->parent) {
00656                         _path.push_back(n->pos);
00657                         n = n->parent;
00658                     }
00659 
00660                     pathFound = true;
00661                     break;
00662                 }
00663 
00664                 for (Common::List<Sector *>::iterator i = sectors.begin(); i != sectors.end(); ++i) {
00665                     Sector *s = *i;
00666                     bool inClosed = false;
00667                     for (Common::List<PathNode *>::iterator j = closedList.begin(); j != closedList.end(); ++j) {
00668                         if ((*j)->sect == s) {
00669                             inClosed = true;
00670                             break;
00671                         }
00672                     }
00673                     if (inClosed)
00674                         continue;
00675 
00676                     // Get "bridges" from the current sector to the other.
00677                     Common::List<Math::Line3d> bridges = sector->getBridgesTo(s);
00678                     if (bridges.empty())
00679                         continue; // The sectors are not adjacent.
00680 
00681                     Math::Vector3d closestPoint;
00682                     if (g_grim->getGameType() == GType_GRIM)
00683                         closestPoint = s->getClosestPoint(_destPos);
00684                     else
00685                         closestPoint = _destPos;
00686                     Math::Vector3d best;
00687                     float bestDist = 1e6f;
00688                     Math::Line3d l(node->pos, closestPoint);
00689 
00690                     // Pick a point on the boundary of the two sectors to walk towards.
00691                     while (!bridges.empty()) {
00692                         Math::Line3d bridge = bridges.back();
00693                         Math::Vector3d pos;
00694                         const bool useXZ = (g_grim->getGameType() == GType_MONKEY4);
00695 
00696                         // Prefer points on the straight line from this node towards
00697                         // the destination. Otherwise pick the middle point of a bridge
00698                         // that is closest to the destination.
00699                         if (!bridge.intersectLine2d(l, &pos, useXZ)) {
00700                             pos = bridge.middle();
00701                         } else {
00702                             best = pos;
00703                             break;
00704                         }
00705                         float dist = (pos - closestPoint).getMagnitude();
00706                         if (dist < bestDist) {
00707                             bestDist = dist;
00708                             best = pos;
00709                         }
00710                         bridges.pop_back();
00711                     }
00712                     best = handleCollisionTo(node->pos, best);
00713 
00714                     PathNode *n = nullptr;
00715                     for (Common::List<PathNode *>::iterator j = openList.begin(); j != openList.end(); ++j) {
00716                         if ((*j)->sect == s) {
00717                             n = *j;
00718                             break;
00719                         }
00720                     }
00721                     if (n) {
00722                         float newCost = node->cost + (best - node->pos).getMagnitude();
00723                         if (newCost < n->cost) {
00724                             n->cost = newCost;
00725                             n->parent = node;
00726                             n->pos = best;
00727                             n->dist = (n->pos - _destPos).getMagnitude();
00728                         }
00729                     } else {
00730                         n = new PathNode;
00731                         n->parent = node;
00732                         n->sect = s;
00733                         n->pos = best;
00734                         n->dist = (n->pos - _destPos).getMagnitude();
00735                         n->cost = node->cost + (n->pos - node->pos).getMagnitude();
00736                         openList.push_back(n);
00737                     }
00738                 }
00739             } while (!openList.empty());
00740 
00741             for (Common::List<PathNode *>::iterator j = closedList.begin(); j != closedList.end(); ++j) {
00742                 delete *j;
00743             }
00744             for (Common::List<PathNode *>::iterator j = openList.begin(); j != openList.end(); ++j) {
00745                 delete *j;
00746             }
00747 
00748             if (!pathFound) {
00749                 warning("Actor::walkTo(): No path found for %s", _name.c_str());
00750                 if (g_grim->getGameType() == GType_MONKEY4) {
00751                     _walking = false;
00752                     return;
00753                 }
00754             }
00755         }
00756 
00757         _path.push_front(_destPos);
00758     }
00759 }
00760 
00761 bool Actor::isWalking() const {
00762     return _walkedLast || _walkedCur || _walking;
00763 }
00764 
00765 bool Actor::isTurning() const {
00766     if (g_grim->getGameType() == GType_MONKEY4)
00767         if (_singleTurning)
00768             return true;
00769     if (_turning)
00770         return true;
00771 
00772     if (_lastTurnDir != 0 || _currTurnDir != 0)
00773         return true;
00774 
00775     return false;
00776 }
00777 
00778 void Actor::stopTurning() {
00779     _turning = false;
00780     if (_lastTurnDir != 0)
00781         getTurnChore(_lastTurnDir)->stop(true);
00782 
00783     _lastTurnDir = 0;
00784     _currTurnDir = 0;
00785 }
00786 
00787 void Actor::moveTo(const Math::Vector3d &pos) {
00788     // The walking actor doesn't always have the collision mode set, but it must however check
00789     // the collisions. E.g. the set hl doesn't set Manny's mode, but it must check for
00790     // collisions with Raoul.
00791     // On the other hand, it must not *set* the sphere mode, it must just use it for this call:
00792     // the set xb sets Manny's collision scale as 1 and mode as Off. If you then go to set tb
00793     // and talk to Doug at the photo window, Manny's mode *must be* Off, otherwise Doug will
00794     // collide, miss the target point and will walk away, leaving Manny waiting for him at
00795     // the window forever.
00796     CollisionMode mode = _collisionMode;
00797     if (_collisionMode == CollisionOff) {
00798         mode = CollisionSphere;
00799     }
00800 
00801     Math::Vector3d moveVec = pos - _pos;
00802     foreach (Actor *a, g_grim->getActiveActors()) {
00803         handleCollisionWith(a, mode, &moveVec);
00804     }
00805     _pos += moveVec;
00806 }
00807 
00808 void Actor::walkForward() {
00809     float dist = g_grim->getPerSecond(_walkRate);
00810     // Limit the amount of the movement per frame, otherwise with low fps
00811     // scripts that use WalkActorForward and proximity may break.
00812     if ((dist > 0 && dist > _walkRate / 5.f) || (dist < 0 && dist < _walkRate / 5.f))
00813         dist = _walkRate / 5.f;
00814 
00815     // Handle special case where actor is trying to walk but _walkRate is
00816     // currently set to 0.0f by _walkChore to simulate roboter-like walk style:
00817     // set _walkedCur to true to keep _walkChore playing in Actor::update()
00818     if (_walkRate == 0.0f) {
00819         _walkedCur = true;
00820     }
00821 
00822     _walking = false;
00823 
00824     if (!_followBoxes) {
00825         Math::Vector3d forwardVec(-_moveYaw.getSine() * _pitch.getCosine(),
00826                                   _moveYaw.getCosine() * _pitch.getCosine(), _pitch.getSine());
00827 
00828         // EMI: Y is up-down, actors use an X-Z plane for movement
00829         if (g_grim->getGameType() == GType_MONKEY4) {
00830             float temp = forwardVec.z();
00831             forwardVec.x() = -forwardVec.x();
00832             forwardVec.z() = forwardVec.y();
00833             forwardVec.y() = temp;
00834         }
00835 
00836         _pos += forwardVec * dist;
00837         _walkedCur = true;
00838         return;
00839     }
00840 
00841     bool backwards = false;
00842     if (dist < 0) {
00843         dist = -dist;
00844         backwards = true;
00845     }
00846 
00847     int tries = 0;
00848     while (dist > 0.0f) {
00849         Sector *currSector = nullptr, *prevSector = nullptr, *startSector = nullptr;
00850         Sector::ExitInfo ei;
00851 
00852         g_grim->getCurrSet()->findClosestSector(_pos, &currSector, &_pos);
00853         if (!currSector) { // Shouldn't happen...
00854             Math::Vector3d forwardVec(-_moveYaw.getSine() * _pitch.getCosine(),
00855                                       _moveYaw.getCosine() * _pitch.getCosine(), _pitch.getSine());
00856 
00857             // EMI: Y is up-down, actors use an X-Z plane for movement
00858             if (g_grim->getGameType() == GType_MONKEY4) {
00859                 float temp = forwardVec.z();
00860                 forwardVec.x() = -forwardVec.x();
00861                 forwardVec.z() = forwardVec.y();
00862                 forwardVec.y() = temp;
00863             }
00864 
00865             if (backwards)
00866                 forwardVec = -forwardVec;
00867 
00868             moveTo(_pos + forwardVec * dist);
00869             _walkedCur = true;
00870             return;
00871         }
00872         startSector = currSector;
00873 
00874         float oldDist = dist;
00875         while (currSector) {
00876             prevSector = currSector;
00877             Math::Vector3d forwardVec;
00878             const Math::Vector3d &normal = currSector->getNormal();
00879             if (g_grim->getGameType() == GType_GRIM) {
00880                 Math::Angle ax = Math::Vector2d(normal.x(), normal.z()).getAngle();
00881                 Math::Angle ay = Math::Vector2d(normal.y(), normal.z()).getAngle();
00882 
00883                 float z1 = -_moveYaw.getCosine() * (ay - _pitch).getCosine();
00884                 float z2 = _moveYaw.getSine() * (ax - _pitch).getCosine();
00885                 forwardVec = Math::Vector3d(-_moveYaw.getSine() * ax.getSine() * _pitch.getCosine(),
00886                                             _moveYaw.getCosine() * ay.getSine() * _pitch.getCosine(), z1 + z2);
00887             } else {
00888                 Math::Angle ax = Math::Vector2d(normal.x(), normal.y()).getAngle();
00889                 Math::Angle az = Math::Vector2d(normal.z(), normal.y()).getAngle();
00890 
00891                 float y1 = _moveYaw.getCosine() * (az - _pitch).getCosine();
00892                 float y2 = _moveYaw.getSine() * (ax - _pitch).getCosine();
00893                 forwardVec = Math::Vector3d(-_moveYaw.getSine() * ax.getSine() * _pitch.getCosine(), y1 + y2,
00894                                             -_moveYaw.getCosine() * az.getSine() * _pitch.getCosine());
00895             }
00896 
00897             if (backwards)
00898                 forwardVec = -forwardVec;
00899 
00900             Math::Vector3d puckVec = currSector->getProjectionToPuckVector(forwardVec);
00901             puckVec /= puckVec.getMagnitude();
00902             currSector->getExitInfo(_pos, puckVec, &ei);
00903             float exitDist = (ei.exitPoint - _pos).getMagnitude();
00904             if (dist < exitDist) {
00905                 moveTo(_pos + puckVec * dist);
00906                 _walkedCur = true;
00907                 return;
00908             }
00909             _pos = ei.exitPoint;
00910             dist -= exitDist;
00911             if (exitDist > 0.0001)
00912                 _walkedCur = true;
00913 
00914             // Check for an adjacent sector which can continue
00915             // the path
00916             currSector = g_grim->getCurrSet()->findPointSector(ei.exitPoint + (float)0.0001 * puckVec, Sector::WalkType);
00917 
00918             // EMI: some sectors are significantly higher/lower than others.
00919             if (currSector && g_grim->getGameType() == GType_MONKEY4) {
00920                 float planeDist = currSector->distanceToPoint(_pos);
00921                 if (fabs(planeDist) < 1.f)
00922                     _pos -= planeDist * currSector->getNormal();
00923             }
00924 
00925             if (currSector == prevSector || currSector == startSector)
00926                 break;
00927         }
00928 
00929         int turnDir = 1;
00930         if (ei.angleWithEdge > 90) {
00931             ei.angleWithEdge = 180 - ei.angleWithEdge;
00932             ei.edgeDir = -ei.edgeDir;
00933             turnDir = -1;
00934         }
00935         if (ei.angleWithEdge > _reflectionAngle)
00936             return;
00937 
00938         ei.angleWithEdge += (float)1.0f;
00939 
00940         if (g_grim->getGameType() == GType_MONKEY4) {
00941             ei.angleWithEdge = -ei.angleWithEdge;
00942         }
00943         turnTo(0, _moveYaw + ei.angleWithEdge * turnDir, 0, true);
00944 
00945         if (oldDist <= dist + 0.001f) {
00946             // If we didn't move at all, keep trying a couple more times
00947             // in case we can move in the new direction.
00948             tries++;
00949             if (tries > 3)
00950                 break;
00951         }
00952     }
00953 }
00954 
00955 Math::Vector3d Actor::getSimplePuckVector() const {
00956     if (g_grim->getGameType() == GType_MONKEY4) {
00957         Math::Angle yaw = 0;
00958         const Actor *a = this;
00959         while (a) {
00960             yaw += a->_yaw;
00961             if (!a->isAttached())
00962                 break;
00963             a = Actor::getPool().getObject(a->_attachedActor);
00964         }
00965         return Math::Vector3d(yaw.getSine(), 0, yaw.getCosine());
00966     } else {
00967         return Math::Vector3d(-_yaw.getSine(), _yaw.getCosine(), 0);
00968     }
00969 }
00970 
00971 Math::Vector3d Actor::getPuckVector() const {
00972     Math::Vector3d forwardVec = getSimplePuckVector();
00973 
00974     Set *currSet = g_grim->getCurrSet();
00975     if (!currSet)
00976         return forwardVec;
00977 
00978     Sector *sector = currSet->findPointSector(_pos, Sector::WalkType);
00979     if (!sector)
00980         return forwardVec;
00981     else
00982         return sector->getProjectionToPuckVector(forwardVec);
00983 }
00984 
00985 void Actor::setPuckOrient(bool orient) {
00986     _puckOrient = orient;
00987     warning("Actor::setPuckOrient() not implemented");
00988 }
00989 
00990 void Actor::setRestChore(int chore, Costume *cost) {
00991     if (_restChore.equals(cost, chore))
00992         return;
00993 
00994     _restChore.stop(g_grim->getGameType() == GType_GRIM);
00995 
00996     if (!cost) {
00997         cost = _restChore._costume;
00998     }
00999     if (!cost) {
01000         cost = getCurrentCostume();
01001     }
01002 
01003     _restChore = ActionChore(cost, chore);
01004 
01005     _restChore.playLooping(g_grim->getGameType() == GType_GRIM);
01006 }
01007 
01008 int Actor::getRestChore() const {
01009     return _restChore._chore;
01010 }
01011 
01012 void Actor::setWalkChore(int chore, Costume *cost) {
01013     if (_walkChore.equals(cost, chore))
01014         return;
01015 
01016     if (_walkedLast && _walkChore.isPlaying()) {
01017         _walkChore.stop(true);
01018 
01019         if (g_grim->getGameType() == GType_GRIM) {
01020             _restChore.playLooping(true);
01021         }
01022     }
01023 
01024     if (!cost) {
01025         cost = _walkChore._costume;
01026     }
01027     if (!cost) {
01028         cost = getCurrentCostume();
01029     }
01030 
01031     _walkChore = ActionChore(cost, chore);
01032 }
01033 
01034 void Actor::setTurnChores(int left_chore, int right_chore, Costume *cost) {
01035     if (_leftTurnChore.equals(cost, left_chore) && _rightTurnChore.equals(cost, right_chore))
01036         return;
01037 
01038     if (!cost) {
01039         cost = _leftTurnChore._costume;
01040     }
01041     if (!cost) {
01042         cost = getCurrentCostume();
01043     }
01044 
01045     _leftTurnChore.stop(true);
01046     _rightTurnChore.stop(true);
01047     _lastTurnDir = 0;
01048 
01049     _leftTurnChore = ActionChore(cost, left_chore);
01050     _rightTurnChore = ActionChore(cost, right_chore);
01051 
01052     if ((left_chore >= 0 && right_chore < 0) || (left_chore < 0 && right_chore >= 0))
01053         error("Unexpectedly got only one turn chore");
01054 }
01055 
01056 void Actor::setTalkChore(int index, int chore, Costume *cost) {
01057     if (index < 1 || index > 10)
01058         error("Got talk chore index out of range (%d)", index);
01059 
01060     index--;
01061 
01062     if (!cost) {
01063         cost = _talkChore[index]._costume;
01064     }
01065     if (!cost) {
01066         cost = getCurrentCostume();
01067     }
01068 
01069     if (_talkChore[index].equals(cost, chore))
01070         return;
01071 
01072     _talkChore[index].stop();
01073 
01074     _talkChore[index] = ActionChore(cost, chore);
01075 }
01076 
01077 int Actor::getTalkChore(int index) const {
01078     return _talkChore[index]._chore;
01079 }
01080 
01081 Costume *Actor::getTalkCostume(int index) const {
01082     return _talkChore[index]._costume;
01083 }
01084 
01085 void Actor::setMumbleChore(int chore, Costume *cost) {
01086     if (_mumbleChore.isPlaying()) {
01087         _mumbleChore.stop();
01088     }
01089 
01090     if (!cost) {
01091         cost = _mumbleChore._costume;
01092     }
01093     if (!cost) {
01094         cost = getCurrentCostume();
01095     }
01096 
01097     _mumbleChore = ActionChore(cost, chore);
01098 }
01099 
01100 void Actor::stopAllChores(bool ignoreLoopingChores) {
01101     for (Common::List<Costume *>::iterator i = _costumeStack.begin(); i != _costumeStack.end(); ++i) {
01102         Costume *costume = *i;
01103         costume->stopChores(ignoreLoopingChores);
01104         if (costume->isChoring(false) == -1) {
01105             freeCostume(costume);
01106             i = _costumeStack.erase(i);
01107             --i;
01108         }
01109     }
01110 }
01111 
01112 void Actor::turn(int dir) {
01113     _walking = false;
01114     float delta = g_grim->getPerSecond(_turnRate) * dir;
01115     if (g_grim->getGameType() == GType_MONKEY4) {
01116         delta = -delta;
01117     }
01118     _moveYaw = _moveYaw + delta;
01119     _turning = true;
01120     _turnRateMultiplier = 5.f;
01121     _currTurnDir = dir;
01122 }
01123 
01124 Math::Angle Actor::getYawTo(const Actor *a) const {
01125     Math::Vector3d forwardVec = getSimplePuckVector();
01126     Math::Vector3d delta = a->getWorldPos() - getWorldPos();
01127 
01128     if (g_grim->getGameType() == GType_MONKEY4) {
01129         delta.y() = 0;
01130     } else {
01131         delta.z() = 0;
01132     }
01133 
01134     return Math::Vector3d::angle(forwardVec, delta);
01135 }
01136 
01137 Math::Angle Actor::getYawTo(const Math::Vector3d &p) const {
01138     Math::Vector3d dpos = p - _pos;
01139 
01140     if (g_grim->getGameType() == GType_MONKEY4) {
01141         dpos.y() = dpos.z();
01142     }
01143     if (dpos.x() == 0 && dpos.y() == 0)
01144         return 0;
01145     else
01146         return Math::Angle::arcTangent2(-dpos.x(), dpos.y());
01147 }
01148 
01149 void Actor::sayLine(const char *msgId, bool background, float x, float y) {
01150     assert(msgId);
01151 
01152     if (msgId[0] == 0) {
01153         warning("Actor::sayLine: Empty message");
01154         return;
01155     }
01156 
01157     char id[50];
01158     Common::String msg = LuaBase::instance()->parseMsgText(msgId, id);
01159 
01160     if (id[0] == 0) {
01161         error("Actor::sayLine: No message ID for text");
01162         return;
01163     }
01164 
01165     // During Fullscreen movies SayLine is usually called for text display only.
01166     // The movie with Charlie screaming after Manny put the sheet on him instead
01167     // uses sayLine for the voice too.
01168     // However, normal SMUSH movies may call SayLine, for example:
01169     // When Domino yells at Manny (a SMUSH movie) he does it with
01170     // a SayLine request rather than as part of the movie!
01171 
01172     Common::String soundName = id;
01173 
01174     if (g_grim->getGameType() == GType_GRIM) {
01175         soundName += ".wav";
01176     } else if (g_grim->getGameType() == GType_MONKEY4 && g_grim->getGamePlatform() == Common::kPlatformPS2) {
01177         soundName += ".scx";
01178     } else {
01179         soundName += ".wVC";
01180     }
01181     if (_talkSoundName == soundName)
01182         return;
01183 
01184     if (_talking || msg.empty())
01185         shutUp();
01186 
01187     _talkSoundName = soundName;
01188 
01189     Set *currSet = g_grim->getCurrSet();
01190 
01191     if (g_grim->getSpeechMode() != GrimEngine::TextOnly) {
01192         // if there is no costume probably the character is drawn by a smush movie, so
01193         // we don't want to go out of sync with it.
01194         if (g_grim->getGameType() == GType_GRIM && getCurrentCostume()) {
01195             _talkDelay = 500;
01196         }
01197         g_sound->startVoice(_talkSoundName.c_str());
01198     }
01199 
01200     // If the actor is clearly not visible then don't try to play the lip sync
01201     if (_visible && (!g_movie->isPlaying() || g_grim->getMode() == GrimEngine::NormalMode)) {
01202         Common::String soundLip = id;
01203         soundLip += ".lip";
01204 
01205         if (!_talkChore[0].isPlaying()) {
01206             // _talkChore[0] is *_stop_talk
01207             _talkChore[0].setLastFrame();
01208         }
01209         // Sometimes actors speak offscreen before they, including their
01210         // talk chores are initialized.
01211         // For example, when reading the work order (a LIP file exists for no reason).
01212         // Also, some lip sync files have no entries
01213         // In these cases, revert to using the mumble chore.
01214         if (g_grim->getSpeechMode() != GrimEngine::TextOnly)
01215             _lipSync = g_resourceloader->getLipSync(soundLip);
01216         // If there's no lip sync file then load the mumble chore if it exists
01217         // (the mumble chore doesn't exist with the cat races announcer)
01218         if (!_lipSync)
01219             _mumbleChore.playLooping();
01220 
01221         _talkAnim = -1;
01222     }
01223 
01224     _talking = true;
01225     g_grim->addTalkingActor(this);
01226 
01227     _backgroundTalk = background;
01228     if (background)
01229         _isTalkingBackground = true;
01230 
01231     if (_sayLineText && g_grim->getMode() != GrimEngine::SmushMode) {
01232         delete TextObject::getPool().getObject(_sayLineText);
01233         _sayLineText = 0;
01234     }
01235 
01236     if (!msg.empty()) {
01237         GrimEngine::SpeechMode m = g_grim->getSpeechMode();
01238         if (!g_grim->_sayLineDefaults.getFont() || m == GrimEngine::VoiceOnly)
01239             return;
01240 
01241         if (background) {
01242             // if we're talking background draw the text object only if there are no no-background
01243             // talking actors. This prevents glottis and nick subtitles overlapping in the high roller lounge,
01244             // where glottis is background and nick isn't. (https://github.com/residualvm/residualvm/issues/685)
01245             foreach (Actor *a, g_grim->getTalkingActors()) {
01246                 if (!a->_backgroundTalk && a->_sayLineText) {
01247                     return;
01248                 }
01249             }
01250         } else {
01251             // if we're not background then delete the TextObject of any talking background actor.
01252             foreach (Actor *a, g_grim->getTalkingActors()) {
01253                 if (a->_backgroundTalk && a->_sayLineText) {
01254                     delete TextObject::getPool().getObject(a->_sayLineText);
01255                     a->_sayLineText = 0;
01256                 }
01257             }
01258         }
01259 
01260         TextObject *textObject = new TextObject();
01261         textObject->setDefaults(&g_grim->_sayLineDefaults);
01262         textObject->setFGColor(_talkColor);
01263         textObject->setIsSpeech();
01264         if (m == GrimEngine::TextOnly || g_grim->getMode() == GrimEngine::SmushMode) {
01265             textObject->setDuration(500 + msg.size() * 15 * (11 - g_grim->getTextSpeed()));
01266         }
01267         if (g_grim->getGameType() == GType_MONKEY4 && (x != -1 || y != -1)) {
01268             textObject->setX(320 * (x + 1));
01269             textObject->setY(240 * (y + 1));
01270         } else if (g_grim->getMode() == GrimEngine::SmushMode) {
01271             textObject->setX(640 / 2);
01272             textObject->setY(456);
01273             g_grim->setMovieSubtitle(textObject);
01274         } else {
01275             if (_visible && isInSet(currSet->getName())) {
01276                 _mustPlaceText = true;
01277             } else {
01278                 _mustPlaceText = false;
01279                 textObject->setX(640 / 2);
01280                 textObject->setY(463);
01281             }
01282         }
01283         textObject->setText(msgId, _mustPlaceText);
01284         if (g_grim->getMode() != GrimEngine::SmushMode)
01285             _sayLineText = textObject->getId();
01286     }
01287 }
01288 
01289 void Actor::lineCleanup() {
01290     if (_sayLineText) {
01291         delete TextObject::getPool().getObject(_sayLineText);
01292         _sayLineText = 0;
01293     }
01294 }
01295 
01296 bool Actor::isTalking() {
01297     // This should return if the actor is actually talking, disregarding of the background status,
01298     // otherwise when Naranja is asleep Toto's lines will be cut sometimes. Naranja and Toto both
01299     // talk in background, and when Naranja lines stop toto:wait_for_message() should not return
01300     // until he actually stops talking.
01301     return _talking;
01302 }
01303 
01304 bool Actor::isTalkingForeground() const {
01305     if (!_talking) {
01306         return false;
01307     }
01308 
01309     if (_backgroundTalk)
01310         return _isTalkingBackground;
01311 
01312     return true;
01313 }
01314 
01315 void Actor::shutUp() {
01316     // While the call to stop the sound is usually made by the game,
01317     // we also need to handle when the user terminates the dialog.
01318     // Some warning messages will occur when the user terminates the
01319     // actor dialog but the game will continue alright.
01320     if (_talkSoundName != "") {
01321         g_sound->stopSound(_talkSoundName.c_str());
01322         _talkSoundName = "";
01323     }
01324 
01325     if (_lipSync) {
01326         if (_talkAnim != -1)
01327             _talkChore[_talkAnim].stop(g_grim->getGameType() == GType_MONKEY4, ActionChore::talkFadeTime);
01328         _lipSync = nullptr;
01329     }
01330     // having a lipsync is no guarantee the mumble chore is no running. the talk chores may be -1 (domino in do)
01331     stopMumbleChore();
01332     stopTalking();
01333 
01334     if (_sayLineText) {
01335         delete TextObject::getPool().getObject(_sayLineText);
01336         _sayLineText = 0;
01337     }
01338 
01339     // The actors talking in background have a special behaviour: if there are two or more of them
01340     // talking at the same time, after one of them finishes talking calling isTalking() an *all*
01341     // of them must return false. This is necessary for the angelitos in set fo: when they start crying
01342     // they talk in background, and the lua script that hangs on IsMessageGoing() must return before they
01343     // stop, since they can go on forever.
01344     if (_backgroundTalk)
01345         _isTalkingBackground = false;
01346 
01347     _talking = false;
01348 }
01349 
01350 void Actor::pushCostume(const char *n) {
01351     Costume *c = findCostume(n);
01352     if (c) {
01353         Debug::debug(Debug::Actors, "Trying to push a costume already in the stack. %s, %s", getName().c_str(), n);
01354         return;
01355     }
01356 
01357     Costume *newCost = g_resourceloader->loadCostume(n, this, getCurrentCostume());
01358 
01359     _costumeStack.push_back(newCost);
01360 }
01361 
01362 void Actor::setColormap(const char *map) {
01363     if (!_costumeStack.empty()) {
01364         Costume *cost = _costumeStack.back();
01365         cost->setColormap(map);
01366     } else {
01367         warning("Actor::setColormap: No costumes");
01368     }
01369 }
01370 
01371 void Actor::setCostume(const char *n) {
01372     if (!_costumeStack.empty())
01373         popCostume();
01374 
01375     pushCostume(n);
01376 }
01377 
01378 void Actor::popCostume() {
01379     if (!_costumeStack.empty()) {
01380         freeCostume(_costumeStack.back());
01381         _costumeStack.pop_back();
01382 
01383         if (_costumeStack.empty()) {
01384             Debug::debug(Debug::Actors, "Popped (freed) the last costume for an actor.\n");
01385         }
01386     } else {
01387         Debug::warning(Debug::Actors, "Attempted to pop (free) a costume when the stack is empty!");
01388     }
01389 }
01390 
01391 void Actor::clearCostumes() {
01392     // Make sure to destroy costume copies in reverse order
01393     while (!_costumeStack.empty())
01394         popCostume();
01395 }
01396 
01397 Costume *Actor::getCurrentCostume() const {
01398     if (g_grim->getGameType() == GType_MONKEY4) {
01399         // Return the first costume that has a model component.
01400         for (Common::List<Costume *>::const_iterator i = _costumeStack.begin(); i != _costumeStack.end(); ++i) {
01401             EMICostume *costume = static_cast<EMICostume *>(*i);
01402             if (costume->getEMIModel()) {
01403                 return costume;
01404             }
01405         }
01406         return nullptr;
01407     } else {
01408         if (_costumeStack.empty())
01409             return nullptr;
01410         else
01411             return _costumeStack.back();
01412     }
01413 }
01414 
01415 void Actor::setHead(int joint1, int joint2, int joint3, float maxRoll, float maxPitch, float maxYaw) {
01416     Costume *curCostume = getCurrentCostume();
01417     if (curCostume) {
01418         curCostume->setHead(joint1, joint2, joint3, maxRoll, maxPitch, maxYaw);
01419     }
01420 }
01421 
01422 void Actor::setHead(const char *joint, const Math::Vector3d &offset) {
01423     Costume *curCostume = getCurrentCostume();
01424     if (curCostume) {
01425         EMICostume *costume = static_cast<EMICostume *>(curCostume);
01426         costume->setHead(joint, offset);
01427     }
01428 }
01429 
01430 void Actor::setHeadLimits(float yawRange, float maxPitch, float minPitch) {
01431     Costume *curCostume = getCurrentCostume();
01432     if (curCostume) {
01433         EMICostume *costume = static_cast<EMICostume *>(curCostume);
01434         costume->setHeadLimits(yawRange, maxPitch, minPitch);
01435     }
01436 }
01437 
01438 void Actor::setLookAtRate(float rate) {
01439     getCurrentCostume()->setLookAtRate(rate);
01440 }
01441 
01442 float Actor::getLookAtRate() const {
01443     return getCurrentCostume()->getLookAtRate();
01444 }
01445 
01446 EMIModel *Actor::findModelWithMesh(const Common::String &mesh) {
01447     for (Common::List<Costume *>::iterator i = _costumeStack.begin(); i != _costumeStack.end(); ++i) {
01448         EMICostume *costume = static_cast<EMICostume *>(*i);
01449         if (!costume) {
01450             continue;
01451         }
01452         for (int j = 0; j < costume->getNumChores(); j++) {
01453             EMIModel *model = costume->getEMIModel(j);
01454             if (!model) {
01455                 continue;
01456             }
01457             if (mesh == model->_meshName) {
01458                 return model;
01459             }
01460         }
01461     }
01462     return nullptr;
01463 }
01464 
01465 void Actor::setGlobalAlpha(float alpha, const Common::String &mesh) {
01466     if (mesh.empty()) {
01467         _globalAlpha = alpha;
01468     } else {
01469         EMIModel *model = findModelWithMesh(mesh);
01470         if (model != nullptr) {
01471             model->_meshAlpha = alpha;
01472         }
01473     }
01474 }
01475 
01476 void Actor::setAlphaMode(AlphaMode mode, const Common::String &mesh) {
01477     if (mesh.empty()) {
01478         _alphaMode = mode;
01479     } else {
01480         EMIModel *model = findModelWithMesh(mesh);
01481         if (model != nullptr) {
01482             model->_meshAlphaMode = mode;
01483         }
01484     }
01485 }
01486 
01487 Costume *Actor::findCostume(const Common::String &n) {
01488     for (Common::List<Costume *>::iterator i = _costumeStack.begin(); i != _costumeStack.end(); ++i) {
01489         if ((*i)->getFilename().compareToIgnoreCase(n) == 0)
01490             return *i;
01491     }
01492 
01493     return nullptr;
01494 }
01495 
01496 void Actor::setFollowBoxes(bool follow) {
01497     _followBoxes = follow;
01498     if (follow)
01499         _hasFollowedBoxes = true;
01500 }
01501 
01502 void Actor::updateWalk() {
01503     if (_path.empty()) {
01504         return;
01505     }
01506 
01507     Math::Vector3d destPos = _path.back();
01508     Math::Vector3d dir = destPos - _pos;
01509     float dist = dir.getMagnitude();
01510 
01511     _walkedCur = true;
01512     float walkAmt = g_grim->getPerSecond(_walkRate);
01513     if (walkAmt >= dist) {
01514         walkAmt = dist;
01515         _path.pop_back();
01516         if (_path.empty()) {
01517             _walking = false;
01518             _pos = destPos;
01519 // It seems that we need to allow an already active turning motion to
01520 // continue or else turning actors away from barriers won't work right
01521             _turning = false;
01522             return;
01523         }
01524     }
01525 
01526     turnTo(destPos, true);
01527 
01528     dir = destPos - _pos;
01529     dir.normalize();
01530     _pos += dir * walkAmt;
01531 }
01532 
01533 void Actor::update(uint frameTime) {
01534     Set *set = g_grim->getCurrSet();
01535     // Snap actor to walkboxes if following them.  This might be
01536     // necessary for example after activating/deactivating
01537     // walkboxes, etc.
01538     if (_followBoxes && !_walking) {
01539         set->findClosestSector(_pos, nullptr, &_pos);
01540     }
01541 
01542     if (g_grim->getGameType() == GType_MONKEY4) {
01543         if (_followBoxes) {
01544             // Check for sort order information in the current sector
01545             int oldSortOrder = getEffectiveSortOrder();
01546             _sectorSortOrder = set->findSectorSortOrder(_pos, Sector::WalkType);
01547 
01548             if (oldSortOrder != getEffectiveSortOrder())
01549                 g_emi->invalidateSortOrder();
01550         } else if (_sectorSortOrder >= 0) {
01551             _sectorSortOrder = -1;
01552             g_emi->invalidateSortOrder();
01553         }
01554     }
01555 
01556     if (_turning) {
01557         float turnAmt = g_grim->getPerSecond(_turnRate) * _turnRateMultiplier;
01558         _currTurnDir = animTurn(turnAmt, _moveYaw, &_yaw);
01559         if (g_grim->getGameType() == GType_MONKEY4) {
01560             _currTurnDir = -_currTurnDir;
01561         }
01562         int p = animTurn(turnAmt, _movePitch, &_pitch);
01563         int r = animTurn(turnAmt, _moveRoll, &_roll);
01564         if (_currTurnDir == 0 && p == 0 && r == 0) {
01565             _turning = false;
01566             _turnRateMultiplier = 1.f;
01567         }
01568     }
01569 
01570     if (_walking) {
01571         updateWalk();
01572     }
01573 
01574     if (_walkChore.isValid()) {
01575         if (_walkedCur) {
01576             if (!_walkChore.isPlaying()) {
01577                 _walkChore.playLooping(true);
01578             }
01579             if (g_grim->getGameType() == GType_GRIM && _restChore.isPlaying()) {
01580                 _restChore.stop(true);
01581             }
01582         } else {
01583             if (_walkedLast && _walkChore.isPlaying()) {
01584                 _walkChore.stop(true);
01585                 if (!_restChore.isPlaying()) {
01586                     _restChore.playLooping(true);
01587                 }
01588             }
01589         }
01590     }
01591 
01592     if (_leftTurnChore.isValid()) {
01593         if (_walkedCur || _walkedLast)
01594             _currTurnDir = 0;
01595 
01596         if (g_grim->getGameType() == GType_GRIM && _restChore.isValid()) {
01597             if (_currTurnDir != 0) {
01598                 if (getTurnChore(_currTurnDir)->isPlaying() && _restChore.isPlaying()) {
01599                     _restChore.stop(true, 500);
01600                 }
01601             } else if (_lastTurnDir != 0) {
01602                 if (!_walkedCur && getTurnChore(_lastTurnDir)->isPlaying()) {
01603                     _restChore.playLooping(true);
01604                 }
01605             }
01606         }
01607 
01608         if (_lastTurnDir != 0 && _lastTurnDir != _currTurnDir) {
01609             getTurnChore(_lastTurnDir)->stop(true);
01610         }
01611         if (_currTurnDir != 0 && _currTurnDir != _lastTurnDir) {
01612             getTurnChore(_currTurnDir)->playLooping(true, 500);
01613             if (_currTurnDir == 1) {
01614                 // When turning to the left, ensure that the components of the right turn chore
01615                 // are fading out (or stopped).
01616                 // This is necessary because the left turn chore typically contains both the
01617                 // left turn and right turn keyframe components. The above call to playLooping
01618                 // will thus start fading in both of the components, overriding the right turn's
01619                 // fade out that was started before.
01620                 // The left turn chore's keys will eventually stop the right turn keyframe from
01621                 // playing, but the stopping will be instantaneous. To get a smooth transition,
01622                 // we want to keep fading out the right turn. The chore's "stop" key will be
01623                 // ignored when the keyframe is fading out (see KeyframeComponent::stop()).
01624                 _rightTurnChore.stop(true);
01625             }
01626         }
01627     } else {
01628         _currTurnDir = 0;
01629     }
01630 
01631     // The rest chore might have been stopped because of a
01632     // StopActorChore(nil).  Restart it if so.
01633     if (!_walkedCur && _currTurnDir == 0 && !_restChore.isPlaying()) {
01634         _restChore.playLooping(g_grim->getGameType() == GType_GRIM);
01635     }
01636 
01637     _walkedLast = _walkedCur;
01638     _walkedCur = false;
01639     _lastTurnDir = _currTurnDir;
01640     _currTurnDir = 0;
01641 
01642     // Update lip syncing
01643     if (_lipSync) {
01644         int posSound;
01645 
01646         // While getPosIn16msTicks will return "-1" to indicate that the
01647         // sound is no longer playing, it is more appropriate to check first
01648         if (g_grim->getSpeechMode() != GrimEngine::TextOnly && g_sound->getSoundStatus(_talkSoundName.c_str()))
01649             posSound = g_sound->getPosIn16msTicks(_talkSoundName.c_str());
01650         else
01651             posSound = -1;
01652         if (posSound != -1) {
01653             int anim = _lipSync->getAnim(posSound);
01654             if (_talkAnim != anim) {
01655                 if (anim != -1) {
01656                     if (_talkChore[anim].isValid()) {
01657                         stopMumbleChore();
01658                         if (_talkAnim != -1) {
01659                             _talkChore[_talkAnim].stop(g_grim->getGameType() == GType_MONKEY4, ActionChore::talkFadeTime);
01660                         }
01661                         if (g_grim->getGameType() == GType_GRIM) {
01662                             // Run the stop_talk chore so that it resets the components
01663                             // to the right visibility.
01664                             stopTalking();
01665                         } else {
01666                             // Make sure the talk rest chore isn't playing.
01667                             _talkChore[0].stop();
01668                         }
01669                         _talkAnim = anim;
01670                         _talkChore[_talkAnim].play(g_grim->getGameType() == GType_MONKEY4, ActionChore::talkFadeTime);
01671                     } else if (_mumbleChore.isValid() && !_mumbleChore.isPlaying()) {
01672                         _mumbleChore.playLooping();
01673                         _talkAnim = -1;
01674                     }
01675                 } else {
01676                     stopMumbleChore();
01677                     if (_talkAnim != -1)
01678                         _talkChore[_talkAnim].stop(true, ActionChore::talkFadeTime);
01679 
01680                     _talkAnim = 0;
01681                     stopTalking();
01682                 }
01683             }
01684         }
01685     }
01686 
01687     frameTime = (uint)(frameTime * _timeScale);
01688     for (Common::List<Costume *>::iterator i = _costumeStack.begin(); i != _costumeStack.end(); ++i) {
01689         Costume *c = *i;
01690         c->setPosRotate(_pos, _pitch, _yaw, _roll);
01691         int marker = c->update(frameTime);
01692         if (marker > 0) {
01693             costumeMarkerCallback(marker);
01694         }
01695         if (g_grim->getGameType() == GType_MONKEY4 && c->isChoring(false) == -1) {
01696             freeCostume(c);
01697             i = _costumeStack.erase(i);
01698             --i;
01699         }
01700     }
01701 
01702     Costume *c = getCurrentCostume();
01703     if (c) {
01704         c->animate();
01705     }
01706 
01707     if (_lookingMode && _lookAtActor != 0) {
01708         Actor *actor = Actor::getPool().getObject(_lookAtActor);
01709         if (actor)
01710             _lookAtVector = actor->getHeadPos();
01711     }
01712 
01713     for (Common::List<Costume *>::iterator i = _costumeStack.begin(); i != _costumeStack.end(); ++i) {
01714         (*i)->moveHead(_lookingMode, _lookAtVector);
01715     }
01716 }
01717 
01718 // Not all the talking actors are in the current set, and so on not all the talking actors
01719 // update() is called. For example, Don when he comes out of his office after reaping Meche.
01720 bool Actor::updateTalk(uint frameTime) {
01721     if (_talking) {
01722         // If there's no sound file then we're obviously not talking
01723         GrimEngine::SpeechMode m = g_grim->getSpeechMode();
01724         TextObject *textObject = nullptr;
01725         if (_sayLineText)
01726             textObject = TextObject::getPool().getObject(_sayLineText);
01727         if (m == GrimEngine::TextOnly && !textObject) {
01728             shutUp();
01729             return false;
01730         } else if (m != GrimEngine::TextOnly && (_talkSoundName.empty() || !g_sound->getSoundStatus(_talkSoundName.c_str()))) {
01731             _talkDelay -= frameTime;
01732             if (_talkDelay <= 0) {
01733                 _talkDelay = 0;
01734                 shutUp();
01735                 return false;
01736             }
01737         }
01738         return true;
01739     }
01740 
01741     return false;
01742 }
01743 
01744 void Actor::draw() {
01745     if (!g_driver->isHardwareAccelerated() && g_grim->getFlagRefreshShadowMask()) {
01746         for (int l = 0; l < MAX_SHADOWS; l++) {
01747             if (!_shadowArray[l].active)
01748                 continue;
01749             g_driver->setShadow(&_shadowArray[l]);
01750             g_driver->drawShadowPlanes();
01751             g_driver->setShadow(nullptr);
01752         }
01753     }
01754 
01755     // FIXME: if isAttached(), factor in the joint rotation as well.
01756     const Math::Vector3d &absPos = getWorldPos();
01757     if (!_costumeStack.empty()) {
01758         g_grim->getCurrSet()->setupLights(absPos, _inOverworld);
01759         if (g_grim->getGameType() == GType_GRIM) {
01760             Costume *costume = _costumeStack.back();
01761             drawCostume(costume);
01762         } else {
01763             for (Common::List<Costume *>::iterator it = _costumeStack.begin(); it != _costumeStack.end(); ++it) {
01764                 Costume *costume = *it;
01765                 drawCostume(costume);
01766             }
01767         }
01768     }
01769 
01770     if (_mustPlaceText) {
01771         Common::Point p1, p2;
01772         if (g_grim->getGameType() == GType_GRIM) {
01773             if (!_costumeStack.empty()) {
01774                 int x1 = 1000, y1 = 1000, x2 = -1000, y2 = -1000;
01775                 g_driver->startActorDraw(this);
01776                 _costumeStack.back()->getBoundingBox(&x1, &y1, &x2, &y2);
01777                 g_driver->finishActorDraw();
01778                 p1.x = x1;
01779                 p1.y = y1;
01780                 p2.x = x2;
01781                 p2.y = y2;
01782             }
01783         } else {
01784             g_driver->getActorScreenBBox(this, p1, p2);
01785         }
01786 
01787         TextObject *textObject = TextObject::getPool().getObject(_sayLineText);
01788         if (textObject) {
01789             if (p1.x == 1000 || p2.x == -1000 || p2.x == -1000) {
01790                 textObject->setX(640 / 2);
01791                 textObject->setY(463);
01792             } else {
01793                 textObject->setX((p1.x + p2.x) / 2);
01794                 textObject->setY(p1.y);
01795             }
01796             // Deletes the original text and rebuilds it with the newly placed text
01797             textObject->reset();
01798         }
01799         _mustPlaceText = false;
01800     }
01801 }
01802 
01803 void Actor::drawCostume(Costume *costume) {
01804     for (int l = 0; l < MAX_SHADOWS; l++) {
01805         if (!shouldDrawShadow(l))
01806             continue;
01807         g_driver->setShadow(&_shadowArray[l]);
01808         g_driver->setShadowMode();
01809         if (g_driver->isHardwareAccelerated())
01810             g_driver->drawShadowPlanes();
01811         g_driver->startActorDraw(this);
01812         costume->draw();
01813         g_driver->finishActorDraw();
01814         g_driver->clearShadowMode();
01815         g_driver->setShadow(nullptr);
01816     }
01817 
01818     // normal draw actor
01819     g_driver->startActorDraw(this);
01820     costume->draw();
01821     g_driver->finishActorDraw();
01822 }
01823 
01824 void Actor::setShadowPlane(const char *n) {
01825     assert(_activeShadowSlot != -1);
01826 
01827     _shadowArray[_activeShadowSlot].name = n;
01828 }
01829 
01830 void Actor::addShadowPlane(const char *n, Set *scene, int shadowId) {
01831     assert(shadowId != -1);
01832 
01833     // This needs to be an exact match, because with a substring search a search for sector
01834     // "shadow1" would return sector "shadow10" in set st, causing shadows not to be cast
01835     // on the street.
01836     Sector *sector = scene->getSectorByName(n);
01837     if (sector) {
01838         // Create a copy so we are sure it will not be deleted by the Set destructor
01839         // behind our back. This is important when Membrillo phones Velasco to tell him
01840         // Naranja is dead, because the scene changes back and forth few times and so
01841         // the scenes' sectors are deleted while they are still keeped by the actors.
01842         Plane p = { scene->getName(), new Sector(*sector) };
01843         _shadowArray[shadowId].planeList.push_back(p);
01844         g_grim->flagRefreshShadowMask(true);
01845     }
01846 }
01847 
01848 bool Actor::shouldDrawShadow(int shadowId) {
01849     Shadow *shadow = &_shadowArray[shadowId];
01850     if (!shadow->active)
01851         return false;
01852 
01853     // Don't draw a shadow if the shadow caster and the actor are on different sides
01854     // of the the shadow plane.
01855     Sector *sector = shadow->planeList.front().sector;
01856     Math::Vector3d n = sector->getNormal();
01857     Math::Vector3d p = sector->getVertices()[0];
01858     float d = -(n.x() * p.x() + n.y() * p.y() + n.z() * p.z());
01859 
01860     Math::Vector3d bboxPos, bboxSize;
01861     getBBoxInfo(bboxPos, bboxSize);
01862     Math::Vector3d centerOffset = bboxPos + bboxSize * 0.5f;
01863     p = getPos() + centerOffset;
01864 
01865     bool actorSide = n.x() * p.x() + n.y() * p.y() + n.z() * p.z() + d < 0.f;
01866     p = shadow->pos;
01867     bool shadowSide = n.x() * p.x() + n.y() * p.y() + n.z() * p.z() + d < 0.f;
01868 
01869     if (actorSide == shadowSide)
01870         return true;
01871     return false;
01872 }
01873 
01874 void Actor::addShadowPlane(const char *n) {
01875     addShadowPlane(n, g_grim->getCurrSet(), _activeShadowSlot);
01876 }
01877 
01878 void Actor::setActiveShadow(int shadowId) {
01879     assert(shadowId >= 0 && shadowId < MAX_SHADOWS);
01880 
01881     _activeShadowSlot = shadowId;
01882     _shadowArray[_activeShadowSlot].active = true;
01883 }
01884 
01885 void Actor::setShadowValid(int valid) {
01886     if (valid == -1)
01887         _shadowArray[_activeShadowSlot].dontNegate = true;
01888     else
01889         _shadowArray[_activeShadowSlot].dontNegate = false;
01890 }
01891 
01892 void Actor::setActivateShadow(int shadowId, bool state) {
01893     assert(shadowId >= 0 && shadowId < MAX_SHADOWS);
01894 
01895     _shadowArray[shadowId].active = state;
01896 }
01897 
01898 void Actor::setShadowPoint(const Math::Vector3d &p) {
01899     assert(_activeShadowSlot != -1);
01900 
01901     _shadowArray[_activeShadowSlot].pos = p;
01902 }
01903 
01904 void Actor::setShadowColor(const Color &color) {
01905     assert(_activeShadowSlot != -1);
01906 
01907     _shadowArray[_activeShadowSlot].color = color;
01908 }
01909 
01910 void Actor::clearShadowPlanes() {
01911     for (int i = 0; i < MAX_SHADOWS; i++) {
01912         clearShadowPlane(i);
01913     }
01914 }
01915 
01916 void Actor::clearShadowPlane(int i) {
01917     Shadow *shadow = &_shadowArray[i];
01918     while (!shadow->planeList.empty()) {
01919         delete shadow->planeList.back().sector;
01920         shadow->planeList.pop_back();
01921     }
01922     delete[] shadow->shadowMask;
01923     shadow->shadowMaskSize = 0;
01924     shadow->shadowMask = nullptr;
01925     shadow->active = false;
01926     shadow->dontNegate = false;
01927 
01928     g_driver->destroyShadow(shadow);
01929 }
01930 
01931 void Actor::putInSet(const Common::String &set) {
01932     if (_drawnToClean) {
01933         // actor was frozen and...
01934         if (set == "") {
01935             // ...is getting unbound from set.
01936             // This is done in game scripts to reduce the drawing
01937             // workload in original engine, and we should not need
01938             // it here. And implementing off-screen buffers is
01939             // highly non-trivial. Disobey so it is still drawn.
01940             _fakeUnbound = true;
01941             return;
01942         } else {
01943             // ...is getting bound to a set. Thaw and continue.
01944             _drawnToClean = false;
01945         }
01946     }
01947     _fakeUnbound = false;
01948     // The set should change immediately, otherwise a very rapid set change
01949     // for an actor will be recognized incorrectly and the actor will be lost.
01950     _setName = set;
01951 
01952     g_grim->invalidateActiveActorsList();
01953 }
01954 
01955 bool Actor::isDrawableInSet(const Common::String &set) const {
01956     return _setName == set;
01957 }
01958 
01959 bool Actor::isInSet(const Common::String &set) const {
01960     return !_fakeUnbound && _setName == set;
01961 }
01962 
01963 void Actor::freeCostume(Costume *costume) {
01964     Debug::debug(Debug::Actors, "Freeing costume %s", costume->getFilename().c_str());
01965     freeCostumeChore(costume, &_restChore);
01966     freeCostumeChore(costume, &_walkChore);
01967     freeCostumeChore(costume, &_leftTurnChore);
01968     freeCostumeChore(costume, &_rightTurnChore);
01969     freeCostumeChore(costume, &_mumbleChore);
01970     for (int i = 0; i < 10; i++)
01971         freeCostumeChore(costume, &_talkChore[i]);
01972     delete costume;
01973 }
01974 
01975 void Actor::freeCostumeChore(const Costume *toFree, ActionChore *chore) {
01976     if (chore->_costume == toFree) {
01977         *chore = ActionChore();
01978     }
01979 }
01980 
01981 void Actor::stopTalking() {
01982     // _talkChore[0] is *_stop_talk
01983     // Don't playLooping it, or else manny's mouth will flicker when he smokes.
01984     _talkChore[0].setLastFrame();
01985 }
01986 
01987 bool Actor::stopMumbleChore() {
01988     if (_mumbleChore.isPlaying()) {
01989         _mumbleChore.stop();
01990         return true;
01991     }
01992 
01993     return false;
01994 }
01995 
01996 void Actor::setCollisionMode(CollisionMode mode) {
01997     _collisionMode = mode;
01998 }
01999 
02000 void Actor::setCollisionScale(float scale) {
02001     _collisionScale = scale;
02002 }
02003 
02004 Math::Vector3d Actor::handleCollisionTo(const Math::Vector3d &from, const Math::Vector3d &pos) const {
02005     if (_collisionMode == CollisionOff) {
02006         return pos;
02007     }
02008 
02009     Math::Vector3d p = pos;
02010     Math::Vector3d moveVec = pos - _pos;
02011     foreach (Actor *a, Actor::getPool()) {
02012         if (a != this && a->isInSet(_setName) && a->isVisible()) {
02013             p = a->getTangentPos(from, p);
02014             handleCollisionWith(a, _collisionMode, &moveVec);
02015         }
02016     }
02017     return p;
02018 }
02019 
02020 Math::Vector3d Actor::getTangentPos(const Math::Vector3d &pos, const Math::Vector3d &dest) const {
02021     if (_collisionMode == CollisionOff) {
02022         return dest;
02023     }
02024 
02025     Math::Vector3d p;
02026     float size;
02027     if (!getSphereInfo(false, size, p))
02028         return dest;
02029     Math::Vector2d p1(pos.x(), pos.y());
02030     Math::Vector2d p2(dest.x(), dest.y());
02031     Math::Segment2d segment(p1, p2);
02032 
02033     // TODO: collision with Box
02034 //  if (_collisionMode == CollisionSphere) {
02035     Math::Vector2d center(p.x(), p.y());
02036 
02037     Math::Vector2d inter;
02038     float distance = segment.getLine().getDistanceTo(center, &inter);
02039 
02040     if (distance < size && segment.containsPoint(inter)) {
02041         Math::Vector2d v(inter - center);
02042         v.normalize();
02043         v *= size;
02044         v += center;
02045 
02046         return Math::Vector3d(v.getX(), v.getY(), dest.z());
02047     }
02048 //  } else {
02049 
02050 //  }
02051 
02052     return dest;
02053 }
02054 
02055 void Actor::setLocalAlpha(unsigned int vertex, float alpha) {
02056     if (vertex >= _localAlpha.size()) {
02057         _localAlpha.resize(MAX(MAX_LOCAL_ALPHA_VERTICES, vertex + 1));
02058     }
02059     _localAlpha[vertex] = alpha;
02060 }
02061 
02062 void Actor::setLocalAlphaMode(unsigned int vertex, AlphaMode alphaMode) {
02063     if (vertex >= _localAlphaMode.size()) {
02064         _localAlphaMode.resize(MAX(MAX_LOCAL_ALPHA_VERTICES, vertex + 1));
02065     }
02066     _localAlphaMode[vertex] = alphaMode;
02067 }
02068 
02069 bool Actor::hasLocalAlpha() const {
02070     return !_localAlphaMode.empty();
02071 }
02072 
02073 float Actor::getLocalAlpha(unsigned int vertex) const {
02074     if (vertex < _localAlphaMode.size() && vertex < _localAlpha.size() && _localAlphaMode[vertex] == Actor::AlphaReplace) {
02075         return _localAlpha[vertex];
02076     } else {
02077         return 1.0f;
02078     }
02079 }
02080 
02081 void Actor::getBBoxInfo(Math::Vector3d &bboxPos, Math::Vector3d &bboxSize) const {
02082     if (g_grim->getGameType() == GType_MONKEY4) {
02083         EMICostume *costume = static_cast<EMICostume *>(getCurrentCostume());
02084         if (!costume) {
02085             bboxPos = Math::Vector3d(0, 0, 0);
02086             bboxSize = Math::Vector3d(0, 0, 0);
02087             return;
02088         }
02089         EMIModel *model = costume->getEMIModel();
02090         bboxPos = *model->_center;
02091         bboxSize = *model->_boxData2 - *model->_boxData;
02092     } else {
02093         Model *model = getCurrentCostume()->getModel();
02094         bboxPos = model->_bboxPos;
02095         bboxSize = model->_bboxSize;
02096     }
02097 }
02098 
02099 bool Actor::getSphereInfo(bool adjustZ, float &size, Math::Vector3d &p) const {
02100     if (g_grim->getGameType() == GType_MONKEY4) {
02101         EMICostume *costume = static_cast<EMICostume *>(getCurrentCostume());
02102         if (!costume) {
02103             Debug::warning(Debug::Actors, "Actor::getSphereInfo: actor \"%s\" has no costume", getName().c_str());
02104             return false;
02105         }
02106         EMIModel *model = costume->getEMIModel();
02107         assert(model);
02108         p = _pos + *(model->_center);
02109         // pre-scale factor of 0.8 was guessed by comparing with the original game
02110         size = model->_radius * _collisionScale * 0.8f;
02111     } else {
02112         Model *model = getCurrentCostume()->getModel();
02113         assert(model);
02114 
02115         p = _pos + model->_insertOffset;
02116         // center the sphere on the model center.
02117         if (adjustZ) {
02118             p.z() += model->_bboxSize.z() / 2.f;
02119         }
02120         size = model->_radius * _collisionScale;
02121     }
02122     return true;
02123 }
02124 
02125 bool Actor::handleCollisionWith(Actor *actor, CollisionMode mode, Math::Vector3d *vec) const {
02126     if (actor->_collisionMode == CollisionOff || actor == this) {
02127         return false;
02128     }
02129 
02130     if (!actor->getCurrentCostume()) {
02131         return false;
02132     }
02133 
02134     Math::Vector3d p1, p2;
02135     float size1, size2;
02136     // you may ask: why center the sphere of the first actor only (by setting adjustZ to true)?
02137     // because it seems the original does so.
02138     // if you change this code test this places: the rocks in lb and bv (both when booting directly in the
02139     // set and when coming in from another one) and the poles in xb.
02140     if (!this->getSphereInfo(true, size1, p1) || 
02141         !actor->getSphereInfo(false, size2, p2)) {
02142         return false;
02143     }
02144 
02145     CollisionMode mode1 = mode;
02146     CollisionMode mode2 = actor->_collisionMode;
02147 
02148     if (mode1 == CollisionSphere && mode2 == CollisionSphere) {
02149         Math::Vector3d pos = p1 + *vec;
02150         float distance = (pos - p2).getMagnitude();
02151         if (distance < size1 + size2) {
02152             // Move the destination point so that its distance from the
02153             // center of the circle is size1+size2.
02154             Math::Vector3d v = pos - p2;
02155             v.normalize();
02156             v *= size1 + size2;
02157             *vec = v + p2 - p1;
02158 
02159             collisionHandlerCallback(actor);
02160             return true;
02161         }
02162     } else if (mode1 == CollisionBox && mode2 == CollisionBox) {
02163         warning("Collision between box and box not implemented!");
02164         return false;
02165     } else {
02166         Math::Vector3d bboxSize1, bboxSize2;
02167         Math::Vector3d bboxPos1, bboxPos2;
02168 
02169         // get bboxSize and bboxPos for the current and the colliding actor
02170         this->getBBoxInfo(bboxPos1, bboxSize1);
02171         actor->getBBoxInfo(bboxPos2, bboxSize2);
02172 
02173         Math::Rect2d rect;
02174 
02175         Math::Vector3d bboxPos;
02176         Math::Vector3d size;
02177         float scale;
02178         Math::Vector3d pos;
02179         Math::Vector3d circlePos;
02180         Math::Angle yaw;
02181 
02182         Math::Vector2d circle;
02183         float radius;
02184 
02185         if (mode1 == CollisionBox) {
02186             pos = p1 + *vec;
02187             bboxPos = pos + bboxPos1;
02188             size =  bboxSize1;
02189             scale = _collisionScale;
02190             yaw = _yaw;
02191 
02192             circle.setX(p2.x());
02193             circle.setY(p2.y());
02194             circlePos = p2;
02195             radius = size2;
02196         } else {
02197             pos = p2;
02198             bboxPos = p2  + bboxPos2;
02199             size = bboxSize2;
02200             scale = actor->_collisionScale;
02201             yaw = actor->_yaw;
02202 
02203             circle.setX(p1.x() + vec->x());
02204             circle.setY(p1.y() + vec->y());
02205             circlePos = p1;
02206             radius = size1;
02207         }
02208 
02209         rect._topLeft = Math::Vector2d(bboxPos.x(), bboxPos.y() + size.y());
02210         rect._topRight = Math::Vector2d(bboxPos.x() + size.x(), bboxPos.y() + size.y());
02211         rect._bottomLeft = Math::Vector2d(bboxPos.x(), bboxPos.y());
02212         rect._bottomRight = Math::Vector2d(bboxPos.x() + size.x(), bboxPos.y());
02213 
02214         rect.scale(scale);
02215         rect.rotateAround(Math::Vector2d(pos.x(), pos.y()), yaw);
02216 
02217         if (rect.intersectsCircle(circle, radius)) {
02218             Math::Vector2d center = rect.getCenter();
02219             // Draw a line from the center of the rect to the place the character
02220             // would go to.
02221             Math::Vector2d v = circle - center;
02222             v.normalize();
02223 
02224             Math::Segment2d edge;
02225             // That line intersects (usually) an edge
02226             rect.getIntersection(center, v, &edge);
02227             // Take the perpendicular of that edge
02228             Math::Line2d perpendicular = edge.getPerpendicular(circle);
02229 
02230             Math::Vector3d point;
02231             Math::Vector2d p;
02232             // If that perpendicular intersects the edge
02233             if (edge.intersectsLine(perpendicular, &p)) {
02234                 Math::Vector2d direction = perpendicular.getDirection();
02235                 direction.normalize();
02236 
02237                 // Move from the intersection until we are at a safe distance
02238                 Math::Vector2d point1(p - direction * radius);
02239                 Math::Vector2d point2(p + direction * radius);
02240 
02241                 if (center.getDistanceTo(point1) < center.getDistanceTo(point2)) {
02242                     point = point2.toVector3d();
02243                 } else {
02244                     point = point1.toVector3d();
02245                 }
02246             } else { //if not we're around a corner
02247                 // Find the nearest vertex of the rect
02248                 Math::Vector2d vertex = rect.getTopLeft();
02249                 float distance = vertex.getDistanceTo(circle);
02250 
02251                 Math::Vector2d other = rect.getTopRight();
02252                 float otherDist = other.getDistanceTo(circle);
02253                 if (otherDist < distance) {
02254                     distance = otherDist;
02255                     vertex = other;
02256                 }
02257 
02258                 other = rect.getBottomLeft();
02259                 otherDist = other.getDistanceTo(circle);
02260                 if (otherDist < distance) {
02261                     distance = otherDist;
02262                     vertex = other;
02263                 }
02264 
02265                 other = rect.getBottomRight();
02266                 if (other.getDistanceTo(circle) < distance) {
02267                     vertex = other;
02268                 }
02269 
02270                 // and move on a circle around it
02271                 Math::Vector2d dst = circle - vertex;
02272                 dst.normalize();
02273                 dst = dst * radius;
02274                 point = (vertex + dst).toVector3d();
02275             }
02276 
02277             float z = vec->z();
02278             *vec = point - circlePos;
02279             vec->z() = z;
02280             collisionHandlerCallback(actor);
02281             return true;
02282         }
02283     }
02284 
02285     return false;
02286 }
02287 
02288 void Actor::costumeMarkerCallback(int marker) {
02289     LuaObjects objects;
02290     objects.add(this);
02291     objects.add(marker);
02292 
02293     LuaBase::instance()->callback("costumeMarkerHandler", objects);
02294 }
02295 
02296 void Actor::collisionHandlerCallback(Actor *other) const {
02297     LuaObjects objects;
02298     objects.add(this);
02299     objects.add(other);
02300 
02301     LuaBase::instance()->callback("collisionHandler", objects);
02302 
02303     LuaObjects objects2;
02304     objects2.add(other);
02305     objects2.add(this);
02306     LuaBase::instance()->callback("collisionHandler", objects2);
02307 }
02308 
02309 const Math::Matrix4 Actor::getFinalMatrix() const {
02310     // Defaults to identity (no rotation)
02311     Math::Matrix4 m;
02312 
02313     // Add the rotation and translation from the parent actor, if this actor is attached
02314     if (isAttached()) {
02315         Actor *attachedActor = Actor::getPool().getObject(_attachedActor);
02316         m = attachedActor->getFinalMatrix();
02317 
02318         // If this actor is attached to a joint, add that rotation
02319         EMICostume *cost = static_cast<EMICostume *>(attachedActor->getCurrentCostume());
02320         if (cost && cost->_emiSkel && cost->_emiSkel->_obj) {
02321             Joint *j = cost->_emiSkel->_obj->getJointNamed(_attachedJoint);
02322             m = m * j->_finalMatrix;
02323         }
02324     }
02325 
02326     // Translate by this actor's position
02327     Math::Vector3d pp = getPos();
02328     Math::Matrix4 t;
02329     t.setToIdentity();
02330     t.setPosition(pp);
02331     m = m * t;
02332 
02333     // Scaling could be applied here as follows:
02334     //
02335     // const float &scale = getScale();
02336     // t.setToIdentity();
02337     // t.setValue(3, 3, 1.0 / scale);
02338     // m = m * t;
02339     //
02340     // However, actor's _scale is only changed via the lua call SetActorScale
02341     // which is not used in EMI. Actor::getFinalMatrix() is only used for EMI
02342     // so the additional scaling can be omitted.
02343 
02344     // Finish with this actor's rotation
02345     Math::Matrix4 rotMat(getRoll(), getYaw(), getPitch(), Math::EO_ZYX);
02346     m = m * rotMat;
02347     return  m;
02348 }
02349 
02350 Math::Vector3d Actor::getWorldPos() const {
02351     if (! isAttached())
02352         return getPos();
02353 
02354     return getFinalMatrix().getPosition();
02355 }
02356 
02357 Math::Quaternion Actor::getRotationQuat() const {
02358     if (g_grim->getGameType() == GType_MONKEY4) {
02359         const Math::Matrix4 m = getFinalMatrix();
02360         return Math::Quaternion(m).inverse();
02361     } else {
02362         return Math::Quaternion::fromEuler(_yaw, _pitch, _roll, Math::EO_ZXY).inverse();
02363     }
02364 }
02365 
02366 Math::Vector3d Actor::getHeadPos() const {
02367     if (g_grim->getGameType() == GType_GRIM) {
02368         for (Common::List<Costume *>::const_iterator i = _costumeStack.begin(); i != _costumeStack.end(); ++i) {
02369             int headJoint = (*i)->getHeadJoint();
02370             if (headJoint == -1)
02371                 continue;
02372 
02373             ModelNode *allNodes = (*i)->getModelNodes();
02374             ModelNode *node = allNodes + headJoint;
02375 
02376             node->_needsUpdate = true;
02377             ModelNode *root = node;
02378             while (root->_parent) {
02379                 root = root->_parent;
02380                 root->_needsUpdate = true;
02381             }
02382 
02383             Math::Matrix4 matrix;
02384             matrix.setPosition(_pos);
02385             matrix.buildFromEuler(_yaw, _pitch, _roll, Math::EO_ZXY);
02386             root->setMatrix(matrix);
02387             root->update();
02388 
02389             return node->_pivotMatrix.getPosition();
02390         }
02391     }
02392 
02393     return getWorldPos();
02394 }
02395 
02396 void Actor::setSortOrder(const int order) {
02397     _sortOrder = order;
02398 
02399     // If this actor is attached to another actor, we'll use the
02400     // explicitly specified sort order instead of the parent actor's
02401     // sort order from now on.
02402     if (_useParentSortOrder)
02403         _useParentSortOrder = false;
02404 }
02405 
02406 int Actor::getSortOrder() const {
02407     return _sortOrder;
02408 }
02409 
02410 int Actor::getEffectiveSortOrder() const {
02411     if (_useParentSortOrder && _attachedActor != 0) {
02412         Actor *attachedActor = Actor::getPool().getObject(_attachedActor);
02413         return attachedActor->getEffectiveSortOrder();
02414     }
02415     return _sectorSortOrder >= 0 ? _sectorSortOrder : getSortOrder();
02416 }
02417 
02418 void Actor::activateShadow(bool active, const char *shadowName) {
02419     Set *set = g_grim->getCurrSet();
02420     if (!set) {
02421         warning("Actor %s trying to activate shadow to null Set", getName().c_str());
02422         return;
02423     }
02424     if (!shadowName) {
02425         for (int i = 0; i < set->getShadowCount(); ++i) {
02426             activateShadow(active, set->getShadow(i));
02427         }
02428     } else {
02429         SetShadow *shadow = set->getShadowByName(shadowName);
02430         if (shadow)
02431             activateShadow(active, shadow);
02432     }
02433 }
02434 
02435 void Actor::activateShadow(bool active, SetShadow *setShadow) {
02436     int shadowId = -1;
02437     for (int i = 0; i < MAX_SHADOWS; i++) {
02438         if (setShadow->_name.equals(_shadowArray[i].name)) {
02439             shadowId = i;
02440             break;
02441         }
02442     }
02443 
02444     if (shadowId == -1) {
02445         for (int i = 0; i < MAX_SHADOWS; i++) {
02446             if (!_shadowArray[i].active) {
02447                 shadowId = i;
02448                 break;
02449             }
02450         }
02451     }
02452 
02453     if (shadowId == -1) {
02454         warning("Actor %s trying to activate shadow %s, but all shadow slots are in use", getName().c_str(), setShadow->_name.c_str());
02455         return;
02456     }
02457 
02458     clearShadowPlane(shadowId);
02459     setActivateShadow(shadowId, active);
02460 
02461     if (active) {
02462         setActiveShadow(shadowId);
02463         setShadowPoint(setShadow->_shadowPoint);
02464         setShadowPlane(setShadow->_name.c_str());
02465         setShadowColor(setShadow->_color);
02466         setShadowValid(-1); // Don't negate the normal.
02467 
02468         Common::List<Common::String>::iterator it;
02469         for (it = setShadow->_sectorNames.begin(); it != setShadow->_sectorNames.end(); ++it) {
02470             addShadowPlane((*it).c_str(), g_grim->getCurrSet(), shadowId);
02471         }
02472     }
02473 }
02474 
02475 void Actor::attachToActor(Actor *parent, const char *joint) {
02476     assert(parent != nullptr);
02477     // No need to attach if we're already attached to this parent
02478     if (parent->getId() == _attachedActor)
02479         return;
02480     // If we're attached to a different parent, detach first
02481     if (_attachedActor != 0)
02482         detach();
02483 
02484     // Find the new rotation relative to the parent actor's rotation
02485     // Note: Any joint rotation is a part of the parent actor's rotation Quat
02486     Math::Quaternion newRot = getRotationQuat().inverse() * parent->getRotationQuat();
02487 
02488     // Find the new position coordinates
02489     Math::Matrix4 parentMatrix = parent->getFinalMatrix();
02490 
02491     // If the parent has a skeleton, check if it has the requested joint
02492     // Some models (pile o' boulders) don't have a skeleton
02493     Common::String jointStr = joint ? joint : "";
02494     EMICostume *cost = static_cast<EMICostume *>(parent->getCurrentCostume());
02495     if (cost && cost->_emiSkel && cost->_emiSkel->_obj) {
02496         assert(cost->_emiSkel->_obj->hasJoint(jointStr));
02497 
02498         // Add the rotation from the attached actor's joint
02499         Joint *j = cost->_emiSkel->_obj->getJointNamed(_attachedJoint);
02500         newRot = newRot.inverse() * j->_finalQuat;
02501 
02502         // Get the final position coordinates
02503         _pos = _pos - j->_finalMatrix.getPosition();
02504         j->_finalMatrix.transpose();
02505         j->_finalMatrix.transform(&_pos, true);
02506     }
02507 
02508     // Get the final rotation euler coordinates
02509     newRot.getEuler(&_roll, &_yaw, &_pitch, Math::EO_ZYX);
02510 
02511     // Get the final position coordinates
02512     _pos = _pos - parentMatrix.getPosition();
02513     parentMatrix.transpose();
02514     parentMatrix.transform(&_pos, true);
02515 
02516     // Save the attachement info
02517     _attachedActor = parent->getId();
02518     _attachedJoint = jointStr;
02519 
02520     // Use the parent actor's sort order.
02521     _useParentSortOrder = true;
02522 }
02523 
02524 void Actor::detach() {
02525     if (!isAttached())
02526         return;
02527 
02528     // Replace our sort order with the parent actor's sort order. Note
02529     // that we do this even if a sort order was explicitly specified for
02530     // the actor during the time it was attached (in which case the actor
02531     // doesn't respect the parent actor's sort order). This seems weird,
02532     // but matches the behavior of the original engine.
02533     Actor *attachedActor = Actor::getPool().getObject(_attachedActor);
02534     _sortOrder = attachedActor->getEffectiveSortOrder();
02535     _useParentSortOrder = false;
02536 
02537     // Position and rotate the actor in relation to the world coords
02538     setPos(getWorldPos());
02539     Math::Quaternion q = getRotationQuat();
02540     q.inverse().getEuler(&_roll, &_yaw, &_pitch, Math::EO_ZYX);
02541 
02542     // Remove the attached actor
02543     _attachedActor = 0;
02544     _attachedJoint = "";
02545 }
02546 
02547 void Actor::drawToCleanBuffer() {
02548     _drawnToClean = true;
02549 }
02550 
02551 MaterialPtr Actor::findMaterial(const Common::String &name) {
02552     Common::String fixedName = g_resourceloader->fixFilename(name, false);
02553     Common::List<MaterialPtr>::iterator it = _materials.begin();
02554     for (; it != _materials.end(); ++it) {
02555         if (*it) {
02556             if ((*it)->getFilename() == fixedName) {
02557                 return *it;
02558             }
02559         } else {
02560             it = _materials.erase(it);
02561             --it;
02562         }
02563     }
02564     return (MaterialPtr)nullptr;
02565 }
02566 
02567 MaterialPtr Actor::loadMaterial(const Common::String &name, bool clamp) {
02568     MaterialPtr mat = findMaterial(name);
02569     if (!mat) {
02570         mat = g_resourceloader->loadMaterial(name.c_str(), nullptr, clamp);
02571         // Note: We store a weak reference.
02572         _materials.push_back(mat);
02573         mat->dereference();
02574     }
02575     return mat;
02576 }
02577 
02578 unsigned const int Actor::ActionChore::fadeTime = 150;
02579 unsigned const int Actor::ActionChore::talkFadeTime = 50;
02580 
02581 Actor::ActionChore::ActionChore() :
02582     _costume(nullptr),
02583     _chore(-1) {
02584 
02585 }
02586 
02587 Actor::ActionChore::ActionChore(Costume *cost, int chore) :
02588     _costume(cost),
02589     _chore(chore) {
02590 
02591 }
02592 
02593 void Actor::ActionChore::play(bool fade, unsigned int time) {
02594     if (isValid()) {
02595         if (fade) {
02596             _costume->playChore(_chore, time);
02597         } else {
02598             _costume->playChore(_chore);
02599         }
02600     }
02601 }
02602 
02603 void Actor::ActionChore::playLooping(bool fade, unsigned int time) {
02604     if (isValid()) {
02605         if (fade) {
02606             _costume->playChoreLooping(_chore, time);
02607         } else {
02608             _costume->playChoreLooping(_chore);
02609         }
02610     }
02611 }
02612 
02613 void Actor::ActionChore::stop(bool fade, unsigned int time) {
02614     if (isValid()) {
02615         if (fade) {
02616             _costume->stopChore(_chore, time);
02617         } else {
02618             _costume->stopChore(_chore);
02619         }
02620     }
02621 }
02622 
02623 void Actor::ActionChore::setLastFrame() {
02624     if (isValid()) {
02625         _costume->setChoreLastFrame(_chore);
02626     }
02627 }
02628 
02629 bool Actor::ActionChore::isPlaying() const {
02630     return (isValid() && _costume->isChoring(_chore, false) >= 0);
02631 }
02632 
02633 void Actor::ActionChore::saveState(SaveGame *savedState) const {
02634     if (_costume) {
02635         savedState->writeBool(true);
02636         savedState->writeString(_costume->getFilename());
02637     } else {
02638         savedState->writeBool(false);
02639     }
02640     savedState->writeLESint32(_chore);
02641 }
02642 
02643 void Actor::ActionChore::restoreState(SaveGame *savedState, Actor *actor) {
02644     if (savedState->readBool()) {
02645         Common::String fname = savedState->readString();
02646         _costume = actor->findCostume(fname);
02647     } else {
02648         _costume = nullptr;
02649     }
02650     _chore = savedState->readLESint32();
02651 }
02652 
02653 } // end of namespace Grim


Generated on Sat Apr 20 2019 05:01:54 for ResidualVM by doxygen 1.7.1
curved edge   curved edge