/* 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 3 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, see .
*
*/
/*
* This file is based on WME.
* http://dead-code.org/redir.php?target=wme
* Copyright (c) 2003-2013 Jan Nedoma and contributors
*/
#include "common/util.h"
#include "engines/wintermute/ad/ad_actor_3dx.h"
#include "engines/wintermute/ad/ad_attach_3dx.h"
#include "engines/wintermute/ad/ad_entity.h"
#include "engines/wintermute/ad/ad_game.h"
#include "engines/wintermute/ad/ad_path.h"
#include "engines/wintermute/ad/ad_path3d.h"
#include "engines/wintermute/ad/ad_scene.h"
#include "engines/wintermute/ad/ad_scene_geometry.h"
#include "engines/wintermute/ad/ad_sentence.h"
#include "engines/wintermute/ad/ad_waypoint_group.h"
#include "engines/wintermute/base/base_active_rect.h"
#include "engines/wintermute/base/base_file_manager.h"
#include "engines/wintermute/base/base_game.h"
#include "engines/wintermute/base/base_parser.h"
#include "engines/wintermute/base/base_region.h"
#include "engines/wintermute/base/base_surface_storage.h"
#include "engines/wintermute/base/gfx/base_renderer.h"
#include "engines/wintermute/base/gfx/3dshadow_volume.h"
#include "engines/wintermute/base/gfx/opengl/base_render_opengl3d.h"
#include "engines/wintermute/base/gfx/xmodel.h"
#include "engines/wintermute/base/gfx/3deffect.h"
#include "engines/wintermute/base/gfx/xmath.h"
#include "engines/wintermute/base/gfx/3dutils.h"
#include "engines/wintermute/base/particles/part_emitter.h"
#include "engines/wintermute/base/scriptables/script.h"
#include "engines/wintermute/base/scriptables/script_stack.h"
#include "engines/wintermute/base/scriptables/script_value.h"
#include "engines/wintermute/base/sound/base_sound.h"
#include "engines/wintermute/utils/path_util.h"
#include "engines/wintermute/utils/utils.h"
#include "engines/wintermute/dcgf.h"
namespace Wintermute {
IMPLEMENT_PERSISTENT(AdActor3DX, false)
//////////////////////////////////////////////////////////////////////////
AdActor3DX::AdActor3DX(BaseGame *inGame) : AdObject3D(inGame) {
_targetPoint3D = DXVector3(0.0f, 0.0f, 0.0f);
_targetPoint2D = new BasePoint;
_targetAngle = 0.0f;
_afterWalkAngle = -1.0f;
_path3D = new AdPath3D(inGame);
_path2D = new AdPath(inGame);
_talkAnimName = nullptr;
BaseUtils::setString(&_talkAnimName, "talk");
_idleAnimName = nullptr;
BaseUtils::setString(&_idleAnimName, "idle");
_walkAnimName = nullptr;
BaseUtils::setString(&_walkAnimName, "walk");
_turnLeftAnimName = nullptr;
BaseUtils::setString(&_turnLeftAnimName, "turnleft");
_turnRightAnimName = nullptr;
BaseUtils::setString(&_turnRightAnimName, "turnright");
_talkAnimChannel = 0;
_game->_renderer3D->enableShadows();
// direct controls
_directWalkMode = DIRECT_WALK_NONE;
_directTurnMode = DIRECT_TURN_NONE;
_directWalkAnim = nullptr;
_directTurnAnim = nullptr;
_directWalkVelocity = 0.0f;
_directTurnVelocity = 0.0f;
_defaultTransTime = 200;
_defaultStopTransTime = 200;
_stateAnimChannel = -1;
_goToTolerance = 2;
_partBone = nullptr;
_partOffset = DXVector3(0.0f, 0.0f, 0.0f);
}
//////////////////////////////////////////////////////////////////////////
AdActor3DX::~AdActor3DX() {
// delete attachments
for (int32 i = 0; i < _attachments.getSize(); i++) {
delete _attachments[i];
}
_attachments.removeAll();
// delete transition times
for (int32 i = 0; i < _transitionTimes.getSize(); i++) {
delete _transitionTimes[i];
}
_transitionTimes.removeAll();
SAFE_DELETE_ARRAY(_talkAnimName);
SAFE_DELETE_ARRAY(_idleAnimName);
SAFE_DELETE_ARRAY(_walkAnimName);
SAFE_DELETE_ARRAY(_turnLeftAnimName);
SAFE_DELETE_ARRAY(_turnRightAnimName);
SAFE_DELETE_ARRAY(_directWalkAnim);
SAFE_DELETE_ARRAY(_directTurnAnim);
SAFE_DELETE(_path2D);
SAFE_DELETE(_path3D);
SAFE_DELETE(_targetPoint2D);
SAFE_DELETE_ARRAY(_partBone);
}
//////////////////////////////////////////////////////////////////////////
bool AdActor3DX::update() {
if (!_xmodel) {
return true;
}
if (_game->_state == GAME_FROZEN) {
return true;
}
AdGame *adGame = (AdGame *)_game;
if (_state == STATE_READY && _stateAnimChannel >= 0 && _xmodel) {
_stateAnimChannel = -1;
}
if (_sentence && _state != STATE_TALKING) {
_sentence->finish();
SAFE_DELETE(_sentence);
// kill talking anim
if (_talkAnimChannel > 0)
_xmodel->stopAnim(_talkAnimChannel, _defaultStopTransTime);
}
// update state
switch (_state) {
//////////////////////////////////////////////////////////////////////////
case STATE_DIRECT_CONTROL:
if (_directWalkMode == DIRECT_WALK_NONE && _directTurnMode == DIRECT_TURN_NONE) {
_state = _nextState;
_nextState = STATE_READY;
} else {
// set animation
if (_directWalkMode != DIRECT_WALK_NONE) {
// disabled in original code
} else if (_directTurnMode != DIRECT_TURN_NONE) {
if (_directTurnAnim && _directTurnAnim[0]) {
_xmodel->playAnim(0, _directTurnAnim, _defaultTransTime, false, _defaultStopTransTime);
} else {
_xmodel->playAnim(0, _idleAnimName, _defaultTransTime, false, _defaultStopTransTime);
}
}
// move and/or turn
float turnVel = _directTurnVelocity == 0.0f ? _angVelocity : _directTurnVelocity;
if (_directTurnMode == DIRECT_TURN_CW) {
_angle += turnVel * (float)_game->_timerDelta / 1000.f;
_angle = BaseUtils::normalizeAngle(_angle);
}
if (_directTurnMode == DIRECT_TURN_CCW) {
_angle -= turnVel * (float)_game->_timerDelta / 1000.f;
_angle = BaseUtils::normalizeAngle(_angle);
}
float walkVel = _directWalkVelocity == 0.0f ? _velocity : _directWalkVelocity;
DXVector3 newPos = _posVector;
if (_directWalkMode == DIRECT_WALK_FW) {
newPos._x += -sinf(degToRad(_angle)) * walkVel * _scale3D * (float)_game->_timerDelta / 1000.f;
newPos._z += -cosf(degToRad(_angle)) * walkVel * _scale3D * (float)_game->_timerDelta / 1000.f;
}
if (_directWalkMode == DIRECT_WALK_BK) {
newPos._x -= -sinf(degToRad(_angle)) * walkVel * _scale3D * (float)_game->_timerDelta / 1000.f;
newPos._z -= -cosf(degToRad(_angle)) * walkVel * _scale3D * (float)_game->_timerDelta / 1000.f;
}
AdScene *scene = ((AdGame *)_game)->_scene;
if (scene && scene->_geom) {
bool canWalk = false;
if (scene->_2DPathfinding) {
DXMatrix newWorldMat;
getMatrix(&newWorldMat, &newPos);
int32 newX, newY;
convert3DTo2D(&newWorldMat, &newX, &newY);
canWalk = !scene->isBlockedAt(newX, newY, false, this);
} else {
canWalk = scene->_geom->directPathExists(&_posVector, &newPos);
}
if (canWalk) {
if (_directWalkAnim && _directWalkAnim[0]) {
_xmodel->playAnim(0, _directWalkAnim, _defaultTransTime, false, _defaultStopTransTime);
} else {
_xmodel->playAnim(0, _walkAnimName, _defaultTransTime, false, _defaultStopTransTime);
}
_posVector = newPos;
} else {
_xmodel->playAnim(0, _idleAnimName, _defaultTransTime, false, _defaultStopTransTime);
}
}
}
break;
//////////////////////////////////////////////////////////////////////////
case STATE_TURNING:
if (_turningLeft) {
_xmodel->playAnim(0, _turnLeftAnimName, _defaultTransTime, false, _defaultStopTransTime);
} else {
_xmodel->playAnim(0, _turnRightAnimName, _defaultTransTime, false, _defaultStopTransTime);
}
if (turnToStep(_angVelocity)) {
_state = _nextState;
_nextState = STATE_READY;
}
break;
//////////////////////////////////////////////////////////////////////////
case STATE_SEARCHING_PATH:
// keep asking scene for the path
_xmodel->playAnim(0, _idleAnimName, _defaultTransTime, false, _defaultStopTransTime);
if (adGame->_scene->_2DPathfinding) {
if (adGame->_scene->getPath(BasePoint(_posX, _posY), *_targetPoint2D, _path2D, this)) {
_state = STATE_WAITING_PATH;
}
} else {
if (adGame->_scene->_geom->getPath(_posVector, _targetPoint3D, _path3D))
_state = STATE_WAITING_PATH;
}
break;
//////////////////////////////////////////////////////////////////////////
case STATE_WAITING_PATH:
// wait until the scene finished the path
_xmodel->playAnim(0, _idleAnimName, _defaultTransTime, false, _defaultStopTransTime);
if (adGame->_scene->_2DPathfinding) {
if (_path2D->_ready) {
followPath2D();
}
} else {
if (_path3D->_ready) {
followPath3D();
}
}
break;
//////////////////////////////////////////////////////////////////////////
case STATE_FOLLOWING_PATH:
if (adGame->_scene->_2DPathfinding) {
getNextStep2D();
} else {
getNextStep3D();
}
_xmodel->playAnim(0, _walkAnimName, _defaultTransTime, false, _defaultStopTransTime);
break;
//////////////////////////////////////////////////////////////////////////
case STATE_TALKING: {
if (!_sentence)
break;
_sentence->update();
if (_sentence->_currentSkelAnim && _sentence->_currentSkelAnim[0]) {
_tempSkelAnim = _sentence->_currentSkelAnim;
}
bool timeIsUp = (_sentence->_sound && _sentence->_soundStarted && (!_sentence->_sound->isPlaying() && !_sentence->_sound->isPaused())) || (!_sentence->_sound && _sentence->_duration <= _game->_timer - _sentence->_startTime);
if (_tempSkelAnim == nullptr || !_xmodel->isAnimPending(0, _tempSkelAnim) || timeIsUp) {
if (timeIsUp) {
_sentence->finish();
_tempSkelAnim = nullptr;
_state = _nextState;
_nextState = STATE_READY;
if (_talkAnimChannel > 0)
_xmodel->stopAnim(_talkAnimChannel, _defaultStopTransTime);
} else {
_tempSkelAnim = _sentence->getNextStance();
if (_tempSkelAnim)
_xmodel->playAnim(0, _tempSkelAnim, _defaultTransTime, true, _defaultStopTransTime);
else {
if (_xmodel->getAnimationSetByName(_talkAnimName))
_xmodel->playAnim(_talkAnimChannel, _talkAnimName, _defaultTransTime, false, _defaultStopTransTime);
else
_xmodel->playAnim(0, _idleAnimName, _defaultTransTime, false, _defaultStopTransTime);
}
((AdGame *)_game)->addSentence(_sentence);
}
} else {
if (_tempSkelAnim) {
_xmodel->playAnim(0, _tempSkelAnim, _defaultTransTime, false, _defaultStopTransTime);
}
((AdGame *)_game)->addSentence(_sentence);
}
break;
}
case STATE_PLAYING_ANIM:
if (_stateAnimChannel != 0) {
_xmodel->playAnim(0, _idleAnimName, _defaultTransTime, false, _defaultStopTransTime);
}
break;
//////////////////////////////////////////////////////////////////////////
case STATE_READY:
_xmodel->playAnim(0, _idleAnimName, _defaultTransTime, false, _defaultStopTransTime);
break;
case STATE_IDLE:
case STATE_TURNING_LEFT:
case STATE_TURNING_RIGHT:
case STATE_PLAYING_ANIM_SET:
case STATE_NONE:
break;
} // switch(_state)
// finished playing animation?
if (_state == STATE_PLAYING_ANIM && !_xmodel->isAnimPending(_stateAnimChannel)) {
_state = _nextState;
_nextState = STATE_READY;
}
updateBlockRegion();
_ready = (_state == STATE_READY);
// setup 2D position
int origX = _posX;
int origY = _posY;
bool ret = AdObject3D::update();
if (origX != _posX || origY != _posY) {
afterMove();
}
if (_xmodel) {
_xmodel->update();
if (_shadowModel) {
_shadowModel->update();
}
}
updateAttachments();
updatePartEmitter();
return ret;
}
//////////////////////////////////////////////////////////////////////////
bool AdActor3DX::display() {
if (!_xmodel) {
return true;
}
updateSounds();
setupLights();
_game->_renderer3D->setSpriteBlendMode(_blendMode);
if (_hasAmbientLightColor) {
_game->_renderer3D->setAmbientLightColor(_ambientLightColor);
}
TShadowType shadowType = _game->getMaxShadowType(this);
if (shadowType == SHADOW_STENCIL) {
// Skip shadow volume rendering if not supported
if (_game->_renderer3D->shadowVolumeSupported()) {
displayShadowVolume();
}
} else if (shadowType > SHADOW_NONE) {
if (_game->_maxShadowType > SHADOW_NONE) {
bool simpleShadow = shadowType <= SHADOW_SIMPLE;
if (!_game->_supportsRealTimeShadows)
simpleShadow = true;
if (simpleShadow)
_game->_renderer3D->displaySimpleShadow(this);
else
displayFlatShadow();
}
}
_game->_renderer3D->setSpriteBlendMode(_blendMode, true);
_game->_renderer3D->setWorldTransform(_worldMatrix);
bool res = _xmodel->render();
if (_registrable) {
_game->_renderer->_rectList.add(new BaseActiveRect(_game, this, _xmodel, _xmodel->_boundingRect.left, _xmodel->_boundingRect.top, _xmodel->_boundingRect.right - _xmodel->_boundingRect.left, _xmodel->_boundingRect.bottom - _xmodel->_boundingRect.top, true));
}
_game->_renderer3D->invalidateLastTexture();
displayAttachments(true);
if (_hasAmbientLightColor) {
_game->_renderer3D->setDefaultAmbientLightColor();
}
if (_active && _partEmitter) {
_game->_renderer3D->setup2D();
_partEmitter->display();
}
// accessibility
//if (_game->_accessMgr->GetActiveObject() == this) {
// _game->_accessMgr->SetHintRect(&_xmodel->m_BoundingRect);
//}
return res;
}
//////////////////////////////////////////////////////////////////////////
bool AdActor3DX::renderModel() {
if (!_xmodel) {
return true;
}
_game->_renderer3D->setWorldTransform(_worldMatrix);
if (_shadowModel) {
_shadowModel->render();
} else {
_xmodel->render();
}
displayAttachments(false);
return true;
}
//////////////////////////////////////////////////////////////////////////
bool AdActor3DX::displayShadowVolume() {
DXVector3 pos;
DXVector3 target;
DXVector3 lightVector;
float extrusionDepth;
if (!_xmodel) {
return false;
}
_game->_renderer3D->setWorldTransform(_worldMatrix);
DXVector3 lightPos = DXVector3(_shadowLightPos._x * _scale3D, _shadowLightPos._y * _scale3D, _shadowLightPos._z * _scale3D);
pos = _posVector + lightPos;
target = _posVector;
lightVector = pos - target;
extrusionDepth = DXVec3Length(&lightVector) * 1.5f;
DXVec3Normalize(&lightVector, &lightVector);
getShadowVolume()->setColor(_shadowColor);
getShadowVolume()->reset();
XModel *shadowModel;
if (_shadowModel) {
shadowModel = _shadowModel;
} else {
shadowModel = _xmodel;
}
shadowModel->updateShadowVol(getShadowVolume(), &_worldMatrix, &lightVector, extrusionDepth);
DXMatrix origWorld;
_game->_renderer3D->getWorldTransform(&origWorld);
// handle the attachments
for (int32 i = 0; i < _attachments.getSize(); i++) {
AdAttach3DX *at = _attachments[i];
if (!at->_active) {
continue;
}
DXMatrix *boneMat = _xmodel->getBoneMatrix(at->getParentBone());
if (!boneMat) {
continue;
}
DXMatrix viewMat;
DXMatrixMultiply(&viewMat, boneMat, &_worldMatrix);
at->displayShadowVol(&viewMat, &lightVector, extrusionDepth, true);
}
// restore model's world matrix and render the shadow volume
_game->_renderer3D->setWorldTransform(origWorld);
getShadowVolume()->renderToStencilBuffer();
// finally display all the shadows rendered into stencil buffer
getShadowVolume()->renderToScene();
return true;
}
bool AdActor3DX::displayFlatShadow() {
DXMatrix shadowMat, origWorld;
if (!_xmodel) {
return false;
}
DXVector3 lightPos = DXVector3(_shadowLightPos._x * _scale3D,
_shadowLightPos._y * _scale3D,
_shadowLightPos._z * _scale3D);
_game->_renderer3D->getWorldTransform(&origWorld);
DXVector4 lightVector = { lightPos._x, lightPos._y, lightPos._z, 0 };
DXPlane plane = { 0, 1, 0, -_posVector._y };
DXMatrixShadow(&shadowMat, &lightVector, &plane);
DXMatrix shadowWorld = _worldMatrix * shadowMat;
_game->_renderer3D->setWorldTransform(shadowWorld);
_xmodel->renderFlatShadowModel(_shadowColor);
_game->_renderer3D->setWorldTransform(origWorld);
return true;
}
//////////////////////////////////////////////////////////////////////////
bool AdActor3DX::updateAttachments() {
for (int32 i = 0; i < _attachments.getSize(); i++) {
if (_attachments[i]->_active) {
_attachments[i]->update();
}
}
return true;
}
//////////////////////////////////////////////////////////////////////////
bool AdActor3DX::displayAttachments(bool registerObjects) {
if (!_xmodel) {
return false;
}
if (_attachments.getSize() == 0) {
return true;
}
DXMatrix origView;
_game->_renderer3D->getWorldTransform(&origView);
for (int32 i = 0; i < _attachments.getSize(); i++) {
AdAttach3DX *at = _attachments[i];
if (!at->_active) {
continue;
}
DXMatrix *boneMat = _xmodel->getBoneMatrix(at->getParentBone());
if (!boneMat) {
continue;
}
DXMatrix viewMat;
DXMatrixMultiply(&viewMat, boneMat, &origView);
at->displayAttachable(&viewMat, registerObjects);
}
_game->_renderer3D->setWorldTransform(origView);
return true;
}
//////////////////////////////////////////////////////////////////////////
bool AdActor3DX::turnTo(float angle) {
_turningLeft = prepareTurn(angle);
if (_targetAngle == _angle) { // no need to turn
_state = _nextState;
_nextState = STATE_READY;
} else {
_state = STATE_TURNING;
}
return true;
}
//////////////////////////////////////////////////////////////////////////
void AdActor3DX::goTo3D(DXVector3 targetPos, float targetAngle) {
_afterWalkAngle = targetAngle;
if (_targetPoint3D == targetPos && _state == STATE_FOLLOWING_PATH) {
return;
}
_path3D->reset();
_path3D->setReady(false);
_targetPoint3D = targetPos;
_state = STATE_SEARCHING_PATH;
}
//////////////////////////////////////////////////////////////////////////
void AdActor3DX::goTo2D(int x, int y, float targetAngle) {
_afterWalkAngle = targetAngle;
if (x == _targetPoint2D->x && y == _targetPoint2D->y && _state == STATE_FOLLOWING_PATH) {
return;
}
_path2D->reset();
_path2D->setReady(false);
_targetPoint2D->x = x;
_targetPoint2D->y = y;
((AdGame *)_game)->_scene->correctTargetPoint(_posX, _posY, &_targetPoint2D->x, &_targetPoint2D->y, true, this);
_state = STATE_SEARCHING_PATH;
}
//////////////////////////////////////////////////////////////////////////
void AdActor3DX::followPath3D() {
_path3D->getFirst();
// are there points to follow?
if (_path3D->getCurrent() != nullptr) {
_state = STATE_FOLLOWING_PATH;
initLine3D(_posVector, *_path3D->getCurrent(), true);
} else {
if (_afterWalkAngle != -1.0f) {
turnTo(_afterWalkAngle);
} else {
_state = STATE_READY;
}
}
}
//////////////////////////////////////////////////////////////////////////
void AdActor3DX::getNextStep3D() {
if (_angle != _targetAngle)
turnToStep(_angVelocity);
DXVector3 newPos = _posVector;
newPos._x += -sinf(degToRad(_targetAngle)) * _velocity * _scale3D * (float)_game->_timerDelta / 1000.f;
newPos._z += -cosf(degToRad(_targetAngle)) * _velocity * _scale3D * (float)_game->_timerDelta / 1000.f;
DXVector3 origVec, newVec;
DXVector3 *currentPos = _path3D->getCurrent();
if (currentPos != nullptr) {
origVec = *currentPos - _posVector;
newVec = *currentPos - newPos;
}
if (currentPos == nullptr || DXVec3Length(&origVec) < DXVec3Length(&newVec)) {
if (currentPos != nullptr) {
_posVector = *currentPos;
}
if (_path3D->getNext() == nullptr) {
_path3D->reset();
if (_afterWalkAngle != -1.0f) {
turnTo(_afterWalkAngle);
} else {
_state = _nextState;
_nextState = STATE_READY;
}
} else {
initLine3D(_posVector, *_path3D->getCurrent(), false);
}
} else {
_posVector = newPos;
}
}
//////////////////////////////////////////////////////////////////////////
void AdActor3DX::initLine3D(DXVector3 startPt, DXVector3 endPt, bool firstStep) {
if (firstStep) {
_nextState = STATE_FOLLOWING_PATH;
turnTo(radToDeg(-atan2(endPt._z - startPt._z, endPt._x - startPt._x)) - 90);
} else {
_turningLeft = prepareTurn(radToDeg(-atan2(endPt._z - startPt._z, endPt._x - startPt._x)) - 90);
}
}
//////////////////////////////////////////////////////////////////////////
void AdActor3DX::getNextStep2D() {
AdGame *adGame = (AdGame *)_game;
if (!adGame || !adGame->_scene || !adGame->_scene->_geom || !_path2D || !_path2D->getCurrent()) {
_state = _nextState;
_nextState = STATE_READY;
return;
}
if (_angle != _targetAngle) {
turnToStep(_angVelocity);
}
DXVector3 newPos = _posVector;
newPos._x += -sinf(degToRad(_targetAngle)) * _velocity * _scale3D * (float)_game->_timerDelta / 1000.f;
newPos._z += -cosf(degToRad(_targetAngle)) * _velocity * _scale3D * (float)_game->_timerDelta / 1000.f;
DXVector3 currentPoint;
adGame->_scene->_geom->convert2Dto3DTolerant(_path2D->getCurrent()->x, _path2D->getCurrent()->y, ¤tPoint);
DXVector3 origVec, newVec;
origVec = currentPoint - _posVector;
newVec = currentPoint - newPos;
if (DXVec3Length(&origVec) < DXVec3Length(&newVec)) {
_posVector = currentPoint;
if (_path2D->getNext() == nullptr) {
_path2D->reset();
if (_afterWalkAngle != -1.0f) {
turnTo(_afterWalkAngle);
} else {
_state = _nextState;
_nextState = STATE_READY;
}
} else {
adGame->_scene->_geom->convert2Dto3DTolerant(_path2D->getCurrent()->x, _path2D->getCurrent()->y, ¤tPoint);
initLine3D(_posVector, currentPoint, false);
}
} else
_posVector = newPos;
}
//////////////////////////////////////////////////////////////////////////
void AdActor3DX::followPath2D() {
AdGame *adGame = (AdGame *)_game;
// skip current position
_path2D->getFirst();
while (_path2D->getCurrent() != nullptr) {
if (_path2D->getCurrent()->x != _posX || _path2D->getCurrent()->y != _posY) {
break;
}
_path2D->getNext();
}
// are there points to follow?
if (_path2D->getCurrent() != nullptr) {
_state = STATE_FOLLOWING_PATH;
DXVector3 currentPoint;
adGame->_scene->_geom->convert2Dto3DTolerant(_path2D->getCurrent()->x, _path2D->getCurrent()->y, ¤tPoint);
initLine3D(_posVector, currentPoint, true);
} else {
if (_afterWalkAngle != -1.0f) {
turnTo(_afterWalkAngle);
} else {
_state = STATE_READY;
}
}
}
//////////////////////////////////////////////////////////////////////////
bool AdActor3DX::prepareTurn(float targetAngle) {
bool turnLeft;
_angle = BaseUtils::normalizeAngle(_angle);
targetAngle = BaseUtils::normalizeAngle(targetAngle);
if (_angle == targetAngle) {
_targetAngle = _angle;
return true;
}
float delta1, delta2, delta3, delta;
delta1 = targetAngle - _angle;
delta2 = targetAngle + 360 - _angle;
delta3 = targetAngle - 360 - _angle;
delta1 = (fabs(delta1) <= fabs(delta2)) ? delta1 : delta2;
delta = (fabs(delta1) <= fabs(delta3)) ? delta1 : delta3;
_targetAngle = _angle + delta;
turnLeft = (delta < 0);
return turnLeft;
}
//////////////////////////////////////////////////////////////////////////
bool AdActor3DX::turnToStep(float velocity) {
if (_turningLeft) {
_angle -= velocity * (float)_game->_timerDelta / 1000.f;
if (_angle < _targetAngle) {
_angle = _targetAngle;
}
} else {
_angle += velocity * (float)_game->_timerDelta / 1000.f;
if (_angle > _targetAngle) {
_angle = _targetAngle;
}
}
// done turning?
if (_angle == _targetAngle) {
_angle = BaseUtils::normalizeAngle(_angle);
_targetAngle = _angle;
return true;
} else {
return false;
}
}
//////////////////////////////////////////////////////////////////////////
bool AdActor3DX::loadFile(const char *filename) {
char *buffer = (char *)BaseFileManager::getEngineInstance()->readWholeFile(filename);
if (buffer == nullptr) {
_game->LOG(0, "AdActor3DX::LoadFile failed for file '%s'", filename);
return false;
}
setFilename(filename);
bool ret = loadBuffer(buffer, true);
if (!ret) {
_game->LOG(0, "Error parsing ACTOR3D file '%s'", filename);
}
delete[] buffer;
return ret;
}
TOKEN_DEF_START
TOKEN_DEF(ACTOR3DX)
TOKEN_DEF(X)
TOKEN_DEF(Y)
TOKEN_DEF(Z)
TOKEN_DEF(ANGLE)
TOKEN_DEF(VELOCITY)
TOKEN_DEF(ANGULAR_VELOCITY)
TOKEN_DEF(TEMPLATE)
TOKEN_DEF(NAME)
TOKEN_DEF(REGISTRABLE)
TOKEN_DEF(INTERACTIVE)
TOKEN_DEF(ACTIVE)
TOKEN_DEF(MODEL)
TOKEN_DEF(EVENTS)
TOKEN_DEF(FONT)
TOKEN_DEF(CURSOR)
TOKEN_DEF(DROP_TO_FLOOR)
TOKEN_DEF(SCRIPT)
TOKEN_DEF(CAPTION)
TOKEN_DEF(PROPERTY)
TOKEN_DEF(ANIMATION)
TOKEN_DEF(EDITOR_PROPERTY)
TOKEN_DEF(SHADOW_IMAGE)
TOKEN_DEF(SHADOW_SIZE)
TOKEN_DEF(SIMPLE_SHADOW)
TOKEN_DEF(SHADOW_COLOR)
TOKEN_DEF(SHADOW_MODEL)
TOKEN_DEF(SHADOW_TYPE)
TOKEN_DEF(LIGHT_POSITION)
TOKEN_DEF(SHADOW)
TOKEN_DEF(SCALE)
TOKEN_DEF(DRAW_BACKFACES)
TOKEN_DEF(BLOCKED_REGION)
TOKEN_DEF(WAYPOINTS)
TOKEN_DEF(EFFECT_FILE)
TOKEN_DEF(EFFECT)
TOKEN_DEF(MATERIAL)
TOKEN_DEF_END
//////////////////////////////////////////////////////////////////////////
bool AdActor3DX::loadBuffer(char *buffer, bool complete) {
TOKEN_TABLE_START(commands)
TOKEN_TABLE(ACTOR3DX)
TOKEN_TABLE(X)
TOKEN_TABLE(Y)
TOKEN_TABLE(Z)
TOKEN_TABLE(ANGLE)
TOKEN_TABLE(VELOCITY)
TOKEN_TABLE(ANGULAR_VELOCITY)
TOKEN_TABLE(TEMPLATE)
TOKEN_TABLE(NAME)
TOKEN_TABLE(REGISTRABLE)
TOKEN_TABLE(INTERACTIVE)
TOKEN_TABLE(ACTIVE)
TOKEN_TABLE(MODEL)
TOKEN_TABLE(EVENTS)
TOKEN_TABLE(FONT)
TOKEN_TABLE(CURSOR)
TOKEN_TABLE(DROP_TO_FLOOR)
TOKEN_TABLE(SCRIPT)
TOKEN_TABLE(CAPTION)
TOKEN_TABLE(PROPERTY)
TOKEN_TABLE(ANIMATION)
TOKEN_TABLE(EDITOR_PROPERTY)
TOKEN_TABLE(SHADOW_IMAGE)
TOKEN_TABLE(SHADOW_SIZE)
TOKEN_TABLE(SIMPLE_SHADOW)
TOKEN_TABLE(SHADOW_COLOR)
TOKEN_TABLE(SHADOW_MODEL)
TOKEN_TABLE(SHADOW_TYPE)
TOKEN_TABLE(LIGHT_POSITION)
TOKEN_TABLE(SHADOW)
TOKEN_TABLE(SCALE)
TOKEN_TABLE(DRAW_BACKFACES)
TOKEN_TABLE(BLOCKED_REGION)
TOKEN_TABLE(WAYPOINTS)
TOKEN_TABLE(EFFECT)
TOKEN_TABLE_END
char *params;
int cmd;
BaseParser parser(_game);
if (complete) {
if (parser.getCommand(&buffer, commands, ¶ms) != TOKEN_ACTOR3DX) {
_game->LOG(0, "'ACTOR3DX' keyword expected.");
return false;
}
buffer = params;
}
SAFE_DELETE(_xmodel);
SAFE_DELETE(_shadowModel);
while ((cmd = parser.getCommand(&buffer, commands, ¶ms)) > 0) {
switch (cmd) {
case TOKEN_TEMPLATE:
if (!loadFile(params)) {
cmd = PARSERR_GENERIC;
}
break;
case TOKEN_X:
parser.scanStr(params, "%f", &_posVector._x);
break;
case TOKEN_Y:
parser.scanStr(params, "%f", &_posVector._y);
break;
case TOKEN_Z:
parser.scanStr(params, "%f", &_posVector._z);
break;
case TOKEN_ANGLE:
parser.scanStr(params, "%f", &_angle);
BaseUtils::normalizeAngle(_angle);
break;
case TOKEN_SHADOW_SIZE:
parser.scanStr(params, "%f", &_shadowSize);
_shadowSize = MAX(_shadowSize, 0.0f);
break;
case TOKEN_SIMPLE_SHADOW: {
bool simpleShadow;
parser.scanStr(params, "%b", &simpleShadow);
if (simpleShadow) {
_shadowType = SHADOW_SIMPLE;
}
break;
}
case TOKEN_SHADOW_COLOR: {
int r, g, b, a;
parser.scanStr(params, "%d,%d,%d,%d", &r, &g, &b, &a);
_shadowColor = BYTETORGBA(r, g, b, a);
break;
}
case TOKEN_LIGHT_POSITION:
parser.scanStr(params, "%f,%f,%f", &_shadowLightPos._x, &_shadowLightPos._y, &_shadowLightPos._z);
break;
case TOKEN_SHADOW: {
bool shadowEnabled;
parser.scanStr(params, "%b", &shadowEnabled);
if (!shadowEnabled) {
_shadowType = SHADOW_NONE;
}
break;
}
case TOKEN_DRAW_BACKFACES:
parser.scanStr(params, "%b", &_drawBackfaces);
break;
case TOKEN_VELOCITY:
parser.scanStr(params, "%f", &_velocity);
break;
case TOKEN_ANGULAR_VELOCITY:
parser.scanStr(params, "%f", &_angVelocity);
break;
case TOKEN_SCALE:
parser.scanStr(params, "%f", &_scale3D);
_scale3D /= 100.0f;
break;
case TOKEN_NAME:
setName(params);
break;
case TOKEN_CAPTION:
setCaption(params);
break;
case TOKEN_FONT:
setFont(params);
break;
case TOKEN_REGISTRABLE:
case TOKEN_INTERACTIVE:
parser.scanStr(params, "%b", &_registrable);
break;
case TOKEN_ACTIVE:
parser.scanStr(params, "%b", &_active);
break;
case TOKEN_DROP_TO_FLOOR:
parser.scanStr(params, "%b", &_dropToFloor);
break;
case TOKEN_SHADOW_TYPE: {
char *typeName = params;
if (scumm_stricmp(typeName, "none") == 0) {
_shadowType = SHADOW_NONE;
} else if (scumm_stricmp(typeName, "simple") == 0) {
_shadowType = SHADOW_SIMPLE;
} else if (scumm_stricmp(typeName, "flat") == 0) {
_shadowType = SHADOW_FLAT;
} else if (scumm_stricmp(typeName, "stencil") == 0) {
_shadowType = SHADOW_STENCIL;
} else {
_shadowType = (TShadowType)atoi(typeName);
if (_shadowType < 0) {
_shadowType = SHADOW_NONE;
}
if (_shadowType > SHADOW_STENCIL) {
_shadowType = SHADOW_STENCIL;
}
}
break;
}
case TOKEN_MODEL:
if (!_xmodel) {
_xmodel = new XModel(_game, this);
if (!_xmodel || !_xmodel->loadFromFile(params)) {
SAFE_DELETE(_xmodel);
cmd = PARSERR_GENERIC;
}
} else {
if (!_xmodel->mergeFromFile(params)) {
cmd = PARSERR_GENERIC;
}
}
break;
case TOKEN_SHADOW_MODEL:
if (_xmodel) {
SAFE_DELETE(_shadowModel);
_shadowModel = new XModel(_game, this);
if (!_shadowModel || !_shadowModel->loadFromFile(params, _xmodel)) {
SAFE_DELETE(_shadowModel);
cmd = PARSERR_GENERIC;
}
} else {
_game->LOG(0, "Error: a MODEL= line must precede shadow model assignment (file: %s)", _filename);
}
break;
case TOKEN_CURSOR:
SAFE_DELETE(_cursor);
_cursor = new BaseSprite(_game);
if (!_cursor || !_cursor->loadFile(params)) {
SAFE_DELETE(_cursor);
cmd = PARSERR_GENERIC;
}
break;
case TOKEN_SCRIPT:
addScript(params);
break;
case TOKEN_PROPERTY:
parseProperty(params, false);
break;
case TOKEN_EDITOR_PROPERTY:
parseEditorProperty(params, false);
break;
case TOKEN_ANIMATION:
if (_xmodel) {
_xmodel->parseAnim(params);
} else {
_game->LOG(0, "Error: a MODEL= line must precede any animation definitions (file: %s)", _filename);
}
break;
case TOKEN_EFFECT:
if (_xmodel)
parseEffect(params);
else
_game->LOG(0, "Error: a MODEL= line must precede any effect definitions (file: %s)", _filename);
break;
case TOKEN_SHADOW_IMAGE:
if (_shadowImage)
_game->_surfaceStorage->removeSurface(_shadowImage);
_shadowImage = nullptr;
_shadowImage = _game->_surfaceStorage->addSurface(params, false);
break;
case TOKEN_BLOCKED_REGION: {
SAFE_DELETE(_blockRegion);
SAFE_DELETE(_currentBlockRegion);
BaseRegion *rgn = new BaseRegion(_game);
BaseRegion *crgn = new BaseRegion(_game);
if (!rgn || !crgn || !rgn->loadBuffer(params, false)) {
SAFE_DELETE(rgn);
SAFE_DELETE(crgn);
cmd = PARSERR_GENERIC;
} else {
_blockRegion = rgn;
_currentBlockRegion = crgn;
_currentBlockRegion->mimic(_blockRegion);
}
break;
}
case TOKEN_WAYPOINTS: {
SAFE_DELETE(_wptGroup);
SAFE_DELETE(_currentWptGroup);
AdWaypointGroup *wpt = new AdWaypointGroup(_game);
AdWaypointGroup *cwpt = new AdWaypointGroup(_game);
if (!wpt || !cwpt || !wpt->loadBuffer(params, false)) {
SAFE_DELETE(wpt);
SAFE_DELETE(cwpt);
cmd = PARSERR_GENERIC;
} else {
_wptGroup = wpt;
_currentWptGroup = cwpt;
_currentWptGroup->mimic(_wptGroup);
}
break;
}
}
}
if (cmd == PARSERR_TOKENNOTFOUND) {
_game->LOG(0, "Syntax error in ACTOR3DX definition");
return false;
}
if (cmd == PARSERR_GENERIC) {
_game->LOG(0, "Error loading ACTOR3DX definition");
return false;
}
if (!_xmodel) {
_game->LOG(0, "Error: No model has been loaded for 3D actor");
return false;
}
_state = _nextState = STATE_READY;
return true;
}
//////////////////////////////////////////////////////////////////////////
float AdActor3DX::dirToAngle(TDirection dir) {
switch (dir) {
case DI_UP:
return 180.0f;
case DI_UPRIGHT:
return 225.0f;
case DI_RIGHT:
return 270.0f;
case DI_DOWNRIGHT:
return 315.0f;
case DI_DOWN:
return 0.0f;
case DI_DOWNLEFT:
return 45.0f;
case DI_LEFT:
return 90.0f;
case DI_UPLEFT:
return 135.0f;
case DI_NONE:
return -1.0f;
default:
return 0.0f;
}
}
//////////////////////////////////////////////////////////////////////////
TDirection AdActor3DX::angleToDir(float angle) {
if (angle >= 337.0f || angle < 22.0f)
return DI_DOWN;
if (angle >= 22.0f && angle < 67.0f)
return DI_DOWNLEFT;
if (angle >= 67.0f && angle < 112.0f)
return DI_LEFT;
if (angle >= 112.0f && angle < 157.0f)
return DI_UPLEFT;
if (angle >= 157.0f && angle < 202.0f)
return DI_UP;
if (angle >= 202.0f && angle < 247.0f)
return DI_UPRIGHT;
if (angle >= 247.0f && angle < 292.0f)
return DI_RIGHT;
if (angle >= 292.0f && angle < 337.0f)
return DI_DOWNRIGHT;
return DI_NONE;
}
//////////////////////////////////////////////////////////////////////////
bool AdActor3DX::playAnim3DX(const char *name, bool setState) {
return playAnim3DX(0, name, setState);
}
//////////////////////////////////////////////////////////////////////////
bool AdActor3DX::playAnim3DX(int channel, const char *name, bool setState) {
if (!_xmodel) {
return false;
}
bool res = _xmodel->playAnim(channel, name, _defaultTransTime, true, _defaultStopTransTime);
if (res && setState) {
_state = STATE_PLAYING_ANIM;
_stateAnimChannel = channel;
}
return res;
}
//////////////////////////////////////////////////////////////////////////
void AdActor3DX::talk(const char *text, const char *sound, uint32 duration, const char *stances, TTextAlign align) {
AdObject::talk(text, sound, duration, stances, align);
}
//////////////////////////////////////////////////////////////////////////
int32 AdActor3DX::getHeight() {
if (!_xmodel) {
return 0;
} else {
return _posY - _xmodel->_boundingRect.top - 5;
}
}
//////////////////////////////////////////////////////////////////////////
// high level scripting interface
//////////////////////////////////////////////////////////////////////////
bool AdActor3DX::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
//////////////////////////////////////////////////////////////////////////
// PlayAnim / PlayAnimAsync
//////////////////////////////////////////////////////////////////////////
if (strcmp(name, "PlayAnim") == 0 || strcmp(name, "PlayAnimAsync") == 0) {
bool async = strcmp(name, "PlayAnimAsync") == 0;
stack->correctParams(1);
if (!playAnim3DX(stack->pop()->getString(), true)) {
stack->pushBool(false);
} else {
if (!async)
script->waitFor(this);
stack->pushBool(true);
}
return true;
}
//////////////////////////////////////////////////////////////////////////
// StopAnim
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "StopAnim") == 0) {
stack->correctParams(1);
int transTime = stack->pop()->getInt(_defaultStopTransTime);
bool ret = false;
if (_xmodel) {
ret = _xmodel->stopAnim(0, transTime);
}
stack->pushBool(ret);
return true;
}
//////////////////////////////////////////////////////////////////////////
// StopAnimChannel
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "StopAnimChannel") == 0) {
stack->correctParams(2);
int channel = stack->pop()->getInt();
int transTime = stack->pop()->getInt();
bool ret = false;
if (_xmodel) {
ret = _xmodel->stopAnim(channel, transTime);
}
stack->pushBool(ret);
return true;
}
//////////////////////////////////////////////////////////////////////////
// PlayAnimChannel / PlayAnimChannelAsync
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "PlayAnimChannel") == 0 || strcmp(name, "PlayAnimChannelAsync") == 0) {
bool async = strcmp(name, "PlayAnimChannelAsync") == 0;
stack->correctParams(2);
int channel = stack->pop()->getInt();
const char *animName = stack->pop()->getString();
if (!playAnim3DX(channel, animName, !async)) {
stack->pushBool(false);
} else {
if (!async) {
script->waitFor(this);
}
stack->pushBool(true);
}
return true;
}
//////////////////////////////////////////////////////////////////////////
// IsAnimPlaying
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "IsAnimPlaying") == 0) {
stack->correctParams(1);
ScValue *val = stack->pop();
const char *animName;
if (val->isNULL()) {
animName = nullptr;
} else {
animName = val->getString();
}
if (_xmodel) {
stack->pushBool(_xmodel->isAnimPending(0, animName));
} else {
stack->pushBool(false);
}
return true;
}
//////////////////////////////////////////////////////////////////////////
// IsAnimChannelPlaying
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "IsAnimChannelPlaying") == 0) {
stack->correctParams(2);
int channel = stack->pop()->getInt(0);
ScValue *val = stack->pop();
const char *animName;
if (val->isNULL()) {
animName = nullptr;
} else {
animName = val->getString();
}
if (_xmodel) {
stack->pushBool(_xmodel->isAnimPending(channel, animName));
} else {
stack->pushBool(false);
}
return true;
}
//////////////////////////////////////////////////////////////////////////
// AddAttachment / AddMesh
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "AddAttachment") == 0 || strcmp(name, "AddMesh") == 0) {
if (strcmp(name, "AddMesh") == 0)
_game->LOG(0, "Warning: AddMesh is now obsolete, use AddAttachment");
stack->correctParams(3);
const char *filename = stack->pop()->getString();
const char *attachName = stack->pop()->getString();
const char *boneName = stack->pop()->getString();
if (!_xmodel) {
stack->pushBool(false);
} else {
if (!_xmodel->getBoneMatrix(boneName)) {
script->runtimeError("Bone '%s' cannot be found", boneName);
stack->pushBool(false);
} else {
AdAttach3DX *at = new AdAttach3DX(_game, this);
if (!at || !at->init(filename, attachName, boneName)) {
script->runtimeError("Error adding attachment");
SAFE_DELETE(at);
stack->pushBool(false);
} else {
bool isSet = false;
for (int32 i = 0; i < _attachments.getSize(); i++) {
if (scumm_stricmp(_attachments[i]->_name, attachName) == 0) {
SAFE_DELETE(_attachments[i]);
_attachments[i] = at;
isSet = true;
break;
}
}
if (!isSet) {
_attachments.add(at);
}
stack->pushBool(true);
}
}
}
return true;
}
//////////////////////////////////////////////////////////////////////////
// RemoveAttachment / RemoveMesh
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "RemoveAttachment") == 0 || strcmp(name, "RemoveMesh") == 0) {
if (strcmp(name, "RemoveMesh") == 0) {
_game->LOG(0, "Warning: RemoveMesh is now obsolete, use RemoveAttachment");
}
stack->correctParams(1);
const char *attachmentName = stack->pop()->getString();
if (!_xmodel) {
stack->pushBool(false);
} else {
bool isFound = false;
for (int32 i = 0; i < _attachments.getSize(); i++) {
if (scumm_stricmp(_attachments[i]->_name, attachmentName) == 0) {
SAFE_DELETE(_attachments[i]);
_attachments.removeAt(i);
isFound = true;
break;
}
}
stack->pushBool(isFound);
}
return true;
}
//////////////////////////////////////////////////////////////////////////
// GetAttachment
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "GetAttachment") == 0) {
stack->correctParams(1);
const char *attachmentName = stack->pop()->getString();
if (!_xmodel) {
stack->pushNULL();
} else {
bool isFound = false;
for (int32 i = 0; i < _attachments.getSize(); i++) {
if (scumm_stricmp(_attachments[i]->_name, attachmentName) == 0) {
stack->pushNative(_attachments[i], true);
isFound = true;
break;
}
}
if (!isFound)
stack->pushNULL();
}
return true;
}
//////////////////////////////////////////////////////////////////////////
// GoTo3D / GoTo3DAsync
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "GoTo3D") == 0 || strcmp(name, "GoTo3DAsync") == 0) {
stack->correctParams(3);
DXVector3 pos;
pos._x = stack->pop()->getFloat();
pos._y = stack->pop()->getFloat();
pos._z = stack->pop()->getFloat();
goTo3D(pos);
if (strcmp(name, "GoTo3DAsync") != 0) {
script->waitForExclusive(this);
}
stack->pushNULL();
return true;
}
//////////////////////////////////////////////////////////////////////////
// GoTo / GoToAsync
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "GoTo") == 0 || strcmp(name, "GoToAsync") == 0) {
stack->correctParams(2);
int x = stack->pop()->getInt();
int y = stack->pop()->getInt();
AdGame *adGame = (AdGame *)_game;
if (isGoToNeeded(x, y)) {
// check for adGame->_scene first if it's null
if (adGame->_scene && adGame->_scene->_2DPathfinding) {
goTo2D(x, y);
if (strcmp(name, "GoToAsync") != 0) {
script->waitForExclusive(this);
}
} else {
if (adGame->_scene && adGame->_scene->_geom) {
DXVector3 pos;
if (adGame->_scene->_geom->convert2Dto3DTolerant(x, y, &pos)) {
goTo3D(pos);
if (strcmp(name, "GoToAsync") != 0) {
script->waitForExclusive(this);
}
}
}
}
} else {
if (_path2D) {
_path2D->reset();
}
if (_path3D) {
_path3D->reset();
}
}
stack->pushNULL();
return true;
}
//////////////////////////////////////////////////////////////////////////
// GoToObject / GoToObjectAsync
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "GoToObject") == 0 || strcmp(name, "GoToObjectAsync") == 0) {
stack->correctParams(1);
ScValue *val = stack->pop();
if (!val->isNative()) {
script->runtimeError("actor.%s method accepts an entity reference only", name);
stack->pushNULL();
return true;
}
AdObject *obj = (AdObject *)val->getNative();
if (!obj || obj->_type != OBJECT_ENTITY) {
script->runtimeError("actor.%s method accepts an entity reference only", name);
stack->pushNULL();
return true;
}
AdEntity *ent = (AdEntity *)obj;
AdGame *adGame = (AdGame *)_game;
bool goToNeeded = true;
if (ent->_walkToX == 0 && ent->_walkToY == 0) {
goToNeeded = isGoToNeeded(ent->_posX, ent->_posY);
} else {
goToNeeded = isGoToNeeded(ent->_walkToX, ent->_walkToY);
}
if (!goToNeeded) {
// no goto needed, but we still want to turn
if (ent->_walkToX != 0 || ent->_walkToY != 0) {
turnTo(dirToAngle(ent->_walkToDir));
if (strcmp(name, "GoToObjectAsync") != 0) {
script->waitForExclusive(this);
}
}
if (_path2D) {
_path2D->reset();
}
if (_path3D) {
_path3D->reset();
}
stack->pushNULL();
return true;
}
if (adGame->_scene->_2DPathfinding) {
if (ent->_walkToX== 0 && ent->_walkToY== 0) {
goTo2D(ent->_posX, ent->_posY);
} else {
goTo2D(ent->_walkToX, ent->_walkToY, dirToAngle(ent->_walkToDir));
}
if (strcmp(name, "GoToObjectAsync") != 0) {
script->waitForExclusive(this);
}
} else {
if (adGame->_scene->_geom) {
DXVector3 pos;
if (adGame->_scene->_geom->convert2Dto3DTolerant(ent->_walkToX, ent->_walkToY, &pos)) {
if (ent->_walkToX == 0 && ent->_walkToY == 0) {
goTo3D(pos);
} else {
goTo3D(pos, dirToAngle(ent->_walkToDir));
}
if (strcmp(name, "GoToObjectAsync") != 0) {
script->waitForExclusive(this);
}
}
}
}
stack->pushNULL();
return true;
}
//////////////////////////////////////////////////////////////////////////
// TurnTo / TurnToAsync
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "TurnTo") == 0 || strcmp(name, "TurnToAsync") == 0) {
stack->correctParams(1);
int dir;
ScValue *val = stack->pop();
float angle = 0.0;
// turn to object?
if (val->isNative() && _game->validObject((BaseObject *)val->getNative())) {
BaseObject *obj = (BaseObject *)val->getNative();
DXVector3 objPos;
((AdGame *)_game)->_scene->_geom->convert2Dto3D(obj->_posX, obj->_posY, &objPos);
angle = radToDeg(-atan2(objPos._z - _posVector._z, objPos._x - _posVector._x)) - 90;
} else {
// otherwise turn to direction
dir = val->getInt();
angle = dirToAngle((TDirection)dir);
}
if (_path2D)
_path2D->reset();
if (_path3D)
_path3D->reset();
turnTo(angle);
if (strcmp(name, "TurnToAsync") != 0) {
script->waitForExclusive(this);
}
stack->pushNULL();
return true;
}
//////////////////////////////////////////////////////////////////////////
// TurnToAngle / TurnToAngleAsync
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "TurnToAngle") == 0 || strcmp(name, "TurnToAngleAsync") == 0) {
stack->correctParams(1);
float angle = stack->pop()->getFloat();
if (_path2D) {
_path2D->reset();
}
if (_path3D) {
_path3D->reset();
}
turnTo(angle);
if (strcmp(name, "TurnToAngleAsync") != 0) {
script->waitForExclusive(this);
}
stack->pushNULL();
return true;
}
//////////////////////////////////////////////////////////////////////////
// IsWalking
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "IsWalking") == 0) {
stack->correctParams(0);
stack->pushBool(_state == STATE_FOLLOWING_PATH);
return true;
}
//////////////////////////////////////////////////////////////////////////
// DirectWalk / DirectWalkBack
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "DirectWalk") == 0 || strcmp(name, "DirectWalkBack") == 0) {
stack->correctParams(2);
ScValue *valVelocity = stack->pop();
ScValue *valAnim = stack->pop();
SAFE_DELETE_ARRAY(_directWalkAnim);
if (!valVelocity->isNULL()) {
_directWalkVelocity = valVelocity->getFloat();
} else {
_directWalkVelocity = 0.0f;
}
if (!valAnim->isNULL()) {
BaseUtils::setString(&_directWalkAnim, valAnim->getString());
}
_state = STATE_DIRECT_CONTROL;
if (strcmp(name, "DirectWalk") == 0) {
_directWalkMode = DIRECT_WALK_FW;
} else {
_directWalkMode = DIRECT_WALK_BK;
}
stack->pushBool(true);
return true;
}
//////////////////////////////////////////////////////////////////////////
// DirectWalkStop
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "DirectWalkStop") == 0) {
stack->correctParams(0);
_directWalkMode = DIRECT_WALK_NONE;
stack->pushBool(true);
return true;
}
//////////////////////////////////////////////////////////////////////////
// DirectTurnLeft / DirectTurnRight
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "DirectTurnLeft") == 0 || strcmp(name, "DirectTurnRight") == 0) {
stack->correctParams(2);
ScValue *valVelocity = stack->pop();
ScValue *valAnim = stack->pop();
SAFE_DELETE_ARRAY(_directTurnAnim);
if (!valVelocity->isNULL()) {
_directTurnVelocity = valVelocity->getFloat();
} else {
_directTurnVelocity = 0.0f;
}
if (!valAnim->isNULL()) {
BaseUtils::setString(&_directTurnAnim, valAnim->getString());
}
_state = STATE_DIRECT_CONTROL;
if (strcmp(name, "DirectTurnLeft") == 0) {
_directTurnMode = DIRECT_TURN_CCW;
} else {
_directTurnMode = DIRECT_TURN_CW;
}
stack->pushBool(true);
return true;
}
//////////////////////////////////////////////////////////////////////////
// DirectTurnStop
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "DirectTurnStop") == 0) {
stack->correctParams(0);
_directTurnMode = DIRECT_TURN_NONE;
stack->pushBool(true);
return true;
}
//////////////////////////////////////////////////////////////////////////
// SetTexture
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "SetTexture") == 0) {
stack->correctParams(2);
const char *materialName = stack->pop()->getString();
const char *textureFilename = stack->pop()->getString();
if (_xmodel && _xmodel->setMaterialSprite(materialName, textureFilename)) {
stack->pushBool(true);
} else {
stack->pushBool(false);
}
return true;
}
//////////////////////////////////////////////////////////////////////////
// SetTheoraTexture
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "SetTheoraTexture") == 0) {
stack->correctParams(2);
const char *materialName = stack->pop()->getString();
const char *theoraFilename = stack->pop()->getString();
if (_xmodel && _xmodel->setMaterialTheora(materialName, theoraFilename)) {
stack->pushBool(true);
} else {
stack->pushBool(false);
}
return true;
}
//////////////////////////////////////////////////////////////////////////
// SetEffect
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "SetEffect") == 0) {
stack->correctParams(2);
const char *materialName = stack->pop()->getString();
const char *effectFilename = stack->pop()->getString();
if (_xmodel && _xmodel->setMaterialEffect(materialName, effectFilename)) {
stack->pushBool(true);
} else {
stack->pushBool(false);
}
return true;
}
//////////////////////////////////////////////////////////////////////////
// RemoveEffect
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "RemoveEffect") == 0) {
stack->correctParams(1);
const char *materialName = stack->pop()->getString();
if (_xmodel && _xmodel->removeMaterialEffect(materialName)) {
stack->pushBool(true);
} else {
stack->pushBool(false);
}
return true;
}
//////////////////////////////////////////////////////////////////////////
// SetEffectParam
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "SetEffectParam") == 0) {
stack->correctParams(3);
const char *materialName = stack->pop()->getString();
const char *paramName = stack->pop()->getString();
ScValue *val = stack->pop();
if (_xmodel && _xmodel->setMaterialEffectParam(materialName, paramName, val)) {
stack->pushBool(true);
} else {
stack->pushBool(false);
}
return true;
}
//////////////////////////////////////////////////////////////////////////
// SetEffectParamVector
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "SetEffectParamVector") == 0) {
stack->correctParams(6);
const char *materialName = stack->pop()->getString();
const char *paramName = stack->pop()->getString();
float x = stack->pop()->getFloat();
float y = stack->pop()->getFloat();
float z = stack->pop()->getFloat();
float w = stack->pop()->getFloat();
if (_xmodel && _xmodel->setMaterialEffectParam(materialName, paramName, DXVector4(x, y, z, w))) {
stack->pushBool(true);
} else {
stack->pushBool(false);
}
return true;
}
//////////////////////////////////////////////////////////////////////////
// SetEffectParamColor
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "SetEffectParamColor") == 0) {
stack->correctParams(3);
const char *materialName = stack->pop()->getString();
const char *paramName = stack->pop()->getString();
uint32 color = stack->pop()->getInt();
float r = RGBCOLGetR(color) / 255.0f;
float g = RGBCOLGetG(color) / 255.0f;
float b = RGBCOLGetB(color) / 255.0f;
float a = RGBCOLGetA(color) / 255.0f;
if (_xmodel && _xmodel->setMaterialEffectParam(materialName, paramName, DXVector4(r, g, b, a))) {
stack->pushBool(true);
} else {
stack->pushBool(false);
}
return true;
}
//////////////////////////////////////////////////////////////////////////
// MergeAnims
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "MergeAnims") == 0) {
stack->correctParams(1);
const char *filename = stack->pop()->getString();
stack->pushBool(mergeAnimations(filename));
return true;
}
//////////////////////////////////////////////////////////////////////////
// UnloadAnim
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "UnloadAnim") == 0) {
stack->correctParams(1);
const char *animName = stack->pop()->getString();
stack->pushBool(unloadAnimation(animName));
return true;
}
//////////////////////////////////////////////////////////////////////////
// SetAnimTransitionTime
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "SetAnimTransitionTime") == 0) {
stack->correctParams(3);
const char *animFrom = stack->pop()->getString();
const char *animTo = stack->pop()->getString();
int time = stack->pop()->getInt();
bool found = false;
for (int32 i = 0; i < _transitionTimes.getSize(); i++) {
BaseAnimationTransitionTime *trans = _transitionTimes[i];
if (trans->_animFrom && trans->_animTo && scumm_stricmp(trans->_animFrom, animFrom) == 0 && scumm_stricmp(trans->_animTo, animTo) == 0) {
found = true;
if (time < 0) {
delete trans;
_transitionTimes.removeAt(i);
} else
trans->_time = (uint32)time;
break;
}
}
if (!found && time >= 0) {
BaseAnimationTransitionTime *trans = new BaseAnimationTransitionTime(animFrom, animTo, (uint32)time);
_transitionTimes.add(trans);
}
stack->pushNULL();
return true;
}
//////////////////////////////////////////////////////////////////////////
// GetAnimTransitionTime
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "GetAnimTransitionTime") == 0) {
stack->correctParams(2);
const char *animFrom = stack->pop()->getString();
const char *animTo = stack->pop()->getString();
int time = -1;
for (int32 i = 0; i < _transitionTimes.getSize(); i++) {
BaseAnimationTransitionTime *trans = _transitionTimes[i];
if (trans->_animFrom && trans->_animTo && scumm_stricmp(trans->_animFrom, animFrom) == 0 && scumm_stricmp(trans->_animTo, animTo) == 0) {
time = trans->_time;
break;
}
}
stack->pushInt(time);
return true;
}
//////////////////////////////////////////////////////////////////////////
// CreateParticleEmitterBone
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "CreateParticleEmitterBone") == 0) {
stack->correctParams(4);
const char *boneName = stack->pop()->getString();
float offsetX = stack->pop()->getFloat();
float offsetY = stack->pop()->getFloat();
float offsetZ = stack->pop()->getFloat();
PartEmitter *emitter = createParticleEmitter(boneName, DXVector3(offsetX, offsetY, offsetZ));
if (emitter)
stack->pushNative(_partEmitter, true);
else
stack->pushNULL();
return true;
} else {
return AdObject3D::scCallMethod(script, stack, thisStack, name);
}
}
//////////////////////////////////////////////////////////////////////////
ScValue *AdActor3DX::scGetProperty(const char *name) {
_scValue->setNULL();
//////////////////////////////////////////////////////////////////////////
// Type
//////////////////////////////////////////////////////////////////////////
if (strcmp(name, "Type") == 0) {
_scValue->setString("actor3dx");
return _scValue;
}
//////////////////////////////////////////////////////////////////////////
// TalkAnimName
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "TalkAnimName") == 0) {
_scValue->setString(_talkAnimName);
return _scValue;
}
//////////////////////////////////////////////////////////////////////////
// TalkAnimChannel
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "TalkAnimChannel") == 0) {
_scValue->setInt(_talkAnimChannel);
return _scValue;
}
//////////////////////////////////////////////////////////////////////////
// WalkAnimName
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "WalkAnimName") == 0) {
_scValue->setString(_talkAnimName);
return _scValue;
}
//////////////////////////////////////////////////////////////////////////
// IdleAnimName
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "IdleAnimName") == 0) {
_scValue->setString(_idleAnimName);
return _scValue;
}
//////////////////////////////////////////////////////////////////////////
// TurnLeftAnimName
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "TurnLeftAnimName") == 0) {
_scValue->setString(_turnLeftAnimName);
return _scValue;
}
//////////////////////////////////////////////////////////////////////////
// TurnRightAnimName
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "TurnRightAnimName") == 0) {
_scValue->setString(_turnRightAnimName);
return _scValue;
}
//////////////////////////////////////////////////////////////////////////
// DirectionAngle / DirAngle
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "DirectionAngle") == 0 || strcmp(name, "DirAngle") == 0) {
_scValue->setFloat(_angle);
return _scValue;
}
//////////////////////////////////////////////////////////////////////////
// Direction
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "Direction") == 0) {
_scValue->setInt(angleToDir(_angle));
return _scValue;
}
//////////////////////////////////////////////////////////////////////////
// AnimTransitionTime
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "AnimTransitionTime") == 0) {
_scValue->setInt(_defaultTransTime);
return _scValue;
}
//////////////////////////////////////////////////////////////////////////
// AnimStopTransitionTime
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "AnimStopTransitionTime") == 0) {
_scValue->setInt(_defaultStopTransTime);
return _scValue;
}
//////////////////////////////////////////////////////////////////////////
// GoToTolerance
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "GoToTolerance") == 0) {
_scValue->setInt(_goToTolerance);
return _scValue;
}
else {
return AdObject3D::scGetProperty(name);
}
}
//////////////////////////////////////////////////////////////////////////
bool AdActor3DX::scSetProperty(const char *name, ScValue *value) {
//////////////////////////////////////////////////////////////////////////
// TalkAnimName
//////////////////////////////////////////////////////////////////////////
if (strcmp(name, "TalkAnimName") == 0) {
if (value->isNULL()) {
BaseUtils::setString(&_talkAnimName, "talk");
} else {
BaseUtils::setString(&_talkAnimName, value->getString());
}
return true;
}
//////////////////////////////////////////////////////////////////////////
// TalkAnimChannel
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "TalkAnimChannel") == 0) {
_talkAnimChannel = value->getInt();
return true;
}
//////////////////////////////////////////////////////////////////////////
// WalkAnimName
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "WalkAnimName") == 0) {
if (value->isNULL()) {
BaseUtils::setString(&_walkAnimName, "walk");
} else {
BaseUtils::setString(&_walkAnimName, value->getString());
}
return true;
}
//////////////////////////////////////////////////////////////////////////
// IdleAnimName
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "IdleAnimName") == 0) {
if (value->isNULL()) {
BaseUtils::setString(&_idleAnimName, "idle");
} else {
BaseUtils::setString(&_idleAnimName, value->getString());
}
return true;
}
//////////////////////////////////////////////////////////////////////////
// TurnLeftAnimName
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "TurnLeftAnimName") == 0) {
if (value->isNULL()) {
BaseUtils::setString(&_turnLeftAnimName, "turnleft");
} else {
BaseUtils::setString(&_turnLeftAnimName, value->getString());
}
return true;
}
//////////////////////////////////////////////////////////////////////////
// TurnRightAnimName
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "TurnRightAnimName") == 0) {
if (value->isNULL()) {
BaseUtils::setString(&_turnRightAnimName, "turnright");
} else {
BaseUtils::setString(&_turnRightAnimName, value->getString());
}
return true;
}
//////////////////////////////////////////////////////////////////////////
// DirectionAngle / DirAngle
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "DirectionAngle") == 0 || strcmp(name, "DirAngle") == 0) {
_angle = BaseUtils::normalizeAngle(value->getFloat());
return true;
}
//////////////////////////////////////////////////////////////////////////
// Direction
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "Direction") == 0) {
_angle = dirToAngle((TDirection)value->getInt());
return true;
}
//////////////////////////////////////////////////////////////////////////
// AnimTransitionTime
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "AnimTransitionTime") == 0) {
_defaultTransTime = value->getInt();
return true;
}
//////////////////////////////////////////////////////////////////////////
// AnimStopTransitionTime
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "AnimStopTransitionTime") == 0) {
_defaultStopTransTime = value->getInt();
return true;
}
//////////////////////////////////////////////////////////////////////////
// GoToTolerance
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "GoToTolerance") == 0) {
_goToTolerance = value->getInt();
return true;
}
else {
return AdObject3D::scSetProperty(name, value);
}
}
//////////////////////////////////////////////////////////////////////////
const char *AdActor3DX::scToString() {
return "[actor3dx object]";
}
//////////////////////////////////////////////////////////////////////////
bool AdActor3DX::persist(BasePersistenceManager *persistMgr) {
AdObject3D::persist(persistMgr);
persistMgr->transferPtr(TMEMBER(_path3D));
persistMgr->transferPtr(TMEMBER(_path2D));
persistMgr->transferFloat(TMEMBER(_targetAngle));
persistMgr->transferVector3d(TMEMBER(_targetPoint3D));
persistMgr->transferPtr(TMEMBER(_targetPoint2D));
persistMgr->transferBool(TMEMBER(_turningLeft));
persistMgr->transferFloat(TMEMBER(_afterWalkAngle));
persistMgr->transferCharPtr(TMEMBER(_talkAnimName));
persistMgr->transferCharPtr(TMEMBER(_idleAnimName));
persistMgr->transferCharPtr(TMEMBER(_walkAnimName));
persistMgr->transferCharPtr(TMEMBER(_turnLeftAnimName));
persistMgr->transferCharPtr(TMEMBER(_turnRightAnimName));
// direct controls
persistMgr->transferSint32(TMEMBER_INT(_directWalkMode));
persistMgr->transferSint32(TMEMBER_INT(_directTurnMode));
persistMgr->transferCharPtr(TMEMBER(_directWalkAnim));
persistMgr->transferCharPtr(TMEMBER(_directTurnAnim));
persistMgr->transferFloat(TMEMBER(_directWalkVelocity));
persistMgr->transferFloat(TMEMBER(_directTurnVelocity));
// new for X
persistMgr->transferUint32(TMEMBER(_defaultTransTime));
_attachments.persist(persistMgr);
persistMgr->transferSint32(TMEMBER(_stateAnimChannel));
persistMgr->transferSint32(TMEMBER(_goToTolerance));
persistMgr->transferUint32(TMEMBER(_defaultStopTransTime));
if (persistMgr->getIsSaving()) {
int32 numItems = _transitionTimes.getSize();
persistMgr->transferSint32(TMEMBER(numItems));
for (int32 i = 0; i < _transitionTimes.getSize(); i++) {
_transitionTimes[i]->persist(persistMgr);
}
} else {
int32 numItems = _transitionTimes.getSize();
persistMgr->transferSint32(TMEMBER(numItems));
for (int32 i = 0; i < numItems; i++) {
BaseAnimationTransitionTime *trans = new BaseAnimationTransitionTime();
trans->persist(persistMgr);
_transitionTimes.add(trans);
}
}
persistMgr->transferSint32(TMEMBER(_talkAnimChannel));
persistMgr->transferCharPtr(TMEMBER(_partBone));
persistMgr->transferVector3d(TMEMBER(_partOffset));
return true;
}
//////////////////////////////////////////////////////////////////////////
bool AdActor3DX::invalidateDeviceObjects() {
if (_xmodel)
_xmodel->invalidateDeviceObjects();
if (_shadowModel)
_shadowModel->invalidateDeviceObjects();
for (int32 i = 0; i < _attachments.getSize(); i++) {
_attachments[i]->invalidateDeviceObjects();
}
return true;
}
//////////////////////////////////////////////////////////////////////////
bool AdActor3DX::restoreDeviceObjects() {
if (_xmodel) {
_xmodel->restoreDeviceObjects();
}
if (_shadowModel) {
_shadowModel->restoreDeviceObjects();
}
for (int32 i = 0; i < _attachments.getSize(); i++) {
_attachments[i]->restoreDeviceObjects();
}
return true;
}
//////////////////////////////////////////////////////////////////////////
bool AdActor3DX::mergeAnimations(const char *filename) {
if (!_xmodel) {
return false;
}
bool res = _xmodel->mergeFromFile(filename);
if (!res) {
_game->LOG(res, "Error: MergeAnims failed for file '%s'", filename);
return res;
}
AnsiString path = PathUtil::getDirectoryName(filename);
AnsiString name = PathUtil::getFileNameWithoutExtension(filename);
AnsiString animExtFile = PathUtil::combine(path, name + ".anim");
if (BaseFileManager::getEngineInstance()->hasFile(animExtFile)) {
return mergeAnimations2(animExtFile.c_str());
} else {
return true;
}
}
//////////////////////////////////////////////////////////////////////////
bool AdActor3DX::mergeAnimations2(const char *filename) {
TOKEN_TABLE_START(commands)
TOKEN_TABLE(ANIMATION)
TOKEN_TABLE_END
char *buffer = (char *)BaseFileManager::getEngineInstance()->readWholeFile(filename);
if (buffer == nullptr) {
return false;
}
char *bufferOrig = buffer;
char *params;
int cmd;
BaseParser parser(_game);
while ((cmd = parser.getCommand(&buffer, commands, ¶ms)) > 0) {
switch (cmd) {
case TOKEN_ANIMATION:
if (!_xmodel->parseAnim(params)) {
cmd = PARSERR_GENERIC;
}
}
}
delete[] bufferOrig;
if (cmd == PARSERR_TOKENNOTFOUND) {
_game->LOG(0, "Syntax error in animation definition file");
return false;
}
if (cmd == PARSERR_GENERIC) {
_game->LOG(0, "Error loading animation definition file");
return false;
}
return true;
}
//////////////////////////////////////////////////////////////////////////
bool AdActor3DX::unloadAnimation(const char *animName) {
if (_xmodel) {
return _xmodel->unloadAnimation(animName);
} else {
return false;
}
}
//////////////////////////////////////////////////////////////////////////
bool AdActor3DX::isGoToNeeded(int32 x, int32 y) {
if (ABS(x - _posX) <= _goToTolerance && ABS(y - _posY) <= _goToTolerance) {
return false;
} else {
return true;
}
}
//////////////////////////////////////////////////////////////////////////
uint32 AdActor3DX::getAnimTransitionTime(const char *from, const char *to) {
for (int32 i = 0; i < _transitionTimes.getSize(); i++) {
BaseAnimationTransitionTime *trans = _transitionTimes[i];
if (trans->_animFrom && trans->_animTo && scumm_stricmp(trans->_animFrom, from) == 0 && scumm_stricmp(trans->_animTo, to) == 0) {
return trans->_time;
}
}
return _defaultTransTime;
}
//////////////////////////////////////////////////////////////////////////
PartEmitter *AdActor3DX::createParticleEmitter(bool followParent, int offsetX, int offsetY) {
SAFE_DELETE_ARRAY(_partBone);
return AdObject::createParticleEmitter(followParent, offsetX, offsetY);
}
//////////////////////////////////////////////////////////////////////////
PartEmitter *AdActor3DX::createParticleEmitter(const char *boneName, DXVector3 offset) {
BaseUtils::setString(&_partBone, boneName);
_partOffset = offset;
return AdObject::createParticleEmitter(true);
}
//////////////////////////////////////////////////////////////////////////
bool AdActor3DX::updatePartEmitter() {
if (!_partEmitter) {
return false;
}
if (!_partBone) {
return AdObject::updatePartEmitter();
}
AdGame *adGame = (AdGame *)_game;
if (!adGame->_scene || !adGame->_scene->_geom) {
return false;
}
DXVector3 bonePos;
getBonePosition3D(_partBone, &bonePos, &_partOffset);
int32 x = 0, y = 0;
static_cast(_game)->_scene->_geom->convert3Dto2D(&bonePos, &x, &y);
_partEmitter->_posX = x - _game->_renderer->_drawOffsetX;
_partEmitter->_posY = y - _game->_renderer->_drawOffsetY;
return _partEmitter->update();
}
//////////////////////////////////////////////////////////////////////////
bool AdActor3DX::parseEffect(char *buffer) {
TOKEN_TABLE_START(commands)
TOKEN_TABLE(MATERIAL)
TOKEN_TABLE(EFFECT_FILE)
TOKEN_TABLE_END
char *params;
int cmd;
BaseParser parser(_game);
char *effectFile = nullptr;
char *material = nullptr;
while ((cmd = parser.getCommand(&buffer, commands, ¶ms)) > 0) {
switch (cmd) {
case TOKEN_EFFECT_FILE:
BaseUtils::setString(&effectFile, params);
break;
case TOKEN_MATERIAL:
BaseUtils::setString(&material, params);
break;
}
}
if (cmd != PARSERR_EOF) {
return false;
}
if (effectFile && material) {
if (!_xmodel->setMaterialEffect(material, effectFile)) {
_game->LOG(0, "Error assigning effect to material '%s'", material);
}
}
SAFE_DELETE_ARRAY(effectFile);
SAFE_DELETE_ARRAY(material);
return true;
}
} // namespace Wintermute