2537 lines
69 KiB
C++
2537 lines
69 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 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 <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* 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<AdGame *>(_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
|