mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-14 21:59:17 +00:00
06f934dc9f
This has been added in commit 5624ba23d0
and is no longer needed.
The gem is shown correctly over the compact disk in that scene,
and the behavior is the same as the original
1272 lines
39 KiB
C++
1272 lines
39 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
#include "saga/saga.h"
|
|
|
|
#include "saga/actor.h"
|
|
#include "saga/animation.h"
|
|
#include "saga/console.h"
|
|
#include "saga/events.h"
|
|
#include "saga/isomap.h"
|
|
#include "saga/objectmap.h"
|
|
#include "saga/resource.h"
|
|
#include "saga/script.h"
|
|
#include "saga/sndres.h"
|
|
#include "saga/sound.h"
|
|
#include "saga/scene.h"
|
|
|
|
#include "common/config-manager.h"
|
|
|
|
namespace Saga {
|
|
|
|
ActorData::ActorData() {
|
|
_frames = NULL;
|
|
_frameListResourceId = 0;
|
|
_speechColor = 0;
|
|
_inScene = false;
|
|
|
|
_actorFlags = 0;
|
|
_currentAction = 0;
|
|
_facingDirection = 0;
|
|
_actionDirection = 0;
|
|
_actionCycle = 0;
|
|
_targetObject = 0;
|
|
_lastZone = NULL;
|
|
|
|
_cycleFrameSequence = 0;
|
|
_cycleDelay = 0;
|
|
_cycleTimeCount = 0;
|
|
_cycleFlags = 0;
|
|
|
|
_fallVelocity = 0;
|
|
_fallAcceleration = 0;
|
|
_fallPosition = 0;
|
|
|
|
_dragonBaseFrame = 0;
|
|
_dragonStepCycle = 0;
|
|
_dragonMoveType = 0;
|
|
|
|
_frameNumber = 0;
|
|
|
|
_walkStepsCount = 0;
|
|
_walkStepIndex = 0;
|
|
|
|
_walkFrameSequence = 0;
|
|
}
|
|
|
|
void ActorData::saveState(Common::OutSaveFile *out) {
|
|
uint i = 0;
|
|
CommonObjectData::saveState(out);
|
|
out->writeUint16LE(_actorFlags);
|
|
out->writeSint32LE(_currentAction);
|
|
out->writeSint32LE(_facingDirection);
|
|
out->writeSint32LE(_actionDirection);
|
|
out->writeSint32LE(_actionCycle);
|
|
out->writeUint16LE(_targetObject);
|
|
|
|
out->writeSint32LE(_cycleFrameSequence);
|
|
out->writeByte(_cycleDelay);
|
|
out->writeByte(_cycleTimeCount);
|
|
out->writeByte(_cycleFlags);
|
|
out->writeSint16LE(_fallVelocity);
|
|
out->writeSint16LE(_fallAcceleration);
|
|
out->writeSint16LE(_fallPosition);
|
|
out->writeByte(_dragonBaseFrame);
|
|
out->writeByte(_dragonStepCycle);
|
|
out->writeByte(_dragonMoveType);
|
|
out->writeSint32LE(_frameNumber);
|
|
|
|
out->writeSint32LE(_tileDirections.size());
|
|
for (i = 0; i < _tileDirections.size(); i++) {
|
|
out->writeByte(_tileDirections[i]);
|
|
}
|
|
|
|
out->writeSint32LE(_walkStepsPoints.size());
|
|
for (i = 0; i < _walkStepsPoints.size(); i++) {
|
|
out->writeSint16LE(_walkStepsPoints[i].x);
|
|
out->writeSint16LE(_walkStepsPoints[i].y);
|
|
}
|
|
|
|
out->writeSint32LE(_walkStepsCount);
|
|
out->writeSint32LE(_walkStepIndex);
|
|
_finalTarget.saveState(out);
|
|
_partialTarget.saveState(out);
|
|
out->writeSint32LE(_walkFrameSequence);
|
|
}
|
|
|
|
void ActorData::loadState(uint32 version, Common::InSaveFile *in) {
|
|
uint i = 0;
|
|
CommonObjectData::loadState(in);
|
|
_actorFlags = in->readUint16LE();
|
|
_currentAction = in->readSint32LE();
|
|
_facingDirection = in->readSint32LE();
|
|
_actionDirection = in->readSint32LE();
|
|
_actionCycle = in->readSint32LE();
|
|
_targetObject = in->readUint16LE();
|
|
|
|
_lastZone = NULL;
|
|
_cycleFrameSequence = in->readSint32LE();
|
|
_cycleDelay = in->readByte();
|
|
_cycleTimeCount = in->readByte();
|
|
_cycleFlags = in->readByte();
|
|
if (version > 1) {
|
|
_fallVelocity = in->readSint16LE();
|
|
_fallAcceleration = in->readSint16LE();
|
|
_fallPosition = in->readSint16LE();
|
|
} else {
|
|
_fallVelocity = _fallAcceleration = _fallPosition = 0;
|
|
}
|
|
if (version > 2) {
|
|
_dragonBaseFrame = in->readByte();
|
|
_dragonStepCycle = in->readByte();
|
|
_dragonMoveType = in->readByte();
|
|
} else {
|
|
_dragonBaseFrame = _dragonStepCycle = _dragonMoveType = 0;
|
|
}
|
|
|
|
_frameNumber = in->readSint32LE();
|
|
|
|
|
|
_tileDirections.resize(in->readSint32LE());
|
|
for (i = 0; i < _tileDirections.size(); i++) {
|
|
_tileDirections[i] = in->readByte();
|
|
}
|
|
|
|
_walkStepsPoints.resize(in->readSint32LE());
|
|
for (i = 0; i < _walkStepsPoints.size(); i++) {
|
|
_walkStepsPoints[i].x = in->readSint16LE();
|
|
_walkStepsPoints[i].y = in->readSint16LE();
|
|
}
|
|
|
|
_walkStepsCount = in->readSint32LE();
|
|
_walkStepIndex = in->readSint32LE();
|
|
_finalTarget.loadState(in);
|
|
_partialTarget.loadState(in);
|
|
_walkFrameSequence = in->readSint32LE();
|
|
}
|
|
|
|
void ActorData::cycleWrap(int cycleLimit) {
|
|
if (_actionCycle >= cycleLimit)
|
|
_actionCycle = 0;
|
|
}
|
|
|
|
void ActorData::addWalkStepPoint(const Point &point) {
|
|
_walkStepsPoints.resize(_walkStepsCount + 1);
|
|
_walkStepsPoints[_walkStepsCount++] = point;
|
|
}
|
|
|
|
static int commonObjectCompare(const CommonObjectDataPointer& obj1, const CommonObjectDataPointer& obj2) {
|
|
int p1 = obj1->_location.y - obj1->_location.z;
|
|
int p2 = obj2->_location.y - obj2->_location.z;
|
|
if (p1 == p2)
|
|
return 0;
|
|
if (p1 < p2)
|
|
return -1;
|
|
return 1;
|
|
}
|
|
|
|
#ifdef ENABLE_IHNM
|
|
static int commonObjectCompareIHNM(const CommonObjectDataPointer& obj1, const CommonObjectDataPointer& obj2) {
|
|
int p1 = obj1->_location.y;
|
|
int p2 = obj2->_location.y;
|
|
if (p1 == p2)
|
|
return 0;
|
|
if (p1 < p2)
|
|
return -1;
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
static int tileCommonObjectCompare(const CommonObjectDataPointer& obj1, const CommonObjectDataPointer& obj2) {
|
|
int p1 = -obj1->_location.u() - obj1->_location.v() - obj1->_location.z;
|
|
int p2 = -obj2->_location.u() - obj2->_location.v() - obj2->_location.z;
|
|
//TODO: for kObjNotFlat obj Height*3 of sprite should be added to p1 and p2
|
|
//if (validObjId(obj1->id)) {
|
|
|
|
if (p1 == p2)
|
|
return 0;
|
|
if (p1 < p2)
|
|
return -1;
|
|
return 1;
|
|
}
|
|
|
|
Actor::Actor(SagaEngine *vm) : _vm(vm) {
|
|
int i;
|
|
ByteArray stringsData;
|
|
debug(9, "Actor::Actor()");
|
|
_handleActionDiv = 15;
|
|
|
|
#ifdef ACTOR_DEBUG
|
|
_debugPointsCount = 0;
|
|
#endif
|
|
|
|
_pathList.resize(600);
|
|
_pathListIndex = 0;
|
|
|
|
_centerActor = _protagonist = NULL;
|
|
_protagState = 0;
|
|
_lastTickMsec = 0;
|
|
|
|
_yCellCount = _vm->_scene->getHeight();
|
|
_xCellCount = _vm->getDisplayInfo().width;
|
|
|
|
_pathCell.resize(_yCellCount * _xCellCount);
|
|
|
|
_pathRect.left = 0;
|
|
_pathRect.right = _vm->getDisplayInfo().width;
|
|
_pathRect.top = _vm->getDisplayInfo().pathStartY;
|
|
_pathRect.bottom = _vm->_scene->getHeight();
|
|
|
|
// Get actor resource file context
|
|
_actorContext = _vm->_resource->getContext(GAME_RESOURCEFILE);
|
|
if (_actorContext == NULL) {
|
|
error("Actor::Actor() resource context not found");
|
|
}
|
|
|
|
// Load ITE actor strings. (IHNM actor strings are loaded by
|
|
// loadGlobalResources() instead.)
|
|
|
|
if (_vm->getGameId() == GID_ITE) {
|
|
|
|
_vm->_resource->loadResource(_actorContext, _vm->getResourceDescription()->actorsStringsResourceId, stringsData);
|
|
|
|
_vm->loadStrings(_actorsStrings, stringsData);
|
|
}
|
|
|
|
if (_vm->getGameId() == GID_ITE) {
|
|
_actors.resize(ITE_ACTORCOUNT);
|
|
i = 0;
|
|
for (ActorDataArray::iterator actor = _actors.begin(); actor != _actors.end(); ++actor, i++) {
|
|
actor->_index = i;
|
|
actor->_id = actorIndexToId(actor->_index);
|
|
debug(9, "init actor id=%d index=%d", actor->_id, actor->_index);
|
|
actor->_nameIndex = ITE_ActorTable[i].nameIndex;
|
|
actor->_scriptEntrypointNumber = ITE_ActorTable[i].scriptEntrypointNumber;
|
|
actor->_spriteListResourceId = ITE_ActorTable[i].spriteListResourceId;
|
|
actor->_frameListResourceId = ITE_ActorTable[i].frameListResourceId;
|
|
actor->_speechColor = ITE_ActorTable[i].speechColor;
|
|
actor->_sceneNumber = ITE_ActorTable[i].sceneIndex;
|
|
actor->_flags = ITE_ActorTable[i].flags;
|
|
actor->_currentAction = ITE_ActorTable[i].currentAction;
|
|
actor->_facingDirection = ITE_ActorTable[i].facingDirection;
|
|
actor->_actionDirection = ITE_ActorTable[i].actionDirection;
|
|
|
|
actor->_location.x = ITE_ActorTable[i].x;
|
|
actor->_location.y = ITE_ActorTable[i].y;
|
|
actor->_location.z = ITE_ActorTable[i].z;
|
|
|
|
loadActorResources(actor);
|
|
}
|
|
_objs.resize(ITE_OBJECTCOUNT);
|
|
i = 0;
|
|
for (ObjectDataArray::iterator obj = _objs.begin(); obj != _objs.end(); ++obj, i++) {
|
|
obj->_index = i;
|
|
obj->_id = objIndexToId(obj->_index);
|
|
debug(9, "init obj id=%d index=%d", obj->_id, obj->_index);
|
|
obj->_nameIndex = ITE_ObjectTable[i].nameIndex;
|
|
obj->_scriptEntrypointNumber = ITE_ObjectTable[i].scriptEntrypointNumber;
|
|
obj->_spriteListResourceId = ITE_ObjectTable[i].spriteListResourceId;
|
|
obj->_sceneNumber = ITE_ObjectTable[i].sceneIndex;
|
|
obj->_interactBits = ITE_ObjectTable[i].interactBits;
|
|
|
|
obj->_location.x = ITE_ObjectTable[i].x;
|
|
obj->_location.y = ITE_ObjectTable[i].y;
|
|
obj->_location.z = ITE_ObjectTable[i].z;
|
|
}
|
|
}
|
|
|
|
_dragonHunt = true;
|
|
}
|
|
|
|
Actor::~Actor() {
|
|
debug(9, "Actor::~Actor()");
|
|
}
|
|
|
|
void Actor::loadFrameList(int frameListResourceId, ActorFrameSequences &frames) {
|
|
ByteArray resourceData;
|
|
|
|
debug(9, "Loading frame resource id %d", frameListResourceId);
|
|
_vm->_resource->loadResource(_actorContext, frameListResourceId, resourceData);
|
|
|
|
frames.resize(resourceData.size() / 16);
|
|
debug(9, "Frame resource contains %d frames (res length is %d)", frames.size(), (int)resourceData.size());
|
|
|
|
ByteArrayReadStreamEndian readS(resourceData, _actorContext->isBigEndian());
|
|
|
|
for (ActorFrameSequences::iterator frame = frames.begin(); frame != frames.end(); ++frame) {
|
|
for (int orient = 0; orient < ACTOR_DIRECTIONS_COUNT; orient++) {
|
|
// Load all four orientations
|
|
frame->directions[orient].frameIndex = readS.readUint16();
|
|
if (_vm->getGameId() == GID_ITE) {
|
|
frame->directions[orient].frameCount = readS.readSint16();
|
|
} else {
|
|
frame->directions[orient].frameCount = readS.readByte();
|
|
readS.readByte();
|
|
}
|
|
if (frame->directions[orient].frameCount < 0)
|
|
warning("frameCount < 0 (%d)", frame->directions[orient].frameCount);
|
|
debug(9, "frameIndex %d frameCount %d", frame->directions[orient].frameIndex, frame->directions[orient].frameCount);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Actor::loadActorResources(ActorData *actor) {
|
|
if (actor->_frameListResourceId) {
|
|
loadFrameList(actor->_frameListResourceId, actor->_framesContainer);
|
|
actor->_frames = &actor->_framesContainer;
|
|
}
|
|
}
|
|
|
|
void Actor::loadActorSpriteList(ActorData *actor) {
|
|
uint lastFrame = 0;
|
|
uint curFrameIndex;
|
|
int resourceId = actor->_spriteListResourceId;
|
|
|
|
if (actor->_frames != NULL) {
|
|
for (ActorFrameSequences::const_iterator i = actor->_frames->begin(); i != actor->_frames->end(); ++i) {
|
|
for (int orient = 0; orient < ACTOR_DIRECTIONS_COUNT; orient++) {
|
|
curFrameIndex = i->directions[orient].frameIndex;
|
|
if (curFrameIndex > lastFrame) {
|
|
lastFrame = curFrameIndex;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
debug(9, "Loading actor sprite resource id %d", resourceId);
|
|
|
|
_vm->_sprite->loadList(resourceId, actor->_spriteList);
|
|
|
|
if (_vm->getGameId() == GID_ITE) {
|
|
if (actor->_flags & kExtended) {
|
|
while ((lastFrame >= actor->_spriteList.size())) {
|
|
resourceId++;
|
|
debug(9, "Appending to actor sprite list %d", resourceId);
|
|
_vm->_sprite->loadList(resourceId, actor->_spriteList);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Actor::loadActorList(int protagonistIdx, int actorCount, int actorsResourceID, int protagStatesCount, int protagStatesResourceID) {
|
|
int i, j;
|
|
ByteArray actorListData;
|
|
byte walk[128];
|
|
byte acv[6];
|
|
int movementSpeed;
|
|
int walkStepIndex;
|
|
int walkStepCount;
|
|
int stateResourceId;
|
|
|
|
_vm->_resource->loadResource(_actorContext, actorsResourceID, actorListData);
|
|
|
|
if (actorListData.size() != (uint)actorCount * ACTOR_INHM_SIZE) {
|
|
error("Actor::loadActorList wrong actorlist length");
|
|
}
|
|
|
|
ByteArrayReadStreamEndian actorS(actorListData);
|
|
|
|
_actors.clear();
|
|
_actors.resize(actorCount);
|
|
i = 0;
|
|
for (ActorDataArray::iterator actor = _actors.begin(); actor != _actors.end(); ++actor, i++) {
|
|
actor->_index = i;
|
|
actor->_id = objectIndexToId(kGameObjectActor, actor->_index); //actorIndexToId(i);
|
|
debug(4, "init actor id=0x%X index=%d", actor->_id, actor->_index);
|
|
actorS.readUint32LE(); //next displayed
|
|
actorS.readByte(); //type
|
|
actor->_flags = actorS.readByte();
|
|
actor->_nameIndex = actorS.readUint16LE();
|
|
actor->_sceneNumber = actorS.readUint32LE();
|
|
actor->_location.fromStream(actorS);
|
|
actor->_screenPosition.x = actorS.readUint16LE();
|
|
actor->_screenPosition.y = actorS.readUint16LE();
|
|
actor->_screenScale = actorS.readUint16LE();
|
|
actor->_screenDepth = actorS.readUint16LE();
|
|
actor->_spriteListResourceId = actorS.readUint32LE();
|
|
actor->_frameListResourceId = actorS.readUint32LE();
|
|
debug(4, "%d: %d, %d [%d]", i, actor->_spriteListResourceId, actor->_frameListResourceId, actor->_nameIndex);
|
|
actor->_scriptEntrypointNumber = actorS.readUint32LE();
|
|
actorS.readUint32LE(); // xSprite *dSpr;
|
|
actorS.readUint16LE(); //LEFT
|
|
actorS.readUint16LE(); //RIGHT
|
|
actorS.readUint16LE(); //TOP
|
|
actorS.readUint16LE(); //BOTTOM
|
|
actor->_speechColor = actorS.readByte();
|
|
actor->_currentAction = actorS.readByte();
|
|
actor->_facingDirection = actorS.readByte();
|
|
actor->_actionDirection = actorS.readByte();
|
|
actor->_actionCycle = actorS.readUint16LE();
|
|
actor->_frameNumber = actorS.readUint16LE();
|
|
actor->_finalTarget.fromStream(actorS);
|
|
actor->_partialTarget.fromStream(actorS);
|
|
movementSpeed = actorS.readUint16LE(); //movement speed
|
|
if (movementSpeed) {
|
|
error("Actor::loadActorList movementSpeed != 0");
|
|
}
|
|
actorS.read(walk, 128);
|
|
for (j = 0; j < 128; j++) {
|
|
if (walk[j]) {
|
|
error("Actor::loadActorList walk[128] != 0");
|
|
}
|
|
}
|
|
//actorS.seek(128, SEEK_CUR);
|
|
walkStepCount = actorS.readByte();//walkStepCount
|
|
if (walkStepCount) {
|
|
error("Actor::loadActorList walkStepCount != 0");
|
|
}
|
|
walkStepIndex = actorS.readByte();//walkStepIndex
|
|
if (walkStepIndex) {
|
|
error("Actor::loadActorList walkStepIndex != 0");
|
|
}
|
|
//no need to check pointers
|
|
actorS.readUint32LE(); //sprites
|
|
actorS.readUint32LE(); //frames
|
|
actorS.readUint32LE(); //last zone
|
|
actor->_targetObject = actorS.readUint16LE();
|
|
actor->_actorFlags = actorS.readUint16LE();
|
|
//no need to check pointers
|
|
actorS.readUint32LE(); //next in scene
|
|
actorS.read(acv, 6);
|
|
for (j = 0; j < 6; j++) {
|
|
if (acv[j]) {
|
|
error("Actor::loadActorList acv[%d] != 0", j);
|
|
}
|
|
}
|
|
// actorS.seek(6, SEEK_CUR); //action vars
|
|
}
|
|
|
|
_actors[protagonistIdx]._flags |= kProtagonist | kExtended;
|
|
|
|
for (ActorDataArray::iterator actor = _actors.begin(); actor != _actors.end(); ++actor) {
|
|
//if (actor->_flags & kProtagonist) {
|
|
loadActorResources(actor);
|
|
//break;
|
|
//}
|
|
}
|
|
|
|
_centerActor = _protagonist = &_actors[protagonistIdx];
|
|
_protagState = 0;
|
|
|
|
if (protagStatesResourceID) {
|
|
_protagStates.resize(protagStatesCount);
|
|
|
|
ByteArray idsResourceData;
|
|
|
|
_vm->_resource->loadResource(_actorContext, protagStatesResourceID, idsResourceData);
|
|
|
|
if (idsResourceData.size() < (size_t)protagStatesCount * 4) {
|
|
error("Wrong protagonist states resource");
|
|
}
|
|
|
|
ByteArrayReadStreamEndian statesIds(idsResourceData);
|
|
|
|
for (i = 0; i < protagStatesCount; i++) {
|
|
stateResourceId = statesIds.readUint32LE();
|
|
|
|
loadFrameList(stateResourceId, _protagStates[i]._frames);
|
|
}
|
|
|
|
_protagonist->_frames = &_protagStates[_protagState]._frames;
|
|
}
|
|
|
|
}
|
|
|
|
void Actor::loadObjList(int objectCount, int objectsResourceID) {
|
|
uint i;
|
|
int frameListResourceId;
|
|
ByteArray objectListData;
|
|
|
|
_vm->_resource->loadResource(_actorContext, objectsResourceID, objectListData);
|
|
|
|
_objs.resize(objectCount);
|
|
|
|
ByteArrayReadStreamEndian objectS(objectListData);
|
|
|
|
i = 0;
|
|
for (ObjectDataArray::iterator object = _objs.begin(); object != _objs.end(); ++object, i++) {
|
|
object->_index = i;
|
|
object->_id = objectIndexToId(kGameObjectObject, object->_index);
|
|
debug(9, "init object id=%d index=%d", object->_id, object->_index);
|
|
objectS.readUint32LE(); //next displayed
|
|
objectS.readByte(); //type
|
|
object->_flags = objectS.readByte();
|
|
object->_nameIndex = objectS.readUint16LE();
|
|
object->_sceneNumber = objectS.readUint32LE();
|
|
object->_location.fromStream(objectS);
|
|
object->_screenPosition.x = objectS.readUint16LE();
|
|
object->_screenPosition.y = objectS.readUint16LE();
|
|
object->_screenScale = objectS.readUint16LE();
|
|
object->_screenDepth = objectS.readUint16LE();
|
|
object->_spriteListResourceId = objectS.readUint32LE();
|
|
frameListResourceId = objectS.readUint32LE(); // object->_frameListResourceId
|
|
if (frameListResourceId) {
|
|
error("Actor::loadObjList frameListResourceId != 0");
|
|
}
|
|
object->_scriptEntrypointNumber = objectS.readUint32LE();
|
|
objectS.readUint32LE(); // xSprite *dSpr;
|
|
objectS.readUint16LE(); //LEFT
|
|
objectS.readUint16LE(); //RIGHT
|
|
objectS.readUint16LE(); //TOP
|
|
objectS.readUint16LE(); //BOTTOM
|
|
object->_interactBits = objectS.readUint16LE();
|
|
}
|
|
}
|
|
|
|
void Actor::takeExit(uint16 actorId, const HitZone *hitZone) {
|
|
ActorData *actor;
|
|
actor = getActor(actorId);
|
|
actor->_lastZone = NULL;
|
|
|
|
_vm->_scene->changeScene(hitZone->getSceneNumber(), hitZone->getActorsEntrance(), kTransitionNoFade);
|
|
if (_vm->_interface->getMode() != kPanelSceneSubstitute) {
|
|
_vm->_script->setNoPendingVerb();
|
|
}
|
|
}
|
|
|
|
void Actor::stepZoneAction(ActorData *actor, const HitZone *hitZone, bool exit, bool stopped) {
|
|
Event event;
|
|
|
|
if (actor != _protagonist) {
|
|
return;
|
|
}
|
|
if (((hitZone->getFlags() & kHitZoneTerminus) && !stopped) || (!(hitZone->getFlags() & kHitZoneTerminus) && stopped)) {
|
|
return;
|
|
}
|
|
|
|
if (!exit) {
|
|
if (hitZone->getFlags() & kHitZoneAutoWalk) {
|
|
actor->_currentAction = kActionWalkDir;
|
|
actor->_actionDirection = actor->_facingDirection = hitZone->getDirection();
|
|
actor->_walkFrameSequence = getFrameType(kFrameWalk);
|
|
return;
|
|
}
|
|
} else if (!(hitZone->getFlags() & kHitZoneAutoWalk)) {
|
|
return;
|
|
}
|
|
if (hitZone->getFlags() & kHitZoneExit) {
|
|
takeExit(actor->_id, hitZone);
|
|
} else if (hitZone->getScriptNumber() > 0) {
|
|
event.type = kEvTOneshot;
|
|
event.code = kScriptEvent;
|
|
event.op = kEventExecNonBlocking;
|
|
event.time = 0;
|
|
event.param = _vm->_scene->getScriptModuleNumber(); // module number
|
|
event.param2 = hitZone->getScriptNumber(); // script entry point number
|
|
event.param3 = _vm->_script->getVerbType(kVerbEnter); // Action
|
|
event.param4 = ID_NOTHING; // Object
|
|
event.param5 = ID_NOTHING; // With Object
|
|
event.param6 = ID_PROTAG; // Actor
|
|
|
|
_vm->_events->queue(event);
|
|
}
|
|
}
|
|
|
|
ObjectData *Actor::getObj(uint16 objId) {
|
|
ObjectData *obj;
|
|
|
|
if (!validObjId(objId))
|
|
error("Actor::getObj Wrong objId 0x%X", objId);
|
|
|
|
obj = &_objs[objIdToIndex(objId)];
|
|
|
|
return obj;
|
|
}
|
|
|
|
ActorData *Actor::getActor(uint16 actorId) {
|
|
ActorData *actor;
|
|
|
|
if (!validActorId(actorId)) {
|
|
warning("Actor::getActor Wrong actorId 0x%X", actorId);
|
|
assert(0);
|
|
}
|
|
|
|
if (actorId == ID_PROTAG) {
|
|
if (_protagonist == NULL) {
|
|
error("_protagonist == NULL");
|
|
}
|
|
return _protagonist;
|
|
}
|
|
|
|
actor = &_actors[actorIdToIndex(actorId)];
|
|
|
|
return actor;
|
|
}
|
|
|
|
void Actor::setProtagState(int state) {
|
|
_protagState = state;
|
|
|
|
#ifdef ENABLE_IHNM
|
|
if (_vm->getGameId() == GID_IHNM) {
|
|
|
|
_protagonist->_frames = &_protagStates[state]._frames;
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
int Actor::getFrameType(ActorFrameTypes frameType) {
|
|
|
|
if (_vm->getGameId() == GID_ITE) {
|
|
switch (frameType) {
|
|
case kFrameStand:
|
|
return kFrameITEStand;
|
|
case kFrameWalk:
|
|
return kFrameITEWalk;
|
|
case kFrameSpeak:
|
|
return kFrameITESpeak;
|
|
case kFrameGive:
|
|
return kFrameITEGive;
|
|
case kFrameGesture:
|
|
return kFrameITEGesture;
|
|
case kFrameWait:
|
|
return kFrameITEWait;
|
|
case kFramePickUp:
|
|
return kFrameITEPickUp;
|
|
case kFrameLook:
|
|
return kFrameITELook;
|
|
}
|
|
#ifdef ENABLE_IHNM
|
|
} else if (_vm->getGameId() == GID_IHNM) {
|
|
switch (frameType) {
|
|
case kFrameStand:
|
|
return kFrameIHNMStand;
|
|
case kFrameWalk:
|
|
return kFrameIHNMWalk;
|
|
case kFrameSpeak:
|
|
return kFrameIHNMSpeak;
|
|
case kFrameGesture:
|
|
return kFrameIHNMGesture;
|
|
case kFrameWait:
|
|
return kFrameIHNMWait;
|
|
case kFrameGive:
|
|
case kFramePickUp:
|
|
case kFrameLook:
|
|
error("Actor::getFrameType() unknown frame type %d", frameType);
|
|
return kFrameIHNMStand; // for compilers that don't support NORETURN
|
|
}
|
|
#endif
|
|
}
|
|
error("Actor::getFrameType() unknown frame type %d", frameType);
|
|
}
|
|
|
|
ActorFrameRange *Actor::getActorFrameRange(uint16 actorId, int frameType) {
|
|
ActorData *actor;
|
|
int fourDirection;
|
|
static ActorFrameRange def = {0, 0};
|
|
|
|
actor = getActor(actorId);
|
|
|
|
if ((actor->_facingDirection < kDirUp) || (actor->_facingDirection > kDirUpLeft))
|
|
error("Actor::getActorFrameRange Wrong direction 0x%X actorId 0x%X", actor->_facingDirection, actorId);
|
|
|
|
ActorFrameSequences *frames;
|
|
frames = actor->_frames;
|
|
|
|
if (_vm->getGameId() == GID_ITE) {
|
|
if (uint(frameType) >= frames->size()) {
|
|
warning("Actor::getActorFrameRange Wrong frameType 0x%X (%d) actorId 0x%X", frameType, frames->size(), actorId);
|
|
return &def;
|
|
}
|
|
|
|
|
|
fourDirection = actorDirectionsLUT[actor->_facingDirection];
|
|
return &(*frames)[frameType].directions[fourDirection];
|
|
}
|
|
|
|
#ifdef ENABLE_IHNM
|
|
if (_vm->getGameId() == GID_IHNM) {
|
|
// It is normal for some actors to have no frames for a given frameType
|
|
// These are mainly actors with no frames at all (e.g. narrators or immovable actors)
|
|
// Examples are AM and the boy when he is talking to Benny via the computer screen.
|
|
// Both of them are invisible and immovable
|
|
// There is no point to keep throwing warnings about this, the original checks for
|
|
// a valid framecount too
|
|
if ((frames == NULL) || (frames->empty())) {
|
|
return &def;
|
|
}
|
|
frameType = CLIP(frameType, 0, int(frames->size() - 1));
|
|
fourDirection = actorDirectionsLUT[actor->_facingDirection];
|
|
return &(*frames)[frameType].directions[fourDirection];
|
|
}
|
|
#endif
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void Actor::handleSpeech(int msec) {
|
|
int stringLength;
|
|
int sampleLength;
|
|
bool removeFirst;
|
|
int i;
|
|
ActorData *actor;
|
|
int width, height, height2;
|
|
|
|
if (_activeSpeech.playing) {
|
|
_activeSpeech.playingTime -= msec;
|
|
stringLength = strlen(_activeSpeech.strings[0]);
|
|
|
|
removeFirst = false;
|
|
if (_activeSpeech.playingTime <= 0) {
|
|
if (_activeSpeech.speechFlags & kSpeakSlow) {
|
|
_activeSpeech.slowModeCharIndex++;
|
|
if (_activeSpeech.slowModeCharIndex >= stringLength)
|
|
removeFirst = true;
|
|
} else {
|
|
removeFirst = true;
|
|
}
|
|
_activeSpeech.playing = false;
|
|
if (_activeSpeech.speechFlags & kSpeakForceText)
|
|
_activeSpeech.speechFlags = 0;
|
|
if (_activeSpeech.actorIds[0] != 0) {
|
|
actor = getActor(_activeSpeech.actorIds[0]);
|
|
if (!(_activeSpeech.speechFlags & kSpeakNoAnimate)) {
|
|
actor->_currentAction = kActionWait;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (removeFirst) {
|
|
for (i = 1; i < _activeSpeech.stringsCount; i++) {
|
|
_activeSpeech.strings[i - 1] = _activeSpeech.strings[i];
|
|
}
|
|
_activeSpeech.stringsCount--;
|
|
}
|
|
|
|
if (_vm->_script->_skipSpeeches) {
|
|
_activeSpeech.stringsCount = 0;
|
|
_vm->_script->wakeUpThreads(kWaitTypeSpeech);
|
|
return;
|
|
}
|
|
|
|
if (_activeSpeech.stringsCount == 0) {
|
|
_vm->_script->wakeUpThreadsDelayed(kWaitTypeSpeech, _vm->ticksToMSec(kScriptTimeTicksPerSecond / 3));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (_vm->_script->_skipSpeeches) {
|
|
_activeSpeech.stringsCount = 0;
|
|
_vm->_script->wakeUpThreads(kWaitTypeSpeech);
|
|
}
|
|
|
|
if (_activeSpeech.stringsCount == 0) {
|
|
return;
|
|
}
|
|
|
|
stringLength = strlen(_activeSpeech.strings[0]);
|
|
|
|
if (_activeSpeech.speechFlags & kSpeakSlow) {
|
|
if (_activeSpeech.slowModeCharIndex >= stringLength)
|
|
error("Wrong string index");
|
|
|
|
_activeSpeech.playingTime = 1000 / 8;
|
|
|
|
} else {
|
|
sampleLength = _vm->_sndRes->getVoiceLength(_activeSpeech.sampleResourceId);
|
|
|
|
if (sampleLength < 0) {
|
|
_activeSpeech.playingTime = stringLength * 1000 / 22;
|
|
switch (_vm->_readingSpeed) {
|
|
case 2:
|
|
_activeSpeech.playingTime *= 2;
|
|
break;
|
|
case 1:
|
|
_activeSpeech.playingTime *= 4;
|
|
break;
|
|
case 0:
|
|
_activeSpeech.playingTime = 0x7fffff;
|
|
break;
|
|
}
|
|
} else {
|
|
_activeSpeech.playingTime = sampleLength;
|
|
}
|
|
}
|
|
|
|
if (_activeSpeech.sampleResourceId != -1) {
|
|
_vm->_sndRes->playVoice(_activeSpeech.sampleResourceId);
|
|
_activeSpeech.sampleResourceId++;
|
|
}
|
|
|
|
if (_activeSpeech.actorIds[0] != 0) {
|
|
actor = getActor(_activeSpeech.actorIds[0]);
|
|
if (!(_activeSpeech.speechFlags & kSpeakNoAnimate)) {
|
|
actor->_currentAction = kActionSpeak;
|
|
actor->_actionCycle = _vm->_rnd.getRandomNumber(63);
|
|
}
|
|
}
|
|
|
|
if (_activeSpeech.actorsCount == 1) {
|
|
if (_speechBoxScript.width() > 0) {
|
|
_activeSpeech.drawRect.left = _speechBoxScript.left;
|
|
_activeSpeech.drawRect.right = _speechBoxScript.right;
|
|
_activeSpeech.drawRect.top = _speechBoxScript.top;
|
|
_activeSpeech.drawRect.bottom = _speechBoxScript.bottom;
|
|
} else {
|
|
width = _activeSpeech.speechBox.width();
|
|
height = _vm->_font->getHeight(kKnownFontScript, _activeSpeech.strings[0], width - 2, _activeSpeech.getFontFlags(0)) + 1;
|
|
|
|
if (_vm->getGameId() == GID_IHNM) {
|
|
if (height > _vm->_scene->getHeight(true) / 2 && width < _vm->getDisplayInfo().width - 20) {
|
|
width = _vm->getDisplayInfo().width - 20;
|
|
height = _vm->_font->getHeight(kKnownFontScript, _activeSpeech.strings[0], width - 2, _activeSpeech.getFontFlags(0)) + 1;
|
|
}
|
|
} else if (_vm->getGameId() == GID_ITE) {
|
|
if (height > 40 && width < _vm->getDisplayInfo().width - 100) {
|
|
width = _vm->getDisplayInfo().width - 100;
|
|
height = _vm->_font->getHeight(kKnownFontScript, _activeSpeech.strings[0], width - 2, _activeSpeech.getFontFlags(0)) + 1;
|
|
}
|
|
}
|
|
|
|
_activeSpeech.speechBox.setWidth(width);
|
|
|
|
if (_activeSpeech.actorIds[0] != 0) {
|
|
actor = getActor(_activeSpeech.actorIds[0]);
|
|
_activeSpeech.speechBox.setHeight(height);
|
|
|
|
if (_activeSpeech.speechBox.right > _vm->getDisplayInfo().width - 10) {
|
|
_activeSpeech.drawRect.left = _vm->getDisplayInfo().width - 10 - width;
|
|
} else {
|
|
_activeSpeech.drawRect.left = _activeSpeech.speechBox.left;
|
|
}
|
|
|
|
height2 = actor->_screenPosition.y - 50;
|
|
if (height2 > _vm->_scene->getHeight(true))
|
|
_activeSpeech.speechBox.top = _activeSpeech.drawRect.top = _vm->_scene->getHeight(true) - 1 - height - 10;
|
|
else
|
|
_activeSpeech.speechBox.top = _activeSpeech.drawRect.top = MAX(10, (height2 - height) / 2);
|
|
} else {
|
|
_activeSpeech.drawRect.left = _activeSpeech.speechBox.left;
|
|
_activeSpeech.drawRect.top = _activeSpeech.speechBox.top + (_activeSpeech.speechBox.height() - height) / 2;
|
|
}
|
|
_activeSpeech.drawRect.setWidth(width);
|
|
_activeSpeech.drawRect.setHeight(height);
|
|
}
|
|
}
|
|
|
|
_activeSpeech.playing = true;
|
|
}
|
|
|
|
bool Actor::calcScreenPosition(CommonObjectData *commonObjectData) {
|
|
int beginSlope, endSlope, middle;
|
|
bool result;
|
|
if (_vm->_scene->getFlags() & kSceneFlagISO) {
|
|
_vm->_isoMap->tileCoordsToScreenPoint(commonObjectData->_location, commonObjectData->_screenPosition);
|
|
commonObjectData->_screenScale = 256;
|
|
} else {
|
|
middle = _vm->_scene->getHeight() - commonObjectData->_location.y / ACTOR_LMULT;
|
|
|
|
_vm->_scene->getSlopes(beginSlope, endSlope);
|
|
|
|
commonObjectData->_screenDepth = (14 * middle) / endSlope + 1;
|
|
|
|
if (middle <= beginSlope) {
|
|
commonObjectData->_screenScale = 256;
|
|
#ifdef ENABLE_IHNM
|
|
} else if (_vm->getGameId() == GID_IHNM && (objectTypeId(commonObjectData->_id) & kGameObjectObject)) {
|
|
commonObjectData->_screenScale = 256;
|
|
} else if (_vm->getGameId() == GID_IHNM && (commonObjectData->_flags & kNoScale)) {
|
|
commonObjectData->_screenScale = 256;
|
|
#endif
|
|
} else if (middle >= endSlope) {
|
|
commonObjectData->_screenScale = 1;
|
|
} else {
|
|
middle -= beginSlope;
|
|
endSlope -= beginSlope;
|
|
commonObjectData->_screenScale = 256 - (middle * 256) / endSlope;
|
|
}
|
|
|
|
commonObjectData->_location.toScreenPointXYZ(commonObjectData->_screenPosition);
|
|
}
|
|
|
|
result = commonObjectData->_screenPosition.x > -64 &&
|
|
commonObjectData->_screenPosition.x < _vm->getDisplayInfo().width + 64 &&
|
|
commonObjectData->_screenPosition.y > -64 &&
|
|
commonObjectData->_screenPosition.y < _vm->_scene->getHeight() + 64;
|
|
|
|
return result;
|
|
}
|
|
|
|
uint16 Actor::hitTest(const Point &testPoint, bool skipProtagonist) {
|
|
// We can only interact with objects or actors that are inside the
|
|
// scene area. While this is usually the entire upper part of the
|
|
// screen, it could also be an inset. Note that other kinds of hit
|
|
// areas may be outside the inset, and that those are still perfectly
|
|
// fine to interact with. For example, the door entrance at the glass
|
|
// makers's house in ITE's ferret village.
|
|
|
|
// Note that in IHNM, there are some items that overlap on other items
|
|
// Since we're checking the draw list from the FIRST item drawn to the
|
|
// LAST one, sometimes the object drawn first is incorrectly returned.
|
|
// An example is the chalk on the magic circle in Ted's chapter, which
|
|
// is drawn AFTER the circle, but HitTest incorrectly returns the circle
|
|
// id in this case, even though the chalk was drawn after the circle.
|
|
// Therefore, for IHNM, we iterate through the whole draw list and
|
|
// return the last match found, not the first one.
|
|
// Unfortunately, it is only possible to search items in the sorted draw
|
|
// list from start to end, not reverse, so it's necessary to search
|
|
// through the whole list to get the item drawn last
|
|
|
|
uint16 result = ID_NOTHING;
|
|
|
|
if (!_vm->_scene->getSceneClip().contains(testPoint))
|
|
return ID_NOTHING;
|
|
|
|
CommonObjectOrderList::iterator drawOrderIterator;
|
|
CommonObjectDataPointer drawObject;
|
|
int frameNumber = 0;
|
|
SpriteList *spriteList = NULL;
|
|
|
|
createDrawOrderList();
|
|
|
|
for (drawOrderIterator = _drawOrderList.begin(); drawOrderIterator != _drawOrderList.end(); ++drawOrderIterator) {
|
|
drawObject = *drawOrderIterator;
|
|
if (skipProtagonist && (drawObject == _protagonist)) {
|
|
continue;
|
|
}
|
|
if (!getSpriteParams(drawObject, frameNumber, spriteList)) {
|
|
continue;
|
|
}
|
|
if (_vm->_sprite->hitTest(*spriteList, frameNumber, drawObject->_screenPosition, drawObject->_screenScale, testPoint)) {
|
|
result = drawObject->_id;
|
|
if (_vm->getGameId() == GID_ITE)
|
|
return result; // in ITE, return the first result found (read above)
|
|
}
|
|
}
|
|
return result; // in IHNM, return the last result found (read above)
|
|
}
|
|
|
|
void Actor::drawOrderListAdd(const CommonObjectDataPointer& element, CompareFunction compareFunction) {
|
|
int res;
|
|
|
|
for (CommonObjectOrderList::iterator i = _drawOrderList.begin(); i !=_drawOrderList.end(); ++i) {
|
|
res = compareFunction(element, *i);
|
|
if (res < 0) {
|
|
_drawOrderList.insert(i, element);
|
|
return;
|
|
}
|
|
}
|
|
_drawOrderList.push_back(element);
|
|
}
|
|
|
|
void Actor::createDrawOrderList() {
|
|
CompareFunction compareFunction = 0;
|
|
|
|
if (_vm->_scene->getFlags() & kSceneFlagISO) {
|
|
compareFunction = &tileCommonObjectCompare;
|
|
} else {
|
|
if (_vm->getGameId() == GID_ITE)
|
|
compareFunction = &commonObjectCompare;
|
|
#ifdef ENABLE_IHNM
|
|
else if (_vm->getGameId() == GID_IHNM)
|
|
compareFunction = &commonObjectCompareIHNM;
|
|
#endif
|
|
}
|
|
|
|
_drawOrderList.clear();
|
|
for (ActorDataArray::iterator actor = _actors.begin(); actor != _actors.end(); ++actor) {
|
|
|
|
if (!actor->_inScene)
|
|
continue;
|
|
|
|
if (calcScreenPosition(actor)) {
|
|
drawOrderListAdd(actor, compareFunction);
|
|
}
|
|
}
|
|
|
|
for (ObjectDataArray::iterator obj = _objs.begin(); obj != _objs.end(); ++obj) {
|
|
if (obj->_sceneNumber != _vm->_scene->currentSceneNumber())
|
|
continue;
|
|
|
|
// WORKAROUND for a bug found in the original interpreter of IHNM
|
|
// If an object's x or y value is negative, don't draw it
|
|
// Scripts set negative values for an object's x and y when it shouldn't
|
|
// be drawn anymore (i.e. when it's picked up or used)
|
|
if (obj->_location.x < 0 || obj->_location.y < 0)
|
|
continue;
|
|
|
|
if (calcScreenPosition(obj)) {
|
|
drawOrderListAdd(obj, compareFunction);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Actor::getSpriteParams(CommonObjectData *commonObjectData, int &frameNumber, SpriteList *&spriteList) {
|
|
if (_vm->_scene->currentSceneResourceId() == ITE_SCENE_OVERMAP) {
|
|
if (!(commonObjectData->_flags & kProtagonist)){
|
|
// warning("not protagonist");
|
|
return false;
|
|
}
|
|
frameNumber = 8;
|
|
spriteList = &_vm->_sprite->_mainSprites;
|
|
} else if (validActorId(commonObjectData->_id)) {
|
|
ActorData *actor = (ActorData *)commonObjectData;
|
|
spriteList = &(actor->_spriteList);
|
|
frameNumber = actor->_frameNumber;
|
|
if (spriteList->empty()) {
|
|
loadActorSpriteList(actor);
|
|
}
|
|
|
|
} else if (validObjId(commonObjectData->_id)) {
|
|
spriteList = &_vm->_sprite->_mainSprites;
|
|
frameNumber = commonObjectData->_spriteListResourceId;
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
if (spriteList->empty()) {
|
|
return false;
|
|
}
|
|
|
|
if ((frameNumber < 0) || (spriteList->size() <= uint(frameNumber))) {
|
|
debug(1, "Actor::getSpriteParams frameNumber invalid for %s id 0x%X (%d)",
|
|
validObjId(commonObjectData->_id) ? "object" : "actor",
|
|
commonObjectData->_id, frameNumber);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void Actor::drawActors() {
|
|
// Do nothing for SAGA2 games for now
|
|
if (_vm->isSaga2()) {
|
|
return;
|
|
}
|
|
|
|
// WORKAROUND
|
|
// Bug #2928923: 'ITE: Graphic Glitches during racoon death "Cut Scene"'
|
|
if (_vm->_anim->hasCutaway() || _vm->_scene->currentSceneNumber() == 287 || _vm->_scene->currentSceneNumber() == 286) {
|
|
drawSpeech();
|
|
return;
|
|
}
|
|
|
|
if (_vm->_scene->currentSceneNumber() <= 0) {
|
|
return;
|
|
}
|
|
|
|
if (_vm->_scene->_entryList.empty()) {
|
|
return;
|
|
}
|
|
|
|
CommonObjectOrderList::iterator drawOrderIterator;
|
|
CommonObjectDataPointer drawObject;
|
|
int frameNumber = 0;
|
|
SpriteList *spriteList = NULL;
|
|
|
|
createDrawOrderList();
|
|
|
|
for (drawOrderIterator = _drawOrderList.begin(); drawOrderIterator != _drawOrderList.end(); ++drawOrderIterator) {
|
|
drawObject = *drawOrderIterator;
|
|
|
|
if (!getSpriteParams(drawObject, frameNumber, spriteList)) {
|
|
continue;
|
|
}
|
|
|
|
if (_vm->_scene->getFlags() & kSceneFlagISO) {
|
|
_vm->_isoMap->drawSprite(*spriteList, frameNumber, drawObject->_location, drawObject->_screenPosition, drawObject->_screenScale);
|
|
} else {
|
|
_vm->_sprite->drawOccluded(*spriteList, frameNumber, drawObject->_screenPosition, drawObject->_screenScale, drawObject->_screenDepth);
|
|
}
|
|
}
|
|
|
|
drawSpeech();
|
|
}
|
|
|
|
void Actor::drawSpeech() {
|
|
if (!isSpeaking() || !_activeSpeech.playing || _vm->_script->_skipSpeeches
|
|
|| (!_vm->_subtitlesEnabled && _vm->getGameId() == GID_ITE && !(_vm->getFeatures() & GF_ITE_FLOPPY))
|
|
|| (!_vm->_subtitlesEnabled && (_vm->getGameId() == GID_IHNM)))
|
|
return;
|
|
|
|
Point textPoint;
|
|
ActorData *actor;
|
|
int width, height;
|
|
int stringLength = strlen(_activeSpeech.strings[0]);
|
|
Common::Array<char> outputString;
|
|
outputString.resize(stringLength + 1);
|
|
|
|
if (_activeSpeech.speechFlags & kSpeakSlow)
|
|
strncpy(&outputString.front(), _activeSpeech.strings[0], _activeSpeech.slowModeCharIndex + 1);
|
|
else
|
|
strncpy(&outputString.front(), _activeSpeech.strings[0], stringLength);
|
|
|
|
if (_activeSpeech.actorsCount > 1) {
|
|
height = _vm->_font->getHeight(kKnownFontScript);
|
|
width = _vm->_font->getStringWidth(kKnownFontScript, _activeSpeech.strings[0], 0, kFontNormal);
|
|
|
|
for (int i = 0; i < _activeSpeech.actorsCount; i++) {
|
|
actor = getActor(_activeSpeech.actorIds[i]);
|
|
calcScreenPosition(actor);
|
|
|
|
textPoint.x = CLIP(actor->_screenPosition.x - width / 2, 10, _vm->getDisplayInfo().width - 10 - width);
|
|
|
|
if (_vm->getGameId() == GID_ITE)
|
|
textPoint.y = CLIP(actor->_screenPosition.y - 58, 10, _vm->_scene->getHeight(true) - 10 - height);
|
|
else if (_vm->getGameId() == GID_IHNM)
|
|
textPoint.y = 10; // CLIP(actor->_screenPosition.y - 160, 10, _vm->_scene->getHeight(true) - 10 - height);
|
|
|
|
_vm->_font->textDraw(kKnownFontScript, &outputString.front(), textPoint,
|
|
_activeSpeech.speechColor[i], _activeSpeech.outlineColor[i], _activeSpeech.getFontFlags(i));
|
|
}
|
|
} else {
|
|
_vm->_font->textDrawRect(kKnownFontScript, &outputString.front(), _activeSpeech.drawRect, _activeSpeech.speechColor[0],
|
|
_activeSpeech.outlineColor[0], _activeSpeech.getFontFlags(0));
|
|
}
|
|
}
|
|
|
|
void Actor::actorSpeech(uint16 actorId, const char **strings, int stringsCount, int sampleResourceId, int speechFlags) {
|
|
ActorData *actor;
|
|
int i;
|
|
int16 dist;
|
|
|
|
actor = getActor(actorId);
|
|
calcScreenPosition(actor);
|
|
for (i = 0; i < stringsCount; i++) {
|
|
_activeSpeech.strings[i] = strings[i];
|
|
}
|
|
|
|
_activeSpeech.stringsCount = stringsCount;
|
|
_activeSpeech.speechFlags = speechFlags;
|
|
_activeSpeech.actorsCount = 1;
|
|
_activeSpeech.actorIds[0] = actorId;
|
|
_activeSpeech.speechColor[0] = actor->_speechColor;
|
|
_activeSpeech.outlineColor[0] = _vm->KnownColor2ColorId(kKnownColorBlack);
|
|
_activeSpeech.sampleResourceId = sampleResourceId;
|
|
_activeSpeech.playing = false;
|
|
_activeSpeech.slowModeCharIndex = 0;
|
|
|
|
dist = MIN(actor->_screenPosition.x - 10, _vm->getDisplayInfo().width - 10 - actor->_screenPosition.x);
|
|
|
|
if (_vm->getGameId() == GID_ITE)
|
|
dist = CLIP<int16>(dist, 60, 150);
|
|
else
|
|
dist = CLIP<int16>(dist, 120, 300);
|
|
|
|
_activeSpeech.speechBox.left = actor->_screenPosition.x - dist;
|
|
_activeSpeech.speechBox.right = actor->_screenPosition.x + dist;
|
|
|
|
if (_activeSpeech.speechBox.left < 10) {
|
|
_activeSpeech.speechBox.right += 10 - _activeSpeech.speechBox.left;
|
|
_activeSpeech.speechBox.left = 10;
|
|
}
|
|
if (_activeSpeech.speechBox.right > _vm->getDisplayInfo().width - 10) {
|
|
_activeSpeech.speechBox.left -= _activeSpeech.speechBox.right - _vm->getDisplayInfo().width - 10;
|
|
_activeSpeech.speechBox.right = _vm->getDisplayInfo().width - 10;
|
|
}
|
|
|
|
}
|
|
|
|
void Actor::nonActorSpeech(const Common::Rect &box, const char **strings, int stringsCount, int sampleResourceId, int speechFlags) {
|
|
int i;
|
|
|
|
_vm->_script->wakeUpThreads(kWaitTypeSpeech);
|
|
|
|
for (i = 0; i < stringsCount; i++) {
|
|
_activeSpeech.strings[i] = strings[i];
|
|
}
|
|
_activeSpeech.stringsCount = stringsCount;
|
|
_activeSpeech.speechFlags = speechFlags;
|
|
_activeSpeech.actorsCount = 1;
|
|
_activeSpeech.actorIds[0] = 0;
|
|
if (_vm->getFeatures() & GF_ITE_FLOPPY)
|
|
_activeSpeech.sampleResourceId = -1;
|
|
else
|
|
_activeSpeech.sampleResourceId = sampleResourceId;
|
|
_activeSpeech.playing = false;
|
|
_activeSpeech.slowModeCharIndex = 0;
|
|
_activeSpeech.speechBox = box;
|
|
}
|
|
|
|
void Actor::simulSpeech(const char *string, uint16 *actorIds, int actorIdsCount, int speechFlags, int sampleResourceId) {
|
|
int i;
|
|
|
|
for (i = 0; i < actorIdsCount; i++) {
|
|
ActorData *actor;
|
|
|
|
actor = getActor(actorIds[i]);
|
|
_activeSpeech.actorIds[i] = actorIds[i];
|
|
_activeSpeech.speechColor[i] = actor->_speechColor;
|
|
_activeSpeech.outlineColor[i] = _vm->KnownColor2ColorId(kKnownColorBlack);
|
|
}
|
|
_activeSpeech.actorsCount = actorIdsCount;
|
|
_activeSpeech.strings[0] = string;
|
|
_activeSpeech.stringsCount = 1;
|
|
_activeSpeech.speechFlags = speechFlags;
|
|
_activeSpeech.sampleResourceId = sampleResourceId;
|
|
_activeSpeech.playing = false;
|
|
_activeSpeech.slowModeCharIndex = 0;
|
|
|
|
// caller should call thread->wait(kWaitTypeSpeech) by itself
|
|
}
|
|
|
|
void Actor::abortAllSpeeches() {
|
|
// WORKAROUND: Don't abort speeches in scene 31 (tree with beehive). This prevents the
|
|
// making fire animation from breaking
|
|
if (_vm->getGameId() == GID_ITE && _vm->_scene->currentSceneNumber() == 31)
|
|
return;
|
|
|
|
abortSpeech();
|
|
|
|
if (_vm->_script->_abortEnabled)
|
|
_vm->_script->_skipSpeeches = true;
|
|
|
|
for (int i = 0; i < 10; i++)
|
|
_vm->_script->executeThreads(0);
|
|
}
|
|
|
|
void Actor::abortSpeech() {
|
|
_vm->_sound->stopVoice();
|
|
_activeSpeech.playingTime = 0;
|
|
}
|
|
|
|
void Actor::saveState(Common::OutSaveFile *out) {
|
|
|
|
out->writeSint16LE(getProtagState());
|
|
|
|
for (ActorDataArray::iterator actor = _actors.begin(); actor != _actors.end(); ++actor) {
|
|
actor->saveState(out);
|
|
}
|
|
|
|
for (ObjectDataArray::iterator obj = _objs.begin(); obj != _objs.end(); ++obj) {
|
|
obj->saveState(out);
|
|
}
|
|
}
|
|
|
|
void Actor::loadState(Common::InSaveFile *in) {
|
|
|
|
int16 protagState = in->readSint16LE();
|
|
if (protagState != 0 || (_protagonist->shareFrames())) {
|
|
setProtagState(protagState);
|
|
}
|
|
|
|
for (ActorDataArray::iterator actor = _actors.begin(); actor != _actors.end(); ++actor) {
|
|
actor->loadState(_vm->getCurrentLoadVersion(), in);
|
|
}
|
|
|
|
for (ObjectDataArray::iterator obj = _objs.begin(); obj != _objs.end(); ++obj) {
|
|
obj->loadState(in);
|
|
}
|
|
}
|
|
|
|
} // End of namespace Saga
|