Files
scummvm/engines/bladerunner/script/police_maze.cpp
T

604 lines
12 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "bladerunner/bladerunner.h"
#include "bladerunner/game_constants.h"
#include "bladerunner/items.h"
#include "bladerunner/mouse.h"
#include "bladerunner/savefile.h"
#include "bladerunner/scene.h"
#include "bladerunner/scene_objects.h"
#include "bladerunner/script/police_maze.h"
#include "bladerunner/script/scene_script.h"
namespace BladeRunner {
PoliceMaze::PoliceMaze(BladeRunnerEngine *vm) : ScriptBase(vm) {
reset();
for (int i = 0; i < kNumMazeTracks; i++) {
_tracks[i] = new PoliceMazeTargetTrack(vm);
}
}
PoliceMaze::~PoliceMaze() {
for (int i = 0; i < kNumMazeTracks; i++) {
delete _tracks[i];
}
reset();
}
void PoliceMaze::reset() {
_isPaused = false;
_isActive = false;
_isEnding = false;
for (int i = 0; i < kNumMazeTracks; i++) {
_tracks[i] = 0;
}
_pm_var1 = 0;
_pm_var2 = 0;
}
void PoliceMaze::clear(bool isLoadingGame) {
for (int i = 0; i < kNumMazeTracks; i++) {
if (_tracks[i]->isPresent()) {
_tracks[i]->clear(isLoadingGame);
}
}
}
void PoliceMaze::activate() {
_isActive = true;
_isEnding = false;
}
void PoliceMaze::setPauseState(bool state) {
warning("PAUSE: %d", state);
_isPaused = state;
uint32 t = _vm->getTotalPlayTime();
for (int i = 0; i < kNumMazeTracks; i++) {
_tracks[i]->setTime(t);
}
}
void PoliceMaze::tick() {
if (_isPaused) {
return;
}
if (_vm->_scene->getSetId() != kSetPS10_PS11_PS12_PS13) {
return;
}
if (_isEnding) {
_isActive = false;
return;
}
for (int i = 0; i < kNumMazeTracks; i++) {
_tracks[i]->tick();
}
bool notFound = true;
for (int i = 0; i < kNumMazeTracks; i++) {
if (!_tracks[i]->isPaused()) {
notFound = false;
break;
}
}
if (notFound && _isActive && !_isEnding) {
_isActive = false;
_isEnding = true;
if (_vm->_scene->getSceneId() == kScenePS13) {
Actor_Voice_Over(320, kActorAnsweringMachine);
} else {
Actor_Voice_Over(310, kActorAnsweringMachine);
}
}
}
void PoliceMaze::save(SaveFileWriteStream &f) {
f.writeBool(_isPaused);
f.writeBool(_isActive);
f.writeBool(_isEnding);
for (int i = 0; i < kNumMazeTracks; ++i) {
_tracks[i]->save(f);
}
}
void PoliceMaze::load(SaveFileReadStream &f) {
_isPaused = f.readBool();
_isActive = f.readBool();
_isEnding = f.readBool();
for (int i = 0; i < kNumMazeTracks; ++i) {
_tracks[i]->load(f);
}
}
PoliceMazeTargetTrack::PoliceMazeTargetTrack(BladeRunnerEngine *vm) : ScriptBase(vm) {
reset();
}
PoliceMazeTargetTrack::~PoliceMazeTargetTrack() {
reset();
}
void PoliceMazeTargetTrack::reset() {
_isPresent = false;
_itemId = -1;
_pointCount = 0;
_data = nullptr;
_dataIndex = 0;
_timeLeftUpdate = 0;
_timeLeftWait = 0;
_time = 0;
_isWaiting = false;
_isMoving = false;
_pointIndex = 0;
_pointTarget = 0;
_isRotating = false;
_angleTarget = 0;
_angleDelta = 0;
_isPaused = true;
}
void PoliceMazeTargetTrack::clear(bool isLoadingGame) {
reset();
}
void PoliceMazeTargetTrack::add(int trackId, float startX, float startY, float startZ, float endX, float endY, float endZ, int steps, const int *instructions, bool isActive) {
_data = (const int *)instructions;
if (true /* !GameIsLoading */) { // TODO: FIXME
_itemId = trackId;
_pointCount = steps;
_dataIndex = 0;
double coef = 1.0f / (long double)steps;
double coefX = (endX - startX) * coef;
double coefY = (endY - startY) * coef;
double coefZ = (endZ - startZ) * coef;
for (int i = 0; i < steps - 1; i++) {
_points[i].x = i * coefX + startX;
_points[i].y = i * coefY + startY;
_points[i].z = i * coefZ + startZ;
}
_points[steps - 1].x = endX;
_points[steps - 1].y = endY;
_points[steps - 1].z = endZ;
_isPaused = !isActive;
}
_isPresent = true;
}
bool PoliceMazeTargetTrack::tick() {
if (!_isPresent) {
return false;
}
uint32 oldTime = _time;
_time = _vm->getTotalPlayTime();
int32 timeDiff = _time - oldTime;
_timeLeftUpdate -= timeDiff;
if (_timeLeftUpdate > 0) {
return false;
}
_timeLeftUpdate = 66;
if (_isPaused) {
return false;
}
if (_isWaiting) {
_timeLeftWait -= timeDiff;
if (_timeLeftWait > 0) {
return true;
}
_isWaiting = false;
_timeLeftWait = 0;
}
if (_vm->_items->isSpinning(_itemId)) {
return true;
}
if (_isRotating) {
float angle = _vm->_items->getFacing(_itemId) + _angleDelta;
if (_angleDelta > 0) {
if (angle >= _angleTarget) {
angle = _angleTarget;
_isRotating = false;
}
} else if (_angleDelta < 0) {
if (angle <= _angleTarget) {
angle = _angleTarget;
_isRotating = false;
}
} else {
_isRotating = false;
}
_vm->_items->setFacing(_itemId, angle);
if (_isRotating)
return false;
}
bool advancePoint = false;
if (_isMoving) {
if (_pointIndex < _pointTarget) {
_pointIndex++;
advancePoint = true;
} else if (_pointIndex > _pointTarget) {
_pointIndex--;
advancePoint = true;
} else {
_isMoving = 0;
}
}
if (advancePoint) {
_vm->_items->setXYZ(_itemId, _points[_pointIndex]);
readdObject(_itemId);
return true;
}
bool cont = true;
while (cont) {
_dataIndex++;
debug ("ItemId %3i, pos %3i, instruction %3i", _itemId, _dataIndex - 1, _data[_dataIndex - 1]);
switch (_data[_dataIndex - 1]) {
case kPMTIActivate:
{
int variableId = _data[_dataIndex++];
int maxValue = _data[_dataIndex++];
if (Global_Variable_Query(variableId) >= maxValue) {
setPaused();
cont = false;
} else {
cont = true;
}
break;
}
case kPMTILeave:
if (!_vm->_items->isPoliceMazeEnemy(_itemId) && _vm->_items->isTarget(_itemId)) {
Police_Maze_Increment_Score(1);
}
break;
case kPMTIShoot:
{
int soundId = _data[_dataIndex++];
_dataIndex++; // second argument is not used
if (_vm->_items->isTarget(_itemId)) {
Sound_Play(soundId, 90, 0, 0, 50);
Police_Maze_Decrement_Score(1);
Actor_Force_Stop_Walking(kActorMcCoy);
if (Player_Query_Combat_Mode()) {
Actor_Change_Animation_Mode(kActorMcCoy, kAnimationModeCombatHit);
} else {
Actor_Change_Animation_Mode(kActorMcCoy, kAnimationModeHit);
}
int snd;
if (Random_Query(1, 2) == 1) {
snd = 9900;
} else {
snd = 9905;
}
Sound_Play_Speech_Line(kActorMcCoy, snd, 75, 0, 99);
_vm->_mouse->setMouseJitterDown();
}
cont = false;
break;
}
case kPMTIEnemyReset:
{
int itemId = _data[_dataIndex++];
_vm->_items->setPoliceMazeEnemy(itemId, false);
break;
}
case kPMTIEnemySet:
{
int itemId = _data[_dataIndex++];
_vm->_items->setPoliceMazeEnemy(itemId, true);
break;
}
case kPMTIFlagReset:
{
int gameFlagId = _data[_dataIndex++];
Game_Flag_Reset(gameFlagId);
break;
}
case kPMTIFlagSet:
{
int gameFlagId = _data[_dataIndex++];
Game_Flag_Set(gameFlagId);
break;
}
case kPMTIVariableDec:
{
int variableId = _data[_dataIndex++];
Global_Variable_Decrement(variableId, 1);
break;
}
case kPMTIVariableInc:
{
int variableId = _data[_dataIndex++];
int maxValue = _data[_dataIndex++];
if (Global_Variable_Query(variableId) < maxValue) {
Global_Variable_Increment(variableId, 1);
}
break;
}
case kPMTIVariableReset:
{
int variableId = _data[_dataIndex++];
Global_Variable_Reset(variableId);
break;
}
case kPMTIVariableSet:
{
int variableId = _data[_dataIndex++];
int value = _data[_dataIndex++];
Global_Variable_Set(variableId, value);
break;
}
case kPMTITargetSet:
{
int itemId = _data[_dataIndex++];
int value = _data[_dataIndex++];
_vm->_items->setIsTarget(itemId, value);
break;
}
case kPMTI12:
{
int trackId1 = _data[_dataIndex++];
int trackId2 = _data[_dataIndex++];
int trackId3 = _data[_dataIndex++];
switch (Random_Query(1, 3)) {
case 1:
_vm->_policeMaze->_tracks[trackId1]->resetPaused();
break;
case 2:
_vm->_policeMaze->_tracks[trackId2]->resetPaused();
break;
case 3:
_vm->_policeMaze->_tracks[trackId3]->resetPaused();
break;
}
break;
}
case kPMTI13:
{
int trackId1 = _data[_dataIndex++];
int trackId2 = _data[_dataIndex++];
if (Random_Query(1, 2) == 1) {
_vm->_policeMaze->_tracks[trackId1]->resetPaused();
} else {
_vm->_policeMaze->_tracks[trackId2]->resetPaused();
}
break;
}
case kPMTIPausedSet:
{
int trackId = _data[_dataIndex++];
_vm->_policeMaze->_tracks[trackId]->setPaused();
break;
}
case kPMTIPausedReset:
{
int trackId = _data[_dataIndex++];
_vm->_policeMaze->_tracks[trackId]->resetPaused();
break;
}
case kPMTIPlaySound:
{
int soundId = _data[_dataIndex++];
int volume = _data[_dataIndex++];
Sound_Play(soundId, volume, 0, 0, 50);
break;
}
case kPMTIObstacleReset:
{
int itemId = _data[_dataIndex++];
_vm->_items->setIsObstacle(itemId, 0);
break;
}
case kPMTIObstacleSet:
{
int itemId = _data[_dataIndex++];
_vm->_items->setIsObstacle(itemId, 1);
break;
}
case kPMTIWaitRandom:
{
int randomMin = _data[_dataIndex++];
int randomMax = _data[_dataIndex++];
_timeLeftWait = Random_Query(randomMin, randomMax);
_isWaiting = true;
cont = false;
break;
}
case kPMTIRotate:
_angleTarget = _data[_dataIndex++];
_angleDelta = _data[_dataIndex++];
_isRotating = true;
cont = false;
break;
case kPMTIFacing:
{
int angle = _data[_dataIndex++];
_vm->_items->setFacing(_itemId, angle);
break;
}
case kPMTIRestart:
_dataIndex = 0;
cont = false;
break;
case kPMTIWait:
_timeLeftWait = _data[_dataIndex++];
_isWaiting = true;
cont = false;
break;
case kPMTIMove:
_pointTarget = _data[_dataIndex++];
_isMoving = true;
cont = false;
break;
case kPMTIPosition:
_pointIndex = _data[_dataIndex++];
_isMoving = false;
_vm->_items->setXYZ(_itemId, _points[_pointIndex]);
readdObject(_itemId);
break;
default:
return false;
}
if (_isPaused || _isWaiting) {
cont = false;
}
}
return true;
}
void PoliceMazeTargetTrack::readdObject(int itemId) {
if (_vm->_sceneObjects->remove(itemId + kSceneObjectOffsetItems)) {
const BoundingBox &boundingBox = _vm->_items->getBoundingBox(itemId);
const Common::Rect &screenRect = _vm->_items->getScreenRectangle(itemId);
bool targetable = _vm->_items->isTarget(itemId);
bool obstacle = _vm->_items->isVisible(itemId);
_vm->_sceneObjects->addItem(itemId + kSceneObjectOffsetItems, boundingBox, screenRect, targetable, obstacle);
}
}
void PoliceMazeTargetTrack::save(SaveFileWriteStream &f) {
f.writeBool(_isPresent);
f.writeInt(_itemId);
f.writeInt(_pointCount);
f.writeInt(_dataIndex);
f.writeBool(_isWaiting);
f.writeBool(_isMoving);
f.writeInt(_pointIndex);
f.writeInt(_pointTarget);
f.writeBool(_isRotating);
f.writeInt(_angleTarget);
f.writeInt(_angleDelta);
f.writeBool(_isPaused);
for (int i = 0; i < kNumTrackPoints; ++i) {
f.writeVector3(_points[i]);
}
f.writeInt(_timeLeftUpdate);
f.writeInt(_timeLeftWait);
}
void PoliceMazeTargetTrack::load(SaveFileReadStream &f) {
_isPresent = f.readBool();
_itemId = f.readInt();
_pointCount = f.readInt();
_dataIndex = f.readInt();
_isWaiting = f.readBool();
_isMoving = f.readBool();
_pointIndex = f.readInt();
_pointTarget = f.readInt();
_isRotating = f.readBool();
_angleTarget = f.readInt();
_angleDelta = f.readInt();
_isPaused = f.readBool();
for (int i = 0; i < kNumTrackPoints; ++i) {
_points[i] = f.readVector3();
}
_timeLeftUpdate = f.readInt();
_timeLeftWait = f.readInt();
}
} // End of namespace BladeRunner