Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,2 @@
engines/illusions/menusystem.cpp
engines/illusions/metaengine.cpp

1519
engines/illusions/actor.cpp Normal file

File diff suppressed because it is too large Load Diff

291
engines/illusions/actor.h Normal file
View File

@@ -0,0 +1,291 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_ACTOR_H
#define ILLUSIONS_ACTOR_H
#include "illusions/resources/actorresource.h"
#include "illusions/resources/backgroundresource.h"
#include "illusions/graphics.h"
#include "illusions/pathfinder.h"
#include "common/algorithm.h"
#include "common/func.h"
#include "common/list.h"
#include "graphics/surface.h"
namespace Illusions {
class Control;
class IllusionsEngine;
class SequenceOpcodes;
struct OpCall;
enum ActorFlags {
ACTOR_FLAG_IS_VISIBLE = 1,
ACTOR_FLAG_HAS_WALK_POINTS = 2,
ACTOR_FLAG_SCALED = 4,
ACTOR_FLAG_PRIORITY = 8,
ACTOR_FLAG_HAS_WALK_RECTS = 0x10,
ACTOR_FLAG_REGION = 0x20,
ACTOR_FLAG_40 = 0x40,
ACTOR_FLAG_80 = 0x80,
ACTOR_FLAG_100 = 0x100,
ACTOR_FLAG_200 = 0x200,
ACTOR_FLAG_400 = 0x400,
ACTOR_FLAG_800 = 0x800,
ACTOR_FLAG_1000 = 0x1000,
ACTOR_FLAG_2000 = 0x2000,
ACTOR_FLAG_4000 = 0x4000,
ACTOR_FLAG_8000 = 0x8000
};
enum ControlObjectID {
CURSOR_OBJECT_ID = 0x40004
};
const uint kSubObjectsCount = 15;
struct DefaultSequence {
uint32 _sequenceId;
uint32 _newSequenceId;
DefaultSequence()
: _sequenceId(0), _newSequenceId(0) {}
DefaultSequence(uint32 sequenceId, uint32 newSequenceId)
: _sequenceId(sequenceId), _newSequenceId(newSequenceId) {}
};
class DefaultSequences {
public:
uint32 use(uint32 sequenceId);
void set(uint32 sequenceId, uint32 newSequenceId);
protected:
typedef Common::Array<DefaultSequence> Items;
typedef Items::iterator ItemsIterator;
struct DefaultSequenceEqual : public Common::UnaryFunction<const DefaultSequence&, bool> {
uint32 _sequenceId;
DefaultSequenceEqual(uint32 sequenceId) : _sequenceId(sequenceId) {}
bool operator()(const DefaultSequence &defaultSequence) const {
return defaultSequence._sequenceId == _sequenceId;
}
};
Common::Array<DefaultSequence> _items;
};
typedef Common::Functor2<Control*, uint32, void> ActorControlRoutine;
class Actor {
public:
Actor(IllusionsEngine *vm);
~Actor();
void pause();
void unpause();
void createSurface(SurfInfo &surfInfo);
void destroySurface();
void initSequenceStack();
void pushSequenceStack(int16 value);
int16 popSequenceStack();
void setControlRoutine(ActorControlRoutine *controlRoutine);
void runControlRoutine(Control *control, uint32 deltaTime);
bool findNamedPoint(uint32 namedPointId, Common::Point &pt);
public:
IllusionsEngine *_vm;
byte _drawFlags;
uint _spriteFlags;
int _pauseCtr;
uint _flags;
int _scale;
int16 _frameIndex;
int16 _newFrameIndex;
SurfInfo _surfInfo;
Graphics::Surface *_surface;
FramesList *_frames;
NamedPoints *_namedPoints;
ScaleLayer *_scaleLayer;
PriorityLayer *_priorityLayer;
RegionLayer *_regionLayer;
PathWalkPoints *_pathWalkPoints;
PathWalkRects *_pathWalkRects;
uint _seqStackCount;
int16 _seqStack[5];
Common::Point _position;
Common::Point _position2;
uint _facing;
int _regionIndex;
uint32 _fontId;
int16 _actorIndex;
DefaultSequences _defaultSequences;
uint32 _parentObjectId;
int _linkIndex;
int _linkIndex2;
uint32 _subobjects[kSubObjectsCount];
uint32 _notifyThreadId1;
uint32 _notifyId3C;
uint32 _notifyThreadId2;
byte *_entryTblPtr;
ActorControlRoutine *_controlRoutine;
uint32 _sequenceId;
int _seqCodeValue2;
byte *_seqCodeIp;
int _seqCodeValue1;
int _seqCodeValue3;
int _pathCtrX, _pathCtrY;
int _pathAngle;
int32 _posXShl, _posYShl;
uint _pathPointIndex;
uint _pathPointsCount;
Common::Point _pathInitialPos;
bool _pathInitialPosFlag;
bool _pathFlag50;
PointArray *_pathNode;
uint _pathPoints;
uint32 _walkCallerThreadId1;
RGB _color;
int16 _choiceJumpOffs;
};
class Control {
public:
Control(IllusionsEngine *vm);
~Control();
void pause();
void unpause();
void appearActor();
void disappearActor();
bool isActorVisible();
void activateObject();
void deactivateObject();
void readPointsConfig(byte *pointsConfig);
void setActorPosition(Common::Point position);
Common::Point getActorPosition();
void setActorScale(int scale);
void faceActor(uint facing);
void linkToObject(uint32 parentObjectId, uint32 linkedObjectValue);
void unlinkObject();
void clearNotifyThreadId1();
void clearNotifyThreadId2();
void setPriority(int16 priority);
uint32 getOverlapPriority();
uint32 getDrawPriority();
uint32 getPriority();
Common::Point calcPosition(Common::Point posDelta);
uint32 getSubActorParent();
void getCollisionRectAccurate(Common::Rect &collisionRect);
void getCollisionRect(Common::Rect &collisionRect);
void setActorUsePan(int usePan);
void setActorFrameIndex(int16 frameIndex);
void stopActor();
void startSequenceActor(uint32 sequenceId, int value, uint32 notifyThreadId);
void stopSequenceActor();
void startTalkActor(uint32 sequenceId, byte *entryTblPtr, uint32 threadId);
void sequenceActor();
void setActorIndex(int actorIndex);
void setActorIndexTo1();
void setActorIndexTo2();
void startSubSequence(int linkIndex, uint32 sequenceId);
void stopSubSequence(int linkIndex);
void startMoveActor(uint32 sequenceId, Common::Point destPt, uint32 callerThreadId1, uint32 callerThreadId2);
PointArray *createPath(Common::Point destPt);
void updateActorMovement(uint32 deltaTime);
void refreshSequenceCode();
void getActorFrameDimensions(WidthHeight &dimensions);
void drawActorRect(const Common::Rect &r, byte color);
void fillActor(byte color);
bool isPixelCollision(Common::Point &pt);
public:
IllusionsEngine *_vm;
uint _flags;
int _pauseCtr;
int16 _priority;
Actor *_actor;
uint32 _sceneId;
uint32 _objectId;
uint32 _actorTypeId;
WRect _bounds;
Common::Point _feetPt;
Common::Point _position;
Common::Point _subobjectsPos[kSubObjectsCount];
void startSequenceActorIntern(uint32 sequenceId, int value, byte *entryTblPtr, uint32 notifyThreadId);
void execSequenceOpcode(OpCall &opCall);
};
class Controls {
public:
Controls(IllusionsEngine *vm);
~Controls();
void placeBackgroundObject(BackgroundObject *backgroundObject);
void placeActor(uint32 actorTypeId, Common::Point placePt, uint32 sequenceId, uint32 objectId, uint32 notifyThreadId);
void placeSequenceLessActor(uint32 objectId, Common::Point placePt, WidthHeight dimensions, int16 priority);
void placeActorLessObject(uint32 objectId, Common::Point feetPt, Common::Point pt, int16 priority, uint flags);
void placeSubActor(uint32 objectId, int linkIndex, uint32 actorTypeId, uint32 sequenceId);
void placeDialogItem(uint16 objectNum, uint32 actorTypeId, uint32 sequenceId, Common::Point placePt, int16 choiceJumpOffs);
void destroyControls();
void destroyActiveControls();
void destroyControlsBySceneId(uint32 sceneId);
void destroyDialogItems();
void threadIsDead(uint32 threadId);
void pauseControls();
void unpauseControls();
void pauseControlsBySceneId(uint32 sceneId);
void unpauseControlsBySceneId(uint32 sceneId);
bool getOverlappedObject(Control *control, Common::Point pt, Control **outOverlappedControl, int minPriority);
bool getOverlappedObjectAccurate(Control *control, Common::Point pt, Control **outOverlappedControl, int minPriority);
bool getDialogItemAtPos(Control *control, Common::Point pt, Control **outOverlappedControl);
bool getOverlappedWalkObject(Control *control, Common::Point pt, Control **outOverlappedControl);
void destroyControl(Control *control);
bool findNamedPoint(uint32 namedPointId, Common::Point &pt);
void actorControlRoutine(Control *control, uint32 deltaTime);
void dialogItemControlRoutine(Control *control, uint32 deltaTime);
void disappearActors();
void appearActors();
void pauseActors(uint32 objectId);
void unpauseActors(uint32 objectId);
public:
typedef Common::List<Control*> Items;
typedef Items::iterator ItemsIterator;
IllusionsEngine *_vm;
Items _controls;
SequenceOpcodes *_sequenceOpcodes;
uint32 _nextTempObjectId;
Actor *newActor();
Control *newControl();
uint32 newTempObjectId();
void destroyControlInternal(Control *control);
};
} // End of namespace Illusions
#endif // ILLUSIONS_ACTOR_H

View File

@@ -0,0 +1,293 @@
/* 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/>.
*
*/
#include "illusions/bbdou/illusions_bbdou.h"
#include "illusions/bbdou/bbdou_bubble.h"
#include "illusions/actor.h"
#include "illusions/camera.h"
#include "illusions/dictionary.h"
#include "illusions/input.h"
namespace Illusions {
BbdouBubble::BbdouBubble(IllusionsEngine_BBDOU *vm, BbdouSpecialCode *bbdou)
: _vm(vm), _bbdou(bbdou) {
}
BbdouBubble::~BbdouBubble() {
}
void BbdouBubble::init() {
static const uint32 kTrailObjectIds[] = {
0x0004003B, 0x0004003C, 0x0004003D, 0x0004003E,
0x0004003F, 0x00040040, 0x00040041, 0x00040042,
0x00040043, 0x00040044, 0x00040045, 0x00040046,
0x00040047, 0x00040048, 0x00040049, 0x0004004A,
0x0004004B, 0x0004004C, 0x0004004D, 0x0004004E,
0x0004004F, 0x00040050, 0x00040051, 0x00040052,
0x00040053, 0x00040054, 0x00040055, 0x00040056,
0x00040057, 0x00040058, 0x00040059, 0x0004005A
};
static const uint32 kIconObjectIds[] = {
0x0004001B, 0x0004001C, 0x0004001D, 0x0004001E,
0x0004001F, 0x00040020, 0x00040021, 0x00040022,
0x00040023, 0x00040024, 0x00040025, 0x00040026,
0x00040027, 0x00040028, 0x00040029, 0x0004002A,
0x0004002B, 0x0004002C, 0x0004002D, 0x0004002E,
0x0004002F, 0x00040030, 0x00040031, 0x00040032,
0x00040033, 0x00040034, 0x00040035, 0x00040036,
0x00040037, 0x00040038, 0x00040039, 0x0004003A
};
_bubbleObjectId1 = 0x4005B;
_bubbleObjectId2 = 0x4005C;
for (uint i = 0; i < 32; ++i) {
_trailObjectIds[i] = kTrailObjectIds[i];
}
for (uint i = 0; i < 32; ++i) {
_icons[i]._objectId = kIconObjectIds[i];
_icons[i]._enabled = false;
_icons[i]._position.x = 0;
_icons[i]._position.y = 0;
_icons[i]._sequenceId = 0;
}
_currBubbleStyle = nullptr;
_showingBubbleStyle = nullptr;
_hidingBubbleStyle = nullptr;
_sourcePt.x = 0;
_sourcePt.y = 0;
_destPt.x = 0;
_destPt.y = 0;
}
void BbdouBubble::addBubbleStyle(uint32 showSequenceId, uint32 hideSequenceId, uint32 progResKeywordId,
uint32 namedPointId, int16 count, uint32 *namedPointIds) {
BubbleStyle style;
style._showSequenceId = showSequenceId;
style._hideSequenceId = hideSequenceId;
style._progResKeywordId = progResKeywordId;
style._baseNamedPointId = namedPointId;
style._count = count;
for (int16 i = 0; i < count; ++i) {
style._namedPointIds[i] = FROM_LE_32(namedPointIds[i]);
}
style._objectId = 0;
style._position.x = 0;
style._position.y = 0;
_bubbleStyles.push_back(style);
}
void BbdouBubble::show() {
if (_showingBubbleStyle) {
hide();
}
_showingBubbleStyle = _currBubbleStyle;
_currBubbleStyle = nullptr;
calcBubbleTrail(_sourcePt, _destPt);
Control *bubbleControl = _vm->_dict->getObjectControl(_showingBubbleStyle->_objectId);
bubbleControl->setActorPosition(_destPt);
bubbleControl->startSequenceActor(0x60057, 2, 0);
bubbleControl->startSequenceActor(_showingBubbleStyle->_showSequenceId, 2, 0);
bubbleControl->appearActor();
bubbleControl->deactivateObject();
for (uint i = 0; i < 32; ++i) {
if (_icons[i]._enabled) {
Control *iconControl = _vm->_dict->getObjectControl(_icons[i]._objectId);
iconControl->setActorPosition(_icons[i]._position);
iconControl->startSequenceActor(_icons[i]._sequenceId, 2, 0);
}
}
}
void BbdouBubble::hide() {
_hidingBubbleStyle = _showingBubbleStyle;
_showingBubbleStyle = nullptr;
if (_hidingBubbleStyle) {
Control *bubbleControl = _vm->_dict->getObjectControl(_hidingBubbleStyle->_objectId);
bubbleControl->startSequenceActor(_hidingBubbleStyle->_hideSequenceId, 2, 0);
for (uint i = 0; i < 32; ++i) {
Control *trailControl = _vm->_dict->getObjectControl(_trailObjectIds[i]);
trailControl->stopActor();
trailControl->disappearActor();
}
for (uint i = 0; i < 32; ++i) {
Control *iconControl = _vm->_dict->getObjectControl(_icons[i]._objectId);
iconControl->stopActor();
iconControl->disappearActor();
}
}
}
void BbdouBubble::selectBubbleStyle(int16 minCount, Common::Point sourcePt, Common::Point destPt, uint32 progResKeywordId) {
for (uint i = 0; i < 32; ++i) {
_icons[i]._enabled = false;
}
int16 maxCount = 32;
for (uint i = 0; i < _bubbleStyles.size(); ++i) {
BubbleStyle *style = &_bubbleStyles[i];
if (style->_count < maxCount && style->_count >= minCount &&
(progResKeywordId == 0 || progResKeywordId == style->_progResKeywordId)) {
maxCount = style->_count;
_currBubbleStyle = style;
}
}
_sourcePt = sourcePt;
_destPt = destPt;
_currBubbleStyle->_position = destPt;
_currBubbleStyle->_objectId = _bubbleObjectId1;
if (_showingBubbleStyle && _showingBubbleStyle->_objectId == _currBubbleStyle->_objectId)
_currBubbleStyle->_objectId = _bubbleObjectId2;
}
uint32 BbdouBubble::addBubbleIcon(uint positionIndex, uint32 sequenceId) {
for (uint i = 0; i < 32; ++i) {
BubbleIcon *icon = &_icons[i];
if (!icon->_enabled) {
Common::Point itemPos = _vm->getNamedPointPosition(_currBubbleStyle->_namedPointIds[positionIndex]);
Common::Point basePos = _vm->getNamedPointPosition(_currBubbleStyle->_baseNamedPointId);
icon->_enabled = true;
icon->_sequenceId = sequenceId;
icon->_position.x = itemPos.x + _currBubbleStyle->_position.x - basePos.x;
icon->_position.y = itemPos.y + _currBubbleStyle->_position.y - basePos.y;
return icon->_objectId;
}
}
return 0;
}
void BbdouBubble::calcBubbleTrail(Common::Point &sourcePt, Common::Point &destPt) {
const int kSequenceIdsCount = 10;
const float kDistanceBetweenPoints = 30.0;
static const uint32 kBubbleTrailSequenceIds[] = {
0x00060042, 0x00060043, 0x00060044, 0x00060045, 0x00060046,
0x00060047, 0x00060048, 0x00060049, 0x0006004A, 0x0006004B
};
static const int kIndexTbl[kSequenceIdsCount] = {4, 0, 8, 2, 6, 5, 1, 9, 3, 7};
int sequenceCounters[kSequenceIdsCount];
bool swapY;
int centerX, centerY;
float currentAngle, radius;
for (int i = 0; i < 32; ++i) {
Control *control = _vm->_dict->getObjectControl(_trailObjectIds[i]);
control->startSequenceActor(0x00060056, 2, 0);
}
for (int i = 0; i < kSequenceIdsCount; ++i) {
sequenceCounters[i] = 0;
}
if (destPt.y >= sourcePt.y) {
swapY = true;
if (sourcePt.x == destPt.x)
destPt.x = destPt.x + 20;
} else {
swapY = false;
if (sourcePt.y == destPt.y)
destPt.y = destPt.y + 20;
}
if (swapY) {
centerX = (destPt.x * destPt.x - (destPt.y - sourcePt.y) * (destPt.y - sourcePt.y) - sourcePt.x * sourcePt.x) / (2 * (destPt.x - sourcePt.x));
centerY = destPt.y;
radius = ABS(destPt.x - centerX);
} else {
centerX = destPt.x;
centerY = (destPt.y * destPt.y - (destPt.x - sourcePt.x) * (destPt.x - sourcePt.x) - sourcePt.y * sourcePt.y) / (2 * (destPt.y - sourcePt.y));
radius = ABS(destPt.y - centerY);
}
const float fullDistance = sqrt((destPt.y - sourcePt.y) * (destPt.y - sourcePt.y) + (destPt.x - sourcePt.x) * (destPt.x - sourcePt.x));
const float arcAngle = 2 * asin(CLIP(0.5 * fullDistance / radius, -1.0, 1.0));
const float arcLength = arcAngle * radius;
int pointsCount = (int)(arcLength / kDistanceBetweenPoints);
float partAngle = ABS(kDistanceBetweenPoints / radius);
for (int i = 0; i < pointsCount; ++i) {
++sequenceCounters[kIndexTbl[i % kSequenceIdsCount]];
}
if (!swapY) {
if (destPt.y < sourcePt.y) {
currentAngle = (float)M_PI * 0.5F;
} else {
currentAngle = (float)M_PI * 1.5F;
partAngle = -partAngle;
}
if (destPt.x < sourcePt.x)
partAngle = -partAngle;
} else {
if (destPt.x <= sourcePt.x) {
currentAngle = (float)M_PI;
} else {
currentAngle = 0.0;
partAngle = -partAngle;
}
if (destPt.y > sourcePt.y)
partAngle = -partAngle;
}
float angleStep = partAngle / (float)pointsCount * 0.5;
float angleIncr = (float)(pointsCount / 2) * angleStep + partAngle;
if (pointsCount > 32)
pointsCount = 32;
for (int i = 0; i < pointsCount; ++i) {
currentAngle += angleIncr;
angleIncr -= angleStep;
Common::Point newPoint(
centerX + _vm->getRandom(8) - 2 + (int)(cos(currentAngle) * radius),
centerY + _vm->getRandom(8) - 2 - (int)(sin(currentAngle) * radius));
Control *trailControl = _vm->_dict->getObjectControl(_trailObjectIds[i]);
for (int index = kSequenceIdsCount - 1; index >= 0; --index) {
if (sequenceCounters[index] > 0) {
--sequenceCounters[index];
trailControl->setActorPosition(newPoint);
trailControl->startSequenceActor(kBubbleTrailSequenceIds[index], 2, 0);
trailControl->appearActor();
trailControl->deactivateObject();
break;
}
}
}
}
} // End of namespace Illusions

View File

@@ -0,0 +1,82 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_BBDOU_BBDOU_BUBBLE_H
#define ILLUSIONS_BBDOU_BBDOU_BUBBLE_H
#include "illusions/specialcode.h"
#include "common/rect.h"
namespace Illusions {
class IllusionsEngine_BBDOU;
class BbdouSpecialCode;
class Control;
struct BubbleStyle {
uint32 _showSequenceId;
uint32 _hideSequenceId;
int16 _count;
uint32 _progResKeywordId;
uint32 _baseNamedPointId;
uint32 _namedPointIds[32];
uint32 _objectId;
Common::Point _position;
BubbleStyle() : _count(0) {}
};
struct BubbleIcon {
uint32 _objectId;
bool _enabled;
Common::Point _position;
uint32 _sequenceId;
};
class BbdouBubble {
public:
BbdouBubble(IllusionsEngine_BBDOU *vm, BbdouSpecialCode *bbdou);
~BbdouBubble();
void init();
void addBubbleStyle(uint32 showSequenceId, uint32 hideSequenceId, uint32 progResKeywordId,
uint32 namedPointId, int16 count, uint32 *namedPointIds);
void show();
void hide();
void selectBubbleStyle(int16 minCount, Common::Point sourcePt, Common::Point destPt, uint32 progResKeywordId);
uint32 addBubbleIcon(uint positionIndex, uint32 sequenceId);
void calcBubbleTrail(Common::Point &sourcePt, Common::Point &destPt);
protected:
IllusionsEngine_BBDOU *_vm;
BbdouSpecialCode *_bbdou;
Common::Array<BubbleStyle> _bubbleStyles;
BubbleStyle *_currBubbleStyle;
BubbleStyle *_showingBubbleStyle;
BubbleStyle *_hidingBubbleStyle;
uint32 _trailObjectIds[32];
Common::Point _sourcePt;
Common::Point _destPt;
int _bubbleObjectId1;
int _bubbleObjectId2;
BubbleIcon _icons[32];
};
} // End of namespace Illusions
#endif // ILLUSIONS_BBDOU_BBDOU_BUBBLE_H

View File

@@ -0,0 +1,244 @@
/* 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/>.
*
*/
#include "illusions/bbdou/illusions_bbdou.h"
#include "illusions/bbdou/bbdou_credits.h"
#include "illusions/actor.h"
#include "illusions/dictionary.h"
#include "illusions/textdrawer.h"
#include "illusions/time.h"
#include "illusions/resources/scriptresource.h"
namespace Illusions {
BbdouCredits::BbdouCredits(IllusionsEngine_BBDOU *vm)
: _vm(vm) {
}
BbdouCredits::~BbdouCredits() {
}
void BbdouCredits::start(uint32 endSignalPropertyId, float speedModifier) {
_endSignalPropertyId = endSignalPropertyId;
_currFontId = 0x120004;
_currLineIndex = 1;
_split = false;
// convertTextData();
initCreditsItems();
createCreditsThread(speedModifier);
}
void BbdouCredits::stop() {
freeCreditsItems();
}
void BbdouCredits::drawNextLine() {
uint leftIndex, rightIndex;
if (!readNextLine(leftIndex, rightIndex)) {
_vm->_scriptResource->_properties.set(_endSignalPropertyId, true);
return;
}
if (leftIndex) {
const char *leftText = getText(leftIndex);
if (leftText && strlen(leftText) != 0) {
uint32 objectId = getNextFreeObjectId();
int alignment = rightIndex ? 1 : 2;
drawTextToControl(objectId, leftText, alignment);
}
}
if (rightIndex) {
const char *rightText = getText(rightIndex);
if (rightText && strlen(rightText) != 0) {
uint32 objectId = getNextFreeObjectId();
drawTextToControl(objectId, rightText, 4);
}
}
}
void charToWChar(const char *text, uint16 *wtext, uint size) {
while (*text != 0 && size > 1) {
*wtext++ = (byte)*text++;
/*
byte c = (byte)*text++;
if (c > 127) c = 32;
*wtext = c;
debug("%04X", *wtext);
++wtext;
*/
--size;
}
*wtext++ = 0;
}
void BbdouCredits::drawTextToControl(uint32 objectId, const char *text, uint alignment) {
uint16 wtext[128];
charToWChar(text, wtext, ARRAYSIZE(wtext));
Control *control = _vm->getObjectControl(objectId);
FontResource *font = _vm->_dict->findFont(_currFontId);
TextDrawer textDrawer;
WidthHeight dimensions;
uint16 *outText;
control->getActorFrameDimensions(dimensions);
control->fillActor(0);
textDrawer.wrapText(font, wtext, &dimensions, Common::Point(0, 0), alignment, outText);
textDrawer.drawText(_vm->_screen, control->_actor->_surface, 0, 0);
control->_actor->_flags |= Illusions::ACTOR_FLAG_4000;
}
bool BbdouCredits::readNextLine(uint &leftIndex, uint &rightIndex) {
bool done = false;
int textLines = 0;
leftIndex = 0;
rightIndex = 0;
do {
uint lineIndex = _currLineIndex++;
const char *text = getText(lineIndex);
if (text[0] == '@') {
const char *command = text + 1;
if (!strcmp(command, "end"))
done = true;
if (!strcmp(command, "bold"))
_currFontId = 0x120005;
else if (!strcmp(command, "normal"))
_currFontId = 0x120004;
else if (!strcmp(command, "center"))
_split = false;
else if (!strcmp(command, "split"))
_split = true;
else
done = true;
} else {
++textLines;
if (!_split) {
leftIndex = lineIndex;
done = true;
} else if (textLines > 1) {
rightIndex = lineIndex;
done = true;
} else {
leftIndex = lineIndex;
}
}
} while (!done);
return textLines > 0;
}
void BbdouCredits::initCreditsItems() {
for (uint i = 0; i < kCreditsItemsCount; ++i) {
uint32 objectId = _vm->_controls->newTempObjectId();
_vm->_controls->placeActor(0x00050188, Common::Point(320, 480), 0x00060BE1, objectId, 0);
Control *control = _vm->_dict->getObjectControl(objectId);
control->startSequenceActor(0x60BE2, 2, 0);
_items[i].isUsed = false;
_items[i].objectId = objectId;
}
}
void BbdouCredits::freeCreditsItems() {
for (uint i = 0; i < kCreditsItemsCount; ++i) {
Control *control = _vm->_dict->getObjectControl(_items[i].objectId);
control->disappearActor();
}
}
uint32 BbdouCredits::getNextFreeObjectId() {
for (uint i = 0; i < kCreditsItemsCount; ++i) {
if (!_items[i].isUsed) {
_items[i].isUsed = true;
return _items[i].objectId;
}
}
return 0;
}
void BbdouCredits::removeText(uint32 objectId) {
for (uint i = 0; i < kCreditsItemsCount; ++i) {
if (_items[i].objectId == objectId) {
_items[i].isUsed = false;
resetObjectPos(objectId);
}
}
}
void BbdouCredits::resetObjectPos(uint32 objectId) {
Control *control = _vm->_dict->getObjectControl(objectId);
control->setActorPosition(Common::Point(320, 480));
}
void BbdouCredits::createCreditsThread(float speedModifier) {
uint32 tempThreadId = _vm->newTempThreadId();
CreditsThread *creditsThread = new CreditsThread(_vm, this, tempThreadId, speedModifier);
_vm->_threads->startThread(creditsThread);
}
void BbdouCredits::updateTexts(int yIncr) {
for (uint i = 0; i < kCreditsItemsCount; ++i) {
if (_items[i].isUsed) {
Control *control = _vm->_dict->getObjectControl(_items[i].objectId);
Common::Point pt = control->getActorPosition();
pt.y += yIncr;
control->setActorPosition(pt);
if (pt.y <= 0)
removeText(_items[i].objectId);
}
}
}
// CreditsThread
CreditsThread::CreditsThread(IllusionsEngine_BBDOU *vm, BbdouCredits *credits, uint32 threadId, float speedModifier)
: Thread(vm, threadId, 0, 0), _speedModifier(speedModifier), _lastFraction(0.0), _credits(credits) {
_type = kTTSpecialThread;
_lastUpdateTime = getCurrentTime();
}
int CreditsThread::onUpdate() {
uint32 currTime = getCurrentTime();
float fltDelta = (currTime - _lastUpdateTime) * _speedModifier + _lastFraction;
int delta = (int)fltDelta;
_lastFraction = fltDelta - delta;
if (delta != 0)
_credits->updateTexts(-delta);
_lastUpdateTime = currTime;
return 2;
}
void CreditsThread::onNotify() {
_lastUpdateTime = getCurrentTime();
}
void CreditsThread::onResume() {
onNotify();
}
void CreditsThread::onTerminated() {
_credits->stop();
}
} // End of namespace Illusions

View File

@@ -0,0 +1,83 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_BBDOU_BBDOU_CREDITS_H
#define ILLUSIONS_BBDOU_BBDOU_CREDITS_H
#include "illusions/specialcode.h"
#include "illusions/thread.h"
namespace Illusions {
class IllusionsEngine_BBDOU;
class BbdouSpecialCode;
class Control;
struct CreditsItem {
bool isUsed;
uint32 objectId;
};
const uint kCreditsItemsCount = 64;
class BbdouCredits {
public:
BbdouCredits(IllusionsEngine_BBDOU *vm);
~BbdouCredits();
void start(uint32 endSignalPropertyId, float speedModifier);
void stop();
void drawNextLine();
void updateTexts(int yIncr);
protected:
IllusionsEngine_BBDOU *_vm;
uint32 _endSignalPropertyId;
uint32 _currFontId;
uint _currLineIndex;
bool _split;
CreditsItem _items[kCreditsItemsCount];
const char *getText(uint index);
void drawTextToControl(uint32 objectId, const char *text, uint alignment);
bool readNextLine(uint &leftIndex, uint &rightIndex);
void initCreditsItems();
void freeCreditsItems();
uint32 getNextFreeObjectId();
void removeText(uint32 objectId);
void resetObjectPos(uint32 objectId);
void createCreditsThread(float speedModifier);
};
class CreditsThread : public Thread {
public:
CreditsThread(IllusionsEngine_BBDOU *vm, BbdouCredits *credits, uint32 threadId, float speedModifier);
int onUpdate() override;
void onNotify() override;
void onResume() override;
void onTerminated() override;
public:
BbdouCredits *_credits;
float _speedModifier;
float _lastFraction;
uint32 _lastUpdateTime;
};
} // End of namespace Illusions
#endif // ILLUSIONS_BBDOU_BBDOU_CREDITS_H

View File

@@ -0,0 +1,298 @@
/* 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/>.
*
*/
#include "illusions/bbdou/bbdou_credits.h"
namespace Illusions {
const char *kCreditsText[] = {
"@center",
"@normal",
"Directed by",
"@bold",
"Darren Bartlett",
"@normal",
"",
"Produced by",
"@bold",
"James C\xF6liz, Jr.",
"@normal",
"",
"Developed by",
"@bold",
"The Illusions Gaming Company",
"@normal",
"",
"@split",
"Creative Director",
"Darren Bartlett",
"Designer",
"Ryan Modjeski",
"Associate Designer",
"David Sirlin",
"",
"",
"Technical Director",
"James C\xF6liz, Jr.",
"Lead Programmer",
"Bill Fowler",
"Programmer",
"Chuck Woo",
"",
"",
"3D Artist",
"Eric Chyn",
"",
"Bill Eral",
"Production Artist",
"Jim Eral",
"Asst. Production Artist",
"Eli Remus",
"@center",
"",
"2D Animation by",
"@bold",
"LA West Productions",
"@normal",
"",
"@split",
"Director of Animation",
"Ivan Tomicic",
"Production Manager",
"Susan McGirr",
"Studio Supervisor",
"Danijel Tomicic",
"",
"",
"Lead Animator",
"Dario Pustaj",
"",
"Ivica Horvat",
"Animator",
"Kristijan Dulic",
"",
"Elvis Popovic",
"In-Between",
"Maja Surijak",
"",
"Zlatko Zlatunic",
"",
"",
"Lead Ink & Paint",
"Sasa Zec",
"Ink & Paint",
"Darko Dukaric",
"",
"Marcela Kumparic",
"",
"Vlado Lencur",
"",
"Jura Milinkovic",
"",
"Bernard Ojdanic",
"",
"Peggy Skrlec",
"@center",
"",
"3D Backgrounds by",
"@bold",
"LA West Productions",
"@normal",
"",
"@split",
"3D Artist",
"Daniela Tomicic",
"",
"Diana-Barbara Stepanic",
"@center",
"",
"2D Animation by",
"@bold",
"Six Foot Two Productions",
"@normal",
"",
"@split",
"Director of Animation",
"Tom Arndt",
"Producer",
"Suzanne D. Atherly",
"",
"",
"Character Animator",
"Robbin Atherly",
"",
"Alan Lau",
"",
"David Ball",
"",
"Jeff Nevins",
"",
"",
"Ink & Paint",
"Steve Bellin",
"",
"Corrine Wong",
"",
"Jeff Nevins",
"@center",
"",
"Written by",
"@bold",
"Bo Weinberg",
"@normal",
"",
"Principal Voice by",
"@bold",
"Mike Judge",
"@normal",
"",
"Secondary Voice Recorded at",
"@bold",
"Private Island Trax",
"@normal",
"",
"Secondary Voices by",
"Dean Julian",
"Mia Altieri",
"Nicole Schallig",
"Rick Calvert",
"John Campana",
"Alex Mebane",
"Denise Askew",
"Michael Jamal",
"",
"Studio Engineered by",
"Mark V",
"",
"Sound and Music by",
"@bold",
"Tommy Tallarico Studios",
"@normal",
"",
"@split",
"Sound Designer",
"Joey Kuras",
"Foley",
"Scott Barrett",
"@center",
"",
"Illusions is represented by",
"@bold",
"Interactive Studio Management",
"@normal",
"",
"Published by",
"@bold",
"GT Interactive Software",
"@normal",
"",
"@split",
"Producer",
"Nathan Rose",
"Assistant Producer",
"Jamal Jennings",
"Group Product Manager",
"Evan Stein",
"Product Manager",
"Robert J. Ricci",
"Senior Communications Manager",
"Alan Lewis",
"Director, Product Development Services",
"Mary Steer",
"Director, Creative Services",
"Leslie Mills",
"Creative Director",
"Vic Merritt",
"Art/Traffic Manager",
"Liz Fierro",
"Manual Editor",
"Peter Witcher",
"",
"",
"@center",
"",
"Licensed by",
"@bold",
"MTV Networks",
"@normal",
"",
"@split",
"MTV Executive Producer",
"Allie Eberhardt",
"MTV Producer",
"Tony Calandra",
"MTV Creative Consultants",
"Kristofor Brown",
"",
"David Felton",
"",
"Mike Judge",
"",
"Nick Litwinko",
"MTV Standards and Practices",
"Dr. Thomas Shea",
"MTV Legal Affairs",
"Beth Matthews",
"@center",
"",
"MTV would like to thank",
"Mary Frances Budig",
"George Eichen",
"Matt Farber",
"Rick Holzman",
"Jessica Jarrett",
"Mike Judge",
"Judith McGrath",
"David Milch",
"Abby Terkuhle",
"Van Toffler",
"Paige Wolfson",
"Marcia Zellers",
"",
"@bold",
"Special Thanks",
"@normal",
"Clyde Grossman",
"Hiromi Nobata",
"John Durentas",
"Jeff Teachworth",
"John Lawrence",
"Bill Hendrickson",
"Fred Schiller",
"Sam Fletcher",
"Elizabeth, Stephanie & Hannah",
"Sheila Mendoza",
"Yukari Yamano",
"Hang Yim, King Yip & Wayne",
"Li-Ming, Der-Lin & Fansy",
"Bobbi Eral",
"Miss Melissa",
"Yasmin, Aparna & Jenny",
"Tony the Cat",
"Sammy the Cat",
"@end"
};
const char *BbdouCredits::getText(uint index) {
return kCreditsText[index - 1];
}
} // End of namespace Illusions

View File

@@ -0,0 +1,363 @@
/* 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/>.
*
*/
#include "illusions/bbdou/illusions_bbdou.h"
#include "illusions/bbdou/bbdou_cursor.h"
#include "illusions/bbdou/bbdou_specialcode.h"
#include "illusions/actor.h"
#include "illusions/camera.h"
#include "illusions/dictionary.h"
#include "illusions/input.h"
namespace Illusions {
// NOTE It's assumed there's only one game cursor object
// The original stores the _data inside the actor, here it's inside the Cursor class.
// BbdouCursor
BbdouCursor::BbdouCursor(IllusionsEngine_BBDOU *vm, BbdouSpecialCode *bbdou)
: _vm(vm), _bbdou(bbdou) {
}
BbdouCursor::~BbdouCursor() {
}
void BbdouCursor::init(uint32 objectId, uint32 progResKeywordId) {
Common::Point pos = _vm->_camera->getCurrentPan();
_vm->_controls->placeActor(0x50001, pos, 0x6000C, objectId, 0);
Control *control = _vm->_dict->getObjectControl(objectId);
control->_flags |= 8;
_data._mode = 1;
_data._mode2 = 0;
_data._verbId1 = 0x1B0000;
_data._progResKeywordId = progResKeywordId;
_data._currOverlappedObjectId = 0;
_data._overlappedObjectId = 0;
_data._sequenceId = 0x6000F;
_data._holdingObjectId = 0;
_data._holdingObjectId2 = 0;
_data._visibleCtr = 0;
_data._causeThreadId1 = 0;
_data._causeThreadId2 = 0;
_data._field90 = 0;
_data._flags = 0;
_data._verbState._minPriority = 1;
_data._currCursorTrackingSequenceId = 0;
_data._idleCtr = 0;
_data._verbState._verbId = 0x1B0000;
_data._verbState._cursorState = 1;
_data._verbState._isBubbleVisible = 0;
_data._verbState._objectIds[0] = 0;
_data._verbState._objectIds[1] = 0;
_data._verbState._index = 0;
_data._verbState._flag56 = false;
resetActiveVerbs();
control->setActorIndexTo1();
}
void BbdouCursor::enable(uint32 objectId) {
++_data._visibleCtr;
if (_data._visibleCtr == 1) {
Control *control = _vm->_dict->getObjectControl(objectId);
show(control);
_vm->_camera->pushCameraMode();
_vm->_camera->panEdgeFollow(objectId, 360);
_data._idleCtr = 0;
}
_vm->_input->discardAllEvents();
}
void BbdouCursor::disable(uint32 objectId) {
hide(objectId);
}
void BbdouCursor::reset(uint32 objectId) {
Control *control = _vm->_dict->getObjectControl(objectId);
_data._verbState._cursorState = 1;
_data._mode = 1;
_data._mode2 = 0;
_data._verbId1 = 0x1B0000;
_data._currOverlappedObjectId = 0;
_data._overlappedObjectId = 0;
_data._sequenceId = 0x6000F;
_data._holdingObjectId = 0;
_data._holdingObjectId2 = 0;
_data._visibleCtr = 0;
_data._causeThreadId1 = 0;
_data._flags = 0;
_data._verbState._minPriority = 1;
_data._currCursorTrackingSequenceId = 0;
_data._idleCtr = 0;
_data._verbState._verbId = 0x1B0000;
_data._verbState._isBubbleVisible = 0;
_data._verbState._objectIds[0] = 0;
_data._verbState._objectIds[1] = 0;
_data._verbState._index = 0;
_data._verbState._flag56 = false;
resetActiveVerbs();
control->setActorIndexTo1();
control->startSequenceActor(0x60029, 2, 0);
_bbdou->hideVerbBubble(control->_objectId, &_data._verbState);
}
void BbdouCursor::addCursorSequenceId(uint32 objectId, uint32 sequenceId) {
for (uint i = 0; i < kMaxCursorSequences; ++i) {
if (_cursorSequences[i]._objectId == 0) {
_cursorSequences[i]._objectId = objectId;
_cursorSequences[i]._sequenceId = sequenceId;
break;
}
}
}
uint32 BbdouCursor::findCursorSequenceId(uint32 objectId) {
for (uint i = 0; i < kMaxCursorSequences; ++i) {
if (_cursorSequences[i]._objectId == objectId)
return _cursorSequences[i]._sequenceId;
}
return 0;
}
bool BbdouCursor::updateTrackingCursor(Control *control) {
uint32 sequenceId;
if (getTrackingCursorSequenceId(control, sequenceId)) {
if (_data._currCursorTrackingSequenceId != sequenceId) {
saveBeforeTrackingCursor(control, sequenceId);
show(control);
_data._currCursorTrackingSequenceId = sequenceId;
}
return true;
} else {
if (_data._currCursorTrackingSequenceId) {
_data._currCursorTrackingSequenceId = 0;
restoreAfterTrackingCursor();
show(control);
}
return false;
}
}
void BbdouCursor::saveInfo() {
_data._mode2 = _data._mode;
_data._sequenceId2 = _data._sequenceId;
_data._holdingObjectId2 = _data._holdingObjectId;
}
void BbdouCursor::restoreInfo() {
_data._mode = _data._mode2;
_data._holdingObjectId = _data._holdingObjectId2;
_data._sequenceId = _data._sequenceId2;
_data._mode2 = 0;
_data._holdingObjectId2 = 0;
_data._sequenceId2 = 0;
}
void BbdouCursor::saveBeforeTrackingCursor(Control *control, uint32 sequenceId) {
if (_data._currOverlappedObjectId || _data._mode == 3) {
if (_data._mode == 3)
restoreInfo();
control->setActorIndexTo1();
if (_data._verbState._isBubbleVisible)
_bbdou->playSoundEffect(4);
_bbdou->hideVerbBubble(control->_objectId, &_data._verbState);
}
_data._currOverlappedObjectId = 0;
if (_data._mode != 4) {
saveInfo();
_data._mode = 4;
_data._holdingObjectId = 0;
}
_data._sequenceId = sequenceId;
}
void BbdouCursor::restoreAfterTrackingCursor() {
_data._holdingObjectId = _data._holdingObjectId2;
if (_data._holdingObjectId2) {
_data._mode = 2;
_data._sequenceId = findCursorSequenceId(_data._holdingObjectId2);
} else {
_data._mode = 1;
_data._sequenceId = 0x6000F;
}
_data._mode2 = 0;
_data._sequenceId2 = 0;
_data._holdingObjectId2 = 0;
_data._currCursorTrackingSequenceId = 0;
}
uint32 BbdouCursor::getSequenceId1(int sequenceIndex) {
switch (sequenceIndex) {
case 2:
return 0x60010;
case 3:
return 0x60011;
case 4:
return 0x60012;
case 5:
return 0x60013;
case 6:
return 0x60015;
case 7:
return 0x60014;
default:
return 0;
}
}
uint BbdouCursor::calcTrackingFlags(Common::Point actorPos, Common::Point trackingLimits) {
uint trackingFlags = 0;
int16 x = actorPos.x - 320;
int16 y = actorPos.y - 240;
if (x < -trackingLimits.x)
trackingFlags = 1;
else if (x > trackingLimits.x)
trackingFlags = 3;
else
trackingFlags = 2;
if (y < -trackingLimits.y)
trackingFlags += 0;
else if (y > trackingLimits.y)
trackingFlags += 6;
else
trackingFlags += 3;
return trackingFlags;
}
uint BbdouCursor::calcTrackingCursorIndex(uint trackingFlags) {
uint cursorIndex = 0;
switch (trackingFlags) {
case 1:
if (_vm->_camera->isAtPanLimit(1)) {
if (!_vm->_camera->isAtPanLimit(3))
cursorIndex = 4;
} else {
if (!_vm->_camera->isAtPanLimit(3))
cursorIndex = 1;
else
cursorIndex = 2;
}
break;
case 2:
if (!_vm->_camera->isAtPanLimit(1))
cursorIndex = 2;
break;
case 3:
if (_vm->_camera->isAtPanLimit(1)) {
if (!_vm->_camera->isAtPanLimit(4))
cursorIndex = 6;
} else {
if (!_vm->_camera->isAtPanLimit(4))
cursorIndex = 3;
else
cursorIndex = 2;
}
break;
case 4:
if (!_vm->_camera->isAtPanLimit(3))
cursorIndex = 4;
break;
case 6:
if (!_vm->_camera->isAtPanLimit(4))
cursorIndex = 6;
break;
case 7:
if (_vm->_camera->isAtPanLimit(2)) {
if (!_vm->_camera->isAtPanLimit(3))
cursorIndex = 4;
} else {
if (!_vm->_camera->isAtPanLimit(3))
cursorIndex = 8;
else
cursorIndex = 7;
}
break;
case 8:
if (!_vm->_camera->isAtPanLimit(2))
cursorIndex = 8;
break;
case 9:
if (_vm->_camera->isAtPanLimit(2)) {
if (!_vm->_camera->isAtPanLimit(4))
cursorIndex = 6;
} else {
if (!_vm->_camera->isAtPanLimit(4))
cursorIndex = 9;
else
cursorIndex = 8;
}
break;
default:
break;
}
return cursorIndex;
}
bool BbdouCursor::getTrackingCursorSequenceId(Control *control, uint32 &outSequenceId) {
static const uint32 kTrackingCursorSequenceIds[] = {
0, 0x000609BF, 0x00060018, 0x000609C0, 0x00060016,
0, 0x00060017, 0x000609C1, 0x00060019, 0x000609C2
};
Common::Point trackingLimits = _vm->_camera->getTrackingLimits();
uint trackingFlags = calcTrackingFlags(control->_actor->_position, trackingLimits);
uint cursorIndex = calcTrackingCursorIndex(trackingFlags);
outSequenceId = kTrackingCursorSequenceIds[cursorIndex];
return outSequenceId != 0;
}
void BbdouCursor::resetActiveVerbs() {
for (uint i = 0; i < 32; ++i) {
_data._verbState._verbActive[i] = false;
}
if (_data._verbState._cursorState == 1) {
_data._verbState._verbActive[1] = true;
_data._verbState._verbActive[2] = true;
_data._verbState._verbActive[3] = true;
_data._verbState._verbActive[5] = true;
} else if (_data._verbState._cursorState == 3) {
_data._verbState._verbActive[1] = true;
_data._verbState._verbActive[2] = true;
}
}
void BbdouCursor::show(Control *control) {
control->startSequenceActor(_data._sequenceId, 2, 0);
control->appearActor();
}
void BbdouCursor::hide(uint32 objectId) {
--_data._visibleCtr;
if (_data._visibleCtr == 0) {
Control *control = _vm->_dict->getObjectControl(objectId);
control->startSequenceActor(0x60029, 2, 0);
_bbdou->hideVerbBubble(objectId, &_data._verbState);
_vm->_camera->popCameraMode();
}
_vm->_input->discardAllEvents();
}
} // End of namespace Illusions

View File

@@ -0,0 +1,105 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_BBDOU_BBDOU_CURSOR_H
#define ILLUSIONS_BBDOU_BBDOU_CURSOR_H
#include "illusions/specialcode.h"
namespace Illusions {
class IllusionsEngine_BBDOU;
class BbdouSpecialCode;
class Control;
struct VerbState {
int _cursorState;
bool _verbActive[32];
uint32 _verbId;
bool _isBubbleVisible;
uint32 _objectIds[2];
int16 _index;
bool _flag56;
int _minPriority;
};
struct CursorData {
int _mode;
int _mode2;
uint32 _verbId1;
uint32 _progResKeywordId;
VerbState _verbState;
uint32 _currOverlappedObjectId;
uint32 _overlappedObjectId;
uint32 _sequenceId;
uint32 _sequenceId2;
uint32 _holdingObjectId;
uint32 _holdingObjectId2;
int _visibleCtr;
uint32 _causeThreadId1;
uint32 _causeThreadId2;
int16 _field90;
uint _flags;
uint32 _currCursorTrackingSequenceId;
int16 _idleCtr;
};
struct CursorSequence {
uint32 _objectId;
uint32 _sequenceId;
CursorSequence() : _objectId(0), _sequenceId(0) {}
};
const uint kMaxCursorSequences = 100;
class BbdouCursor {
public:
BbdouCursor(IllusionsEngine_BBDOU *vm, BbdouSpecialCode *bbdou);
~BbdouCursor();
void init(uint32 objectId, uint32 progResKeywordId);
void enable(uint32 objectId);
void disable(uint32 objectId);
void reset(uint32 objectId);
void addCursorSequenceId(uint32 objectId, uint32 sequenceId);
uint32 findCursorSequenceId(uint32 objectId);
bool updateTrackingCursor(Control *control);
void saveInfo();
void restoreInfo();
void saveBeforeTrackingCursor(Control *control, uint32 sequenceId);
void restoreAfterTrackingCursor();
uint32 getSequenceId1(int sequenceIndex);
uint calcTrackingFlags(Common::Point actorPos, Common::Point trackingLimits);
uint calcTrackingCursorIndex(uint trackingFlags);
bool getTrackingCursorSequenceId(Control *control, uint32 &outSequenceId);
public:
IllusionsEngine_BBDOU *_vm;
BbdouSpecialCode *_bbdou;
Control *_control;
CursorData _data;
CursorSequence _cursorSequences[kMaxCursorSequences];
void resetActiveVerbs();
void show(Control *control);
void hide(uint32 objectId);
};
} // End of namespace Illusions
#endif // ILLUSIONS_BBDOU_BBDOU_CURSOR_H

View File

@@ -0,0 +1,148 @@
/* 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/>.
*
*/
#include "illusions/bbdou/illusions_bbdou.h"
#include "illusions/bbdou/bbdou_foodctl.h"
#include "illusions/actor.h"
#include "illusions/dictionary.h"
#include "illusions/textdrawer.h"
#include "illusions/time.h"
#include "illusions/resources/scriptresource.h"
namespace Illusions {
BbdouFoodCtl::BbdouFoodCtl(IllusionsEngine_BBDOU *vm)
: _vm(vm) {
}
BbdouFoodCtl::~BbdouFoodCtl() {
}
void BbdouFoodCtl::placeFood(uint totalRoundsCount, uint maxRequestedFoodCount) {
_totalRoundsCount = totalRoundsCount;
_maxRequestedFoodCount = maxRequestedFoodCount;
_requestedFoodCount = 0;
_requestedFoodIndex = 0;
placeActors();
}
void BbdouFoodCtl::addFood(uint32 propertyId) {
_foodPropertyIds[_requestedFoodCount++] = propertyId;
}
void BbdouFoodCtl::requestFirstFood() {
_requestedFoodIndex = 1;
_vm->_scriptResource->_properties.set(_foodPropertyIds[0], true);
}
void BbdouFoodCtl::requestNextFood() {
uint32 propertyId = _foodPropertyIds[_requestedFoodIndex++];
_vm->_scriptResource->_properties.set(propertyId, true);
}
void BbdouFoodCtl::nextRound() {
--_totalRoundsCount;
}
bool BbdouFoodCtl::hasReachedRequestedFoodCount() {
return _requestedFoodIndex > _requestedFoodCount;
}
bool BbdouFoodCtl::hasRoundFinished() {
return _totalRoundsCount == 0 || _requestedFoodCount > _maxRequestedFoodCount;
}
void BbdouFoodCtl::serveFood() {
uint32 foodSequenceId = getFoodSequenceId();
uint32 studentObjectId = getCurrentStudentObjectId();
uint32 foodObjectId = _foodItems[_servedFoodCount++].objectId;
Control *foodControl = _vm->_dict->getObjectControl(foodObjectId);
foodControl->startSequenceActor(foodSequenceId, 2, 0);
foodControl->linkToObject(studentObjectId, _servedFoodCount);
}
void BbdouFoodCtl::resetFood() {
for (uint i = 0; i < _servedFoodCount; ++i) {
Control *control = _vm->_dict->getObjectControl(_foodItems[i].objectId);
control->unlinkObject();
_foodItems[i].value = 0;
}
_servedFoodCount = 0;
resetFoodControls();
}
void BbdouFoodCtl::placeActors() {
static const uint32 kFoodSequenceIds[] = {
0x00060932, 0x00060933, 0x00060934,
0x00060935, 0x00060936, 0x00060937
};
for (uint i = 0; i < kFoodCount; ++i) {
uint32 objectId = _vm->_controls->newTempObjectId();
_vm->_controls->placeActor(0x00050119, Common::Point(0, 0), 0x00060931, objectId, 0);
Control *control = _vm->_dict->getObjectControl(objectId);
control->deactivateObject();
control->setPriority(i + 10);
control->startSequenceActor(kFoodSequenceIds[(i + 1) % 6], 2, 0);
_foodItems[i].objectId = objectId;
_foodItems[i].value = 0;
}
_servedFoodCount = 0;
resetFoodControls();
}
void BbdouFoodCtl::resetFoodControls() {
Common::Point pos(-100, 32);
for (uint i = 0; i < kFoodCount; ++i) {
Control *control = _vm->_dict->getObjectControl(_foodItems[i].objectId);
control->setActorPosition(pos);
pos.y += 20;
}
}
uint32 BbdouFoodCtl::getFoodSequenceId() {
if (_vm->_scriptResource->_properties.get(0x000E014A))
return 0x60932;
if (_vm->_scriptResource->_properties.get(0x000E014B))
return 0x60933;
if (_vm->_scriptResource->_properties.get(0x000E014C))
return 0x60934;
if (_vm->_scriptResource->_properties.get(0x000E014D))
return 0x60935;
if (_vm->_scriptResource->_properties.get(0x000E014E))
return 0x60936;
if (_vm->_scriptResource->_properties.get(0x000E014F))
return 0x60937;
return 0;
}
uint32 BbdouFoodCtl::getCurrentStudentObjectId() {
if (_vm->_scriptResource->_properties.get(0x000E0146))
return 0x40077;
if (_vm->_scriptResource->_properties.get(0x000E0147))
return 0x40255;
if (_vm->_scriptResource->_properties.get(0x000E0148))
return 0x40256;
if (_vm->_scriptResource->_properties.get(0x000E0149))
return 0x40257;
return 0;
}
} // End of namespace Illusions

View File

@@ -0,0 +1,69 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_BBDOU_BBDOU_FOODCTL_H
#define ILLUSIONS_BBDOU_BBDOU_FOODCTL_H
#include "illusions/specialcode.h"
#include "illusions/thread.h"
namespace Illusions {
class IllusionsEngine_BBDOU;
const uint kFoodMaxPropertyIdsCount = 15;
const uint kFoodCount = 16;
struct FoodItem {
uint32 objectId;
int value;
};
class BbdouFoodCtl {
public:
BbdouFoodCtl(IllusionsEngine_BBDOU *vm);
~BbdouFoodCtl();
void placeFood(uint totalRoundsCount, uint maxRequestedFoodCount);
void addFood(uint32 propertyId);
void requestFirstFood();
void requestNextFood();
void nextRound();
bool hasReachedRequestedFoodCount();
bool hasRoundFinished();
void serveFood();
void resetFood();
protected:
IllusionsEngine_BBDOU *_vm;
uint _totalRoundsCount, _maxRequestedFoodCount;
uint32 _foodPropertyIds[kFoodMaxPropertyIdsCount];
uint _requestedFoodCount;
uint _requestedFoodIndex;
FoodItem _foodItems[kFoodCount];
uint _servedFoodCount;
void placeActors();
void resetFoodControls();
uint32 getFoodSequenceId();
uint32 getCurrentStudentObjectId();
};
} // End of namespace Illusions
#endif // ILLUSIONS_BBDOU_BBDOU_FOODCTL_H

View File

@@ -0,0 +1,396 @@
/* 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/>.
*
*/
#include "illusions/bbdou/illusions_bbdou.h"
#include "illusions/bbdou/bbdou_inventory.h"
#include "illusions/bbdou/bbdou_cursor.h"
#include "illusions/bbdou/bbdou_specialcode.h"
#include "illusions/actor.h"
#include "illusions/camera.h"
#include "illusions/dictionary.h"
#include "illusions/input.h"
namespace Illusions {
typedef Common::Functor2Mem<TriggerFunction*, uint32, void, BbdouInventory> InventoryTriggerFunctionCallback;
// InventoryItem
InventoryItem::InventoryItem(uint32 objectId, uint32 sequenceId)
: _objectId(objectId), _sequenceId(sequenceId),
_assigned(false), _flag(false), _timesPresent(0), _fieldE(0) {
}
// InventorySlot
InventorySlot::InventorySlot(uint32 namedPointId)
: _namedPointId(namedPointId), _objectId(0), _inventoryItem(nullptr) {
}
// InventoryBag
InventoryBag::InventoryBag(IllusionsEngine_BBDOU *vm, uint32 sceneId)
: _vm(vm), _sceneId(sceneId), _isActive(false), _fieldA(0) {
}
InventoryBag::~InventoryBag() {
for (uint i = 0; i < _inventorySlots.size(); ++i) {
delete _inventorySlots[i];
}
}
void InventoryBag::registerInventorySlot(uint32 namedPointId) {
_inventorySlots.push_back(new InventorySlot(namedPointId));
}
bool InventoryBag::addInventoryItem(InventoryItem *inventoryItem, InventorySlot *inventorySlot) {
// NOTE Skipped support for multiple items per slot, not used in BBDOU
if (!inventorySlot) {
for (auto &slot : _inventorySlots) {
if (!slot->_inventoryItem) {
inventorySlot = slot;
break;
}
}
}
if (inventorySlot) {
inventorySlot->_inventoryItem = inventoryItem;
return true;
}
return false;
}
void InventoryBag::removeInventoryItem(InventoryItem *inventoryItem) {
for (auto &slot : _inventorySlots) {
if (slot->_inventoryItem && slot->_inventoryItem->_objectId == inventoryItem->_objectId)
slot->_inventoryItem = nullptr;
}
}
bool InventoryBag::hasInventoryItem(uint32 objectId) {
for (InventorySlotsIterator it = _inventorySlots.begin();
it != _inventorySlots.end(); ++it) {
InventorySlot *inventorySlot = *it;
InventoryItem *inventoryItem = inventorySlot->_inventoryItem;
if (inventoryItem && inventoryItem->_objectId == objectId)
return true;
}
return false;
}
void InventoryBag::buildItems() {
for (InventorySlotsIterator it = _inventorySlots.begin();
it != _inventorySlots.end(); ++it) {
InventorySlot *inventorySlot = *it;
InventoryItem *inventoryItem = inventorySlot->_inventoryItem;
if (inventoryItem) {
++inventoryItem->_timesPresent;
if (!inventoryItem->_assigned || inventoryItem->_flag || inventoryItem->_timesPresent > 1)
inventorySlot->_inventoryItem = nullptr;
}
}
}
void InventoryBag::clear() {
for (InventorySlotsIterator it = _inventorySlots.begin();
it != _inventorySlots.end(); ++it) {
InventorySlot *inventorySlot = *it;
inventorySlot->_inventoryItem = nullptr;
}
}
InventorySlot *InventoryBag::getInventorySlot(uint32 objectId) {
for (uint i = 0; i < _inventorySlots.size(); ++i) {
if (_inventorySlots[i]->_objectId == objectId)
return _inventorySlots[i];
}
return nullptr;
}
InventorySlot *InventoryBag::findClosestSlot(Common::Point putPos, int index) {
uint minDistance = 0xFFFFFFFF;
InventorySlot *minDistanceSlot = nullptr;
for (auto &inventorySlot : _inventorySlots) {
Common::Point slotPos = _vm->getNamedPointPosition(inventorySlot->_namedPointId);
uint currDistance = (slotPos.y - putPos.y) * (slotPos.y - putPos.y) + (slotPos.x - putPos.x) * (slotPos.x - putPos.x);
if (currDistance < minDistance) {
minDistance = currDistance;
minDistanceSlot = inventorySlot;
}
}
return minDistanceSlot;
}
// BbdouInventory
BbdouInventory::BbdouInventory(IllusionsEngine_BBDOU *vm, BbdouSpecialCode *bbdou)
: _vm(vm), _bbdou(bbdou), _activeInventorySceneId(0) {
}
BbdouInventory::~BbdouInventory() {
for (uint i = 0; i < _inventoryBags.size(); ++i) {
delete _inventoryBags[i];
}
for (uint i = 0; i < _inventoryItems.size(); ++i) {
delete _inventoryItems[i];
}
}
void BbdouInventory::registerInventoryBag(uint32 sceneId) {
_inventoryBags.push_back(new InventoryBag(_vm, sceneId));
_activeBagSceneId = sceneId;
}
void BbdouInventory::registerInventoryItem(uint32 objectId, uint32 sequenceId) {
_activeBagSceneId = 0;
_inventoryItems.push_back(new InventoryItem(objectId, sequenceId));
}
void BbdouInventory::registerInventorySlot(uint32 namedPointId) {
InventoryBag *inventoryBag = getInventoryBag(_activeBagSceneId);
inventoryBag->registerInventorySlot(namedPointId);
}
void BbdouInventory::addInventoryItem(uint32 objectId) {
_activeBagSceneId = 0;
InventoryItem *inventoryItem = getInventoryItem(objectId);
bool assigned = inventoryItem->_assigned;
inventoryItem->_assigned = true;
if (!assigned && !inventoryItem->_flag) {
for (uint i = 0; i < _inventoryBags.size(); ++i) {
if (!_inventoryBags[i]->addInventoryItem(inventoryItem, nullptr))
inventoryItem->_assigned = false;
}
}
if (_activeInventorySceneId)
refresh();
}
void BbdouInventory::removeInventoryItem(uint32 objectId) {
InventoryItem *inventoryItem = getInventoryItem(objectId);
bool flag = inventoryItem->_flag;
inventoryItem->_flag = true;
if (!flag && inventoryItem->_assigned) {
if (_activeInventorySceneId) {
InventoryBag *inventoryBag = getInventoryBag(_activeInventorySceneId);
inventoryBag->removeInventoryItem(inventoryItem);
}
refresh();
}
}
bool BbdouInventory::hasInventoryItem(uint32 objectId) {
for (uint i = 0; i < _inventoryItems.size(); ++i) {
if (_inventoryItems[i]->_objectId == objectId &&
_inventoryItems[i]->_assigned)
return true;
}
return false;
}
void BbdouInventory::open() {
_activeBagSceneId = 0;
InventoryBag *inventoryBag = getInventoryBag(_vm->getCurrentScene());
buildItems(inventoryBag);
if (_activeInventorySceneId) {
refresh();
refresh();
} else {
_activeInventorySceneId = _vm->getCurrentScene();
_index = 1;
inventoryBag->_isActive = true;
for (InventoryBag::InventorySlotsIterator it = inventoryBag->_inventorySlots.begin();
it != inventoryBag->_inventorySlots.end(); ++it) {
InventorySlot *inventorySlot = *it;
Common::Point slotPos = _vm->getNamedPointPosition(inventorySlot->_namedPointId);
Control *control = _vm->_dict->getObjectControl(inventorySlot->_objectId);
if (control) {
control->setActorPosition(slotPos);
control->startSequenceActor(0x0006005A, 2, 0);
} else {
inventorySlot->_objectId = _vm->_controls->newTempObjectId();
_vm->_controls->placeActor(0x00050012, slotPos, 0x0006005A, inventorySlot->_objectId, 0);
}
_vm->causeDeclare(0x1B0002, 0, inventorySlot->_objectId, new InventoryTriggerFunctionCallback(this, &BbdouInventory::cause0x1B0002));
_vm->causeDeclare(0x1B0001, 0, inventorySlot->_objectId, new InventoryTriggerFunctionCallback(this, &BbdouInventory::cause0x1B0001));
_vm->causeDeclare(0x1B0008, 0, inventorySlot->_objectId, new InventoryTriggerFunctionCallback(this, &BbdouInventory::cause0x1B0001));
}
refresh();
}
}
void BbdouInventory::close() {
if (!_activeInventorySceneId)
return;
InventoryBag *inventoryBag = getInventoryBag(_vm->getCurrentScene());
for (InventoryBag::InventorySlotsIterator it = inventoryBag->_inventorySlots.begin();
it != inventoryBag->_inventorySlots.end(); ++it) {
InventorySlot *inventorySlot = *it;
Control *control = _vm->_dict->getObjectControl(inventorySlot->_objectId);
control->startSequenceActor(0x00060187, 2, 0);
}
inventoryBag->_isActive = false;
_activeInventorySceneId = 0;
}
InventoryBag *BbdouInventory::getInventoryBag(uint32 sceneId) {
for (uint i = 0; i < _inventoryBags.size(); ++i) {
if (_inventoryBags[i]->_sceneId == sceneId)
return _inventoryBags[i];
}
return nullptr;
}
InventoryItem *BbdouInventory::getInventoryItem(uint32 objectId) {
for (uint i = 0; i < _inventoryItems.size(); ++i) {
if (_inventoryItems[i]->_objectId == objectId)
return _inventoryItems[i];
}
return nullptr;
}
void BbdouInventory::refresh() {
if (!_activeInventorySceneId)
return;
InventoryBag *inventoryBag = getInventoryBag(_activeInventorySceneId);
for (InventoryBag::InventorySlotsIterator it = inventoryBag->_inventorySlots.begin();
it != inventoryBag->_inventorySlots.end(); ++it) {
InventorySlot *inventorySlot = *it;
Control *control = _vm->_dict->getObjectControl(inventorySlot->_objectId);
InventoryItem *inventoryItem = inventorySlot->_inventoryItem;
if (inventoryItem) {
control->startSequenceActor(inventoryItem->_sequenceId, 2, 0);
control->appearActor();
} else {
control->startSequenceActor(0x00060187, 2, 0);
}
}
}
void BbdouInventory::buildItems(InventoryBag *inventoryBag) {
for (auto &inventoryItem : _inventoryItems) {
inventoryItem->_timesPresent = 0;
}
inventoryBag->buildItems();
for (auto &inventoryItem : _inventoryItems) {
if (inventoryItem->_assigned && !inventoryItem->_flag &&
inventoryItem->_timesPresent == 0 &&
inventoryItem->_objectId != _bbdou->_cursor->_data._holdingObjectId)
inventoryBag->addInventoryItem(inventoryItem, nullptr);
}
}
void BbdouInventory::clear() {
for (auto &inventoryItem : _inventoryItems) {
inventoryItem->_assigned = false;
inventoryItem->_flag = false;
}
for (uint i = 0; i < _inventoryBags.size(); ++i) {
_inventoryBags[i]->clear();
}
}
void BbdouInventory::cause0x1B0001(TriggerFunction *triggerFunction, uint32 callingThreadId) {
uint32 foundSceneId, foundVerbId, foundObjectId2, foundObjectId;
bool found = false;
InventoryBag *inventoryBag = getInventoryBag(_activeInventorySceneId);
InventorySlot *inventorySlot = inventoryBag->getInventorySlot(triggerFunction->_objectId);
uint32 objectId = inventorySlot->_inventoryItem->_objectId;
foundSceneId = _activeInventorySceneId;
foundVerbId = triggerFunction->_verbId;
foundObjectId = 0;
foundObjectId2 = 0;
if (triggerFunction->_verbId == 0x1B0008) {
foundVerbId = 0x1B0003;
foundObjectId2 = _bbdou->_cursor->_data._holdingObjectId;
}
if (_vm->causeIsDeclared(_activeInventorySceneId, foundVerbId, foundObjectId2, objectId)) {
foundSceneId = _activeInventorySceneId;
foundObjectId = objectId;
found = true;
} else if (foundVerbId == 0x1B0003 && _vm->causeIsDeclared(_activeInventorySceneId, 0x1B0008, 0, objectId)) {
foundSceneId = _activeInventorySceneId;
foundVerbId = 0x1B0008;
foundObjectId2 = 0;
foundObjectId = objectId;
found = true;
} else if (_vm->causeIsDeclared(_activeInventorySceneId, foundVerbId, foundObjectId2, 0x40001)) {
foundSceneId = _activeInventorySceneId;
foundObjectId = 0x40001;
found = true;
} else if (_vm->causeIsDeclared(0x10003, foundVerbId, foundObjectId2, objectId)) {
foundSceneId = 0x10003;
foundObjectId = objectId;
found = true;
} else if (foundVerbId == 0x1B0003 && _vm->causeIsDeclared(0x10003, 0x1B0008, 0, objectId)) {
foundSceneId = 0x10003;
foundVerbId = 0x1B0008;
foundObjectId2 = 0;
foundObjectId = objectId;
found = true;
} else if (_vm->causeIsDeclared(0x10003, foundVerbId, foundObjectId2, 0x40001)) {
foundSceneId = 0x10003;
foundObjectId = 0x40001;
found = true;
}
if (found)
_vm->causeTrigger(foundSceneId, foundVerbId, foundObjectId2, foundObjectId, callingThreadId);
else
_vm->notifyThreadId(callingThreadId);
}
void BbdouInventory::cause0x1B0002(TriggerFunction *triggerFunction, uint32 callingThreadId) {
InventoryBag *inventoryBag = getInventoryBag(_activeInventorySceneId);
InventorySlot *inventorySlot = inventoryBag->getInventorySlot(triggerFunction->_objectId);
uint32 objectId = inventorySlot->_inventoryItem->_objectId;
if (_vm->causeIsDeclared(_activeInventorySceneId, triggerFunction->_verbId, 0, objectId)) {
_vm->causeTrigger(_activeInventorySceneId, triggerFunction->_verbId, 0, objectId, callingThreadId);
} else {
_bbdou->startHoldingObjectId(0x4001A, objectId, 0);
_vm->notifyThreadId(callingThreadId);
}
}
void BbdouInventory::putBackInventoryItem(uint32 objectId, Common::Point cursorPosition) {
InventoryItem *inventoryItem = getInventoryItem(objectId);
bool flag = inventoryItem->_flag;
inventoryItem->_flag = false;
if (!flag && !inventoryItem->_assigned)
return;
for (uint i = 0; i < _inventoryBags.size(); ++i) {
InventoryBag *inventoryBag = _inventoryBags[i];
if (inventoryBag->_sceneId == _activeInventorySceneId) {
InventorySlot *inventorySlot = inventoryBag->findClosestSlot(cursorPosition, _index);
inventoryBag->addInventoryItem(inventoryItem, inventorySlot);
} else {
if (!inventoryBag->hasInventoryItem(objectId))
inventoryBag->addInventoryItem(inventoryItem, nullptr);
}
}
refresh();
}
} // End of namespace Illusions

View File

@@ -0,0 +1,111 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_BBDOU_BBDOU_INVENTORY_H
#define ILLUSIONS_BBDOU_BBDOU_INVENTORY_H
#include "illusions/specialcode.h"
#include "common/array.h"
#include "common/rect.h"
namespace Illusions {
class IllusionsEngine_BBDOU;
class BbdouSpecialCode;
class Control;
struct TriggerFunction;
struct InventoryItem {
uint32 _objectId;
uint32 _sequenceId;
bool _assigned;
bool _flag;
int _timesPresent;
int _fieldE;
InventoryItem(uint32 objectId, uint32 sequenceId);
};
struct InventorySlot {
uint32 _namedPointId;
uint32 _objectId;
InventoryItem *_inventoryItem;
InventorySlot(uint32 namedPointId);
};
class InventoryBag {
public:
InventoryBag(IllusionsEngine_BBDOU *vm, uint32 sceneId);
~InventoryBag();
void registerInventorySlot(uint32 namedPointId);
bool addInventoryItem(InventoryItem *inventoryItem, InventorySlot *inventorySlot);
void removeInventoryItem(InventoryItem *inventoryItem);
bool hasInventoryItem(uint32 objectId);
void buildItems();
void clear();
InventorySlot *getInventorySlot(uint32 objectId);
InventorySlot *findClosestSlot(Common::Point putPos, int index);
protected:
public:
typedef Common::Array<InventorySlot*> InventorySlots;
typedef InventorySlots::iterator InventorySlotsIterator;
IllusionsEngine_BBDOU *_vm;
uint32 _sceneId;
InventorySlots _inventorySlots;
bool _isActive;
int _fieldA;
};
class BbdouInventory {
public:
BbdouInventory(IllusionsEngine_BBDOU *vm, BbdouSpecialCode *bbdou);
~BbdouInventory();
void registerInventoryBag(uint32 sceneId);
void registerInventoryItem(uint32 objectId, uint32 sequenceId);
void registerInventorySlot(uint32 namedPointId);
void addInventoryItem(uint32 objectId);
void removeInventoryItem(uint32 objectId);
bool hasInventoryItem(uint32 objectId);
void open();
void close();
InventoryBag *getInventoryBag(uint32 sceneId);
InventoryItem *getInventoryItem(uint32 objectId);
void refresh();
void buildItems(InventoryBag *inventoryBag);
void clear();
void cause0x1B0001(TriggerFunction *triggerFunction, uint32 callingThreadId);
void cause0x1B0002(TriggerFunction *triggerFunction, uint32 callingThreadId);
void putBackInventoryItem(uint32 objectId, Common::Point cursorPosition);
protected:
typedef Common::Array<InventoryItem*> InventoryItems;
typedef InventoryItems::iterator InventoryItemsIterator;
IllusionsEngine_BBDOU *_vm;
BbdouSpecialCode *_bbdou;
Common::Array<InventoryBag*> _inventoryBags;
InventoryItems _inventoryItems;
uint32 _activeBagSceneId;
uint32 _activeInventorySceneId;
int _index;
//field_12 dw
};
} // End of namespace Illusions
#endif // ILLUSIONS_BBDOU_BBDOU_INVENTORY_H

View File

@@ -0,0 +1,58 @@
/* 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/>.
*
*/
#include "illusions/bbdou/illusions_bbdou.h"
#include "illusions/bbdou/bbdou_menukeys.h"
#include "illusions/bbdou/menusystem_bbdou.h"
#include "illusions/input.h"
#include "illusions/screen.h"
namespace Illusions {
// BBDOUMenuKeys
BBDOUMenuKeys::BBDOUMenuKeys(IllusionsEngine_BBDOU *vm)
: _vm(vm) {
}
BBDOUMenuKeys::~BBDOUMenuKeys() {
}
void BBDOUMenuKeys::addMenuKey(uint bitMask, uint32 threadId) {
MenuKey menuKey;
menuKey.bitMask = bitMask;
menuKey.threadId = threadId;
_menuKeys.push_back(menuKey);
}
void BBDOUMenuKeys::update() {
if (_vm->_screen->isDisplayOn() && !_vm->_menuSystem->isActive()) {
for (auto &menuKey : _menuKeys) {
if (_vm->_input->pollButton(menuKey.bitMask)) {
_vm->startScriptThread(menuKey.threadId, 0, 0, 0, 0);
break;
}
}
}
}
} // End of namespace Illusions

View File

@@ -0,0 +1,52 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_BBDOU_BBDOU_MENUKEYS_H
#define ILLUSIONS_BBDOU_BBDOU_MENUKEYS_H
#include "illusions/specialcode.h"
#include "illusions/thread.h"
#include "common/array.h"
namespace Illusions {
class IllusionsEngine_BBDOU;
struct MenuKey {
uint bitMask;
uint32 threadId;
};
class BBDOUMenuKeys {
public:
BBDOUMenuKeys(IllusionsEngine_BBDOU *vm);
~BBDOUMenuKeys();
void addMenuKey(uint bitMask, uint32 threadId);
void update();
protected:
typedef Common::Array<MenuKey> MenuKeys;
IllusionsEngine_BBDOU *_vm;
MenuKeys _menuKeys;
};
} // End of namespace Illusions
#endif // ILLUSIONS_BBDOU_BBDOU_MENUKEYS_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,203 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_BBDOU_BBDOU_SPECIALCODE_H
#define ILLUSIONS_BBDOU_BBDOU_SPECIALCODE_H
#include "illusions/specialcode.h"
#include "illusions/thread.h"
#include "common/hashmap.h"
namespace Illusions {
class IllusionsEngine_BBDOU;
class BbdouBubble;
class BbdouCredits;
class BbdouCursor;
class BbdouFoodCtl;
class BbdouInventory;
struct CursorData;
struct VerbState;
typedef Common::Functor1<OpCall&, void> SpecialCodeFunction;
class BbdouSpecialCode;
struct Struct10 {
uint32 _verbId;
uint32 _sequenceId1;
uint32 _sequenceId2;
uint32 _sequenceId3;
};
class CauseThread_BBDOU : public Thread {
public:
CauseThread_BBDOU(IllusionsEngine_BBDOU *vm, uint32 threadId, uint32 callingThreadId,
BbdouSpecialCode *bbdou, uint32 cursorObjectId, uint32 sceneId,
uint32 verbId, uint32 objectId2, uint32 objectId);
void onNotify() override;
void onTerminated() override;
public:
BbdouSpecialCode *_bbdou;
uint32 _cursorObjectId;
uint32 _sceneId;
uint32 _verbId;
uint32 _objectId2;
uint32 _objectId;
};
struct RadarMicrophoneZone {
int16 _x;
uint32 _threadId;
};
class RadarMicrophoneThread : public Thread {
public:
RadarMicrophoneThread(IllusionsEngine_BBDOU *vm, uint32 threadId,
uint32 callingThreadId, uint32 cursorObjectId);
int onUpdate() override;
void addZone(uint32 threadId);
void initZones();
public:
uint32 _cursorObjectId;
uint _zonesCount;
uint _currZoneIndex;
RadarMicrophoneZone _zones[8];
};
struct ShooterStatus {
int gridX;
bool flag;
};
struct ObjectInteractMode {
uint32 _objectId;
int _interactMode;
ObjectInteractMode() : _objectId(0), _interactMode(0) {}
};
class ObjectInteractModeMap {
public:
ObjectInteractModeMap();
void setObjectInteractMode(uint32 objectId, int value);
int getObjectInteractMode(uint32 objectId);
protected:
ObjectInteractMode _objectVerbs[512];
};
class BbdouSpecialCode : public SpecialCode {
public:
BbdouSpecialCode(IllusionsEngine_BBDOU *vm);
~BbdouSpecialCode() override;
void init() override;
void run(uint32 specialCodeId, OpCall &opCall) override;
void resetBeforeResumeSavegame() override;
public:
typedef Common::HashMap<uint32, SpecialCodeFunction*> Map;
typedef Map::iterator MapIterator;
IllusionsEngine_BBDOU *_vm;
Map _map;
BbdouCursor *_cursor;
BbdouBubble *_bubble;
BbdouInventory *_inventory;
BbdouCredits *_credits;
// Salad
uint _saladCount;
uint32 _saladObjectIds[12];
// Shooter
ShooterStatus _shooterStatus[2];
uint _shooterObjectIdIndex;
BbdouFoodCtl *_foodCtl;
ObjectInteractModeMap _objectInteractModeMap;
// Special code interface functions
void spcInitCursor(OpCall &opCall);
void spcEnableCursor(OpCall &opCall);
void spcDisableCursor(OpCall &opCall);
void spcAddCursorSequence(OpCall &opCall);
void spcCursorStartHoldingObjectId(OpCall &opCall);
void spcCursorStopHoldingObjectId(OpCall &opCall);
void spcSetCursorState(OpCall &opCall);
void spcInitBubble(OpCall &opCall);
void spcSetupBubble(OpCall &opCall);
void spcSetObjectInteractMode(OpCall &opCall);
void spcInitInventory(OpCall &opCall);
void spcClearInventory(OpCall &opCall);
void spcRegisterInventoryBag(OpCall &opCall);
void spcRegisterInventorySlot(OpCall &opCall);
void spcRegisterInventoryItem(OpCall &opCall);
void spcOpenInventory(OpCall &opCall);
void spcAddInventoryItem(OpCall &opCall);
void spcRemoveInventoryItem(OpCall &opCall);
void spcHasInventoryItem(OpCall &opCall);
void spcCloseInventory(OpCall &opCall);
void spcInitConversation(OpCall &opCall);
void spcClearConversation(OpCall &opCall);
void spcClearBlockCounter(OpCall &opCall);
void spcResetCursor(OpCall &opCall);
void spcSetCursorField90(OpCall &opCall);
void spcFoodCtl(OpCall &opCall);
void spcTestFoodCtl(OpCall &opCall);
void spcInitMenu(OpCall &opCall);
void spcIsCursorHoldingObjectId(OpCall &opCall);
void spcInitRadarMicrophone(OpCall &opCall);
void spcCreditsCtl(OpCall &opCall);
void spcSaladCtl(OpCall &opCall);
void spcRunCause(OpCall &opCall);
void playSoundEffect(int soundIndex);
void hideVerbBubble(uint32 objectId, VerbState *verbState);
void startHoldingObjectId(uint32 objectId1, uint32 holdingObjectId, bool doPlaySound);
void stopHoldingObjectId(uint32 objectId1, bool doPlaySound);
bool isHoldingObjectId(uint32 objectId);
protected:
// Internal functions
bool testInteractModeRange(int value);
void setCursorControlRoutine(uint32 objectId, int num);
Common::Point getBackgroundCursorPos(Common::Point cursorPos);
void showBubble(uint32 objectId, uint32 overlappedObjectId, uint32 holdingObjectId,
VerbState *verbState, uint32 progResKeywordId);
bool findVerbId(VerbState *verbState, uint32 currOverlappedObjectId, int always0, uint32 &outVerbId);
void cursorInteractControlRoutine(Control *cursorControl, uint32 deltaTime);
void cursorCrosshairControlRoutine(Control *cursorControl, uint32 deltaTime);
bool testVerbId(uint32 verbId, uint32 holdingObjectId, uint32 overlappedObjectId);
bool getCause(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId,
uint32 &outVerbId, uint32 &outObjectId2, uint32 &outObjectId);
bool runCause(Control *cursorControl, CursorData &cursorData,
uint32 verbId, uint32 objectId2, uint32 objectId, int soundIndex);
uint32 startCauseThread(uint32 cursorObjectId, uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId);
// Salad
void initSalad();
void addSalad(uint32 sequenceId);
// Shooter
bool getShooterCause(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId,
uint32 &outSceneId, uint32 &outVerbId, uint32 &outObjectId2, uint32 &outObjectId);
};
} // End of namespace Illusions
#endif // ILLUSIONS_BBDOU_BBDOU_SPECIALCODE_H

View File

@@ -0,0 +1,81 @@
/* 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/>.
*
*/
#include "illusions/bbdou/illusions_bbdou.h"
#include "illusions/bbdou/bbdou_triggerfunctions.h"
namespace Illusions {
// TriggerFunction
TriggerFunction::TriggerFunction(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback)
: _sceneId(sceneId), _verbId(verbId), _objectId2(objectId2), _objectId(objectId), _callback(callback) {
}
TriggerFunction::~TriggerFunction() {
delete _callback;
}
void TriggerFunction::run(uint32 callingThreadId) {
(*_callback)(this, callingThreadId);
}
// TriggerFunctions
void TriggerFunctions::add(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback) {
ItemsIterator it = findInternal(sceneId, verbId, objectId2, objectId);
if (it != _triggerFunctions.end()) {
delete *it;
_triggerFunctions.erase(it);
}
_triggerFunctions.push_back(new TriggerFunction(sceneId, verbId, objectId2, objectId, callback));
}
TriggerFunction *TriggerFunctions::find(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId) {
ItemsIterator it = findInternal(sceneId, verbId, objectId2, objectId);
if (it != _triggerFunctions.end())
return (*it);
return nullptr;
}
void TriggerFunctions::removeBySceneId(uint32 sceneId) {
ItemsIterator it = _triggerFunctions.begin();
while (it != _triggerFunctions.end()) {
if ((*it)->_sceneId == sceneId) {
delete *it;
it = _triggerFunctions.erase(it);
} else
++it;
}
}
TriggerFunctions::ItemsIterator TriggerFunctions::findInternal(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId) {
ItemsIterator it = _triggerFunctions.begin();
for (; it != _triggerFunctions.end(); ++it) {
TriggerFunction *triggerFunction = *it;
if (triggerFunction->_sceneId == sceneId && triggerFunction->_verbId == verbId &&
triggerFunction->_objectId2 == objectId2 && triggerFunction->_objectId == objectId)
break;
}
return it;
}
} // End of namespace Illusions

View File

@@ -0,0 +1,59 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_BBDOU_BBDOU_TRIGGERFUNCTIONS_H
#define ILLUSIONS_BBDOU_BBDOU_TRIGGERFUNCTIONS_H
#include "common/algorithm.h"
#include "common/stack.h"
namespace Illusions {
struct TriggerFunction;
typedef Common::Functor2<TriggerFunction*, uint32, void> TriggerFunctionCallback;
struct TriggerFunction {
uint32 _sceneId;
uint32 _verbId;
uint32 _objectId2;
uint32 _objectId;
TriggerFunctionCallback *_callback;
TriggerFunction(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback);
~TriggerFunction();
void run(uint32 callingThreadId);
};
class TriggerFunctions {
public:
void add(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback);
TriggerFunction *find(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId);
void removeBySceneId(uint32 sceneId);
public:
typedef Common::List<TriggerFunction*> Items;
typedef Items::iterator ItemsIterator;
Items _triggerFunctions;
ItemsIterator findInternal(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId);
};
} // End of namespace Illusions
#endif // ILLUSIONS_BBDOU_BBDOU_TRIGGERFUNCTIONS_H

View File

@@ -0,0 +1,108 @@
/* 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/>.
*
*/
#include "illusions/bbdou/illusions_bbdou.h"
#include "illusions/bbdou/bbdou_videoplayer.h"
#include "illusions/actor.h"
#include "illusions/dictionary.h"
#include "illusions/input.h"
#include "illusions/screen.h"
#include "engines/util.h"
namespace Illusions {
// BBDOUVideoPlayer
BBDOUVideoPlayer::BBDOUVideoPlayer(IllusionsEngine_BBDOU *vm)
: _vm(vm), _videoDecoder(nullptr), _callingThreadId(0), _objectId(0) {
}
BBDOUVideoPlayer::~BBDOUVideoPlayer() {
delete _videoDecoder;
}
void BBDOUVideoPlayer::start(uint32 videoId, uint32 objectId, uint32 priority, uint32 callingThreadId) {
debug(0, "BBDOUVideoPlayer::play(%08X, %08X, %d, %08X)", videoId, objectId, priority, callingThreadId);
notifyCallingThread();
_objectId = objectId;
_callingThreadId = callingThreadId;
Control *videoControl = _vm->_dict->getObjectControl(objectId);
videoControl->_flags |= 0x0008;
_vm->_input->discardAllEvents();
Common::Path filename(Common::String::format("%08x.avi", videoId));
_videoDecoder = new Video::AVIDecoder();
if (!_videoDecoder->loadFile(filename)) {
delete _videoDecoder;
_videoDecoder = nullptr;
warning("Unable to open video %s", filename.toString().c_str());
notifyCallingThread();
return;
}
_videoDecoder->start();
}
void BBDOUVideoPlayer::stop() {
_vm->_input->discardAllEvents();
delete _videoDecoder;
_videoDecoder = nullptr;
notifyCallingThread();
_objectId = 0;
}
void BBDOUVideoPlayer::update() {
if (_vm->_input->pollEvent(kEventAbort) || _videoDecoder->endOfVideo()) {
stop();
} else if (_videoDecoder->needsUpdate()) {
Control *videoControl = _vm->_dict->getObjectControl(_objectId);
const Graphics::Surface *frame = _videoDecoder->decodeNextFrame();
Graphics::Surface *backSurface = videoControl->_actor->_surface;
if (frame->format.bytesPerPixel == g_system->getScreenFormat().bytesPerPixel) {
const int width = MIN(frame->w, backSurface->w) * frame->format.bytesPerPixel;
const int height = MIN(frame->h, backSurface->h);
const byte *src = (const byte*)frame->getPixels();
byte *dest = (byte*)backSurface->getPixels();
for (int yc = 0; yc < height; ++yc) {
memcpy(dest, src, width);
src += frame->pitch;
dest += backSurface->pitch;
}
}
ActorType *actorType = _vm->_dict->findActorType(videoControl->_actorTypeId);
videoControl->_actor->_frameIndex = 1;
videoControl->_actor->_surfInfo = actorType->_surfInfo;
videoControl->appearActor();
videoControl->deactivateObject();
videoControl->_actor->_flags &= ~0x2000;
}
}
bool BBDOUVideoPlayer::isPlaying() const {
return _videoDecoder != nullptr;
}
void BBDOUVideoPlayer::notifyCallingThread() {
if (_callingThreadId != 0) {
_vm->notifyThreadId(_callingThreadId);
_callingThreadId = 0;
}
}
} // End of namespace Illusions

View File

@@ -0,0 +1,51 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_BBDOU_VIDEOPLAYER_H
#define ILLUSIONS_BBDOU_VIDEOPLAYER_H
#include "illusions/illusions.h"
#include "video/avi_decoder.h"
namespace Illusions {
class IllusionsEngine_BBDOU;
class BBDOUVideoPlayer {
public:
BBDOUVideoPlayer(IllusionsEngine_BBDOU *vm);
~BBDOUVideoPlayer();
void start(uint32 videoId, uint32 objectId, uint32 priority, uint32 callingThreadId);
void stop();
void update();
bool isPlaying() const;
public:
IllusionsEngine_BBDOU *_vm;
Video::VideoDecoder *_videoDecoder;
uint32 _objectId;
int _priority;
uint32 _callingThreadId;
void notifyCallingThread();
};
} // End of namespace Illusions
#endif // ILLUSIONS_BBDOU_VIDEOPLAYER_H

View File

@@ -0,0 +1,52 @@
/* 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/>.
*
*/
#include "illusions/bbdou/gamestate_bbdou.h"
#include "illusions/bbdou/illusions_bbdou.h"
#include "illusions/resources/scriptresource.h"
namespace Illusions {
BBDOU_GameState::BBDOU_GameState(IllusionsEngine_BBDOU *vm)
: _vm(vm) {
}
uint32 BBDOU_GameState::calcWriteBufferSizeInternal() {
return
4 + // uint32 prevSceneId
_vm->_scriptResource->_properties.getSize() +
_vm->_scriptResource->_blockCounters.getSize();
}
bool BBDOU_GameState::readStateInternal(Common::ReadStream *in) {
_vm->_prevSceneId = in->readUint32LE();
return
_vm->_scriptResource->_properties.readFromStream(in) &&
_vm->_scriptResource->_blockCounters.readFromStream(in);
}
void BBDOU_GameState::writeStateInternal(Common::WriteStream *out) {
out->writeUint32LE(_vm->_prevSceneId);
_vm->_scriptResource->_properties.writeToStream(out);
_vm->_scriptResource->_blockCounters.writeToStream(out);
}
} // End of namespace Illusions

View File

@@ -0,0 +1,43 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_BBDOU_GAMESTATE_BBDOU_H
#define ILLUSIONS_BBDOU_GAMESTATE_BBDOU_H
#include "illusions/gamestate.h"
namespace Illusions {
class IllusionsEngine_BBDOU;
class BBDOU_GameState : public GameState {
public:
BBDOU_GameState(IllusionsEngine_BBDOU *vm);
protected:
IllusionsEngine_BBDOU *_vm;
uint32 calcWriteBufferSizeInternal() override;
bool readStateInternal(Common::ReadStream *in) override;
void writeStateInternal(Common::WriteStream *out) override;
};
} // End of namespace Illusions
#endif // ILLUSIONS_BBDOU_GAMESTATE_BBDOU_H

View File

@@ -0,0 +1,704 @@
/* 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/>.
*
*/
#include "illusions/bbdou/illusions_bbdou.h"
#include "illusions/bbdou/bbdou_menukeys.h"
#include "illusions/bbdou/bbdou_videoplayer.h"
#include "illusions/bbdou/gamestate_bbdou.h"
#include "illusions/bbdou/menusystem_bbdou.h"
#include "illusions/actor.h"
#include "illusions/camera.h"
#include "illusions/console.h"
#include "illusions/cursor.h"
#include "illusions/dictionary.h"
#include "illusions/fileresourcereader.h"
#include "illusions/graphics.h"
#include "illusions/input.h"
#include "illusions/resources/actorresource.h"
#include "illusions/resources/backgroundresource.h"
#include "illusions/resources/fontresource.h"
#include "illusions/resources/scriptresource.h"
#include "illusions/resources/soundresource.h"
#include "illusions/resources/talkresource.h"
#include "illusions/resourcesystem.h"
#include "illusions/screen.h"
#include "illusions/screentext.h"
#include "illusions/scriptstack.h"
#include "illusions/bbdou/scriptopcodes_bbdou.h"
#include "illusions/sound.h"
#include "illusions/specialcode.h"
#include "illusions/bbdou/bbdou_specialcode.h"
#include "illusions/thread.h"
#include "illusions/time.h"
#include "illusions/updatefunctions.h"
#include "illusions/threads/abortablethread.h"
#include "illusions/threads/scriptthread.h"
#include "illusions/threads/talkthread.h"
#include "illusions/threads/timerthread.h"
#include "audio/audiostream.h"
#include "common/config-manager.h"
#include "common/debug-channels.h"
#include "common/error.h"
#include "common/fs.h"
#include "common/timer.h"
#include "engines/util.h"
#include "graphics/cursorman.h"
#include "graphics/font.h"
#include "graphics/fontman.h"
#include "graphics/surface.h"
namespace Illusions {
// ActiveScenes
ActiveScenes::ActiveScenes() {
clear();
}
void ActiveScenes::clear() {
_stack.clear();
}
void ActiveScenes::push(uint32 sceneId) {
ActiveScene activeScene;
activeScene._sceneId = sceneId;
activeScene._pauseCtr = 0;
_stack.push(activeScene);
}
void ActiveScenes::pop() {
_stack.pop();
}
void ActiveScenes::pauseActiveScene() {
++_stack.top()._pauseCtr;
}
void ActiveScenes::unpauseActiveScene() {
--_stack.top()._pauseCtr;
}
uint ActiveScenes::getActiveScenesCount() {
return _stack.size();
}
void ActiveScenes::getActiveSceneInfo(uint index, uint32 *sceneId, int *pauseCtr) {
if (sceneId)
*sceneId = _stack[index - 1]._sceneId;
if (pauseCtr)
*pauseCtr = _stack[index - 1]._pauseCtr;
}
uint32 ActiveScenes::getCurrentScene() {
if (_stack.size() > 0)
return _stack.top()._sceneId;
return 0;
}
bool ActiveScenes::isSceneActive(uint32 sceneId) {
for (uint i = 0; i < _stack.size(); ++i) {
if (_stack[i]._sceneId == sceneId && _stack[i]._pauseCtr <= 0)
return true;
}
return false;
}
// IllusionsEngine_BBDOU
IllusionsEngine_BBDOU::IllusionsEngine_BBDOU(OSystem *syst, const IllusionsGameDescription *gd)
: IllusionsEngine(syst, gd) {
}
Common::Error IllusionsEngine_BBDOU::run() {
// Init search paths
const Common::FSNode gameDataDir(ConfMan.getPath("path"));
SearchMan.addSubDirectoryMatching(gameDataDir, "music");
SearchMan.addSubDirectoryMatching(gameDataDir, "resource");
SearchMan.addSubDirectoryMatching(gameDataDir, "resrem");
SearchMan.addSubDirectoryMatching(gameDataDir, "savegame");
SearchMan.addSubDirectoryMatching(gameDataDir, "sfx", 0, 2);
SearchMan.addSubDirectoryMatching(gameDataDir, "video");
SearchMan.addSubDirectoryMatching(gameDataDir, "voice");
_dict = new Dictionary();
_resReader = new ResourceReaderFileReader();
_resSys = new ResourceSystem(this);
_resSys->addResourceLoader(0x00060000, new ActorResourceLoader(this));
_resSys->addResourceLoader(0x00080000, new SoundGroupResourceLoader(this));
_resSys->addResourceLoader(0x000D0000, new ScriptResourceLoader(this));
_resSys->addResourceLoader(0x000F0000, new TalkResourceLoader(this));
_resSys->addResourceLoader(0x00100000, new ActorResourceLoader(this));
_resSys->addResourceLoader(0x00110000, new BackgroundResourceLoader(this));
_resSys->addResourceLoader(0x00120000, new FontResourceLoader(this));
_resSys->addResourceLoader(0x00170000, new SpecialCodeLoader(this));
setDebugger(new Console(this));
_screen = new Screen16Bit(this, 640, 480);
_screenPalette = new NullScreenPalette();
_screenText = new ScreenText(this);
_input = new Input();
_actorInstances = new ActorInstanceList(this);
_backgroundInstances = new BackgroundInstanceList(this);
_camera = new Camera(this);
_controls = new Controls(this);
_cursor = new Cursor(this);
_talkItems = new TalkInstanceList(this);
_triggerFunctions = new TriggerFunctions();
_threads = new ThreadList(this);
_updateFunctions = new UpdateFunctions();
_soundMan = new SoundMan(this);
_menuSystem = new BBDOUMenuSystem(this);
_videoPlayer = new BBDOUVideoPlayer(this);
_gameState = new BBDOU_GameState(this);
_menuKeys = new BBDOUMenuKeys(this);
_screen->setColorKey1(0xF81F);
initInput();
initUpdateFunctions();
_fader = nullptr;
_scriptOpcodes = new ScriptOpcodes_BBDOU(this);
_stack = new ScriptStack();
_resGetCtr = 0;
_unpauseControlActorFlag = false;
_lastUpdateTime = 0;
_pauseCtr = 0;
_field8 = 1;
_fieldA = 0;
ConfMan.registerDefault("talkspeed", 240);
_subtitleDuration = (uint16)ConfMan.getInt("talkspeed");
_globalSceneId = 0x00010003;
setDefaultTextCoords();
_resSys->loadResource(0x000D0001, 0, 0);
_doScriptThreadInit = false;
startScriptThread(0x00020004, 0, 0, 0, 0);
_doScriptThreadInit = true;
if (ConfMan.hasKey("save_slot")) {
loadGameState(ConfMan.getInt("save_slot"));
}
_walkthroughStarted = false;
_canResumeFromSavegame = false;
while (!shouldQuit()) {
if (_walkthroughStarted) {
//enterScene(0x10003, 0);
startScriptThread(0x00020404, 0, 0, 0, 0);
_walkthroughStarted = false;
}
if (_resumeFromSavegameRequested && _canResumeFromSavegame) {
resumeFromSavegame();
_resumeFromSavegameRequested = false;
}
runUpdateFunctions();
_system->updateScreen();
updateEvents();
}
unloadSpecialCode(0);
_resSys->unloadAllResources();
delete _stack;
delete _scriptOpcodes;
delete _menuKeys;
delete _gameState;
delete _videoPlayer;
delete _menuSystem;
delete _soundMan;
delete _updateFunctions;
delete _threads;
delete _triggerFunctions;
delete _talkItems;
delete _cursor;
delete _controls;
delete _camera;
delete _backgroundInstances;
delete _actorInstances;
delete _input;
delete _screenText;
delete _screenPalette;
delete _screen;
delete _resSys;
delete _resReader;
delete _dict;
debug("Ok");
return Common::kNoError;
}
bool IllusionsEngine_BBDOU::hasFeature(EngineFeature f) const {
return
(f == kSupportsReturnToLauncher) ||
(f == kSupportsLoadingDuringRuntime) ||
(f == kSupportsSavingDuringRuntime);
}
void IllusionsEngine_BBDOU::initInput() {
_input->setInputEvent(kEventLeftClick, 0x01)
.addMouseButton(MOUSE_LEFT_BUTTON);
_input->setInputEvent(kEventRightClick, 0x02)
.addMouseButton(MOUSE_RIGHT_BUTTON);
_input->setInputEvent(kEventInventory, 0x04)
.addMouseButton(MOUSE_RIGHT_BUTTON)
.addKey(kActionInventory);
_input->setInputEvent(kEventAbort, 0x08)
.addKey(kActionAbort);
_input->setInputEvent(kEventSkip, 0x10)
.addKey(kActionSkip);
_input->setInputEvent(kEventF1, 0x20)
.addKey(kActionCheatMode);
_input->setInputEvent(kEventUp, 0x40)
.addKey(kActionCursorUp);
_input->setInputEvent(kEventDown, 0x80)
.addMouseButton(MOUSE_RIGHT_BUTTON)
.addKey(kActionCursorDown);
}
#define UPDATEFUNCTION(priority, sceneId, callback) \
_updateFunctions->add(priority, sceneId, new Common::Functor1Mem<uint, int, IllusionsEngine_BBDOU> \
(this, &IllusionsEngine_BBDOU::callback));
void IllusionsEngine_BBDOU::initUpdateFunctions() {
UPDATEFUNCTION(30, 0, updateScript);
UPDATEFUNCTION(50, 0, updateActors);
UPDATEFUNCTION(60, 0, updateMenuKeys);
UPDATEFUNCTION(60, 0, updateSequences);
UPDATEFUNCTION(70, 0, updateGraphics);
UPDATEFUNCTION(70, 0, updateVideoPlayer);
UPDATEFUNCTION(90, 0, updateSprites);
UPDATEFUNCTION(120, 0, updateSoundMan);
}
#undef UPDATEFUNCTION
int IllusionsEngine_BBDOU::updateScript(uint flags) {
_threads->updateThreads();
return kUFNext;
}
int IllusionsEngine_BBDOU::updateMenuKeys(uint flags) {
_menuKeys->update();
return kUFNext;
}
bool IllusionsEngine_BBDOU::causeIsDeclared(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId) {
uint32 codeOffs;
return
_triggerFunctions->find(sceneId, verbId, objectId2, objectId) ||
findTriggerCause(sceneId, verbId, objectId2, objectId, codeOffs);
}
void IllusionsEngine_BBDOU::causeDeclare(uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback) {
_triggerFunctions->add(getCurrentScene(), verbId, objectId2, objectId, callback);
}
uint32 IllusionsEngine_BBDOU::causeTrigger(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 callingThreadId) {
uint32 codeOffs;
uint32 causeThreadId = 0;
TriggerFunction *triggerFunction = _triggerFunctions->find(sceneId, verbId, objectId2, objectId);
if (triggerFunction) {
triggerFunction->run(callingThreadId);
} else if (findTriggerCause(sceneId, verbId, objectId2, objectId, codeOffs)) {
causeThreadId = startTempScriptThread(_scriptResource->getCode(codeOffs),
callingThreadId, verbId, objectId2, objectId);
}
return causeThreadId;
}
int IllusionsEngine_BBDOU::updateVideoPlayer(uint flags) {
if (_videoPlayer->isPlaying())
_videoPlayer->update();
return kUFNext;
}
void IllusionsEngine_BBDOU::playVideo(uint32 videoId, uint32 objectId, uint32 priority, uint32 callingThreadId) {
_videoPlayer->start(videoId, objectId, priority, callingThreadId);
}
bool IllusionsEngine_BBDOU::isVideoPlaying() {
return _videoPlayer->isPlaying();
}
void IllusionsEngine_BBDOU::setDefaultTextCoords() {
WidthHeight dimensions;
dimensions._width = 480;
dimensions._height = 48;
Common::Point pt(320, 448);
setDefaultTextDimensions(dimensions);
setDefaultTextPosition(pt);
}
void IllusionsEngine_BBDOU::loadSpecialCode(uint32 resId) {
_specialCode = new BbdouSpecialCode(this);
_specialCode->init();
}
void IllusionsEngine_BBDOU::unloadSpecialCode(uint32 resId) {
delete _specialCode;
_specialCode = nullptr;
}
void IllusionsEngine_BBDOU::notifyThreadId(uint32 &threadId) {
if (threadId) {
uint32 tempThreadId = threadId;
threadId = 0;
_threads->notifyId(tempThreadId);
}
}
bool IllusionsEngine_BBDOU::testMainActorFastWalk(Control *control) {
return false;
}
bool IllusionsEngine_BBDOU::testMainActorCollision(Control *control) {
// Not used in BBDOU
return false;
}
Control *IllusionsEngine_BBDOU::getObjectControl(uint32 objectId) {
return _dict->getObjectControl(objectId);
}
Common::Point IllusionsEngine_BBDOU::getNamedPointPosition(uint32 namedPointId) {
Common::Point pt;
if (_backgroundInstances->findActiveBackgroundNamedPoint(namedPointId, pt) ||
_actorInstances->findNamedPoint(namedPointId, pt) ||
_controls->findNamedPoint(namedPointId, pt))
return pt;
// TODO
switch (namedPointId) {
case 0x70001:
return Common::Point(0, 0);
case 0x70002:
return Common::Point(640, 0);
case 0x70023:
return Common::Point(320, 240);
default:
break;
}
debug("getNamedPointPosition(%08X) UNKNOWN", namedPointId);
return Common::Point(0, 0);
}
uint32 IllusionsEngine_BBDOU::getPriorityFromBase(int16 priority) {
return 32000000 * priority;
}
uint32 IllusionsEngine_BBDOU::getCurrentScene() {
return _activeScenes.getCurrentScene();
}
uint32 IllusionsEngine_BBDOU::getPrevScene() {
return _prevSceneId;
}
bool IllusionsEngine_BBDOU::isCursorObject(uint32 actorTypeId, uint32 objectId) {
return actorTypeId == 0x50001 && objectId == Illusions::CURSOR_OBJECT_ID;
}
void IllusionsEngine_BBDOU::setCursorControlRoutine(Control *control) {
control->_actor->setControlRoutine(new Common::Functor2Mem<Control*, uint32, void, IllusionsEngine_BBDOU>
(this, &IllusionsEngine_BBDOU::cursorControlRoutine));
}
void IllusionsEngine_BBDOU::placeCursorControl(Control *control, uint32 sequenceId) {
_cursor->place(control, sequenceId);
}
void IllusionsEngine_BBDOU::setCursorControl(Control *control) {
_cursor->setControl(control);
}
void IllusionsEngine_BBDOU::showCursor() {
_cursor->show();
}
void IllusionsEngine_BBDOU::hideCursor() {
_cursor->hide();
}
void IllusionsEngine_BBDOU::cursorControlRoutine(Control *control, uint32 deltaTime) {
control->_actor->_seqCodeValue1 = 100 * deltaTime;
if (control->_actor->_flags & Illusions::ACTOR_FLAG_IS_VISIBLE) {
switch (_cursor->_status) {
case 2:
// Unused nullsub_1(control);
break;
case 3:
_menuSystem->update(control);
break;
default:
break;
}
}
}
void IllusionsEngine_BBDOU::startScriptThreadSimple(uint32 threadId, uint32 callingThreadId) {
startScriptThread(threadId, callingThreadId, 0, 0, 0);
}
void IllusionsEngine_BBDOU::startScriptThread(uint32 threadId, uint32 callingThreadId,
uint32 value8, uint32 valueC, uint32 value10) {
if (threadId == 0x0002041E && ConfMan.hasKey("save_slot")) {
// Skip intro videos when loading a savegame from the launcher (kludge)
notifyThreadId(callingThreadId);
return;
}
debug(2, "Starting script thread %08X", threadId);
byte *scriptCodeIp = _scriptResource->getThreadCode(threadId);
newScriptThread(threadId, callingThreadId, 0, scriptCodeIp, value8, valueC, value10);
}
void IllusionsEngine_BBDOU::startAnonScriptThread(int32 threadId, uint32 callingThreadId,
uint32 value8, uint32 valueC, uint32 value10) {
debug(2, "Starting anonymous script thread %08X", threadId);
uint32 tempThreadId = newTempThreadId();
byte *scriptCodeIp = _scriptResource->getThreadCode(threadId);
scriptCodeIp = _scriptResource->getThreadCode(threadId);
newScriptThread(tempThreadId, callingThreadId, 0, scriptCodeIp, value8, valueC, value10);
}
uint32 IllusionsEngine_BBDOU::startAbortableTimerThread(uint32 duration, uint32 threadId) {
return newTimerThread(duration, threadId, true);
}
uint32 IllusionsEngine_BBDOU::startTimerThread(uint32 duration, uint32 threadId) {
return newTimerThread(duration, threadId, false);
}
uint32 IllusionsEngine_BBDOU::startAbortableThread(byte *scriptCodeIp1, byte *scriptCodeIp2, uint32 callingThreadId) {
uint32 tempThreadId = newTempThreadId();
debug(2, "Starting abortable thread %08X", tempThreadId);
uint32 scriptThreadId = startTempScriptThread(scriptCodeIp1, tempThreadId, 0, 0, 0);
AbortableThread *abortableThread = new AbortableThread(this, tempThreadId, callingThreadId, 0,
scriptThreadId, scriptCodeIp2);
_threads->startThread(abortableThread);
return tempThreadId;
}
uint32 IllusionsEngine_BBDOU::startTalkThread(int16 duration, uint32 objectId, uint32 talkId, uint32 sequenceId1,
uint32 sequenceId2, uint32 namedPointId, uint32 callingThreadId) {
debug(2, "Starting talk thread");
uint32 tempThreadId = newTempThreadId();
_threads->endTalkThreadsNoNotify();
TalkThread *talkThread = new TalkThread(this, tempThreadId, callingThreadId, 0,
duration, objectId, talkId, sequenceId1, sequenceId2, namedPointId);
_threads->startThread(talkThread);
return tempThreadId;
}
uint32 IllusionsEngine_BBDOU::startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
uint32 value8, uint32 valueC, uint32 value10) {
uint32 tempThreadId = newTempThreadId();
debug(2, "Starting temp script thread %08X", tempThreadId);
newScriptThread(tempThreadId, callingThreadId, 0, scriptCodeIp, value8, valueC, value10);
return tempThreadId;
}
void IllusionsEngine_BBDOU::newScriptThread(uint32 threadId, uint32 callingThreadId, uint notifyFlags,
byte *scriptCodeIp, uint32 value8, uint32 valueC, uint32 value10) {
ScriptThread *scriptThread = new ScriptThread(this, threadId, callingThreadId, notifyFlags,
scriptCodeIp, value8, valueC, value10);
_threads->startThread(scriptThread);
if (_pauseCtr > 0)
scriptThread->pause();
if (_doScriptThreadInit) {
int updateResult = kTSRun;
while (scriptThread->_pauseCtr <= 0 && updateResult != kTSTerminate && updateResult != kTSYield) {
updateResult = scriptThread->update();
}
}
}
uint32 IllusionsEngine_BBDOU::newTimerThread(uint32 duration, uint32 callingThreadId, bool isAbortable) {
uint32 tempThreadId = newTempThreadId();
TimerThread *timerThread = new TimerThread(this, tempThreadId, callingThreadId, 0,
duration, isAbortable);
_threads->startThread(timerThread);
return tempThreadId;
}
uint32 IllusionsEngine_BBDOU::newTempThreadId() {
uint32 threadId = _nextTempThreadId + 2 * _scriptResource->_codeCount;
if (threadId > 65535) {
_nextTempThreadId = 0;
threadId = 2 * _scriptResource->_codeCount;
}
++_nextTempThreadId;
return 0x00020000 | threadId;
}
bool IllusionsEngine_BBDOU::enterScene(uint32 sceneId, uint32 threadId) {
SceneInfo *sceneInfo = _scriptResource->getSceneInfo(sceneId & 0xFFFF);
if (!sceneInfo) {
dumpActiveScenes(_globalSceneId, threadId);
sceneId = _theSceneId;
}
_activeScenes.push(sceneId);
if (sceneId == 0x0001007D) {
// Savegame loading from the ScummVM GUI or command line is only
// possible after resources have been initialized by the startup script.
// Once that script is done, it switches to the start menu scene.
// After that the game is ready and a savegame can finally be loaded.
_canResumeFromSavegame = true;
}
return sceneInfo != nullptr;
}
void IllusionsEngine_BBDOU::exitScene(uint32 threadId) {
uint32 sceneId = _activeScenes.getCurrentScene();
_updateFunctions->terminateByScene(sceneId);
_threads->terminateThreadsBySceneId(sceneId, threadId);
_controls->destroyControlsBySceneId(sceneId);
_triggerFunctions->removeBySceneId(sceneId);
_resSys->unloadResourcesBySceneId(sceneId);
_activeScenes.pop();
}
void IllusionsEngine_BBDOU::enterPause(uint32 threadId) {
uint32 sceneId = _activeScenes.getCurrentScene();
_camera->pushCameraMode();
_threads->suspendThreadsBySceneId(sceneId, threadId);
_controls->pauseControlsBySceneId(sceneId);
_actorInstances->pauseBySceneId(sceneId);
_backgroundInstances->pauseBySceneId(sceneId);
_activeScenes.pauseActiveScene();
}
void IllusionsEngine_BBDOU::leavePause(uint32 threadId) {
uint32 sceneId = _activeScenes.getCurrentScene();
_backgroundInstances->unpauseBySceneId(sceneId);
_actorInstances->unpauseBySceneId(sceneId);
_controls->unpauseControlsBySceneId(sceneId);
_threads->notifyThreadsBySceneId(sceneId, threadId);
_camera->popCameraMode();
_activeScenes.unpauseActiveScene();
}
void IllusionsEngine_BBDOU::dumpActiveScenes(uint32 sceneId, uint32 threadId) {
uint activeScenesCount = _activeScenes.getActiveScenesCount();
while (activeScenesCount > 0) {
uint32 activeSceneId;
_activeScenes.getActiveSceneInfo(activeScenesCount, &activeSceneId, nullptr);
if (activeSceneId == sceneId)
break;
exitScene(threadId);
--activeScenesCount;
}
_camera->clearCameraModeStack();
}
void IllusionsEngine_BBDOU::pause(uint32 callerThreadId) {
if (++_pauseCtr == 1) {
_threads->pauseThreads(callerThreadId);
_camera->pause();
pauseFader();
_controls->pauseActors(0x40004);
}
}
void IllusionsEngine_BBDOU::unpause(uint32 callerThreadId) {
if (--_pauseCtr == 0) {
_controls->unpauseActors(0x40004);
unpauseFader();
_camera->unpause();
_threads->unpauseThreads(callerThreadId);
}
}
void IllusionsEngine_BBDOU::enterMenuPause() {
// TODO suspendAudio();
_screenText->clearText();
}
void IllusionsEngine_BBDOU::leaveMenuPause() {
_screenText->removeText();
// TODO unsuspendAudio();
}
void IllusionsEngine_BBDOU::setSceneIdThreadId(uint32 theSceneId, uint32 theThreadId) {
_theSceneId = theSceneId;
_theThreadId = theThreadId;
}
bool IllusionsEngine_BBDOU::findTriggerCause(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs) {
SceneInfo *sceneInfo = _scriptResource->getSceneInfo(sceneId & 0xFFFF);
if (sceneInfo)
return sceneInfo->findTriggerCause(verbId, objectId2, objectId, codeOffs);
return false;
}
void IllusionsEngine_BBDOU::reset() {
_scriptResource->_blockCounters.clear();
_scriptResource->_properties.clear();
setTextDuration(1, 0);
}
void IllusionsEngine_BBDOU::loadSavegameFromScript(int16 slotNum, uint32 callingThreadId) {
// NOTE Just loads the savegame, doesn't activate it yet
Common::String fileName = getSavegameFilename(_targetName, _savegameSlotNum);
_loadGameResult = loadgame(fileName.c_str());
}
void IllusionsEngine_BBDOU::saveSavegameFromScript(int16 slotNum, uint32 callingThreadId) {
// TODO
// Common::String fileName = getSavegameFilename(_targetName, slotNum);
_saveGameResult = false;//savegame(fileName.c_str(), _savegameDescription.c_str());
}
void IllusionsEngine_BBDOU::activateSavegame(uint32 callingThreadId) {
uint32 sceneId, threadId;
_prevSceneId = 0x10000;
_gameState->readState(sceneId, threadId);
enterScene(sceneId, callingThreadId);
// TODO Check if value8, valueC, value10 are needed at all
startAnonScriptThread(threadId, 0, 0, 0, 0);
_gameState->deleteReadStream();
}
void IllusionsEngine_BBDOU::resumeFromSavegame() {
// Resetting the game is usually done by the script, when loading from the ScummVM menu or
// command line this has to be done manually.
_specialCode->resetBeforeResumeSavegame();
dumpActiveScenes(0x00010003, 0);
activateSavegame(0);
}
} // End of namespace Illusions

View File

@@ -0,0 +1,164 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_ILLUSIONS_BBDOU_H
#define ILLUSIONS_ILLUSIONS_BBDOU_H
#include "illusions/illusions.h"
#include "illusions/bbdou/bbdou_triggerfunctions.h"
#include "common/algorithm.h"
#include "common/stack.h"
namespace Illusions {
class Dictionary;
class ScriptMan;
class ScriptStack;
class BBDOUVideoPlayer;
class BBDOUMenuKeys;
class BBDOUMenuSystem;
struct ActiveScene {
uint32 _sceneId;
int _pauseCtr;
};
class ActiveScenes {
public:
ActiveScenes();
void clear();
void push(uint32 sceneId);
void pop();
void pauseActiveScene();
void unpauseActiveScene();
uint getActiveScenesCount();
void getActiveSceneInfo(uint index, uint32 *sceneId, int *pauseCtr);
uint32 getCurrentScene();
bool isSceneActive(uint32 sceneId);
protected:
Common::FixedStack<ActiveScene, 16> _stack;
};
class IllusionsEngine_BBDOU : public IllusionsEngine {
public:
IllusionsEngine_BBDOU(OSystem *syst, const IllusionsGameDescription *gd);
protected:
Common::Error run() override;
bool hasFeature(EngineFeature f) const override;
public:
ScriptMan *_scriptMan;
TriggerFunctions *_triggerFunctions;
Cursor *_cursor;
ActiveScenes _activeScenes;
uint32 _prevSceneId;
uint32 _theSceneId;
uint32 _theThreadId;
uint32 _globalSceneId;
bool _loadGameResult, _saveGameResult;
BBDOUMenuSystem *_menuSystem;
BBDOUVideoPlayer *_videoPlayer;
BBDOUMenuKeys *_menuKeys;
bool _walkthroughStarted;
bool _canResumeFromSavegame;
void initInput();
void initUpdateFunctions();
int updateScript(uint flags);
int updateMenuKeys(uint flags);
bool causeIsDeclared(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId);
void causeDeclare(uint32 verbId, uint32 objectId2, uint32 objectId, TriggerFunctionCallback *callback);
uint32 causeTrigger(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 callingThreadId);
int updateVideoPlayer(uint flags);
void playVideo(uint32 videoId, uint32 objectId, uint32 priority, uint32 callingThreadId);
bool isVideoPlaying() override;
void setDefaultTextCoords() override;
void loadSpecialCode(uint32 resId) override;
void unloadSpecialCode(uint32 resId) override;
void notifyThreadId(uint32 &threadId) override;
bool testMainActorFastWalk(Control *control) override;
bool testMainActorCollision(Control *control) override;
Control *getObjectControl(uint32 objectId) override;
Common::Point getNamedPointPosition(uint32 namedPointId) override;
uint32 getPriorityFromBase(int16 priority) override;
uint32 getCurrentScene() override;
uint32 getPrevScene() override;
bool isCursorObject(uint32 actorTypeId, uint32 objectId) override;
void setCursorControlRoutine(Control *control) override;
void placeCursorControl(Control *control, uint32 sequenceId) override;
void setCursorControl(Control *control) override;
void showCursor() override;
void hideCursor() override;
void cursorControlRoutine(Control *control, uint32 deltaTime);
void startScriptThreadSimple(uint32 threadId, uint32 callingThreadId) override;
void startScriptThread(uint32 threadId, uint32 callingThreadId,
uint32 value8, uint32 valueC, uint32 value10);
void startAnonScriptThread(int32 threadId, uint32 callingThreadId,
uint32 value8, uint32 valueC, uint32 value10);
uint32 startAbortableTimerThread(uint32 duration, uint32 threadId);
uint32 startTimerThread(uint32 duration, uint32 threadId);
uint32 startAbortableThread(byte *scriptCodeIp1, byte *scriptCodeIp2, uint32 callingThreadId);
uint32 startTalkThread(int16 duration, uint32 objectId, uint32 talkId, uint32 sequenceId1,
uint32 sequenceId2, uint32 namedPointId, uint32 callingThreadId);
uint32 startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
uint32 value8, uint32 valueC, uint32 value10) override;
void newScriptThread(uint32 threadId, uint32 callingThreadId, uint notifyFlags,
byte *scriptCodeIp, uint32 value8, uint32 valueC, uint32 value10);
uint32 newTimerThread(uint32 duration, uint32 callingThreadId, bool isAbortable);
uint32 newTempThreadId();
bool enterScene(uint32 sceneId, uint32 threadId);
void exitScene(uint32 threadId);
void enterPause(uint32 threadId);
void leavePause(uint32 threadId);
void dumpActiveScenes(uint32 sceneId, uint32 threadId);
void pause(uint32 callerThreadId);
void unpause(uint32 callerThreadId);
void enterMenuPause();
void leaveMenuPause();
void setSceneIdThreadId(uint32 theSceneId, uint32 theThreadId);
bool findTriggerCause(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs);
void reset();
void loadSavegameFromScript(int16 slotNum, uint32 callingThreadId);
void saveSavegameFromScript(int16 slotNum, uint32 callingThreadId);
void activateSavegame(uint32 callingThreadId);
void resumeFromSavegame();
};
} // End of namespace Illusions
#endif // ILLUSIONS_ILLUSIONS_H

View File

@@ -0,0 +1,173 @@
/* 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/>.
*
*/
#include "illusions/illusions.h"
#include "illusions/actor.h"
#include "illusions/cursor.h"
#include "illusions/bbdou/illusions_bbdou.h"
#include "illusions/bbdou/menusystem_bbdou.h"
namespace Illusions {
// BBDOUMenuSystem
BBDOUMenuSystem::BBDOUMenuSystem(IllusionsEngine_BBDOU *vm)
: BaseMenuSystem(vm), _vm(vm) {
clearMenus();
}
BBDOUMenuSystem::~BBDOUMenuSystem() {
freeMenus();
}
void BBDOUMenuSystem::runMenu(MenuChoiceOffsets menuChoiceOffsets, int16 *menuChoiceOffset,
uint32 menuId, uint32 duration, uint timeOutMenuChoiceIndex, uint32 menuCallerThreadId) {
debug(0, "BBDOUMenuSystem::runMenu(%08X)", menuId);
setTimeOutDuration(duration, timeOutMenuChoiceIndex);
setMenuCallerThreadId(menuCallerThreadId);
setMenuChoiceOffsets(menuChoiceOffsets, menuChoiceOffset);
int rootMenuId = convertRootMenuId(menuId);
BaseMenu *rootMenu = getMenuById(rootMenuId);
openMenu(rootMenu);
}
void BBDOUMenuSystem::clearMenus() {
for (int i = 0; i < kBBDOULastMenuIndex; ++i) {
_menus[i] = nullptr;
}
}
void BBDOUMenuSystem::freeMenus() {
for (int i = 0; i < kBBDOULastMenuIndex; ++i) {
delete _menus[i];
}
}
BaseMenu *BBDOUMenuSystem::getMenuById(int menuId) {
if (!_menus[menuId])
_menus[menuId] = createMenuById(menuId);
return _menus[menuId];
}
BaseMenu *BBDOUMenuSystem::createMenuById(int menuId) {
switch (menuId) {
case kBBDOUMainMenu:
return createMainMenu();
case kBBDOUPauseMenu:
return createPauseMenu();
// TODO Other menus
default:
error("BBDOUMenuSystem::createMenuById() Invalid menu id %d", menuId);
}
}
BaseMenu *BBDOUMenuSystem::createMainMenu() {
return nullptr; // TODO
}
BaseMenu *BBDOUMenuSystem::createLoadGameMenu() {
return nullptr; // TODO
}
BaseMenu *BBDOUMenuSystem::createOptionsMenu() {
return nullptr; // TODO
}
BaseMenu *BBDOUMenuSystem::createPauseMenu() {
BaseMenu *menu = new BaseMenu(this, 0x00120003, 218, 150, 80, 20, 1);
menu->addText(" Game Paused");
menu->addText("-------------------");
menu->addMenuItem(new MenuItem("Resume", new MenuActionReturnChoice(this, 1)));
// menu->addMenuItem(new MenuItem("Load Game", new MenuActionLoadGame(this, 1)));
// TODO menu->addMenuItem(new MenuItem("Save Game", new MenuActionSaveGame(this, 11)));
// TODO menu->addMenuItem(new MenuItem("Restart Game", new MenuActionEnterQueryMenu(this, kDuckmanQueryRestartMenu, 2)));
// TODO menu->addMenuItem(new MenuItem("Options", new MenuActionEnterMenu(this, kDuckmanOptionsMenu)));
// menu->addMenuItem(new MenuItem("Quit Game", new MenuActionEnterQueryMenu(this, kDuckmanQueryQuitMenu, 23)));
return menu;
}
int BBDOUMenuSystem::convertRootMenuId(uint32 menuId) {
switch (menuId) {
case 0x1C0001:
return kBBDOUMainMenu;
case 0x1C0002:
return kBBDOUPauseMenu;
case 0x1C0006:
return kBBDOULoadGameMenu;
case 0x1C0007:
return kBBDOUSaveGameMenu;
case 0x1C0008:
return kBBDOUGameSavedMenu;
case 0x1C0009:
return kBBDOUSaveFailedMenu;
case 0x1C000A:
return kBBDOULoadFailedMenu;
/* Unused/unimplemented debug menus
case 0x1C0003: debugStartMenu
case 0x1C0004: debugPauseMenu
case 0x1C0005: unitTestsMenu
*/
default:
error("BBDOUMenuSystem() Menu ID %08X not found", menuId);
}
}
bool BBDOUMenuSystem::initMenuCursor() {
bool cursorInitialVisibleFlag = false;
Control *cursorControl = _vm->getObjectControl(0x40004);
if (cursorControl) {
if (cursorControl->_flags & 1) {
cursorInitialVisibleFlag = false;
} else {
cursorInitialVisibleFlag = true;
cursorControl->appearActor();
}
} else {
Common::Point pos = _vm->getNamedPointPosition(0x70023);
_vm->_controls->placeActor(0x50001, pos, 0x60001, 0x40004, 0);
cursorControl = _vm->getObjectControl(0x40004);
}
return cursorInitialVisibleFlag;
}
int BBDOUMenuSystem::getGameState() {
return _vm->_cursor->_status;
}
void BBDOUMenuSystem::setMenuCursorNum(int cursorNum) {
Control *mouseCursor = _vm->getObjectControl(0x40004);
_vm->_cursor->setActorIndex(5, cursorNum, 0);
mouseCursor->startSequenceActor(0x60001, 2, 0);
}
void BBDOUMenuSystem::setGameState(int gameState) {
_vm->_cursor->_status = gameState;
}
void BBDOUMenuSystem::playSoundEffect(int sfxId) {
// TODO
}
} // End of namespace Illusions

View File

@@ -0,0 +1,73 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_BBDOU_MENUSYSTEM_BBDOU_H
#define ILLUSIONS_BBDOU_MENUSYSTEM_BBDOU_H
#include "illusions/menusystem.h"
namespace Illusions {
enum {
kBBDOUMainMenu,
kBBDOUPauseMenu,
kBBDOULoadGameMenu,
kBBDOUSaveGameMenu,
kBBDOUGameSavedMenu,
kBBDOUSaveFailedMenu,
kBBDOULoadFailedMenu,
kBBDOULastMenuIndex
};
class IllusionsEngine_BBDOU;
class BBDOUMenuSystem : public BaseMenuSystem {
public:
BBDOUMenuSystem(IllusionsEngine_BBDOU *vm);
~BBDOUMenuSystem() override;
void runMenu(MenuChoiceOffsets menuChoiceOffsets, int16 *menuChoiceOffset,
uint32 menuId, uint32 duration, uint timeOutMenuChoiceIndex, uint32 menuCallerThreadId);
public://protected:
IllusionsEngine_BBDOU *_vm;
BaseMenu *_menus[kBBDOULastMenuIndex];
void clearMenus();
void freeMenus();
BaseMenu *getMenuById(int menuId) override;
BaseMenu *createMenuById(int menuId);
BaseMenu *createMainMenu();
BaseMenu *createPauseMenu();
BaseMenu *createLoadGameMenu();
BaseMenu *createSaveGameMenu();
BaseMenu *createGameSavedMenu();
BaseMenu *createSaveFailedMenu();
BaseMenu *createLoadFailedMenu();
BaseMenu *createOptionsMenu();
int convertRootMenuId(uint32 menuId);
bool initMenuCursor() override;
int getGameState() override;
void setGameState(int gameState) override;
void setMenuCursorNum(int cursorNum) override;
void playSoundEffect(int sfxId) override;
};
} // End of namespace Illusions
#endif // ILLUSIONS_BBDOU_MENUSYSTEM_BBDOU_H

View File

@@ -0,0 +1,992 @@
/* 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/>.
*
*/
#include "illusions/bbdou/illusions_bbdou.h"
#include "illusions/bbdou/scriptopcodes_bbdou.h"
#include "illusions/bbdou/bbdou_menukeys.h"
#include "illusions/bbdou/gamestate_bbdou.h"
#include "illusions/bbdou/menusystem_bbdou.h"
#include "illusions/actor.h"
#include "illusions/camera.h"
#include "illusions/dictionary.h"
#include "illusions/input.h"
#include "illusions/resources/scriptresource.h"
#include "illusions/resources/talkresource.h"
#include "illusions/screen.h"
#include "illusions/scriptstack.h"
#include "illusions/sound.h"
#include "illusions/specialcode.h"
#include "illusions/threads/scriptthread.h"
namespace Illusions {
// ScriptOpcodes_BBDOU
ScriptOpcodes_BBDOU::ScriptOpcodes_BBDOU(IllusionsEngine_BBDOU *vm)
: ScriptOpcodes(vm), _vm(vm) {
initOpcodes();
}
ScriptOpcodes_BBDOU::~ScriptOpcodes_BBDOU() {
freeOpcodes();
}
typedef Common::Functor2Mem<ScriptThread*, OpCall&, void, ScriptOpcodes_BBDOU> ScriptOpcodeI;
#define OPCODE(op, func) \
_opcodes[op] = new ScriptOpcodeI(this, &ScriptOpcodes_BBDOU::func); \
_opcodeNames[op] = #func;
void ScriptOpcodes_BBDOU::initOpcodes() {
// First clear everything
for (uint i = 0; i < 256; ++i) {
_opcodes[i] = nullptr;
}
// Register opcodes
OPCODE(2, opSuspend);
OPCODE(3, opYield);
OPCODE(4, opTerminate);
OPCODE(5, opJump);
OPCODE(6, opStartScriptThread);
// 7 unused
OPCODE(8, opStartTempScriptThread);
OPCODE(9, opStartTimerThread);
// 10-11 unused
OPCODE(12, opNotifyThreadId);
// 13 unused
OPCODE(14, opSetThreadSceneId);
OPCODE(15, opEndTalkThreads);
OPCODE(16, opLoadResource);
OPCODE(17, opUnloadResource);
OPCODE(18, opEnterMenuPause);
OPCODE(19, opLeaveMenuPause);
OPCODE(20, opEnterScene);
OPCODE(21, opLeaveScene);
OPCODE(22, opEnterPause);
OPCODE(23, opLeavePause);
OPCODE(24, opUnloadActiveScenes);
OPCODE(25, opChangeScene);
OPCODE(26, opStartModalScene);
OPCODE(27, opExitModalScene);
// 28-29 unused
OPCODE(30, opEnterCloseUpScene);
OPCODE(31, opExitCloseUpScene);
OPCODE(32, opPanCenterObject);
// 33 unused
OPCODE(34, opPanToObject);
OPCODE(35, opPanToNamedPoint);
OPCODE(36, opPanToPoint);
OPCODE(37, opPanStop);
OPCODE(39, opSetDisplay);
OPCODE(40, opSetCameraBounds);
OPCODE(41, opSetCameraBoundsToMasterBg);
OPCODE(42, opIncBlockCounter);
OPCODE(43, opClearBlockCounter);
// 44 unused
OPCODE(45, opSetProperty);
OPCODE(46, opPlaceActor);
OPCODE(47, opFaceActor);
OPCODE(48, opFaceActorToObject);
OPCODE(49, opStartSequenceActor);
// 50 unused
OPCODE(51, opStartMoveActor);
// 52 unused
OPCODE(53, opSetActorToNamedPoint);
OPCODE(54, opSetActorPosition);
// 55 unused
OPCODE(56, opStartTalkThread);
OPCODE(57, opAppearActor);
OPCODE(58, opDisappearActor);
OPCODE(59, opIsActorVisible);
OPCODE(60, opActivateObject);
OPCODE(61, opDeactivateObject);
OPCODE(62, opSetDefaultSequence);
OPCODE(63, opSetSelectSfx);
OPCODE(64, opSetMoveSfx);
OPCODE(65, opSetDenySfx);
OPCODE(66, opSetAdjustUpSfx);
OPCODE(67, opSetAdjustDnSfx);
// 68 unused
OPCODE(69, opPause);
OPCODE(70, opResume);
OPCODE(71, opStartSound);
OPCODE(72, opStartSoundAtPosition);
OPCODE(73, opStartSoundAtActor);
OPCODE(74, opStopSound);
OPCODE(75, opStartMusic);
OPCODE(76, opStopMusic);
// 77 unused
OPCODE(78, opStackPushRandom);
OPCODE(79, opIfLte);
OPCODE(80, opAddMenuChoice);
OPCODE(81, opDisplayMenu);
OPCODE(82, opSwitchMenuChoice);
OPCODE(83, opQuitGame);
OPCODE(84, opResetGame);
OPCODE(85, opSaveGame);
OPCODE(86, opRestoreGameState);
OPCODE(87, opDeactivateButton);
OPCODE(88, opActivateButton);
OPCODE(89, opNop);
// 90 unused
OPCODE(91, opNop);
// 92-102 unused
OPCODE(103, opJumpIf);
OPCODE(104, opIsPrevSceneId);
OPCODE(105, opIsCurrentSceneId);
OPCODE(106, opIsActiveSceneId);
OPCODE(107, opNot);
OPCODE(108, opAnd);
OPCODE(109, opOr);
OPCODE(110, opGetProperty);
OPCODE(111, opCompareBlockCounter);
OPCODE(126, opDebug126);
OPCODE(127, opDebug127);
OPCODE(144, opPlayVideo);
OPCODE(146, opStackPop);
OPCODE(147, opStackDup);
OPCODE(148, opLoadSpecialCodeModule);
OPCODE(150, opRunSpecialCode);
OPCODE(152, opLinkObjectToObject);
OPCODE(153, opUnlinkObject);
OPCODE(160, opStopActor);
OPCODE(161, opSetActorUsePan);
OPCODE(168, opStartAbortableThread);
OPCODE(169, opKillThread);
OPCODE(170, opLoadGame);
OPCODE(171, opPushLoadgameResult);
OPCODE(172, opPushSavegameResult);
// 173, 174 unused
OPCODE(175, opSetSceneIdThreadId);
OPCODE(176, opStackPush0);
OPCODE(177, opSetFontId);
OPCODE(178, opAddMenuKey);
OPCODE(179, opChangeSceneAll);
}
#undef OPCODE
void ScriptOpcodes_BBDOU::freeOpcodes() {
for (uint i = 0; i < 256; ++i) {
delete _opcodes[i];
}
}
// Opcodes
void ScriptOpcodes_BBDOU::opSuspend(ScriptThread *scriptThread, OpCall &opCall) {
opCall._result = kTSSuspend;
}
void ScriptOpcodes_BBDOU::opYield(ScriptThread *scriptThread, OpCall &opCall) {
opCall._result = kTSYield;
}
void ScriptOpcodes_BBDOU::opTerminate(ScriptThread *scriptThread, OpCall &opCall) {
opCall._result = kTSTerminate;
}
void ScriptOpcodes_BBDOU::opJump(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(jumpOffs);
opCall._deltaOfs += jumpOffs;
}
void ScriptOpcodes_BBDOU::opStartScriptThread(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(threadId);
_vm->startScriptThread(threadId, opCall._threadId,
scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
}
void ScriptOpcodes_BBDOU::opStartTempScriptThread(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(codeOffs);
_vm->startTempScriptThread(opCall._code + codeOffs,
opCall._threadId, scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
}
void ScriptOpcodes_BBDOU::opStartTimerThread(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(isAbortable);
ARG_INT16(duration);
ARG_INT16(maxDuration);
if (maxDuration)
duration += _vm->getRandom(maxDuration);
//duration = 1;//DEBUG Speeds up things
if (isAbortable)
_vm->startAbortableTimerThread(duration, opCall._threadId);
else
_vm->startTimerThread(duration, opCall._threadId);
}
void ScriptOpcodes_BBDOU::opNotifyThreadId(ScriptThread *scriptThread, OpCall &opCall) {
Thread *thread = _vm->_threads->findThread(opCall._callerThreadId);
if (!(thread->_notifyFlags & 1))
_vm->notifyThreadId(thread->_callingThreadId);
}
void ScriptOpcodes_BBDOU::opSetThreadSceneId(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(sceneId);
_vm->_threads->setThreadSceneId(opCall._callerThreadId, sceneId);
}
void ScriptOpcodes_BBDOU::opEndTalkThreads(ScriptThread *scriptThread, OpCall &opCall) {
_vm->_threads->endTalkThreads();
}
void ScriptOpcodes_BBDOU::opLoadResource(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(resourceId);
// NOTE Skipped checking for stalled resources
uint32 sceneId = _vm->getCurrentScene();
_vm->_resSys->loadResource(resourceId, sceneId, opCall._threadId);
}
void ScriptOpcodes_BBDOU::opUnloadResource(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(resourceId);
// NOTE Skipped checking for stalled resources
_vm->_resSys->unloadResourceById(resourceId);
}
void ScriptOpcodes_BBDOU::opEnterMenuPause(ScriptThread *scriptThread, OpCall &opCall) {
_vm->enterMenuPause();
}
void ScriptOpcodes_BBDOU::opLeaveMenuPause(ScriptThread *scriptThread, OpCall &opCall) {
_vm->leaveMenuPause();
}
void ScriptOpcodes_BBDOU::opEnterScene(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(sceneId);
uint scenesCount = _vm->_activeScenes.getActiveScenesCount();
if (scenesCount > 0) {
uint32 currSceneId;
_vm->_activeScenes.getActiveSceneInfo(scenesCount, &currSceneId, nullptr);
// TODO krnfileDump(currSceneId);
}
if (!_vm->enterScene(sceneId, opCall._callerThreadId))
opCall._result = kTSTerminate;
}
void ScriptOpcodes_BBDOU::opLeaveScene(ScriptThread *scriptThread, OpCall &opCall) {
_vm->exitScene(opCall._callerThreadId);
}
void ScriptOpcodes_BBDOU::opEnterPause(ScriptThread *scriptThread, OpCall &opCall) {
_vm->enterPause(opCall._callerThreadId);
_vm->_talkItems->pauseBySceneId(_vm->getCurrentScene());
}
void ScriptOpcodes_BBDOU::opLeavePause(ScriptThread *scriptThread, OpCall &opCall) {
_vm->leavePause(opCall._callerThreadId);
}
void ScriptOpcodes_BBDOU::opUnloadActiveScenes(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(sceneId);
_vm->dumpActiveScenes(sceneId, opCall._callerThreadId);
}
//DEBUG Scenes
//uint32 dsceneId = 0x00010031, dthreadId = 0x00020036;//MAP
//uint32 dsceneId = 0x00010028, dthreadId = 0x000202A1;
//uint32 dsceneId = 0x00010007, dthreadId = 0x0002000C;//Auditorium
//uint32 dsceneId = 0x0001000B, dthreadId = 0x00020010;
//uint32 dsceneId = 0x00010013, dthreadId = 0x00020018;//Therapist
//uint32 dsceneId = 0x00010016, dthreadId = 0x0002001B;//Dorms ext
//uint32 dsceneId = 0x00010017, dthreadId = 0x0002001C;//Dorms int
//uint32 dsceneId = 0x0001000D, dthreadId = 0x00020012;//Food minigame
//uint32 dsceneId = 0x00010067, dthreadId = 0x0002022A;
uint32 dsceneId = 0x0001000C, dthreadId = 0x00020011;//Cafeteria
//uint32 dsceneId = 0x0001000B, dthreadId = 0x00020010;
//uint32 dsceneId = 0x0001001A, dthreadId = 0x0002001F;
//uint32 dsceneId = 0x00010047, dthreadId = 0x0002005F;
//uint32 dsceneId = 0x0001007D, dthreadId = 0x000203B9;
// uint32 dsceneId = 0x0001000D, dthreadId = 0x00020012; // Food minigame
void ScriptOpcodes_BBDOU::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(sceneId);
ARG_UINT32(threadId);
if (dsceneId) {
//#define RUN_WALKTHROUGH
#ifdef RUN_WALKTHROUGH
_vm->_walkthroughStarted = true;
dsceneId = 0;
return;
#endif
sceneId = dsceneId;
threadId = dthreadId;
dsceneId = 0;
}
// NOTE Skipped checking for stalled resources
_vm->_input->discardAllEvents();
_vm->_prevSceneId = _vm->getCurrentScene();
_vm->exitScene(opCall._callerThreadId);
_vm->enterScene(sceneId, opCall._callerThreadId);
_vm->_gameState->writeState(sceneId, threadId);
_vm->startAnonScriptThread(threadId, 0,
scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
}
void ScriptOpcodes_BBDOU::opStartModalScene(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(sceneId);
ARG_UINT32(threadId);
// NOTE Skipped checking for stalled resources
_vm->_input->discardAllEvents();
_vm->enterPause(opCall._callerThreadId);
_vm->_talkItems->pauseBySceneId(_vm->getCurrentScene());
_vm->enterScene(sceneId, opCall._callerThreadId);
_vm->startScriptThread(threadId, 0,
scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
opCall._result = kTSSuspend;
}
void ScriptOpcodes_BBDOU::opExitModalScene(ScriptThread *scriptThread, OpCall &opCall) {
// NOTE Skipped checking for stalled resources
_vm->_input->discardAllEvents();
_vm->exitScene(opCall._callerThreadId);
_vm->leavePause(opCall._callerThreadId);
_vm->_talkItems->unpauseBySceneId(_vm->getCurrentScene());
}
void ScriptOpcodes_BBDOU::opEnterCloseUpScene(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(sceneId);
// NOTE Skipped checking for stalled resources
_vm->_input->discardAllEvents();
_vm->enterPause(opCall._callerThreadId);
_vm->enterScene(sceneId, opCall._callerThreadId);
}
void ScriptOpcodes_BBDOU::opExitCloseUpScene(ScriptThread *scriptThread, OpCall &opCall) {
_vm->exitScene(opCall._callerThreadId);
_vm->leavePause(opCall._callerThreadId);
opCall._result = kTSYield;
}
void ScriptOpcodes_BBDOU::opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(speed);
ARG_UINT32(objectId);
_vm->_camera->panCenterObject(objectId, speed);
}
void ScriptOpcodes_BBDOU::opPanToObject(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(speed);
ARG_UINT32(objectId);
Control *control = _vm->_dict->getObjectControl(objectId);
Common::Point pos = control->getActorPosition();
_vm->_camera->panToPoint(pos, speed, opCall._threadId);
}
void ScriptOpcodes_BBDOU::opPanToNamedPoint(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(speed);
ARG_UINT32(namedPointId);
Common::Point pos = _vm->getNamedPointPosition(namedPointId);
_vm->_camera->panToPoint(pos, speed, opCall._threadId);
}
void ScriptOpcodes_BBDOU::opPanToPoint(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(speed);
ARG_INT16(x);
ARG_INT16(y);
_vm->_camera->panToPoint(Common::Point(x, y), speed, opCall._threadId);
}
void ScriptOpcodes_BBDOU::opPanStop(ScriptThread *scriptThread, OpCall &opCall) {
_vm->_camera->stopPan();
}
void ScriptOpcodes_BBDOU::opSetDisplay(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(flag);
_vm->_screen->setDisplayOn(flag != 0);
}
void ScriptOpcodes_BBDOU::opSetCameraBounds(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_INT16(x1);
ARG_INT16(y1);
ARG_INT16(x2);
ARG_INT16(y2);
_vm->_camera->setBounds(Common::Point(x1, y1), Common::Point(x2, y2));
}
void ScriptOpcodes_BBDOU::opSetCameraBoundsToMasterBg(ScriptThread *scriptThread, OpCall &opCall) {
WidthHeight bgDimensions = _vm->_backgroundInstances->getMasterBgDimensions();
_vm->_camera->setBoundsToDimensions(bgDimensions);
}
void ScriptOpcodes_BBDOU::opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(index);
byte value = _vm->_scriptResource->_blockCounters.get(index) + 1;
if (value <= 63)
_vm->_scriptResource->_blockCounters.set(index, value);
}
void ScriptOpcodes_BBDOU::opClearBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(index);
_vm->_scriptResource->_blockCounters.set(index, 0);
}
void ScriptOpcodes_BBDOU::opSetProperty(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(value);
ARG_UINT32(propertyId);
_vm->_scriptResource->_properties.set(propertyId, value != 0);
}
void ScriptOpcodes_BBDOU::opPlaceActor(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(objectId);
ARG_UINT32(actorTypeId);
ARG_UINT32(sequenceId);
ARG_UINT32(namedPointId);
Common::Point pos = _vm->getNamedPointPosition(namedPointId);
_vm->_controls->placeActor(actorTypeId, pos, sequenceId, objectId, opCall._threadId);
}
void ScriptOpcodes_BBDOU::opFaceActor(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(facing);
ARG_UINT32(objectId);
Control *control = _vm->_dict->getObjectControl(objectId);
control->faceActor(facing);
}
void ScriptOpcodes_BBDOU::opFaceActorToObject(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(objectId1);
ARG_UINT32(objectId2);
Control *control1 = _vm->_dict->getObjectControl(objectId1);
Control *control2 = _vm->_dict->getObjectControl(objectId2);
Common::Point pos1 = control1->getActorPosition();
Common::Point pos2 = control2->getActorPosition();
uint facing;
if (_vm->calcPointDirection(pos1, pos2, facing))
control1->faceActor(facing);
}
void ScriptOpcodes_BBDOU::opStartSequenceActor(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(objectId);
ARG_UINT32(sequenceId);
// NOTE Skipped checking for stalled sequence, not sure if needed
Control *control = _vm->_dict->getObjectControl(objectId);
control->startSequenceActor(sequenceId, 2, opCall._threadId);
}
void ScriptOpcodes_BBDOU::opStartMoveActor(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(objectId);
ARG_UINT32(sequenceId);
ARG_UINT32(namedPointId);
// NOTE Skipped checking for stalled sequence, not sure if needed
Control *control = _vm->_dict->getObjectControl(objectId);
if (!control) { return; }// TODO CHECKME
Common::Point pos = _vm->getNamedPointPosition(namedPointId);
control->startMoveActor(sequenceId, pos, opCall._callerThreadId, opCall._threadId);
}
void ScriptOpcodes_BBDOU::opSetActorToNamedPoint(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(objectId);
ARG_UINT32(namedPointId);
Control *control = _vm->_dict->getObjectControl(objectId);
Common::Point pos = _vm->getNamedPointPosition(namedPointId);
control->stopActor();
control->setActorPosition(pos);
}
void ScriptOpcodes_BBDOU::opSetActorPosition(ScriptThread *scriptThread, OpCall &opCall) {
ARG_UINT32(objectId);
ARG_INT16(x);
ARG_INT16(y);
Control *control = _vm->_dict->getObjectControl(objectId);
Common::Point pos(x, y);
control->setActorPosition(pos);
}
void ScriptOpcodes_BBDOU::opStartTalkThread(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(duration);
ARG_UINT32(objectId);
ARG_UINT32(talkId);
ARG_UINT32(sequenceId1);
ARG_UINT32(sequenceId2);
ARG_UINT32(namedPointId);
_vm->startTalkThread(duration, objectId, talkId, sequenceId1, sequenceId2, namedPointId, opCall._threadId);
}
void ScriptOpcodes_BBDOU::opAppearActor(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(objectId);
Control *control = _vm->_dict->getObjectControl(objectId);
if (!control) {
Common::Point pos = _vm->getNamedPointPosition(0x70023);
_vm->_controls->placeActor(0x50001, pos, 0x60001, objectId, 0);
control = _vm->_dict->getObjectControl(objectId);
control->startSequenceActor(0x60001, 2, 0);
}
control->appearActor();
}
void ScriptOpcodes_BBDOU::opDisappearActor(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(objectId);
Control *control = _vm->_dict->getObjectControl(objectId);
control->disappearActor();
}
void ScriptOpcodes_BBDOU::opIsActorVisible(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(objectId);
Control *control = _vm->_dict->getObjectControl(objectId);
const bool visible = control && control->isActorVisible();
_vm->_stack->push(visible ? 1 : 0);
}
void ScriptOpcodes_BBDOU::opActivateObject(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(objectId);
Control *control = _vm->_dict->getObjectControl(objectId);
if (control)
control->activateObject();
}
void ScriptOpcodes_BBDOU::opDeactivateObject(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(objectId);
Control *control = _vm->_dict->getObjectControl(objectId);
control->deactivateObject();
}
void ScriptOpcodes_BBDOU::opSetDefaultSequence(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(objectId);
ARG_UINT32(defaultSequenceId);
ARG_UINT32(sequenceId);
Control *control = _vm->_dict->getObjectControl(objectId);
control->_actor->_defaultSequences.set(sequenceId, defaultSequenceId);
}
void ScriptOpcodes_BBDOU::opSetSelectSfx(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(soundEffectId);
// TODO _vm->setSelectSfx(soundEffectId);
}
void ScriptOpcodes_BBDOU::opSetMoveSfx(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(soundEffectId);
// TODO _vm->setMoveSfx(soundEffectId);
}
void ScriptOpcodes_BBDOU::opSetDenySfx(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(soundEffectId);
// TODO _vm->setDenySfx(soundEffectId);
}
void ScriptOpcodes_BBDOU::opSetAdjustUpSfx(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(soundEffectId);
// TODO _vm->setAdjustUpSfx(soundEffectId);
}
void ScriptOpcodes_BBDOU::opSetAdjustDnSfx(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(soundEffectId);
// TODO _vm->setAdjustDnSfx(soundEffectId);
}
void ScriptOpcodes_BBDOU::opPause(ScriptThread *scriptThread, OpCall &opCall) {
_vm->pause(opCall._callerThreadId);
}
void ScriptOpcodes_BBDOU::opResume(ScriptThread *scriptThread, OpCall &opCall) {
_vm->unpause(opCall._callerThreadId);
}
void ScriptOpcodes_BBDOU::opStartSound(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_INT16(volume);
ARG_INT16(pan);
ARG_UINT32(soundEffectId);
_vm->_soundMan->playSound(soundEffectId, volume, pan);
}
void ScriptOpcodes_BBDOU::opStartSoundAtPosition(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(volume);
ARG_UINT32(soundEffectId);
ARG_UINT32(namedPointId);
Common::Point pos = _vm->getNamedPointPosition(namedPointId);
int16 pan = _vm->convertPanXCoord(pos.x);
_vm->_soundMan->playSound(soundEffectId, volume, pan);
}
void ScriptOpcodes_BBDOU::opStartSoundAtActor(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(volume);
ARG_UINT32(objectId);
ARG_UINT32(soundEffectId);
Control *control = _vm->_dict->getObjectControl(objectId);
Common::Point pos = control->getActorPosition();
int16 pan = _vm->convertPanXCoord(pos.x);
_vm->_soundMan->playSound(soundEffectId, volume, pan);
}
void ScriptOpcodes_BBDOU::opStopSound(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(soundEffectId);
_vm->_soundMan->stopSound(soundEffectId);
}
void ScriptOpcodes_BBDOU::opStartMusic(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_INT16(volume);
ARG_INT16(pan);
ARG_UINT32(musicId);
ARG_UINT32(type);
_vm->_soundMan->playMusic(musicId, type, volume, pan, opCall._threadId);
}
void ScriptOpcodes_BBDOU::opStopMusic(ScriptThread *scriptThread, OpCall &opCall) {
_vm->_soundMan->stopMusic();
}
void ScriptOpcodes_BBDOU::opStackPushRandom(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(maxValue);
_vm->_stack->push(_vm->getRandom(maxValue) + 1);
}
void ScriptOpcodes_BBDOU::opIfLte(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_INT16(rvalue);
ARG_INT16(elseJumpOffs);
int16 lvalue = _vm->_stack->pop();
if (!(lvalue <= rvalue))
opCall._deltaOfs += elseJumpOffs;
}
void ScriptOpcodes_BBDOU::opAddMenuChoice(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_INT16(jumpOffs);
ARG_INT16(endMarker);
_vm->_stack->push(endMarker);
_vm->_stack->push(jumpOffs);
}
void ScriptOpcodes_BBDOU::opDisplayMenu(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(timeoutChoiceOfs);
ARG_UINT32(menuId);
ARG_UINT32(timeOutDuration);
MenuChoiceOffsets menuChoiceOffsets;
// Load menu choices from the stack
do {
int16 choiceOffs = _vm->_stack->pop();
menuChoiceOffsets.push_back(choiceOffs);
} while (_vm->_stack->pop() == 0);
// TODO DBEUG Start menu not yet implemented, fake selection of "Start game"
if (menuId == 0x001C0001) {
_vm->_menuChoiceOfs = 88;
_vm->notifyThreadId(opCall._callerThreadId);
return;
}
// Duckman has the timeout choice offset on the stack and the index as parameter
// BBDOU instead has only the choice offset as parameter
// So we just add the timeout choice offset and use its index.
menuChoiceOffsets.push_back(timeoutChoiceOfs);
uint timeOutMenuChoiceIndex = menuChoiceOffsets.size() - 1;
_vm->_menuSystem->runMenu(menuChoiceOffsets, &_vm->_menuChoiceOfs,
menuId, timeOutDuration, timeOutMenuChoiceIndex,
opCall._callerThreadId);
}
void ScriptOpcodes_BBDOU::opSwitchMenuChoice(ScriptThread *scriptThread, OpCall &opCall) {
opCall._deltaOfs += _vm->_menuChoiceOfs;
}
void ScriptOpcodes_BBDOU::opQuitGame(ScriptThread *scriptThread, OpCall &opCall) {
_vm->quitGame();
}
void ScriptOpcodes_BBDOU::opResetGame(ScriptThread *scriptThread, OpCall &opCall) {
_vm->_threads->terminateThreads(opCall._callerThreadId);
_vm->reset();
_vm->_input->activateButton(0xFFFF);
// TODO _vm->stopMusic();
// TODO _vm->_gameStates->clear();
}
void ScriptOpcodes_BBDOU::opSaveGame(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_INT16(bankNum)
ARG_INT16(slotNum)
_vm->saveSavegameFromScript(slotNum, opCall._callerThreadId);
}
void ScriptOpcodes_BBDOU::opRestoreGameState(ScriptThread *scriptThread, OpCall &opCall) {
_vm->activateSavegame(opCall._callerThreadId);
}
void ScriptOpcodes_BBDOU::opDeactivateButton(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(button)
_vm->_input->deactivateButton(button);
}
void ScriptOpcodes_BBDOU::opActivateButton(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(button)
_vm->_input->activateButton(button);
}
void ScriptOpcodes_BBDOU::opNop(ScriptThread *scriptThread, OpCall &opCall) {
// Opcode empty but still called
}
void ScriptOpcodes_BBDOU::opJumpIf(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(jumpOffs);
int16 value = _vm->_stack->pop();
if (value == 0)
opCall._deltaOfs += jumpOffs;
}
void ScriptOpcodes_BBDOU::opIsPrevSceneId(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(sceneId);
_vm->_stack->push(_vm->_prevSceneId == sceneId ? 1 : 0);
}
void ScriptOpcodes_BBDOU::opIsCurrentSceneId(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(sceneId);
_vm->_stack->push(_vm->getCurrentScene() == sceneId ? 1 : 0);
}
void ScriptOpcodes_BBDOU::opIsActiveSceneId(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(sceneId);
_vm->_stack->push(_vm->_activeScenes.isSceneActive(sceneId) ? 1 : 0);
}
void ScriptOpcodes_BBDOU::opNot(ScriptThread *scriptThread, OpCall &opCall) {
int16 value = _vm->_stack->pop();
_vm->_stack->push(value != 0 ? 0 : 1);
}
void ScriptOpcodes_BBDOU::opAnd(ScriptThread *scriptThread, OpCall &opCall) {
int16 value1 = _vm->_stack->pop();
int16 value2 = _vm->_stack->pop();
_vm->_stack->push(value1 & value2);
}
void ScriptOpcodes_BBDOU::opOr(ScriptThread *scriptThread, OpCall &opCall) {
int16 value1 = _vm->_stack->pop();
int16 value2 = _vm->_stack->pop();
_vm->_stack->push(value1 | value2);
}
void ScriptOpcodes_BBDOU::opGetProperty(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(propertyId)
bool value = _vm->_scriptResource->_properties.get(propertyId);
_vm->_stack->push(value ? 1 : 0);
}
void ScriptOpcodes_BBDOU::opCompareBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(index);
ARG_INT16(compareOp);
ARG_INT16(rvalue);
int16 lvalue = _vm->_scriptResource->_blockCounters.get(index);
bool compareResult = false;
switch (compareOp) {
case 1:
compareResult = lvalue == rvalue;
break;
case 2:
compareResult = lvalue != rvalue;
break;
case 3:
compareResult = lvalue < rvalue;
break;
case 4:
compareResult = lvalue > rvalue;
break;
case 5:
compareResult = lvalue >= rvalue;
break;
case 6:
compareResult = lvalue <= rvalue;
break;
default:
break;
}
_vm->_stack->push(compareResult ? 1 : 0);
}
void ScriptOpcodes_BBDOU::opDebug126(ScriptThread *scriptThread, OpCall &opCall) {
// NOTE Prints some debug text
debug(1, "[DBG126] %s", (char*)opCall._code);
}
void ScriptOpcodes_BBDOU::opDebug127(ScriptThread *scriptThread, OpCall &opCall) {
// NOTE Prints some debug text
debug(1, "[DBG127] %s", (char*)opCall._code);
}
void ScriptOpcodes_BBDOU::opPlayVideo(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(objectId);
ARG_UINT32(videoId);
ARG_UINT32(priority);
#if 0 // TODO DEBUG Set to 0 to skip videos
_vm->playVideo(videoId, objectId, priority, opCall._threadId);
#else
//DEBUG Resume calling thread, later done by the video player
_vm->notifyThreadId(opCall._callerThreadId);
#endif
}
void ScriptOpcodes_BBDOU::opStackPop(ScriptThread *scriptThread, OpCall &opCall) {
_vm->_stack->pop();
}
void ScriptOpcodes_BBDOU::opStackDup(ScriptThread *scriptThread, OpCall &opCall) {
int16 value = _vm->_stack->peek();
_vm->_stack->push(value);
}
void ScriptOpcodes_BBDOU::opLoadSpecialCodeModule(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(specialCodeModuleId);
_vm->_resSys->loadResource(specialCodeModuleId, 0, 0);
}
void ScriptOpcodes_BBDOU::opRunSpecialCode(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(specialCodeId);
_vm->_specialCode->run(specialCodeId, opCall);
}
void ScriptOpcodes_BBDOU::opLinkObjectToObject(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(objectId);
ARG_UINT32(parentObjectId);
ARG_UINT32(linkedObjectValue);
Control *control = _vm->_dict->getObjectControl(objectId);
control->linkToObject(parentObjectId, linkedObjectValue);
}
void ScriptOpcodes_BBDOU::opUnlinkObject(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(objectId);
Control *control = _vm->_dict->getObjectControl(objectId);
control->unlinkObject();
}
void ScriptOpcodes_BBDOU::opStopActor(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(objectId);
Control *control = _vm->_dict->getObjectControl(objectId);
control->stopActor();
}
void ScriptOpcodes_BBDOU::opSetActorUsePan(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(usePan)
ARG_UINT32(objectId);
Control *control = _vm->_dict->getObjectControl(objectId);
control->setActorUsePan(usePan);
}
void ScriptOpcodes_BBDOU::opStartAbortableThread(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_INT16(codeOffs);
ARG_INT16(skipOffs);
_vm->startAbortableThread(opCall._code + codeOffs,
opCall._code + skipOffs, opCall._threadId);
}
void ScriptOpcodes_BBDOU::opKillThread(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(threadId);
_vm->_threads->killThread(threadId);
}
void ScriptOpcodes_BBDOU::opLoadGame(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_INT16(bankNum)
ARG_INT16(slotNum)
_vm->loadSavegameFromScript(slotNum, opCall._callerThreadId);
}
void ScriptOpcodes_BBDOU::opPushLoadgameResult(ScriptThread *scriptThread, OpCall &opCall) {
_vm->_stack->push(_vm->_loadGameResult ? 1 : 0);
}
void ScriptOpcodes_BBDOU::opPushSavegameResult(ScriptThread *scriptThread, OpCall &opCall) {
_vm->_stack->push(_vm->_saveGameResult ? 1 : 0);
}
void ScriptOpcodes_BBDOU::opSetSceneIdThreadId(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(sceneId);
ARG_UINT32(threadId);
_vm->setSceneIdThreadId(sceneId, threadId);
}
void ScriptOpcodes_BBDOU::opStackPush0(ScriptThread *scriptThread, OpCall &opCall) {
_vm->_stack->push(0);
}
void ScriptOpcodes_BBDOU::opSetFontId(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(fontId);
_vm->setCurrFontId(fontId);
}
void ScriptOpcodes_BBDOU::opAddMenuKey(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(key);
ARG_UINT32(threadId);
_vm->_menuKeys->addMenuKey(key, threadId);
}
void ScriptOpcodes_BBDOU::opChangeSceneAll(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(sceneId);
ARG_UINT32(threadId);
// NOTE Skipped checking for stalled resources
_vm->_input->discardAllEvents();
_vm->_prevSceneId = _vm->getCurrentScene();
_vm->dumpActiveScenes(_vm->_globalSceneId, opCall._callerThreadId);
_vm->enterScene(sceneId, opCall._callerThreadId);
_vm->_gameState->writeState(sceneId, threadId);
_vm->startAnonScriptThread(threadId, 0,
scriptThread->_value8, scriptThread->_valueC, scriptThread->_value10);
}
} // End of namespace Illusions

View File

@@ -0,0 +1,152 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_BBDOU_SCRIPTOPCODES_BBDOU_H
#define ILLUSIONS_BBDOU_SCRIPTOPCODES_BBDOU_H
#include "illusions/scriptopcodes.h"
#include "common/func.h"
namespace Illusions {
class IllusionsEngine_BBDOU;
class ScriptThread;
class ScriptOpcodes_BBDOU : public ScriptOpcodes {
public:
ScriptOpcodes_BBDOU(IllusionsEngine_BBDOU *vm);
~ScriptOpcodes_BBDOU() override;
void initOpcodes() override;
void freeOpcodes() override;
protected:
IllusionsEngine_BBDOU *_vm;
// Opcodes
void opSuspend(ScriptThread *scriptThread, OpCall &opCall);
void opYield(ScriptThread *scriptThread, OpCall &opCall);
void opTerminate(ScriptThread *scriptThread, OpCall &opCall);
void opJump(ScriptThread *scriptThread, OpCall &opCall);
void opStartScriptThread(ScriptThread *scriptThread, OpCall &opCall);
void opStartTempScriptThread(ScriptThread *scriptThread, OpCall &opCall);
void opStartTimerThread(ScriptThread *scriptThread, OpCall &opCall);
void opNotifyThreadId(ScriptThread *scriptThread, OpCall &opCall);
void opSetThreadSceneId(ScriptThread *scriptThread, OpCall &opCall);
void opEndTalkThreads(ScriptThread *scriptThread, OpCall &opCall);
void opLoadResource(ScriptThread *scriptThread, OpCall &opCall);
void opUnloadResource(ScriptThread *scriptThread, OpCall &opCall);
void opEnterMenuPause(ScriptThread *scriptThread, OpCall &opCall);
void opLeaveMenuPause(ScriptThread *scriptThread, OpCall &opCall);
void opEnterScene(ScriptThread *scriptThread, OpCall &opCall);
void opLeaveScene(ScriptThread *scriptThread, OpCall &opCall);
void opEnterPause(ScriptThread *scriptThread, OpCall &opCall);
void opLeavePause(ScriptThread *scriptThread, OpCall &opCall);
void opUnloadActiveScenes(ScriptThread *scriptThread, OpCall &opCall);
void opChangeScene(ScriptThread *scriptThread, OpCall &opCall);
void opStartModalScene(ScriptThread *scriptThread, OpCall &opCall);
void opExitModalScene(ScriptThread *scriptThread, OpCall &opCall);
void opEnterCloseUpScene(ScriptThread *scriptThread, OpCall &opCall);
void opExitCloseUpScene(ScriptThread *scriptThread, OpCall &opCall);
void opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall);
void opPanToObject(ScriptThread *scriptThread, OpCall &opCall);
void opPanToNamedPoint(ScriptThread *scriptThread, OpCall &opCall);
void opPanToPoint(ScriptThread *scriptThread, OpCall &opCall);
void opPanStop(ScriptThread *scriptThread, OpCall &opCall);
void opSetDisplay(ScriptThread *scriptThread, OpCall &opCall);
void opSetCameraBounds(ScriptThread *scriptThread, OpCall &opCall);
void opSetCameraBoundsToMasterBg(ScriptThread *scriptThread, OpCall &opCall);
void opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
void opClearBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
void opSetProperty(ScriptThread *scriptThread, OpCall &opCall);
void opPlaceActor(ScriptThread *scriptThread, OpCall &opCall);
void opFaceActor(ScriptThread *scriptThread, OpCall &opCall);
void opFaceActorToObject(ScriptThread *scriptThread, OpCall &opCall);
void opStartSequenceActor(ScriptThread *scriptThread, OpCall &opCall);
void opStartMoveActor(ScriptThread *scriptThread, OpCall &opCall);
void opSetActorToNamedPoint(ScriptThread *scriptThread, OpCall &opCall);
void opSetActorPosition(ScriptThread *scriptThread, OpCall &opCall);
void opStartTalkThread(ScriptThread *scriptThread, OpCall &opCall);
void opAppearActor(ScriptThread *scriptThread, OpCall &opCall);
void opDisappearActor(ScriptThread *scriptThread, OpCall &opCall);
void opIsActorVisible(ScriptThread *scriptThread, OpCall &opCall);
void opActivateObject(ScriptThread *scriptThread, OpCall &opCall);
void opDeactivateObject(ScriptThread *scriptThread, OpCall &opCall);
void opSetDefaultSequence(ScriptThread *scriptThread, OpCall &opCall);
void opSetSelectSfx(ScriptThread *scriptThread, OpCall &opCall);
void opSetMoveSfx(ScriptThread *scriptThread, OpCall &opCall);
void opSetDenySfx(ScriptThread *scriptThread, OpCall &opCall);
void opSetAdjustUpSfx(ScriptThread *scriptThread, OpCall &opCall);
void opSetAdjustDnSfx(ScriptThread *scriptThread, OpCall &opCall);
void opPause(ScriptThread *scriptThread, OpCall &opCall);
void opResume(ScriptThread *scriptThread, OpCall &opCall);
void opStartSound(ScriptThread *scriptThread, OpCall &opCall);
void opStartSoundAtPosition(ScriptThread *scriptThread, OpCall &opCall);
void opStartSoundAtActor(ScriptThread *scriptThread, OpCall &opCall);
void opStopSound(ScriptThread *scriptThread, OpCall &opCall);
void opStartMusic(ScriptThread *scriptThread, OpCall &opCall);
void opStopMusic(ScriptThread *scriptThread, OpCall &opCall);
void opStackPushRandom(ScriptThread *scriptThread, OpCall &opCall);
void opIfLte(ScriptThread *scriptThread, OpCall &opCall);
void opAddMenuChoice(ScriptThread *scriptThread, OpCall &opCall);
void opDisplayMenu(ScriptThread *scriptThread, OpCall &opCall);
void opSwitchMenuChoice(ScriptThread *scriptThread, OpCall &opCall);
void opQuitGame(ScriptThread *scriptThread, OpCall &opCall);
void opResetGame(ScriptThread *scriptThread, OpCall &opCall);
void opSaveGame(ScriptThread *scriptThread, OpCall &opCall);
void opRestoreGameState(ScriptThread *scriptThread, OpCall &opCall);
void opDeactivateButton(ScriptThread *scriptThread, OpCall &opCall);
void opActivateButton(ScriptThread *scriptThread, OpCall &opCall);
void opNop(ScriptThread *scriptThread, OpCall &opCall);
void opJumpIf(ScriptThread *scriptThread, OpCall &opCall);
void opIsPrevSceneId(ScriptThread *scriptThread, OpCall &opCall);
void opIsCurrentSceneId(ScriptThread *scriptThread, OpCall &opCall);
void opIsActiveSceneId(ScriptThread *scriptThread, OpCall &opCall);
void opNot(ScriptThread *scriptThread, OpCall &opCall);
void opAnd(ScriptThread *scriptThread, OpCall &opCall);
void opOr(ScriptThread *scriptThread, OpCall &opCall);
void opGetProperty(ScriptThread *scriptThread, OpCall &opCall);
void opCompareBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
void opDebug126(ScriptThread *scriptThread, OpCall &opCall);
void opDebug127(ScriptThread *scriptThread, OpCall &opCall);
void opPlayVideo(ScriptThread *scriptThread, OpCall &opCall);
void opStackPop(ScriptThread *scriptThread, OpCall &opCall);
void opStackDup(ScriptThread *scriptThread, OpCall &opCall);
void opLoadSpecialCodeModule(ScriptThread *scriptThread, OpCall &opCall);
void opRunSpecialCode(ScriptThread *scriptThread, OpCall &opCall);
void opLinkObjectToObject(ScriptThread *scriptThread, OpCall &opCall);
void opUnlinkObject(ScriptThread *scriptThread, OpCall &opCall);
void opStopActor(ScriptThread *scriptThread, OpCall &opCall);
void opSetActorUsePan(ScriptThread *scriptThread, OpCall &opCall);
void opStartAbortableThread(ScriptThread *scriptThread, OpCall &opCall);
void opKillThread(ScriptThread *scriptThread, OpCall &opCall);
void opLoadGame(ScriptThread *scriptThread, OpCall &opCall);
void opPushLoadgameResult(ScriptThread *scriptThread, OpCall &opCall);
void opPushSavegameResult(ScriptThread *scriptThread, OpCall &opCall);
void opSetSceneIdThreadId(ScriptThread *scriptThread, OpCall &opCall);
void opStackPush0(ScriptThread *scriptThread, OpCall &opCall);
void opSetFontId(ScriptThread *scriptThread, OpCall &opCall);
void opAddMenuKey(ScriptThread *scriptThread, OpCall &opCall);
void opChangeSceneAll(ScriptThread *scriptThread, OpCall &opCall);
};
} // End of namespace Illusions
#endif // ILLUSIONS_BBDOU_SCRIPTOPCODES_BBDOU_H

View File

@@ -0,0 +1,579 @@
/* 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/>.
*
*/
#include "illusions/illusions.h"
#include "illusions/camera.h"
#include "illusions/fixedpoint.h"
#include "illusions/resources/backgroundresource.h"
#include "illusions/time.h"
#include "illusions/actor.h"
namespace Illusions {
Camera::Camera(IllusionsEngine *vm)
: _vm(vm) {
init();
_activeState._cameraMode = 6;
_activeState._paused = false;
_activeState._panStartTime = getCurrentTime();
_activeState._panSpeed = 1;
_activeState._bounds._topLeft.x = _screenMidX;
_activeState._bounds._topLeft.y = _screenMidY;
_activeState._bounds._bottomRight.x = _screenMidX;
_activeState._bounds._bottomRight.y = _screenMidY;
_activeState._currPan.x = _screenMidX;
_activeState._currPan.y = _screenMidY;
_activeState._panXShl = _screenMidX << 16;
_activeState._panYShl = _screenMidY << 16;
_activeState._panTargetPoint.x = _screenMidX;
_activeState._panTargetPoint.y = 240;
_activeState._panToPositionPtr = nullptr;
_activeState._panNotifyId = 0;
_activeState._trackingLimits.x = 0;
_activeState._trackingLimits.y = 0;
_activeState._centerPt.x = _screenMidX;
_activeState._centerPt.y = _screenMidY;
_activeState._pointFlags = 0;
}
void Camera::clearStack() {
_stack.clear();
}
void Camera::set(Common::Point &panPoint, WidthHeight &dimensions) {
_activeState._cameraMode = 6;
_activeState._paused = false;
_activeState._panStartTime = getCurrentTime();
_activeState._panSpeed = 1;
_activeState._bounds._topLeft.x = _screenMidX;
_activeState._bounds._topLeft.y = _screenMidY;
_activeState._bounds._bottomRight.x = MAX(0, dimensions._width - _screenWidth) + _screenMidX;
_activeState._bounds._bottomRight.y = MAX(0, dimensions._height - _screenHeight) + _screenMidY;
_activeState._panTargetPoint = panPoint;
clipPanTargetPoint();
_activeState._currPan = _activeState._panTargetPoint;
_activeState._panXShl = _activeState._currPan.x << 16;
_activeState._panYShl = _activeState._currPan.y << 16;
_vm->_backgroundInstances->refreshPan();
_activeState._panToPositionPtr = nullptr;
_activeState._panObjectId = 0;
_activeState._panNotifyId = 0;
_activeState._trackingLimits.x = 0;
_activeState._trackingLimits.y = 0;
_activeState._pointFlags = 0;
_activeState._centerPt.x = _screenMidX;
_activeState._centerPt.y = _screenMidY;
}
void Camera::panCenterObject(uint32 objectId, int16 panSpeed) {
Common::Point *actorPosition = _vm->getObjectActorPositionPtr(objectId);
if (_vm->getGameId() == kGameIdDuckman) {
if (objectId == Illusions::CURSOR_OBJECT_ID) {
_activeState._cameraMode = 2;
_activeState._trackingLimits.x = 156;
_activeState._trackingLimits.y = 96;
} else {
_activeState._cameraMode = 1;
_activeState._trackingLimits.x = 4;
_activeState._trackingLimits.y = 4;
}
} else if (_vm->getGameId() == kGameIdBBDOU) {
_activeState._cameraMode = 1;
_activeState._trackingLimits = _centerObjectTrackingLimits;
}
_activeState._panSpeed = panSpeed;
_activeState._pointFlags = 0;
_activeState._panObjectId = objectId;
_activeState._panNotifyId = 0;
_activeState._panToPositionPtr = actorPosition;
_activeState._panTargetPoint = *actorPosition;
clipPanTargetPoint();
_activeState._panStartTime = getCurrentTime();
recalcPan(_activeState._panStartTime);
}
void Camera::panTrackObject(uint32 objectId) {
Common::Point *actorPosition = _vm->getObjectActorPositionPtr(objectId);
_activeState._cameraMode = 3;
_activeState._panObjectId = objectId;
_activeState._trackingLimits = _trackObjectTrackingLimits;
_activeState._panSpeed = _trackObjectTrackingLimitsPanSpeed;
_activeState._pointFlags = 0;
_activeState._panToPositionPtr = actorPosition;
_activeState._panNotifyId = 0;
_activeState._panTargetPoint = *actorPosition;
clipPanTargetPoint();
_activeState._panStartTime = getCurrentTime();
recalcPan(_activeState._panStartTime);
}
void Camera::panToPoint(Common::Point pt, int16 panSpeed, uint32 panNotifyId) {
_vm->notifyThreadId(_activeState._panNotifyId);
_activeState._panTargetPoint = getPtOffset(pt);
clipPanTargetPoint();
if (panSpeed) {
_activeState._cameraMode = 5;
_activeState._panSpeed = panSpeed;
_activeState._trackingLimits.x = 0;
_activeState._trackingLimits.y = 0;
_activeState._pointFlags = 0;
_activeState._panToPositionPtr = nullptr;
_activeState._panNotifyId = panNotifyId;
_activeState._panStartTime = getCurrentTime();
recalcPan(_activeState._panStartTime);
} else {
_activeState._currPan = _activeState._panTargetPoint;
stopPan();
_vm->notifyThreadId(panNotifyId);
}
}
void Camera::panEdgeFollow(uint32 objectId, int16 panSpeed) {
Common::Point *actorPosition = _vm->getObjectActorPositionPtr(objectId);
_activeState._cameraMode = 2;
_activeState._trackingLimits.x = 318;
_activeState._trackingLimits.y = 238;
_activeState._pointFlags = 0;
_activeState._panSpeed = panSpeed;
_activeState._panToPositionPtr = actorPosition;
_activeState._panObjectId = objectId;
_activeState._panTargetPoint = _activeState._currPan;
_activeState._panNotifyId = 0;
clipPanTargetPoint();
_activeState._panStartTime = getCurrentTime();
recalcPan(_activeState._panStartTime);
}
void Camera::stopPan() {
_activeState._cameraMode = 6;
_activeState._panTargetPoint = _activeState._currPan;
_activeState._panSpeed = 1;
_activeState._panXShl = _activeState._currPan.x << 16;
_activeState._panYShl = _activeState._currPan.y << 16;
_activeState._panToPositionPtr = nullptr;
_activeState._panObjectId = 0;
_activeState._panNotifyId = 0;
_activeState._pointFlags = 0;
_vm->_backgroundInstances->refreshPan();
}
void Camera::pause() {
_activeState._pauseStartTime = getCurrentTime();
_activeState._paused = true;
}
void Camera::unpause() {
_activeState._paused = false;
uint32 pauseDuration = getCurrentTime() - _activeState._pauseStartTime;
_activeState._time28 += pauseDuration;
_activeState._panStartTime += pauseDuration;
}
void Camera::pushCameraMode() {
CameraModeStackItem item;
item._cameraMode = _activeState._cameraMode;
item._panSpeed = _activeState._panSpeed;
item._panObjectId = 0;
item._panNotifyId = 0;
item._panTargetPoint.x = 0;
item._panTargetPoint.y = 0;
switch (_activeState._cameraMode) {
case 1:
case 2:
case 3:
item._panObjectId = _activeState._panObjectId;
break;
case 4:
item._cameraMode = 3;
item._panObjectId = _activeState._panObjectId;
break;
case 5:
item._panTargetPoint = _activeState._panTargetPoint;
item._panNotifyId = _activeState._panNotifyId;
break;
default:
break;
}
_stack.push(item);
}
void Camera::popCameraMode() {
if (_stack.empty())
return;
CameraModeStackItem item = _stack.pop();
if (item._panObjectId && !_vm->getObjectActorPositionPtr(item._panObjectId)) {
// Tracking object doesn't exist any longer
stopPan();
return;
}
switch (item._cameraMode) {
case 1:
panCenterObject(item._panObjectId, item._panSpeed);
break;
case 2:
panEdgeFollow(item._panObjectId, item._panSpeed);
break;
case 3:
panTrackObject(item._panObjectId);
break;
case 5:
panToPoint(item._panTargetPoint, item._panSpeed, item._panNotifyId);
break;
case 6:
stopPan();
break;
default:
break;
}
}
void Camera::clearCameraModeStack() {
_stack.clear();
}
void Camera::update(uint32 currTime) {
if (_activeState._paused)
return;
switch (_activeState._cameraMode) {
case 1:
updateMode1(currTime);
break;
case 2:
updateMode2(currTime);
break;
case 3:
updateMode3(currTime);
break;
default:
break;
}
if (_activeState._cameraMode != 6) {
if (!isPanFinished() && updatePan(currTime)) {
/* Unused
if (_activeState._cameraMode == 1 || _activeState._cameraMode == 5)
nullsub_2();
*/
_vm->_backgroundInstances->refreshPan();
}
if (isPanFinished()) {
if (_activeState._cameraMode == 5) {
// Notify a thread that the camera panning has finished
_vm->notifyThreadId(_activeState._panNotifyId);
_activeState._cameraMode = 6;
} else if (_activeState._cameraMode == 4) {
_activeState._cameraMode = 3;
}
}
}
}
void Camera::setBounds(Common::Point minPt, Common::Point maxPt) {
_activeState._bounds._topLeft = minPt;
_activeState._bounds._bottomRight = maxPt;
}
void Camera::setBoundsToDimensions(WidthHeight &dimensions) {
// NOTE For the caller dimensions = artdispGetMasterBGDimensions();
_activeState._bounds._topLeft.x = _screenMidX;
_activeState._bounds._topLeft.y = _screenMidY;
_activeState._bounds._bottomRight.x = MAX(0, dimensions._width - _screenWidth) + _screenMidX;
_activeState._bounds._bottomRight.y = MAX(0, dimensions._height - _screenHeight) + _screenMidY;
clipPanTargetPoint();
}
Common::Point Camera::getCurrentPan() {
return _activeState._currPan;
}
Common::Point Camera::getScreenOffset() {
Common::Point screenOffs = getCurrentPan();
screenOffs.x -= _screenMidX;
screenOffs.y -= _screenMidY;
return screenOffs;
}
Common::Point Camera::getTrackingLimits() {
return _activeState._trackingLimits;
}
bool Camera::isAtPanLimit(int limitNum) {
switch (limitNum) {
case 1:
return _activeState._currPan.y <= _activeState._bounds._topLeft.y;
case 2:
return _activeState._currPan.y >= _activeState._bounds._bottomRight.y;
case 3:
return _activeState._currPan.x <= _activeState._bounds._topLeft.x;
case 4:
return _activeState._currPan.x >= _activeState._bounds._bottomRight.x;
default:
break;
}
return false;
}
void Camera::setActiveState(CameraState &state) {
_activeState = state;
_activeState._panStartTime = getCurrentTime();
}
void Camera::getActiveState(CameraState &state) {
state = _activeState;
}
void Camera::refreshPan(BackgroundInstance *backgroundItem, WidthHeight &dimensions) {
Common::Point screenOffs = getScreenOffset();
int x = dimensions._width - _screenWidth;
int y = dimensions._height - _screenHeight;
for (uint i = 0; i < backgroundItem->_bgRes->_bgInfosCount; ++i) {
const BgInfo &bgInfo = backgroundItem->_bgRes->_bgInfos[i];
if (bgInfo._flags & 1) {
backgroundItem->_panPoints[i] = screenOffs;
} else {
Common::Point newOffs(0, 0);
if (x > 0 && bgInfo._surfInfo._dimensions._width - _screenWidth > 0)
newOffs.x = screenOffs.x * (bgInfo._surfInfo._dimensions._width - _screenWidth) / x;
if (y > 0 && bgInfo._surfInfo._dimensions._height - _screenHeight > 0)
newOffs.y = screenOffs.y * (bgInfo._surfInfo._dimensions._height - _screenHeight) / y;
backgroundItem->_panPoints[i] = newOffs;
}
}
}
void Camera::updateMode1(uint32 currTime) {
Common::Point ptOffs = getPtOffset(*_activeState._panToPositionPtr);
int deltaX = ptOffs.x - _activeState._currPan.x + _screenMidX - _activeState._centerPt.x;
int deltaY = ptOffs.y - _activeState._currPan.y + _screenMidY - _activeState._centerPt.y;
int deltaXAbs = ABS(deltaX);
int deltaYAbs = ABS(deltaY);
if (deltaXAbs > _activeState._trackingLimits.x)
_activeState._panTargetPoint.x = _activeState._currPan.x + ABS(deltaXAbs - _activeState._trackingLimits.x) * (deltaX >= 0 ? 1 : -1);
else
_activeState._panTargetPoint.x = _activeState._currPan.x;
if (deltaYAbs > _activeState._trackingLimits.y)
_activeState._panTargetPoint.y = _activeState._currPan.y + ABS(deltaYAbs - _activeState._trackingLimits.y) * (deltaY >= 0 ? 1 : -1);
else
_activeState._panTargetPoint.y = _activeState._currPan.y;
clipPanTargetPoint();
if (!isPanFinished()) {
uint32 oldPanTime = _activeState._panStartTime;
_activeState._panStartTime = _activeState._time28;
recalcPan(oldPanTime);
}
}
void Camera::updateMode2(uint32 currTime) {
// TOOD CHECKME Bigger differences in Duckman
Common::Point panToPosition = *_activeState._panToPositionPtr;
uint pointFlags = 0;
WRect rect;
rect._topLeft.x = _screenMidX - _activeState._trackingLimits.x;
rect._topLeft.y = _screenMidY - _activeState._trackingLimits.y;
rect._bottomRight.x = _screenMidX + _activeState._trackingLimits.x;
rect._bottomRight.y = _screenMidY + _activeState._trackingLimits.y;
if (calcPointFlags(panToPosition, rect, pointFlags)) {
if (pointFlags != _activeState._pointFlags) {
_activeState._pointFlags = pointFlags;
if (pointFlags & 1)
_activeState._panTargetPoint.x = _activeState._bounds._topLeft.x;
else if (pointFlags & 2)
_activeState._panTargetPoint.x = _activeState._bounds._bottomRight.x;
else
_activeState._panTargetPoint.x = _activeState._currPan.x;
if (pointFlags & 4)
_activeState._panTargetPoint.y = _activeState._bounds._topLeft.y;
else if (pointFlags & 8)
_activeState._panTargetPoint.y = _activeState._bounds._bottomRight.y;
else
_activeState._panTargetPoint.y = _activeState._currPan.y;
clipPanTargetPoint();
_activeState._panStartTime = currTime;
recalcPan(currTime);
}
} else if (_activeState._pointFlags) {
_activeState._pointFlags = 0;
_activeState._panTargetPoint = _activeState._currPan;
}
}
void Camera::updateMode3(uint32 currTime) {
Common::Point panToPosition = *_activeState._panToPositionPtr;
int deltaX = panToPosition.x - _activeState._currPan.x;
int deltaY = panToPosition.y - _activeState._currPan.y;
if (ABS(deltaX) > _activeState._trackingLimits.x)
_activeState._panTargetPoint.x = _activeState._currPan.x + 2 * _activeState._trackingLimits.x * (deltaX >= 0 ? 1 : -1);
else
_activeState._panTargetPoint.x = _activeState._currPan.x;
if (ABS(deltaY) > _activeState._trackingLimits.y)
_activeState._panTargetPoint.y = _activeState._currPan.y + 2 * _activeState._trackingLimits.y * (deltaY >= 0 ? 1 : -1);
else
_activeState._panTargetPoint.y = _activeState._currPan.y;
clipPanTargetPoint();
if (!isPanFinished()) {
_activeState._panStartTime = currTime;
recalcPan(currTime);
_activeState._cameraMode = 4;
}
}
bool Camera::updatePan(uint32 currTime) {
if (currTime - _activeState._time28 >= _activeState._time2E) {
_activeState._panXShl = _activeState._panTargetPoint.x << 16;
_activeState._panYShl = _activeState._panTargetPoint.y << 16;
} else {
_activeState._panXShl += fixedMul(_activeState._someX, (currTime - _activeState._panStartTime) << 16);
_activeState._panYShl += fixedMul(_activeState._someY, (currTime - _activeState._panStartTime) << 16);
}
_activeState._panStartTime = currTime;
Common::Point newPan(_activeState._panXShl >> 16, _activeState._panYShl >> 16);
if (_activeState._currPan.x != newPan.x || _activeState._currPan.y != newPan.y) {
_activeState._currPan = newPan;
return true;
}
return false;
}
bool Camera::isPanFinished() {
return _activeState._currPan.x == _activeState._panTargetPoint.x && _activeState._currPan.y == _activeState._panTargetPoint.y;
}
Common::Point Camera::getPtOffset(Common::Point pt) {
pt.x = pt.x - _activeState._centerPt.x + _screenMidX;
pt.y = pt.y - _activeState._centerPt.y + _screenMidY;
return pt;
}
void Camera::recalcPan(uint32 currTime) {
_activeState._currPan2 = getCurrentPan();
_activeState._time28 = currTime;
if (_activeState._panSpeed == 0) {
_activeState._time2E = 0;
} else {
FixedPoint16 x1 = _activeState._currPan2.x << 16;
FixedPoint16 y1 = _activeState._currPan2.y << 16;
FixedPoint16 x2 = _activeState._panTargetPoint.x << 16;
FixedPoint16 y2 = _activeState._panTargetPoint.y << 16;
FixedPoint16 distance = fixedDistance(x1, y1, x2, y2);
_activeState._time2E = 60 * fixedTrunc(distance) / _activeState._panSpeed;
}
if (_activeState._time2E != 0) {
_activeState._someX = fixedDiv((_activeState._panTargetPoint.x - _activeState._currPan2.x) << 16, _activeState._time2E << 16);
_activeState._someY = fixedDiv((_activeState._panTargetPoint.y - _activeState._currPan2.y) << 16, _activeState._time2E << 16);
} else {
_activeState._someX = (_activeState._panTargetPoint.x - _activeState._currPan2.x) << 16;
_activeState._someY = (_activeState._panTargetPoint.y - _activeState._currPan2.y) << 16;
}
}
bool Camera::calcPointFlags(Common::Point &pt, WRect &rect, uint &outFlags) {
bool result = false;
if (pt.x < rect._topLeft.x) {
outFlags |= 1;
result = true;
} else if (pt.x > rect._bottomRight.x) {
outFlags |= 2;
result = true;
}
if (pt.y < rect._topLeft.y) {
outFlags |= 4;
result = true;
} else if (pt.y > rect._bottomRight.y) {
outFlags |= 8;
result = true;
}
return result;
}
void Camera::clipPanTargetPoint() {
_activeState._panTargetPoint.x = CLIP(_activeState._panTargetPoint.x,
_activeState._bounds._topLeft.x, _activeState._bounds._bottomRight.x);
_activeState._panTargetPoint.y = CLIP(_activeState._panTargetPoint.y,
_activeState._bounds._topLeft.y, _activeState._bounds._bottomRight.y);
}
void Camera::init() {
switch (_vm->getGameId()) {
case kGameIdDuckman:
initDuckman();
break;
case kGameIdBBDOU:
initBBDOU();
break;
default:
break;
}
}
void Camera::initDuckman() {
_centerObjectTrackingLimits.x = 4;
_centerObjectTrackingLimits.y = 4;
_screenWidth = 320;
_screenHeight = 200;
_screenMidX = 160;
_screenMidY = 100;
_trackObjectTrackingLimits.x = 80;
_trackObjectTrackingLimits.y = 50;
_trackObjectTrackingLimitsPanSpeed = 353;
}
void Camera::initBBDOU() {
_centerObjectTrackingLimits.x = 8;
_centerObjectTrackingLimits.y = 8;
_screenWidth = 640;
_screenHeight = 480;
_screenMidX = 320;
_screenMidY = 240;
_trackObjectTrackingLimits.x = 160;
_trackObjectTrackingLimits.y = 120;
_trackObjectTrackingLimitsPanSpeed = 710;
}
} // End of namespace Illusions

117
engines/illusions/camera.h Normal file
View File

@@ -0,0 +1,117 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_CAMERA_H
#define ILLUSIONS_CAMERA_H
#include "illusions/graphics.h"
#include "common/rect.h"
#include "common/stack.h"
namespace Illusions {
class BackgroundInstance;
struct CameraState {
int _cameraMode;
//field_2 dw
bool _paused;
int16 _panSpeed;
int _someX, _someY;
Common::Point _currPan;
int _panXShl, _panYShl;
WRect _bounds;
uint32 _panNotifyId;
uint32 _time28;
uint32 _panStartTime;
uint32 _pauseStartTime;
uint32 _time2E;
Common::Point _currPan2;
Common::Point _panTargetPoint;
Common::Point _trackingLimits;
Common::Point _centerPt;
uint32 _panObjectId;
Common::Point *_panToPositionPtr;
uint _pointFlags;
//field_4A dw
};
struct CameraModeStackItem {
int _cameraMode;
uint32 _panObjectId;
int16 _panSpeed;
Common::Point _panTargetPoint;
uint32 _panNotifyId;
};
class Camera {
public:
Camera(IllusionsEngine *vm);
void clearStack();
void set(Common::Point &panPoint, WidthHeight &dimensions);
void panCenterObject(uint32 objectId, int16 panSpeed);
void panTrackObject(uint32 objectId);
void panToPoint(Common::Point pt, int16 panSpeed, uint32 panNotifyId);
void panEdgeFollow(uint32 objectId, int16 panSpeed);
void stopPan();
void pause();
void unpause();
void pushCameraMode();
void popCameraMode();
void clearCameraModeStack();
void update(uint32 currTime);
void setBounds(Common::Point minPt, Common::Point maxPt);
void setBoundsToDimensions(WidthHeight &dimensions);
Common::Point getCurrentPan();
Common::Point getScreenOffset();
Common::Point getTrackingLimits();
bool isAtPanLimit(int limitNum);
void setActiveState(CameraState &state);
void getActiveState(CameraState &state);
void refreshPan(BackgroundInstance *backgroundItem, WidthHeight &dimensions);
protected:
IllusionsEngine *_vm;
CameraState _activeState;
Common::FixedStack<CameraModeStackItem, 8> _stack;
int16 _screenWidth, _screenHeight;
int16 _screenMidX, _screenMidY;
Common::Point _centerObjectTrackingLimits;
Common::Point _trackObjectTrackingLimits;
int16 _trackObjectTrackingLimitsPanSpeed;
void updateMode1(uint32 currTime);
void updateMode2(uint32 currTime);
void updateMode3(uint32 currTime);
bool updatePan(uint32 currTime);
bool isPanFinished();
Common::Point getPtOffset(Common::Point pt);
void recalcPan(uint32 currTime);
bool calcPointFlags(Common::Point &pt, WRect &rect, uint &outFlags);
void clipPanTargetPoint();
void init();
void initDuckman();
void initBBDOU();
};
} // End of namespace Illusions
#endif // ILLUSIONS_CAMERA_H

View File

@@ -0,0 +1,3 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
add_engine illusions "Illusions Engine" yes "" "" "" "midi"

View File

@@ -0,0 +1,48 @@
/* 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/>.
*
*/
#include "illusions/console.h"
#include "illusions/illusions.h"
#include "illusions/input.h"
namespace Illusions {
Console::Console(IllusionsEngine *vm) : GUI::Debugger(), _vm(vm) {
registerCmd("cheat", WRAP_METHOD(Console, Cmd_cheat));
}
Console::~Console() {
}
bool Console::Cmd_cheat(int argc, const char **argv) {
if (argc != 1) {
debugPrintf("Usage: %s\n", argv[0]);
debugPrintf("Switches on/off the cheat mode\n");
return true;
}
bool active = !_vm->_input->isCheatModeActive();
_vm->_input->setCheatModeActive(active);
debugPrintf("Cheat is now %s\n", active ? "ON" : "OFF");
return true;
}
} // End of namespace Illusions

View File

@@ -0,0 +1,43 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_CONSOLE_H
#define ILLUSIONS_CONSOLE_H
#include "gui/debugger.h"
namespace Illusions {
class IllusionsEngine;
class Console : public GUI::Debugger {
private:
IllusionsEngine *_vm;
bool Cmd_cheat(int argc, const char **argv);
public:
Console(IllusionsEngine *vm);
~Console() override;
};
} // End of namespace Illusions
#endif // ILLUSIONS_CONSOLE_H

View File

@@ -0,0 +1,4 @@
begin_section("Illusions");
add_person("Benjamin Haisch", "john_doe", "");
add_person("Eric Fry", "yuv422", "");
end_section();

View File

@@ -0,0 +1,96 @@
/* 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/>.
*
*/
#include "illusions/illusions.h"
#include "illusions/actor.h"
#include "illusions/cursor.h"
#include "illusions/input.h"
namespace Illusions {
Cursor::Cursor(IllusionsEngine *vm)
: _vm(vm) {
_status = 1;
_control = nullptr;
_x = 320;
_y = 240;
_cursorNum = 1;
_field_10 = 0;
_sequenceId = 0;
}
void Cursor::place(Control *control, uint32 sequenceId) {
_status = 2;
_control = control;
_cursorNum = 1;
_field_10 = 0;
_sequenceId = sequenceId;
_visibleCtr = 0;
_control->_flags |= 8;
setActorIndex(_cursorNum, 1, 0);
_vm->_input->setCursorPosition(_control->_actor->_position);
}
void Cursor::setActorIndex(int actorIndex, int a, int b) {
static int kCursorMap[13][2][2] = {
{{ 1, 2}, { 0, 0}},
{{ 3, 4}, { 0, 0}},
{{ 5, 6}, {13, 14}},
{{ 7, 8}, { 0, 0}},
{{ 9, 10}, { 0, 0}},
{{11, 12}, { 0, 0}},
{{ 1, 2}, { 0, 0}},
{{ 0, 0}, { 0, 0}},
{{ 0, 0}, { 0, 0}},
{{15, 16}, { 0, 0}},
{{17, 18}, { 0, 0}},
{{19, 20}, { 0, 0}},
{{21, 22}, { 0, 0}}
};
_control->_actor->_actorIndex = kCursorMap[actorIndex - 1][b][a - 1];
}
void Cursor::setControl(Control *control) {
_control = control;
}
void Cursor::show() {
++_visibleCtr;
if (_visibleCtr > 0) {
_control->_flags |= 1;
_control->_actor->_flags |= Illusions::ACTOR_FLAG_IS_VISIBLE;
if (_control->_actor->_frameIndex) {
_control->_actor->_flags |= Illusions::ACTOR_FLAG_2000;
_control->_actor->_flags |= Illusions::ACTOR_FLAG_4000;
}
_vm->_input->discardAllEvents();
}
}
void Cursor::hide() {
--_visibleCtr;
if (_visibleCtr <= 0) {
_control->_flags &= ~1;
_control->_actor->_flags &= ~Illusions::ACTOR_FLAG_IS_VISIBLE;
}
}
} // End of namespace Illusions

View File

@@ -0,0 +1,53 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_CURSOR_H
#define ILLUSIONS_CURSOR_H
namespace Illusions {
class Control;
class IllusionsEngine;
class Cursor {
public:
Cursor(IllusionsEngine *vm);
void place(Control *control, uint32 sequenceId);
void setActorIndex(int actorIndex, int a, int b);
void setControl(Control *control);
void show();
void hide();
void cursorControlRoutine(Control *control, uint32 deltaTime);
//protected:
public:
IllusionsEngine *_vm;
Control *_control;
uint32 _sequenceId;
int _status;
int _cursorNum;
int _field_10;
int _visibleCtr;
int16 _x, _y;
};
} // End of namespace Illusions
#endif // ILLUSIONS_CURSOR_H

View File

@@ -0,0 +1,132 @@
/* 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/>.
*
*/
#include "engines/advancedDetector.h"
#include "base/plugins.h"
#include "illusions/detection.h"
static const PlainGameDescriptor illusionsGames[] = {
{ "bbdou", "Beavis and Butt-Head: Do U." },
{ "duckman", "Duckman: The Graphic Adventures of a Private Dick" },
{ nullptr, nullptr }
};
namespace Illusions {
static const IllusionsGameDescription gameDescriptions[] = {
{
{
"bbdou",
nullptr,
AD_ENTRY1s("000D0001.scr", "d0c846d5dccc5607a482c7dcbdf06973", 601980),
Common::EN_ANY,
Common::kPlatformWindows,
ADGF_UNSTABLE | ADGF_DROPPLATFORM,
GUIO0()
},
kGameIdBBDOU
},
{
{
"duckman",
nullptr,
AD_ENTRY1s("duckman.gam", "172c0514f3793041718159cf9cf9935f", 29560832),
Common::EN_ANY,
Common::kPlatformWindows,
ADGF_DROPPLATFORM,
GUIO0()
},
kGameIdDuckman
},
{
{
"duckman",
"Demo",
AD_ENTRY1s("duckman.gam", "71d01e3f3d9d4e51cd69f71028745610", 7127040),
Common::EN_ANY,
Common::kPlatformWindows,
ADGF_DROPPLATFORM | ADGF_DEMO,
GUIO0()
},
kGameIdDuckman
},
{
{
"duckman",
nullptr,
AD_ENTRY1s("duckman.gam", "64d16922ffb46b746fc2c12a14d75bcc", 29779968),
Common::DE_DEU,
Common::kPlatformWindows,
ADGF_DROPPLATFORM,
GUIO0()
},
kGameIdDuckman
},
{
{
"duckman",
nullptr,
AD_ENTRY1s("duckman.gam", "a65f9b58427b3543882bddf36abe5878", 29564928),
Common::RU_RUS,
Common::kPlatformWindows,
ADGF_DROPPLATFORM,
GUIO0()
},
kGameIdDuckman
},
{AD_TABLE_END_MARKER, 0}
};
} // End of namespace Illusions
static const char * const directoryGlobs[] = {
"resource",
nullptr
};
class IllusionsMetaEngineDetection : public AdvancedMetaEngineDetection<Illusions::IllusionsGameDescription> {
public:
IllusionsMetaEngineDetection() : AdvancedMetaEngineDetection(Illusions::gameDescriptions, illusionsGames) {
_maxScanDepth = 2;
_directoryGlobs = directoryGlobs;
}
const char *getName() const override {
return "illusions";
}
const char *getEngineName() const override {
return "Illusions";
}
const char *getOriginalCopyright() const override {
return "(C) The Illusions Gaming Company";
}
};
REGISTER_PLUGIN_STATIC(ILLUSIONS_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, IllusionsMetaEngineDetection);

View File

@@ -0,0 +1,43 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_DETECTION_H
#define ILLUSIONS_DETECTION_H
#include "engines/advancedDetector.h"
namespace Illusions {
enum {
kGameIdBBDOU = 1,
kGameIdDuckman = 2
};
struct IllusionsGameDescription {
AD_GAME_DESCRIPTION_HELPERS(desc);
ADGameDescription desc;
int gameId;
};
} // End of namespace Illusions
#endif // ILLUSIONS_DETECTION_H

View File

@@ -0,0 +1,90 @@
/* 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/>.
*
*/
#include "illusions/illusions.h"
#include "illusions/dictionary.h"
#include "illusions/resources/actorresource.h"
#include "illusions/resources/backgroundresource.h"
#include "illusions/resources/fontresource.h"
#include "illusions/resources/talkresource.h"
namespace Illusions {
void Dictionary::addActorType(uint32 id, ActorType *actorType) {
_actorTypes.add(id, actorType);
}
void Dictionary::removeActorType(uint32 id) {
_actorTypes.remove(id);
}
ActorType *Dictionary::findActorType(uint32 id) {
return _actorTypes.find(id);
}
void Dictionary::addFont(uint32 id, FontResource *fontResource) {
_fontResources.add(id, fontResource);
}
void Dictionary::removeFont(uint32 id) {
_fontResources.remove(id);
}
FontResource *Dictionary::findFont(uint32 id) {
return _fontResources.find(id);
}
void Dictionary::addSequence(uint32 id, Sequence *sequence) {
_sequences.add(id, sequence);
}
void Dictionary::removeSequence(uint32 id) {
_sequences.remove(id);
}
Sequence *Dictionary::findSequence(uint32 id) {
return _sequences.find(id);
}
void Dictionary::addTalkEntry(uint32 id, TalkEntry *talkEntry) {
_talkEntries.add(id, talkEntry);
}
void Dictionary::removeTalkEntry(uint32 id) {
_talkEntries.remove(id);
}
TalkEntry *Dictionary::findTalkEntry(uint32 id) {
return _talkEntries.find(id);
}
void Dictionary::setObjectControl(uint32 objectId, Control *control) {
if (control)
_controls.add(objectId, control);
else
_controls.remove(objectId);
}
Control *Dictionary::getObjectControl(uint32 objectId) {
return _controls.find(objectId);
}
} // End of namespace Illusions

View File

@@ -0,0 +1,117 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_DICTIONARY_H
#define ILLUSIONS_DICTIONARY_H
#include "common/hashmap.h"
namespace Illusions {
struct ActorType;
class Control;
class FontResource;
struct Sequence;
struct TalkEntry;
template<class T>
class DictionaryHashMap {
protected:
typedef Common::List<T*> List;
typedef typename List::iterator ListIterator;
typedef Common::HashMap<uint32, List*> Map;
typedef typename Map::iterator MapIterator;
Map _map;
public:
~DictionaryHashMap() {
for (auto &it : _map) {
delete it._value;
}
}
void add(uint32 id, T *value) {
MapIterator it = _map.find(id);
List *list;
if (it != _map.end())
list = it->_value;
else {
list = new List();
_map[id] = list;
}
list->push_back(value);
}
void remove(uint32 id) {
MapIterator it = _map.find(id);
List *list;
if (it != _map.end()) {
list = it->_value;
list->pop_back();
if (list->empty()) {
_map.erase(id);
delete list;
}
}
}
T *find(uint32 id) {
MapIterator it = _map.find(id);
if (it != _map.end())
return it->_value->back();
return 0;
}
};
class Dictionary {
public:
void addActorType(uint32 id, ActorType *actorType);
void removeActorType(uint32 id);
ActorType *findActorType(uint32 id);
void addFont(uint32 id, FontResource *fontResource);
void removeFont(uint32 id);
FontResource *findFont(uint32 id);
void addSequence(uint32 id, Sequence *sequence);
void removeSequence(uint32 id);
Sequence *findSequence(uint32 id);
void addTalkEntry(uint32 id, TalkEntry *talkEntry);
void removeTalkEntry(uint32 id);
TalkEntry *findTalkEntry(uint32 id);
void setObjectControl(uint32 objectId, Control *control);
Control *getObjectControl(uint32 objectId);
protected:
DictionaryHashMap<ActorType> _actorTypes;
DictionaryHashMap<Control> _controls;
DictionaryHashMap<FontResource> _fontResources;
DictionaryHashMap<Sequence> _sequences;
DictionaryHashMap<TalkEntry> _talkEntries;
};
} // End of namespace Illusions
#endif // ILLUSIONS_DICTIONARY_H

View File

@@ -0,0 +1,197 @@
/* 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/>.
*
*/
#include "illusions/duckman/illusions_duckman.h"
#include "illusions/duckman/duckman_credits.h"
#include "illusions/actor.h"
#include "illusions/dictionary.h"
#include "illusions/resources/fontresource.h"
#include "illusions/resources/scriptresource.h"
#include "illusions/textdrawer.h"
#include "illusions/time.h"
#include "illusions/updatefunctions.h"
#include "engines/util.h"
namespace Illusions {
// Duckman_SpecialCode
DuckmanCredits::DuckmanCredits(IllusionsEngine_Duckman *vm)
: _vm(vm) {
}
DuckmanCredits::~DuckmanCredits() {
}
void DuckmanCredits::start() {
static const struct { uint32 objectId; int scrollPosY; } kCreditsItems[] = {
{0x40136, 0}, {0x40137, 16}, {0x40138, 32}, {0x40139, 48},
{0x4013A, 64}, {0x4013B, 80}, {0x4013C, 96}, {0x4013D, 112}
};
_currText = (char*)_vm->_resSys->getResource(0x190052)->_data;
_creditsItems.clear();
for (uint i = 0; i < ARRAYSIZE(kCreditsItems); ++i) {
DCreditsItem creditsItem;
creditsItem.objectId = kCreditsItems[i].objectId;
creditsItem.scrollPosY = kCreditsItems[i].scrollPosY;
creditsItem.scrollPosIndex = 0;
creditsItem.active = false;
_creditsItems.push_back(creditsItem);
}
uint32 currSceneId = _vm->getCurrentScene();
_vm->_updateFunctions->add(0, currSceneId, new Common::Functor1Mem<uint, int, DuckmanCredits>(this, &DuckmanCredits::update));
_nextUpdateTicks = getCurrentTime();
_lastUpdateTicks = _nextUpdateTicks - 4;
}
int DuckmanCredits::update(uint flags) {
if (_vm->_pauseCtr > 0) {
_nextUpdateTicks = getCurrentTime() + 4;
return 1;
}
if (flags & 1) {
_vm->_scriptResource->_properties.set(0x000E0096, true);
_lastItemIndex = -1;
_endReached = false;
return 2;
}
if (!isTimerExpired(_lastUpdateTicks, _nextUpdateTicks)) {
return 1;
}
bool creditsRunning = false;
int index = 0;
for (DCreditsItems::iterator it = _creditsItems.begin(); it != _creditsItems.end(); ++it, ++index) {
DCreditsItem &creditsItem = *it;
Control *control = _vm->getObjectControl(creditsItem.objectId);
if (!creditsItem.active && creditsItem.scrollPosY == 0 && !_endReached) {
creditsItem.active = true;
creditsItem.scrollPosIndex = 0;
control->fillActor(0);
char *text = readNextLine();
if (!strncmp(text, "&&&END", 6)) {
creditsItem.active = false;
_endReached = true;
} else {
uint16 wtext[128];
charToWChar(text, wtext, ARRAYSIZE(wtext));
FontResource *font = _vm->_dict->findFont(0x120001);
TextDrawer textDrawer;
WidthHeight dimensions;
uint16 *outText;
control->getActorFrameDimensions(dimensions);
textDrawer.wrapText(font, wtext, &dimensions, Common::Point(0, 0), 2, outText);
textDrawer.drawText(_vm->_screen, control->_actor->_surface, 0, 0);
control->_actor->_flags |= Illusions::ACTOR_FLAG_4000;
_lastItemIndex = index;
}
}
if (creditsItem.active) {
if (_endReached && _creditsItems[_lastItemIndex].scrollPosIndex > 53) {
creditsItem.active = false;
creditsItem.scrollPosY = -1;
} else {
creditsRunning = true;
control->_actor->_position = getItemPosition(creditsItem.scrollPosIndex);
++creditsItem.scrollPosIndex;
if (getItemPosition(creditsItem.scrollPosIndex).x < 0)
creditsItem.active = false;
}
}
if (creditsItem.scrollPosY > 0)
--creditsItem.scrollPosY;
}
_lastUpdateTicks = _nextUpdateTicks;
_nextUpdateTicks = getCurrentTime() + 4;
if (!creditsRunning) {
_vm->_scriptResource->_properties.set(0x000E0096, true);
_lastItemIndex = -1;
_endReached = false;
return 2;
}
return 1;
}
char *DuckmanCredits::readNextLine() {
static char line[256];
char *dest = line;
char *src = _currText;
do {
if (*src == 10 || *src == 13) {
src += 2;
*dest = 0;
break;
}
*dest++ = *src++;
} while (1);
_currText = src;
return line;
}
Common::Point DuckmanCredits::getItemPosition(int index) {
static const struct { int16 x, y; } kCreditsItemsPoints[] = {
{159, 200}, {158, 195}, {157, 190}, {156, 185}, {156, 180}, {157, 176},
{158, 172}, {159, 168}, {161, 164}, {162, 161}, {163, 158}, {163, 155},
{162, 152}, {161, 149}, {159, 147}, {158, 144}, {157, 142}, {156, 140},
{156, 138}, {157, 136}, {158, 134}, {159, 132}, {161, 130}, {162, 128},
{163, 127}, {163, 126}, {162, 125}, {161, 124}, {159, 123}, {158, 122},
{157, 121}, {156, 120}, {156, 119}, {157, 118}, {158, 117}, {159, 116},
{161, 115}, {162, 114}, {163, 113}, {163, 112}, {162, 111}, {161, 110},
{159, 109}, {158, 108}, {157, 107}, {156, 106}, {156, 105}, {157, 104},
{158, 103}, {159, 102}, {161, 101}, {162, 100}, {163, 99}, {163, 98},
{162, 97}, {161, 96}, {159, 95}, {158, 94}, {157, 93}, {156, 92},
{156, 91}, {157, 90}, {158, 89}, {159, 88}, {161, 87}, {162, 86},
{163, 85}, {163, 84}, {162, 83}, {161, 82}, {159, 81}, {158, 80},
{157, 79}, {156, 78}, {156, 77}, {157, 76}, {158, 75}, {159, 74},
{161, 73}, {162, 72}, {163, 71}, {163, 70}, {162, 69}, {161, 68},
{159, 67}, {158, 66}, {157, 64}, {156, 62}, {156, 60}, {157, 58},
{158, 56}, {159, 54}, {161, 52}, {162, 50}, {163, 40}, {163, 40},
{162, 40}, {161, 40}, {159, 40}, {158, 40}, {157, 40}, {156, 40},
{156, 40}, {157, 40}, {158, 40}, {159, 40}, {161, 40}, {162, 40},
{163, 40}, {163, 40}, {162, 40}, {161, 40}, {159, 40}, {158, 40},
{157, 40}, {156, 40}, {156, 40}, {157, 40}, {158, 40}, {159, 40},
{161, 40}, {162, 40}, {163, 40}, {163, 40}, {162, 40}, {161, 40},
{159, 40}, {158, 40}, { -1, -1}
};
if (index < 0 || index >= ARRAYSIZE(kCreditsItemsPoints))
return Common::Point(-1, -1);
return Common::Point(kCreditsItemsPoints[index].x, kCreditsItemsPoints[index].y);
}
void DuckmanCredits::charToWChar(char *text, uint16 *wtext, uint size) {
while (*text != 0 && size > 1) {
*wtext++ = (byte)*text++;
--size;
}
*wtext++ = 0;
}
} // End of namespace Illusions

View File

@@ -0,0 +1,61 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_DUCKMAN_CREDITS_H
#define ILLUSIONS_DUCKMAN_CREDITS_H
#include "illusions/illusions.h"
namespace Illusions {
class IllusionsEngine_Duckman;
struct DCreditsItem {
uint32 objectId;
bool active;
int16 scrollPosIndex;
int16 scrollPosY;
};
class DuckmanCredits {
public:
DuckmanCredits(IllusionsEngine_Duckman *vm);
~DuckmanCredits();
void start();
public:
typedef Common::Array<DCreditsItem> DCreditsItems;
IllusionsEngine_Duckman *_vm;
uint32 _lastUpdateTicks;
uint32 _nextUpdateTicks;
int _lastItemIndex;
bool _endReached;
DCreditsItems _creditsItems;
char *_currText;
int update(uint flags);
char *readNextLine();
Common::Point getItemPosition(int index);
void charToWChar(char *text, uint16 *wtext, uint size);
};
} // End of namespace Illusions
#endif // ILLUSIONS_ILLUSIONS_H

View File

@@ -0,0 +1,177 @@
/* 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/>.
*
*/
#include "illusions/duckman/illusions_duckman.h"
#include "illusions/duckman/duckman_dialog.h"
#include "illusions/duckman/scriptopcodes_duckman.h"
#include "illusions/actor.h"
#include "illusions/camera.h"
#include "illusions/cursor.h"
#include "illusions/dictionary.h"
#include "illusions/resources/fontresource.h"
#include "illusions/graphics.h"
#include "illusions/input.h"
#include "illusions/resources/actorresource.h"
#include "illusions/resources/backgroundresource.h"
#include "illusions/resources/midiresource.h"
#include "illusions/resources/scriptresource.h"
#include "illusions/resources/soundresource.h"
#include "illusions/resources/talkresource.h"
#include "illusions/resourcesystem.h"
#include "illusions/screen.h"
#include "illusions/screentext.h"
#include "illusions/scriptstack.h"
#include "illusions/sound.h"
#include "illusions/specialcode.h"
#include "illusions/textdrawer.h"
#include "illusions/thread.h"
#include "illusions/time.h"
#include "illusions/updatefunctions.h"
#include "illusions/threads/abortablethread.h"
#include "illusions/threads/causethread_duckman.h"
#include "illusions/threads/scriptthread.h"
#include "illusions/threads/talkthread_duckman.h"
#include "illusions/threads/timerthread.h"
#include "engines/util.h"
namespace Illusions {
// DuckmanDialogSystem
DuckmanDialogSystem::DuckmanDialogSystem(IllusionsEngine_Duckman *vm)
: _vm(vm) {
}
DuckmanDialogSystem::~DuckmanDialogSystem() {
}
void DuckmanDialogSystem::addDialogItem(int16 choiceJumpOffs, uint32 sequenceId) {
DialogItem dialogItem;
dialogItem._choiceJumpOffs = choiceJumpOffs;
dialogItem._sequenceId = sequenceId;
_dialogItems.push_back(dialogItem);
}
void DuckmanDialogSystem::startDialog(int16 *choiceOfsPtr, uint32 actorTypeId, uint32 callerThreadId) {
static const uint32 kDialogSequenceIds[] = {
0,
0x6049C, 0x6049C, 0x6047A, 0x6049D,
0x60479, 0x6049E, 0x6049F, 0x60468
};
if (_dialogItems.size() == 1) {
*choiceOfsPtr = _dialogItems[0]._choiceJumpOffs;
_vm->notifyThreadId(callerThreadId);
} else {
if (!_vm->_cursor._control) {
Common::Point pos = _vm->getNamedPointPosition(0x70001);
_vm->_controls->placeActor(0x50001, pos, 0x60001, Illusions::CURSOR_OBJECT_ID, 0);
_vm->_cursor._control = _vm->_dict->getObjectControl(Illusions::CURSOR_OBJECT_ID);
}
_vm->_cursor._control->appearActor();
_vm->setCursorActorIndex(6, 1, 0);
_vm->_cursor._gameState = 3;
_vm->_cursor._notifyThreadId30 = callerThreadId;
_vm->_cursor._dialogItemsCount = 0;
_vm->_cursor._overlappedObjectId = 0;
_vm->_cursor._op113_choiceOfsPtr = choiceOfsPtr;
_vm->_cursor._currOverlappedControl = nullptr;
/* TODO?
if (!_vm->_input->getCursorMouseMode())
_vm->_input->setMousePos((Point)0xBC0014);
*/
_vm->_cursor._dialogItemsCount = _dialogItems.size();
Common::Point placePt(20, 188);
for (uint i = 1; i <= _dialogItems.size(); ++i) {
DialogItem &dialogItem = _dialogItems[_dialogItems.size() - i];
_vm->_controls->placeDialogItem(i + 1, actorTypeId, dialogItem._sequenceId, placePt, dialogItem._choiceJumpOffs);
placePt.x += 40;
}
Common::Point placePt2 = _vm->getNamedPointPosition(0x700C3);
_vm->_controls->placeActor(0x5006E, placePt2, kDialogSequenceIds[_dialogItems.size()], 0x40148, 0);
Control *control = _vm->_dict->getObjectControl(0x40148);
control->_flags |= 8;
_vm->playSoundEffect(8);
}
_dialogItems.clear();
}
void DuckmanDialogSystem::updateDialogState() {
Common::Point mousePos = _vm->_input->getCursorPosition();
// TODO Handle keyboard input
_vm->_cursor._control->_actor->_position = mousePos;
mousePos = _vm->convertMousePos(mousePos);
Control *currOverlappedControl = _vm->_cursor._currOverlappedControl;
Control *newOverlappedControl;
if (_vm->_controls->getDialogItemAtPos(_vm->_cursor._control, mousePos, &newOverlappedControl)) {
if (currOverlappedControl != newOverlappedControl) {
newOverlappedControl->setActorIndex(2);
newOverlappedControl->startSequenceActor(newOverlappedControl->_actor->_sequenceId, 2, 0);
if (currOverlappedControl) {
currOverlappedControl->setActorIndex(1);
currOverlappedControl->startSequenceActor(currOverlappedControl->_actor->_sequenceId, 2, 0);
}
_vm->playSoundEffect(10);
_vm->startCursorSequence();
_vm->setCursorActorIndex(6, 2, 0);
_vm->_cursor._currOverlappedControl = newOverlappedControl;
_vm->_cursor._overlappedObjectId = newOverlappedControl->_objectId;
}
} else if (currOverlappedControl) {
currOverlappedControl->setActorIndex(1);
currOverlappedControl->startSequenceActor(currOverlappedControl->_actor->_sequenceId, 2, 0);
_vm->playSoundEffect(10);
_vm->_cursor._currOverlappedControl = nullptr;
_vm->_cursor._overlappedObjectId = 0;
_vm->startCursorSequence();
_vm->setCursorActorIndex(6, 1, 0);
}
if (_vm->_input->pollEvent(kEventLeftClick)) {
if (_vm->_cursor._currOverlappedControl) {
_vm->playSoundEffect(9);
*_vm->_cursor._op113_choiceOfsPtr = _vm->_cursor._currOverlappedControl->_actor->_choiceJumpOffs;
_vm->_controls->destroyDialogItems();
Control *control = _vm->_dict->getObjectControl(0x40148);
_vm->_controls->destroyControl(control);
_vm->notifyThreadId(_vm->_cursor._notifyThreadId30);
_vm->_cursor._notifyThreadId30 = 0;
_vm->_cursor._gameState = 2;
_vm->_cursor._dialogItemsCount = 0;
_vm->_cursor._overlappedObjectId = 0;
_vm->_cursor._op113_choiceOfsPtr = nullptr;
_vm->_cursor._control->disappearActor();
}
}
}
} // End of namespace Illusions

View File

@@ -0,0 +1,50 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_DUCKMAN_DUCKMAN_DIALOG_H
#define ILLUSIONS_DUCKMAN_DUCKMAN_DIALOG_H
#include "illusions/illusions.h"
#include "common/algorithm.h"
#include "common/stack.h"
namespace Illusions {
struct DialogItem {
int16 _choiceJumpOffs;
uint32 _sequenceId;
};
class DuckmanDialogSystem {
public:
DuckmanDialogSystem(IllusionsEngine_Duckman *vm);
~DuckmanDialogSystem();
void addDialogItem(int16 choiceJumpOffs, uint32 sequenceId);
void startDialog(int16 *choiceOfsPtr, uint32 actorTypeId, uint32 callerThreadId);
void updateDialogState();
public:
IllusionsEngine_Duckman *_vm;
Common::Array<DialogItem> _dialogItems;
};
} // End of namespace Illusions
#endif // ILLUSIONS_DUCKMAN_DUCKMAN_DIALOG_H

View File

@@ -0,0 +1,176 @@
/* 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/>.
*
*/
#include "illusions/duckman/illusions_duckman.h"
#include "illusions/duckman/duckman_inventory.h"
#include "illusions/actor.h"
#include "illusions/cursor.h"
#include "illusions/input.h"
#include "illusions/resources/scriptresource.h"
#include "engines/util.h"
namespace Illusions {
// DuckmanInventory
DuckmanInventory::DuckmanInventory(IllusionsEngine_Duckman *vm)
: _vm(vm) {
initInventory();
}
DuckmanInventory::~DuckmanInventory() {
}
static const struct DMInventoryItem kInventoryItems[21] = {
{0x40011, 0xE005B},
{0x40099, 0xE001B},
{0x4000F, 0xE000C},
{0x40042, 0xE0012},
{0x40044, 0xE000F},
{0x40029, 0xE000D},
{0x400A7, 0xE005D},
{0x40096, 0xE001C},
{0x40077, 0xE0010},
{0x4008A, 0xE0033},
{0x4004B, 0xE0045},
{0x40054, 0xE0021},
{0x400C6, 0xE005A},
{0x4000B, 0xE005E},
{0x4005F, 0xE0016},
{0x40072, 0xE0017},
{0x400AA, 0xE005F},
{0x400B8, 0xE0050},
{0x4001F, 0xE001A},
{0x40095, 0xE0060},
{0x40041, 0xE0053}
};
void DuckmanInventory::initInventory() {
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 5; x++) {
_inventorySlots.push_back(DMInventorySlot( 64 + x * 48, 52 + y * 32));
}
}
for (int i = 0; i < 21; i++) {
_inventoryItems.push_back(kInventoryItems[i]);
}
}
void DuckmanInventory::openInventory() {
for (uint i = 0; i < _inventorySlots.size(); ++i) {
DMInventorySlot *inventorySlot = &_inventorySlots[i];
if (inventorySlot->_objectId) {
DMInventoryItem *inventoryItem = findInventoryItem(inventorySlot->_objectId);
if (!_vm->_scriptResource->_properties.get(inventoryItem->_propertyId))
inventorySlot->_objectId = 0;
}
}
for (uint i = 0; i < _inventoryItems.size(); ++i) {
DMInventoryItem *inventoryItem = &_inventoryItems[i];
if (_vm->_scriptResource->_properties.get(inventoryItem->_propertyId)) {
DMInventorySlot *inventorySlot = findInventorySlot(inventoryItem->_objectId);
if (inventorySlot) {
Control *control = _vm->getObjectControl(inventoryItem->_objectId);
control->setActorPosition(inventorySlot->_position);
control->appearActor();
} else {
addInventoryItem(inventoryItem->_objectId);
}
}
}
}
void DuckmanInventory::addInventoryItem(uint32 objectId) {
DMInventorySlot *DMInventorySlot = findInventorySlot(0);
DMInventorySlot->_objectId = objectId;
Control *control = _vm->getObjectControl(objectId);
control->setActorPosition(DMInventorySlot->_position);
control->appearActor();
}
void DuckmanInventory::clearInventorySlot(uint32 objectId) {
for (uint i = 0; i < _inventorySlots.size(); ++i) {
if (_inventorySlots[i]._objectId == objectId)
_inventorySlots[i]._objectId = 0;
}
}
void DuckmanInventory::putBackInventoryItem() {
Common::Point mousePos = _vm->_input->getCursorPosition();
if (_vm->_cursor._objectId) {
DMInventorySlot *inventorySlot = findInventorySlot(_vm->_cursor._objectId);
if (inventorySlot)
inventorySlot->_objectId = 0;
inventorySlot = findClosestInventorySlot(mousePos);
inventorySlot->_objectId = _vm->_cursor._objectId;
Control *control = _vm->getObjectControl(_vm->_cursor._objectId);
control->setActorPosition(inventorySlot->_position);
control->appearActor();
_vm->_cursor._actorIndex = 7;
_vm->stopCursorHoldingObject();
_vm->_cursor._actorIndex = 2;
_vm->_cursor._control->startSequenceActor(_vm->_cursor._sequenceId1, 2, 0);
if (_vm->_cursor._currOverlappedControl)
_vm->setCursorActorIndex(_vm->_cursor._actorIndex, 2, 0);
else
_vm->setCursorActorIndex(_vm->_cursor._actorIndex, 1, 0);
}
}
DMInventorySlot *DuckmanInventory::findInventorySlot(uint32 objectId) {
for (uint i = 0; i < _inventorySlots.size(); ++i) {
if (_inventorySlots[i]._objectId == objectId)
return &_inventorySlots[i];
}
return nullptr;
}
DMInventoryItem *DuckmanInventory::findInventoryItem(uint32 objectId) {
for (uint i = 0; i < _inventoryItems.size(); ++i) {
if (_inventoryItems[i]._objectId == objectId)
return &_inventoryItems[i];
}
return nullptr;
}
DMInventorySlot *DuckmanInventory::findClosestInventorySlot(Common::Point pos) {
int minDistance = 0xFFFFFF;
DMInventorySlot *minInventorySlot = nullptr;
for (uint i = 0; i < _inventorySlots.size(); ++i) {
DMInventorySlot *inventorySlot = &_inventorySlots[i];
if (inventorySlot->_objectId == 0) {
int16 deltaX = ABS(inventorySlot->_position.x - pos.x);
int16 deltaY = ABS(inventorySlot->_position.y - pos.y);
int distance = deltaX * deltaX + deltaY * deltaY;
if (inventorySlot->_objectId == 0 && distance < minDistance) {
minDistance = distance;
minInventorySlot = inventorySlot;
}
}
}
return minInventorySlot;
}
} // End of namespace Illusions

View File

@@ -0,0 +1,61 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_DUCKMAN_DUCKMAN_INVENTORY_H
#define ILLUSIONS_DUCKMAN_DUCKMAN_INVENTORY_H
#include "illusions/illusions.h"
namespace Illusions {
struct DMInventorySlot {
Common::Point _position;
uint32 _objectId;
DMInventorySlot() : _objectId(0) {}
DMInventorySlot(int16 x, int16 y) : _objectId(0), _position(x, y) {}
};
struct DMInventoryItem {
uint32 _objectId;
uint32 _propertyId;
};
class DuckmanInventory {
public:
DuckmanInventory(IllusionsEngine_Duckman *vm);
~DuckmanInventory();
public:
IllusionsEngine_Duckman *_vm;
Common::Array<DMInventorySlot> _inventorySlots;
Common::Array<DMInventoryItem> _inventoryItems;
void initInventory();
void openInventory();
void addInventoryItem(uint32 objectId);
void clearInventorySlot(uint32 objectId);
void putBackInventoryItem();
DMInventorySlot *findInventorySlot(uint32 objectId);
DMInventoryItem *findInventoryItem(uint32 objectId);
DMInventorySlot *findClosestInventorySlot(Common::Point pos);
};
} // End of namespace Illusions
#endif // ILLUSIONS_DUCKMAN_DUCKMAN_INVENTORY_H

View File

@@ -0,0 +1,95 @@
/* 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/>.
*
*/
#include "illusions/duckman/illusions_duckman.h"
#include "illusions/duckman/duckman_screenshakereffects.h"
#include "engines/util.h"
namespace Illusions {
static const ScreenShakerPoint kShakerPoints0[] = {
{0, -2}, {0, -4}, {0, -3}, {0, -1}, {0, 1}
};
static const ScreenShakeEffect kShakerEffect0 = {
ARRAYSIZE(kShakerPoints0), 5, kShakerPoints0
};
static const ScreenShakerPoint kShakerPoints1[] = {
{-4, -5}, {4, 5}, {-3, -4}, {3, 4}, {-2, -3}, {2, 3}, {-1, -2},
{ 1, 2}, {0, -1}
};
static const ScreenShakeEffect kShakerEffect1 = {
ARRAYSIZE(kShakerPoints1), 2, kShakerPoints1
};
static const ScreenShakerPoint kShakerPoints2[] = {
{0, -3}, {0, 3}, {0, -2}, {0, 2}, {0, -2}, {0, 2}, {0, -1},
{0, 1}, {0, -1},
};
static const ScreenShakeEffect kShakerEffect2 = {
ARRAYSIZE(kShakerPoints2), 2, kShakerPoints2
};
static const ScreenShakerPoint kShakerPoints3[] = {
{0, 1}, {0, -1}, {0, -2}, {0, 0}, {(int16)32768, 0}
};
static const ScreenShakeEffect kShakerEffect3 = {
ARRAYSIZE(kShakerPoints3), 2, kShakerPoints3
};
static const ScreenShakerPoint kShakerPoints4[] = {
{0, 4}, {0, -1}, {0, 3}, {0, -2}, {0, 1}, {0, -1}, {0, 1}, {0, -1}
};
static const ScreenShakeEffect kShakerEffect4 = {
ARRAYSIZE(kShakerPoints4), 5, kShakerPoints4
};
static const ScreenShakerPoint kShakerPoints5[] = {
{0, -1}, {0, 0}, {0, 1}, {0, 0}, {0, -1}, {0, 0}, {0, 1}, {0, 0},
{0, -1}, {0, 0}, {0, 1}, {0, 0}, {0, -1}, {0, 0}, {0, 1}, {0, 0},
{0, -1}, {0, 0}, {0, 1}, {0, 0}, {0, -1}, {0, 0}, {0, 1}, {0, 0},
{0, -1}, {0, 0}, {0, 1}, {0, 0}, {0, -1}, {0, 0}, {0, 1}, {0, 0}
};
static const ScreenShakeEffect kShakerEffect5 = {
ARRAYSIZE(kShakerPoints5), 2, kShakerPoints5
};
static const ScreenShakeEffect *kShakerEffects[] = {
&kShakerEffect0,
&kShakerEffect1,
&kShakerEffect2,
&kShakerEffect3,
&kShakerEffect4,
&kShakerEffect5
};
const ScreenShakeEffect *getScreenShakeEffect(byte index) {
return kShakerEffects[index];
}
} // End of namespace Illusions

View File

@@ -0,0 +1,33 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_DUCKMAN_SCREENSHAKEEFFECTS_H
#define ILLUSIONS_DUCKMAN_SCREENSHAKEEFFECTS_H
#include "illusions/illusions.h"
namespace Illusions {
const ScreenShakeEffect *getScreenShakeEffect(byte index);
} // End of namespace Illusions
#endif // ILLUSIONS_DUCKMAN_SCREENSHAKEEFFECTS_H

View File

@@ -0,0 +1,405 @@
/* 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/>.
*
*/
#include "illusions/duckman/illusions_duckman.h"
#include "illusions/duckman/duckman_credits.h"
#include "illusions/duckman/duckman_screenshakereffects.h"
#include "illusions/duckman/duckman_specialcode.h"
#include "illusions/duckman/duckman_inventory.h"
#include "illusions/duckman/propertytimers.h"
#include "illusions/duckman/scriptopcodes_duckman.h"
#include "illusions/actor.h"
#include "illusions/dictionary.h"
#include "illusions/resources/fontresource.h"
#include "illusions/resources/scriptresource.h"
#include "illusions/sound.h"
#include "illusions/specialcode.h"
#include "illusions/textdrawer.h"
#include "illusions/time.h"
#include "illusions/updatefunctions.h"
#include "engines/util.h"
namespace Illusions {
// Duckman_SpecialCode
DuckmanSpecialCode::DuckmanSpecialCode(IllusionsEngine_Duckman *vm)
: _vm(vm) {
_propertyTimers = new PropertyTimers(_vm);
_inventory = new DuckmanInventory(_vm);
_credits = new DuckmanCredits(_vm);
_wasCursorHoldingElvisPoster = false;
_counter = 0;
_savedTempMasterSfxVolume = 16;
_lastRandomSoundIndex = 6;
}
DuckmanSpecialCode::~DuckmanSpecialCode() {
delete _propertyTimers;
delete _inventory;
delete _credits;
for (auto &it : _specialCodeMap) {
delete it._value;
}
}
typedef Common::Functor1Mem<OpCall&, void, DuckmanSpecialCode> SpecialCodeFunctionDM;
#define SPECIAL(id, func) _specialCodeMap[id] = new SpecialCodeFunctionDM(this, &DuckmanSpecialCode::func);
void DuckmanSpecialCode::init() {
SPECIAL(0x00160001, spcStartScreenShaker);
SPECIAL(0x00160002, spcSetCursorHandMode);
SPECIAL(0x00160003, spcResetChinesePuzzle);
SPECIAL(0x00160004, spcAddChinesePuzzleAnswer);
SPECIAL(0x00160005, spcOpenInventory);
SPECIAL(0x00160007, spcPutBackInventoryItem);
SPECIAL(0x00160008, spcClearInventorySlot);
SPECIAL(0x0016000A, spcAddPropertyTimer);
SPECIAL(0x0016000B, spcSetPropertyTimer);
SPECIAL(0x0016000C, spcRemovePropertyTimer);
SPECIAL(0x0016000E, spcInitTeleporterPosition);
SPECIAL(0x0016000F, spcUpdateTeleporterPosition);
SPECIAL(0x00160010, spcCenterNewspaper);
SPECIAL(0x00160012, spcStopScreenShaker);
SPECIAL(0x00160013, spcIncrCounter);
SPECIAL(0x00160014, spcUpdateObject272Sequence);
SPECIAL(0x00160017, spcPlayRandomSound);
SPECIAL(0x0016001A, spcHoldGlowingElvisPoster);
SPECIAL(0x0016001B, spcStartCredits);
SPECIAL(0x0016001C, spcSetCursorInventoryMode);
SPECIAL(0x0016001D, spcCenterCurrentScreenText);
SPECIAL(0x0016001E, spcSetDefaultTextCoords);
SPECIAL(0x0016001F, spcSetTextDuration);
SPECIAL(0x00160020, spcSetTempMasterSfxVolume);
SPECIAL(0x00160021, spcRestoreTempMasterSfxVolume);
}
#undef SPECIAL
void DuckmanSpecialCode::run(uint32 specialCodeId, OpCall &opCall) {
SpecialCodeMapIterator it = _specialCodeMap.find(specialCodeId);
if (it != _specialCodeMap.end()) {
(*(*it)._value)(opCall);
} else {
debug("DuckmanSpecialCode::run() Unimplemented special code %08X", specialCodeId);
_vm->notifyThreadId(opCall._threadId);
error("DuckmanSpecialCode::run() Unimplemented special code");
}
}
void DuckmanSpecialCode::spcStartScreenShaker(OpCall &opCall) {
ARG_BYTE(effect);
const ScreenShakeEffect *shakerEffect = getScreenShakeEffect(effect);
_vm->startScreenShaker(shakerEffect->_pointsCount, shakerEffect->_duration, shakerEffect->_points, opCall._threadId);
}
void DuckmanSpecialCode::spcSetCursorHandMode(OpCall &opCall) {
ARG_BYTE(mode);
_vm->setCursorHandMode(mode);
_vm->notifyThreadId(opCall._threadId);
}
void DuckmanSpecialCode::spcResetChinesePuzzle(OpCall &opCall) {
_vm->_scriptResource->_properties.set(0x000E0018, false);
_vm->_scriptResource->_properties.set(0x000E0019, false);
_chinesePuzzleIndex = 0;
_vm->notifyThreadId(opCall._threadId);
}
void DuckmanSpecialCode::spcAddChinesePuzzleAnswer(OpCall &opCall) {
ARG_BYTE(answer);
_chinesePuzzleAnswers[_chinesePuzzleIndex++] = answer;
if (_chinesePuzzleIndex == 3) {
_vm->_scriptResource->_properties.set(0x000E0018, true);
if ((_chinesePuzzleAnswers[0] == 7 && _chinesePuzzleAnswers[1] == 2 && _chinesePuzzleAnswers[2] == 5) ||
(_chinesePuzzleAnswers[0] == 5 && _chinesePuzzleAnswers[1] == 2 && _chinesePuzzleAnswers[2] == 7))
_vm->_scriptResource->_properties.set(0x000E0019, true);
else if ((_chinesePuzzleAnswers[0] == 7 && _chinesePuzzleAnswers[1] == 2 && _chinesePuzzleAnswers[2] == 1) ||
(_chinesePuzzleAnswers[0] == 1 && _chinesePuzzleAnswers[1] == 2 && _chinesePuzzleAnswers[2] == 7))
_vm->_scriptResource->_properties.set(0x000E00A0, true);
}
_vm->notifyThreadId(opCall._threadId);
}
void DuckmanSpecialCode::spcOpenInventory(OpCall &opCall) {
_inventory->openInventory();
_vm->notifyThreadId(opCall._threadId);
}
void DuckmanSpecialCode::spcPutBackInventoryItem(OpCall &opCall) {
_inventory->putBackInventoryItem();
_vm->notifyThreadId(opCall._threadId);
}
void DuckmanSpecialCode::spcClearInventorySlot(OpCall &opCall) {
ARG_UINT32(objectId);
_inventory->clearInventorySlot(objectId);
_vm->notifyThreadId(opCall._threadId);
}
void DuckmanSpecialCode::spcAddPropertyTimer(OpCall &opCall) {
ARG_UINT32(propertyId);
_propertyTimers->addPropertyTimer(propertyId);
_vm->notifyThreadId(opCall._threadId);
}
void DuckmanSpecialCode::spcSetPropertyTimer(OpCall &opCall) {
ARG_INT16(propertyNum);
ARG_INT16(duration);
_propertyTimers->setPropertyTimer(propertyNum | 0xE0000, duration);
_vm->notifyThreadId(opCall._threadId);
}
void DuckmanSpecialCode::spcRemovePropertyTimer(OpCall &opCall) {
ARG_UINT32(propertyId);
_propertyTimers->removePropertyTimer(propertyId);
_vm->notifyThreadId(opCall._threadId);
}
void DuckmanSpecialCode::spcInitTeleporterPosition(OpCall &opCall) {
_teleporterPosition.x = 4;
_teleporterPosition.y = 3;
updateTeleporterProperties();
_vm->_scriptResource->_properties.set(0x000E007A, false);
_vm->notifyThreadId(opCall._threadId);
}
void DuckmanSpecialCode::spcUpdateTeleporterPosition(OpCall &opCall) {
ARG_BYTE(direction);
int16 deltaX = 0;
int16 deltaY = 0;
uint32 sequenceId = 0;
Control *control = _vm->getObjectControl(0x400C0);
switch (direction) {
case 1:
if (_teleporterPosition.y > 1) {
deltaY = -1;
sequenceId = 0x60386;
}
break;
case 4:
if (_teleporterPosition.x < 4) {
deltaX = 1;
sequenceId = 0x60387;
}
break;
case 0x10:
if (_teleporterPosition.y < 3) {
deltaY = 1;
sequenceId = 0x60385;
}
break;
case 0x40:
if (_teleporterPosition.x > 1) {
deltaX = -1;
sequenceId = 0x60388;
}
break;
default:
break;
}
if (sequenceId) {
control->startSequenceActor(sequenceId, 2, opCall._threadId);
_teleporterPosition.x += deltaX;
_teleporterPosition.y += deltaY;
updateTeleporterProperties();
_vm->_scriptResource->_properties.set(0x000E007A, false);
} else {
_vm->notifyThreadId(opCall._threadId);
}
_vm->notifyThreadId(opCall._threadId);
}
void DuckmanSpecialCode::spcCenterNewspaper(OpCall &opCall) {
Control *control = _vm->getObjectControl(0x40017);
control->_flags |= 8;
control->_actor->_position.x = 160;
control->_actor->_position.y = 100;
_vm->notifyThreadId(opCall._threadId);
}
void DuckmanSpecialCode::spcStopScreenShaker(OpCall &opCall) {
_vm->stopScreenShaker();
_vm->notifyThreadId(opCall._threadId);
}
void DuckmanSpecialCode::spcIncrCounter(OpCall &opCall) {
ARG_BYTE(maxCount);
ARG_BYTE(incr);
_vm->_scriptResource->_properties.set(0x000E0088, false);
if (incr) {
_counter += incr;
if (_counter >= maxCount)
_vm->_scriptResource->_properties.set(0x000E0088, true);
} else {
_counter = 0;
}
_vm->notifyThreadId(opCall._threadId);
}
void DuckmanSpecialCode::spcUpdateObject272Sequence(OpCall &opCall) {
byte flags = 0;
uint32 sequenceId;
if (_vm->_scriptResource->_properties.get(0x000E0085))
flags |= 1;
if (_vm->_scriptResource->_properties.get(0x000E0083))
flags |= 2;
if (_vm->_scriptResource->_properties.get(0x000E0084))
flags |= 4;
switch (flags) {
case 0:
sequenceId = 0x603C1;
break;
case 1:
sequenceId = 0x603BF;
break;
case 2:
sequenceId = 0x603C2;
break;
case 3:
sequenceId = 0x603C0;
break;
case 4:
sequenceId = 0x603C3;
break;
case 5:
sequenceId = 0x603C5;
break;
case 6:
sequenceId = 0x603C4;
break;
case 7:
sequenceId = 0x603C6;
break;
default:
sequenceId = 0x603C1;
break;
}
Control *control = _vm->getObjectControl(0x40110);
control->startSequenceActor(sequenceId, 2, opCall._threadId);
}
void DuckmanSpecialCode::spcPlayRandomSound(OpCall &opCall) {
static const uint32 kRandomSoundIds[] = {
0x00090084, 0x00090085, 0x00090086, 0x00090087, 0x00090088, 0x00090089
};
int16 soundIndex;
do {
soundIndex = _vm->getRandom(ARRAYSIZE(kRandomSoundIds));
} while (soundIndex == _lastRandomSoundIndex);
_vm->_soundMan->playSound(kRandomSoundIds[soundIndex], 255, 0);
_lastRandomSoundIndex = soundIndex;
_vm->notifyThreadId(opCall._threadId);
}
void DuckmanSpecialCode::spcHoldGlowingElvisPoster(OpCall &opCall) {
const uint32 kPosterObjectId = 0x40072;
const uint32 kPosterSequenceId = 0x60034;
ARG_BYTE(mode);
switch (mode) {
case 0:
if (_vm->_cursor._objectId == kPosterObjectId) {
_wasCursorHoldingElvisPoster = true;
_inventory->addInventoryItem(_vm->_cursor._objectId);
_vm->stopCursorHoldingObject();
} else {
_wasCursorHoldingElvisPoster = false;
}
break;
case 1:
if (_wasCursorHoldingElvisPoster) {
_inventory->clearInventorySlot(kPosterObjectId);
_vm->_cursor._objectId = kPosterObjectId;
_vm->_cursor._sequenceId2 = kPosterSequenceId;
_vm->_cursor._field14[_vm->_cursor._actorIndex - 1] = true;
}
break;
default:
break;
}
_vm->notifyThreadId(opCall._threadId);
}
void DuckmanSpecialCode::spcStartCredits(OpCall &opCall) {
ARG_BYTE(mode);
if (mode == 0)
_credits->start();
_vm->notifyThreadId(opCall._threadId);
}
void DuckmanSpecialCode::spcSetCursorInventoryMode(OpCall &opCall) {
ARG_BYTE(mode);
ARG_BYTE(value);
_vm->setCursorInventoryMode(mode, value);
_vm->notifyThreadId(opCall._threadId);
}
void DuckmanSpecialCode::spcCenterCurrentScreenText(OpCall &opCall) {
WidthHeight dimensions;
_vm->getDefaultTextDimensions(dimensions);
Common::Point pt(160, dimensions._height / 2 + 8);
_vm->setDefaultTextPosition(pt);
_vm->notifyThreadId(opCall._threadId);
}
void DuckmanSpecialCode::spcSetDefaultTextCoords(OpCall &opCall) {
_vm->setDefaultTextCoords();
_vm->notifyThreadId(opCall._threadId);
}
void DuckmanSpecialCode::spcSetTextDuration(OpCall &opCall) {
ARG_INT16(kind);
ARG_INT16(duration);
_vm->setTextDuration(kind, duration);
_vm->notifyThreadId(opCall._threadId);
}
void DuckmanSpecialCode::spcSetTempMasterSfxVolume(OpCall &opCall) {
ARG_INT16(sfxVolume);
// TODO _savedTempMasterSfxVolume = _vm->getMasterSfxVolume();
// TODO _vm->setMasterSfxVolume(sfxVolume);
_vm->notifyThreadId(opCall._threadId);
}
void DuckmanSpecialCode::spcRestoreTempMasterSfxVolume(OpCall &opCall) {
// TODO _vm->setMasterSfxVolume(_savedTempMasterSfxVolume);
_savedTempMasterSfxVolume = 16;
_vm->notifyThreadId(opCall._threadId);
}
void DuckmanSpecialCode::updateTeleporterProperties() {
_vm->_scriptResource->_properties.set(0x000E0074, _teleporterPosition.x == 4 && _teleporterPosition.y == 2);
_vm->_scriptResource->_properties.set(0x000E0075, _teleporterPosition.x == 4 && _teleporterPosition.y == 3);
_vm->_scriptResource->_properties.set(0x000E0076, _teleporterPosition.x == 3 && _teleporterPosition.y == 3);
_vm->_scriptResource->_properties.set(0x000E0077, _teleporterPosition.x == 2 && _teleporterPosition.y == 2);
_vm->_scriptResource->_properties.set(0x000E0078, _teleporterPosition.x == 1 && _teleporterPosition.y == 1);
}
} // End of namespace Illusions

View File

@@ -0,0 +1,104 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_DUCKMAN_SPECIALCODE_H
#define ILLUSIONS_DUCKMAN_SPECIALCODE_H
#include "illusions/illusions.h"
#include "illusions/specialcode.h"
#include "common/algorithm.h"
namespace Illusions {
class IllusionsEngine_Duckman;
class DuckmanCredits;
class DuckmanInventory;
class PropertyTimers;
typedef Common::Functor1<OpCall&, void> SpecialCodeFunction;
class DuckmanSpecialCode : public SpecialCode {
public:
DuckmanSpecialCode(IllusionsEngine_Duckman *vm);
~DuckmanSpecialCode() override;
void init() override;
void run(uint32 specialCodeId, OpCall &opCall) override;
public:
typedef Common::HashMap<uint32, SpecialCodeFunction*> SpecialCodeMap;
typedef SpecialCodeMap::iterator SpecialCodeMapIterator;
IllusionsEngine_Duckman *_vm;
SpecialCodeMap _specialCodeMap;
PropertyTimers *_propertyTimers;
DuckmanInventory *_inventory;
uint _chinesePuzzleIndex;
byte _chinesePuzzleAnswers[3];
Common::Point _teleporterPosition;
int16 _counter;
bool _wasCursorHoldingElvisPoster;
int16 _savedTempMasterSfxVolume;
int16 _lastRandomSoundIndex;
DuckmanCredits *_credits;
// Special code interface functions
void runSpecialCode(uint32 specialCodeId, OpCall &opCall);
void spcStartScreenShaker(OpCall &opCall);
void spcSetCursorHandMode(OpCall &opCall);
void spcResetChinesePuzzle(OpCall &opCall);
void spcAddChinesePuzzleAnswer(OpCall &opCall);
void spcOpenInventory(OpCall &opCall);
void spcPutBackInventoryItem(OpCall &opCall);
void spcClearInventorySlot(OpCall &opCall);
void spcAddPropertyTimer(OpCall &opCall);
void spcSetPropertyTimer(OpCall &opCall);
void spcRemovePropertyTimer(OpCall &opCall);
void spcInitTeleporterPosition(OpCall &opCall);
void spcUpdateTeleporterPosition(OpCall &opCall);
void spcCenterNewspaper(OpCall &opCall);
void spcStopScreenShaker(OpCall &opCall);
void spcIncrCounter(OpCall &opCall);
void spcUpdateObject272Sequence(OpCall &opCall);
void spcPlayRandomSound(OpCall &opCall);
void spcHoldGlowingElvisPoster(OpCall &opCall);
void spcStartCredits(OpCall &opCall);
void spcSetCursorInventoryMode(OpCall &opCall);
void spcCenterCurrentScreenText(OpCall &opCall);
void spcSetDefaultTextCoords(OpCall &opCall);
void spcSetTextDuration(OpCall &opCall);
void spcSetTempMasterSfxVolume(OpCall &opCall);
void spcRestoreTempMasterSfxVolume(OpCall &opCall);
void updateTeleporterProperties();
void startCredits();
int updateCredits(uint flags);
char *readNextCreditsLine();
Common::Point getCreditsItemPosition(int index);
void charToWChar(char *text, uint16 *wtext, uint size);
};
} // End of namespace Illusions
#endif // ILLUSIONS_ILLUSIONS_H

View File

@@ -0,0 +1,99 @@
/* 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/>.
*
*/
#include "illusions/duckman/illusions_duckman.h"
#include "illusions/duckman/duckman_videoplayer.h"
#include "illusions/input.h"
#include "illusions/screen.h"
#include "engines/util.h"
namespace Illusions {
// DuckmanVideoPlayer
DuckmanVideoPlayer::DuckmanVideoPlayer(IllusionsEngine_Duckman *vm)
: _vm(vm), _videoDecoder(nullptr) {
}
DuckmanVideoPlayer::~DuckmanVideoPlayer() {
delete _videoDecoder;
}
void DuckmanVideoPlayer::start(uint32 videoId, uint32 callingThreadId) {
debug(0, "DuckmanVideoPlayer::play(%08X, %08X)", videoId, callingThreadId);
_callingThreadId = callingThreadId;
_vm->_input->discardAllEvents();
Common::Path filename(Common::String::format("%08x.avi", videoId));
_videoDecoder = new Video::AVIDecoder();
if (!_videoDecoder->loadFile(filename)) {
delete _videoDecoder;
_videoDecoder = nullptr;
warning("Unable to open video %s", filename.toString().c_str());
return;
}
_videoDecoder->start();
}
void DuckmanVideoPlayer::stop() {
_vm->_input->discardAllEvents();
delete _videoDecoder;
_videoDecoder = nullptr;
if (_callingThreadId != 0) {
_vm->notifyThreadId(_callingThreadId);
_callingThreadId = 0;
}
}
void DuckmanVideoPlayer::update() {
if (_vm->_input->pollEvent(kEventSkip) || _videoDecoder->endOfVideo()) {
stop();
} else if (_videoDecoder->needsUpdate()) {
const Graphics::Surface *frame = _videoDecoder->decodeNextFrame();
Graphics::Surface *backSurface = _vm->_screen->getBackSurface();
if (frame && frame->format.bytesPerPixel == g_system->getScreenFormat().bytesPerPixel) {
const int width = MIN(frame->w, backSurface->w);
const int height = MIN(frame->h, backSurface->h);
const byte *src = (const byte*)frame->getPixels();
byte *dest = (byte*)backSurface->getPixels();
for (int yc = 0; yc < height; ++yc) {
memcpy(dest, src, width);
src += frame->pitch;
dest += backSurface->pitch;
}
}
if (_videoDecoder->hasDirtyPalette()) {
const byte *palette = _videoDecoder->getPalette();
byte palette4[1024];
for (uint i = 0; i < 256; ++i) {
palette4[i * 4 + 0] = palette[i * 3 + 0];
palette4[i * 4 + 1] = palette[i * 3 + 1];
palette4[i * 4 + 2] = palette[i * 3 + 2];
}
_vm->_screenPalette->setPalette(palette4, 1, 256);
}
}
}
bool DuckmanVideoPlayer::isPlaying() const {
return _videoDecoder != nullptr;
}
} // End of namespace Illusions

View File

@@ -0,0 +1,48 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_DUCKMAN_VIDEOPLAYER_H
#define ILLUSIONS_DUCKMAN_VIDEOPLAYER_H
#include "illusions/illusions.h"
#include "video/avi_decoder.h"
namespace Illusions {
class IllusionsEngine_Duckman;
class DuckmanVideoPlayer {
public:
DuckmanVideoPlayer(IllusionsEngine_Duckman *vm);
~DuckmanVideoPlayer();
void start(uint32 videoId, uint32 callingThreadId);
void stop();
void update();
bool isPlaying() const;
public:
IllusionsEngine_Duckman *_vm;
Video::VideoDecoder *_videoDecoder;
uint32 _callingThreadId;
};
} // End of namespace Illusions
#endif // ILLUSIONS_DUCKMAN_VIDEOPLAYER_H

View File

@@ -0,0 +1,49 @@
/* 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/>.
*
*/
#include "illusions/duckman/gamestate_duckman.h"
#include "illusions/duckman/illusions_duckman.h"
#include "illusions/resources/scriptresource.h"
namespace Illusions {
Duckman_GameState::Duckman_GameState(IllusionsEngine_Duckman *vm)
: _vm(vm) {
}
uint32 Duckman_GameState::calcWriteBufferSizeInternal() {
return
_vm->_scriptResource->_properties.getSize() +
_vm->_scriptResource->_blockCounters.getSize();
}
bool Duckman_GameState::readStateInternal(Common::ReadStream *in) {
return
_vm->_scriptResource->_properties.readFromStream(in) &&
_vm->_scriptResource->_blockCounters.readFromStream(in);
}
void Duckman_GameState::writeStateInternal(Common::WriteStream *out) {
_vm->_scriptResource->_properties.writeToStream(out);
_vm->_scriptResource->_blockCounters.writeToStream(out);
}
} // End of namespace Illusions

View File

@@ -0,0 +1,43 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_DUCKMAN_GAMESTATE_DUCKMAN_H
#define ILLUSIONS_DUCKMAN_GAMESTATE_DUCKMAN_H
#include "illusions/gamestate.h"
namespace Illusions {
class IllusionsEngine_Duckman;
class Duckman_GameState : public GameState {
public:
Duckman_GameState(IllusionsEngine_Duckman *vm);
protected:
IllusionsEngine_Duckman *_vm;
uint32 calcWriteBufferSizeInternal() override;
bool readStateInternal(Common::ReadStream *in) override;
void writeStateInternal(Common::WriteStream *out) override;
};
} // End of namespace Illusions
#endif // ILLUSIONS_DUCKMAN_GAMESTATE_DUCKMAN_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,208 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_ILLUSIONS_DUCKMAN_H
#define ILLUSIONS_ILLUSIONS_DUCKMAN_H
#include "illusions/illusions.h"
#include "common/algorithm.h"
#include "common/stack.h"
namespace Illusions {
class Dictionary;
class ScriptStack;
class DuckmanDialogSystem;
class DuckmanMenuSystem;
class DuckmanVideoPlayer;
struct Cursor_Duckman {
int _gameState;
Control *_control;
Common::Point _position;
uint32 _objectId;
int _actorIndex;
int _savedActorIndex;
bool _field14[14];
Control *_currOverlappedControl;
uint32 _sequenceId1;
uint32 _sequenceId2;
uint32 _notifyThreadId30;
int16 *_op113_choiceOfsPtr;
int _dialogItemsCount;
uint32 _overlappedObjectId;
uint32 _field3C;
uint32 _field40;
};
struct ScreenShakerPoint {
int16 x, y;
};
struct ScreenShakeEffect {
uint32 _duration;
uint _pointsCount;
const ScreenShakerPoint *_points;
};
struct ScreenShaker {
uint _pointsIndex;
uint _pointsCount;
bool _finished;
uint32 _duration;
uint32 _nextTime;
uint32 _notifyThreadId;
const ScreenShakerPoint *_points;
};
struct OpCall;
class IllusionsEngine_Duckman : public IllusionsEngine {
public:
IllusionsEngine_Duckman(OSystem *syst, const IllusionsGameDescription *gd);
protected:
Common::Error run() override;
bool hasFeature(EngineFeature f) const override;
public:
uint32 _prevSceneId;
uint32 _theSceneId;
uint32 _theThreadId;
uint32 _globalSceneId;
uint _activeScenesCount;
uint32 _activeScenes[6];
Cursor_Duckman _cursor;
Control *_currWalkOverlappedControl;
DuckmanDialogSystem *_dialogSys;
int _savedInventoryActorIndex;
ScreenShaker *_screenShaker;
DuckmanMenuSystem *_menuSystem;
DuckmanVideoPlayer *_videoPlayer;
void initInput();
void initUpdateFunctions();
int updateScript(uint flags);
void startScreenShaker(uint pointsCount, uint32 duration, const ScreenShakerPoint *points, uint32 threadId);
void stopScreenShaker();
int updateScreenShaker(uint flags);
void startFader(int duration, int minValue, int maxValue, int firstIndex, int lastIndex, uint32 threadId);
void updateFader() override;
void clearFader() override;
void pauseFader() override;
void unpauseFader() override;
int updateVideoPlayer(uint flags);
void playVideo(uint32 videoId, uint32 callingThreadId);
bool isVideoPlaying() override;
void setDefaultTextCoords() override;
void loadSpecialCode(uint32 resId) override;
void unloadSpecialCode(uint32 resId) override;
void notifyThreadId(uint32 &threadId) override;
bool testMainActorFastWalk(Control *control) override;
bool testMainActorCollision(Control *control) override;
Control *getObjectControl(uint32 objectId) override;
Common::Point getNamedPointPosition(uint32 namedPointId) override;
uint32 getPriorityFromBase(int16 priority) override;
uint32 getCurrentScene() override;
uint32 getPrevScene() override;
bool isCursorObject(uint32 actorTypeId, uint32 objectId) override;
void setCursorControlRoutine(Control *control) override;
void placeCursorControl(Control *control, uint32 sequenceId) override;
void setCursorControl(Control *control) override;
void showCursor() override;
void hideCursor() override;
void initCursor();
void setCursorActorIndex(int actorIndex, int a, int b);
void enableCursorVerb(int verbNum);
void disableCursorVerb(int verbNum);
void setCursorHandMode(int mode);
void setCursorInventoryMode(int mode, int value);
void startCursorHoldingObject(uint32 objectId, uint32 sequenceId);
void stopCursorHoldingObject();
void cursorControlRoutine(Control *control, uint32 deltaTime);
void startScriptThreadSimple(uint32 threadId, uint32 callingThreadId) override;
void startScriptThread(uint32 threadId, uint32 callingThreadId);
void startScriptThread2(uint32 threadId, uint32 callingThreadId, uint32 unk);
uint32 startAbortableTimerThread(uint32 duration, uint32 threadId);
uint32 startTimerThread(uint32 duration, uint32 threadId);
uint32 startAbortableThread(byte *scriptCodeIp1, byte *scriptCodeIp2, uint32 callingThreadId);
uint32 startTalkThread(uint32 objectId, uint32 talkId, uint32 sequenceId1,
uint32 sequenceId2, uint32 callingThreadId);
uint32 startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
uint32 value8, uint32 valueC, uint32 value10) override;
void resumeFromSavegame(uint32 callingThreadId);
void newScriptThread(uint32 threadId, uint32 callingThreadId, uint notifyFlags,
byte *scriptCodeIp);
uint32 newTimerThread(uint32 duration, uint32 callingThreadId, bool isAbortable);
uint32 newTempThreadId();
void initActiveScenes();
void pushActiveScene(uint32 sceneId);
void popActiveScene();
bool loadScene(uint32 sceneId);
bool enterScene(uint32 sceneId, uint32 threadId);
void exitScene();
bool changeScene(uint32 sceneId, uint32 threadId, uint32 callerThreadId);
void enterPause(uint32 sceneId, uint32 threadId);
void leavePause(uint32 sceneId, uint32 threadId);
void dumpActiveScenes(uint32 sceneId, uint32 threadId);
void dumpCurrSceneFiles(uint32 sceneId, uint32 threadId);
void pause(uint32 callerThreadId);
void unpause(uint32 callerThreadId);
void setSceneIdThreadId(uint32 theSceneId, uint32 theThreadId);
bool findTriggerCause(uint32 sceneId, uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs);
void reset();
uint32 getObjectActorTypeId(uint32 objectId);
Common::Point convertMousePos(Common::Point mousePos);
void startCursorSequence();
int getCursorActorIndex();
void updateGameState2();
void playSoundEffect(int index);
bool getTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &outThreadId);
uint32 runTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId);
void playTriggerCauseSound(uint32 verbId, uint32 objectId2, uint32 objectId);
bool loadSavegameFromScript(int16 slotNum, uint32 callingThreadId);
bool saveSavegameFromScript(int16 slotNum, uint32 callingThreadId);
void activateSavegame(uint32 callingThreadId);
};
} // End of namespace Illusions
#endif // ILLUSIONS_ILLUSIONS_H

View File

@@ -0,0 +1,493 @@
/* 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/>.
*
*/
#include "illusions/illusions.h"
#include "illusions/actor.h"
#include "illusions/sound.h"
#include "illusions/duckman/illusions_duckman.h"
#include "illusions/duckman/menusystem_duckman.h"
#include "illusions/resources/scriptresource.h"
namespace Illusions {
// DuckmanMenuSystem
DuckmanMenuSystem::DuckmanMenuSystem(IllusionsEngine_Duckman *vm)
: BaseMenuSystem(vm), _vm(vm) {
clearMenus();
}
DuckmanMenuSystem::~DuckmanMenuSystem() {
freeMenus();
}
void DuckmanMenuSystem::runMenu(MenuChoiceOffsets menuChoiceOffsets, int16 *menuChoiceOffset,
uint32 menuId, uint32 duration, uint timeOutMenuChoiceIndex, uint32 menuCallerThreadId) {
debug(0, "DuckmanMenuSystem::runMenu(%08X)", menuId);
setTimeOutDuration(duration, timeOutMenuChoiceIndex);
setMenuCallerThreadId(menuCallerThreadId);
setMenuChoiceOffsets(menuChoiceOffsets, menuChoiceOffset);
int rootMenuId = convertRootMenuId(menuId | 0x180000);
BaseMenu *rootMenu = getMenuById(rootMenuId);
openMenu(rootMenu);
}
void DuckmanMenuSystem::clearMenus() {
for (int i = 0; i < kDuckmanLastMenuIndex; ++i) {
_menus[i] = nullptr;
}
}
void DuckmanMenuSystem::freeMenus() {
for (int i = 0; i < kDuckmanLastMenuIndex; ++i) {
delete _menus[i];
}
}
BaseMenu *DuckmanMenuSystem::getMenuById(int menuId) {
if (!_menus[menuId])
_menus[menuId] = createMenuById(menuId);
return _menus[menuId];
}
BaseMenu *DuckmanMenuSystem::createMenuById(int menuId) {
switch (menuId) {
case kDuckmanMainMenu:
return createMainMenu();
case kDuckmanMainMenuDemo:
return createMainMenuDemo();
case kDuckmanPauseMenu:
return createPauseMenu();
case kDuckmanQueryRestartMenu:
return createQueryRestartMenu();
case kDuckmanQueryQuitMenu:
return createQueryQuitMenu();
case kDuckmanSaveCompleteMenu:
return createSaveCompleteMenu();
case kDuckmanOptionsMenu:
return createOptionsMenu();
case kDuckmanDebugPauseMenu:
return createDebugPauseMenu();
case kDuckmanAddRemoveInventoryMenu:
return createAddRemoveInventoryMenu();
case kDuckmanLoadGameFailedMenu:
return createLoadGameFailedMenu();
default:
error("DuckmanMenuSystem::createMenuById() Invalid menu id %d", menuId);
}
}
BaseMenu *DuckmanMenuSystem::createMainMenu() {
BaseMenu *menu = new BaseMenu(this, 0x00120003, 12, 17, 11, 27, 0);
if (_vm->getGameLanguage() != Common::RU_RUS) {
menu->addMenuItem(new MenuItem("Start New Game", new MenuActionReturnChoice(this, 11)));
menu->addMenuItem(new MenuItem("Load Saved Game", new MenuActionLoadGame(this, 1)));
menu->addMenuItem(new MenuItem("Options", new MenuActionEnterMenu(this, kDuckmanOptionsMenu)));
menu->addMenuItem(new MenuItem("Quit Game", new MenuActionEnterQueryMenu(this, kDuckmanQueryQuitMenu, 12)));
} else {
menu->addMenuItem(new MenuItem("3AHOBO ", new MenuActionReturnChoice(this, 11)));
menu->addMenuItem(new MenuItem("B6IHECEM ", new MenuActionLoadGame(this, 1)));
menu->addMenuItem(new MenuItem("YCTAH .", new MenuActionEnterMenu(this, kDuckmanOptionsMenu)));
menu->addMenuItem(new MenuItem("B6IXOD ", new MenuActionEnterQueryMenu(this, kDuckmanQueryQuitMenu, 12)));
}
return menu;
}
BaseMenu *DuckmanMenuSystem::createMainMenuDemo() {
BaseMenu *menu = new BaseMenu(this, 0x00120003, 12, 17, 11, 27, 0);
if (_vm->getGameLanguage() != Common::RU_RUS) {
menu->addMenuItem(new MenuItem("Start New Game", new MenuActionReturnChoice(this, 2)));
menu->addMenuItem(new MenuItem("Load Saved Game", new MenuActionLoadGame(this, 1)));
menu->addMenuItem(new MenuItem("Options", new MenuActionEnterMenu(this, kDuckmanOptionsMenu)));
menu->addMenuItem(new MenuItem("Quit Game", new MenuActionEnterQueryMenu(this, kDuckmanQueryQuitMenu, 3)));
} else {
menu->addMenuItem(new MenuItem("3AHOBO ", new MenuActionReturnChoice(this, 2)));
menu->addMenuItem(new MenuItem("B6IHECEM ", new MenuActionLoadGame(this, 1)));
menu->addMenuItem(new MenuItem("YCTAH .", new MenuActionEnterMenu(this, kDuckmanOptionsMenu)));
menu->addMenuItem(new MenuItem("B6IXOD ", new MenuActionEnterQueryMenu(this, kDuckmanQueryQuitMenu, 3)));
}
return menu;
}
BaseMenu *DuckmanMenuSystem::createLoadGameMenu() {
return nullptr; // TODO
}
BaseMenu *DuckmanMenuSystem::createLoadGameFailedMenu() {
BaseMenu *menu = new BaseMenu(this, 0x00120003, 12, 17, 11, 27, 0);
menu->addText("Load Game Failed");
menu->addText("-------------------");
menu->addMenuItem(new MenuItem("Continue", new MenuActionReturnChoice(this, 1)));
return menu;
}
MenuItem *DuckmanMenuSystem::createOptionsSliderMenuItem(MenuActionUpdateSlider **action, const Common::String &text, SliderActionType type, BaseMenu *baseMenu) {
int sliderValue = 0;
char sliderText[] = "{~~~~~~~~~~~~~~~~}";
switch (type) {
case SFX : sliderValue = _vm->_soundMan->getSfxVolume()/(256/15); break;
case MUSIC : sliderValue = _vm->_soundMan->getMusicVolume()/(256/15); break;
case VOICE : sliderValue = _vm->_soundMan->getSpeechVolume()/(256/15); break;
case TEXT_DURATION : sliderValue = _vm->getSubtitleDuration()/(256/15); break;
default: break;
}
sliderText[sliderValue + 1] = '|';
*action = new MenuActionUpdateSlider(this, baseMenu, type, _vm);
MenuItem *menuItem = new MenuItem(text + sliderText, *action);
(*action)->setMenuItem(menuItem);
return menuItem;
}
BaseMenu *DuckmanMenuSystem::createOptionsMenu() {
BaseMenu *menu = new BaseMenu(this, 0x00120003, 12, 17, 11, 27, 6);
MenuActionUpdateSlider *sfxSlider;
MenuActionUpdateSlider *musicSlider;
MenuActionUpdateSlider *speechSlider;
MenuActionUpdateSlider *textDurationSlider;
if (_vm->getGameLanguage() != Common::RU_RUS) {
menu->addText(" GAME OPTIONS @@@@");
menu->addText("--------------------------------------");
menu->addMenuItem(createOptionsSliderMenuItem(&sfxSlider, "SFX Volume @@", SFX, menu));
menu->addMenuItem(createOptionsSliderMenuItem(&musicSlider, "Music Volume @@@", MUSIC, menu));
menu->addMenuItem(createOptionsSliderMenuItem(&speechSlider, "Speech Volume ", VOICE, menu));
menu->addMenuItem(createOptionsSliderMenuItem(&textDurationSlider, "Text Duration @@@", TEXT_DURATION, menu));
menu->addMenuItem(new MenuItem("Restore Defaults", new MenuActionResetOptionSliders(this, sfxSlider, musicSlider, speechSlider, textDurationSlider)));
menu->addMenuItem(new MenuItem("Back", new MenuActionLeaveMenu(this)));
} else {
menu->addText(" YCTAHOBKA ");
menu->addText("--------------------------------------");
menu->addMenuItem(createOptionsSliderMenuItem(&sfxSlider, "3BYK @@", SFX, menu));
menu->addMenuItem(createOptionsSliderMenuItem(&musicSlider, "MY36IKA @@@", MUSIC, menu));
menu->addMenuItem(createOptionsSliderMenuItem(&speechSlider, "6A3AP ", VOICE, menu));
menu->addMenuItem(createOptionsSliderMenuItem(&textDurationSlider, "TEKCT @@@", TEXT_DURATION, menu));
menu->addMenuItem(new MenuItem("Restore Defaults", new MenuActionResetOptionSliders(this, sfxSlider, musicSlider, speechSlider, textDurationSlider)));
menu->addMenuItem(new MenuItem("Back", new MenuActionLeaveMenu(this)));
}
return menu;
}
BaseMenu *DuckmanMenuSystem::createPauseMenu() {
BaseMenu *menu = new BaseMenu(this, 0x00120003, 12, 17, 11, 27, 1);
if (_vm->getGameLanguage() != Common::RU_RUS) {
menu->addText(" Game Paused");
menu->addText("--------------------");
menu->addMenuItem(new MenuItem("Resume", new MenuActionReturnChoice(this, 21)));
menu->addMenuItem(new MenuItem("Load Game", new MenuActionLoadGame(this, 1)));
menu->addMenuItem(new MenuItem("Save Game", new MenuActionSaveGame(this, 11)));
menu->addMenuItem(new MenuItem("Restart Game", new MenuActionEnterQueryMenu(this, kDuckmanQueryRestartMenu, 2)));
menu->addMenuItem(new MenuItem("Options", new MenuActionEnterMenu(this, kDuckmanOptionsMenu)));
menu->addMenuItem(new MenuItem("Quit Game", new MenuActionEnterQueryMenu(this, kDuckmanQueryQuitMenu, 23)));
} else {
menu->addText(" OCTAHOBKA");
menu->addText("--------------------");
menu->addMenuItem(new MenuItem("YXHEM ", new MenuActionReturnChoice(this, 21)));
menu->addMenuItem(new MenuItem("B6IHECEM ", new MenuActionLoadGame(this, 1)));
menu->addMenuItem(new MenuItem("BHECEM ", new MenuActionSaveGame(this, 11)));
menu->addMenuItem(new MenuItem("3AHOBO ", new MenuActionEnterQueryMenu(this, kDuckmanQueryRestartMenu, 2)));
menu->addMenuItem(new MenuItem("YCTAH .", new MenuActionEnterMenu(this, kDuckmanOptionsMenu)));
menu->addMenuItem(new MenuItem("B6IXOD ", new MenuActionEnterQueryMenu(this, kDuckmanQueryQuitMenu, 23)));
}
return menu;
}
BaseMenu *DuckmanMenuSystem::createQueryRestartMenu() {
BaseMenu *menu = new BaseMenu(this, 0x00120003, 12, 17, 11, 27, 2);
if (_vm->getGameLanguage() != Common::RU_RUS) {
menu->addText("Do you really want to restart?");
menu->addText("-----------------------------------");
menu->addMenuItem(new MenuItem("Yes, let's try again", new MenuActionReturnChoice(this, getQueryConfirmationChoiceIndex())));
menu->addMenuItem(new MenuItem("No, just kidding", new MenuActionLeaveMenu(this)));
} else {
menu->addText("TO4HO 3AHOBO ? ");
menu->addText("-----------------------------------");
menu->addMenuItem(new MenuItem("DA , ECTECTBEHHO ", new MenuActionReturnChoice(this, getQueryConfirmationChoiceIndex())));
menu->addMenuItem(new MenuItem("HET , ODHO3HA4HO", new MenuActionLeaveMenu(this)));
}
return menu;
}
BaseMenu *DuckmanMenuSystem::createQueryQuitMenu() {
BaseMenu *menu = new BaseMenu(this, 0x00120003, 12, 17, 11, 27, 2);
if (_vm->getGameLanguage() != Common::RU_RUS) {
menu->addText("Do you really want to quit?");
menu->addText("-------------------------------");
menu->addMenuItem(new MenuItem("Yes, I'm outta here", new MenuActionReturnChoice(this, getQueryConfirmationChoiceIndex())));
menu->addMenuItem(new MenuItem("No, just kidding", new MenuActionLeaveMenu(this)));
} else {
menu->addText("TO4HO HA B6IXOD ? ");
menu->addText("-------------------------------");
menu->addMenuItem(new MenuItem("DA , ECTECTBEHHO ", new MenuActionReturnChoice(this, getQueryConfirmationChoiceIndex())));
menu->addMenuItem(new MenuItem("HET , ODHO3HA4HO", new MenuActionLeaveMenu(this)));
}
return menu;
}
BaseMenu *DuckmanMenuSystem::createSaveCompleteMenu() {
BaseMenu *menu = new BaseMenu(this, 0x00120003, 12, 17, 11, 27, 1);
menu->addText("Game Saved");
menu->addText("-------------");
menu->addMenuItem(new MenuItem("Continue", new MenuActionReturnChoice(this, 1)));
return menu;
}
BaseMenu *DuckmanMenuSystem::createDebugMenu() {
// TODO
BaseMenu *menu = new BaseMenu(this, 0x00120002, 0, 0, 0, 17, 1);
menu->addText("Debug Menu");
menu->addText("-----------------");
return menu;
}
BaseMenu *DuckmanMenuSystem::createDebugPauseMenu() {
BaseMenu *menu = new BaseMenu(this, 0x00120002, 0, 0, 0, 17, 1);
menu->addText("Debug Pause Menu");
menu->addText("-----------------");
menu->addMenuItem(new MenuItem("Return to Game", new MenuActionReturnChoice(this, 1)));
menu->addMenuItem(new MenuItem("Add/Remove Inventory", new MenuActionEnterMenu(this, kDuckmanAddRemoveInventoryMenu)));
// TODO quit to debug menu.
return menu;
}
typedef struct InventoryMenuItem {
const char *name;
uint32 objectId;
uint32 sequenceId;
uint32 propertyId;
} InventoryMenuItem;
static const InventoryMenuItem kDebugInventoryItems[21] = {
{ "Pick-up Book", 262212, 393231, 917519 },
{ "Bucket and Squeegee", 262314, 393233, 917599 },
{ "Cardboard Cut Out", 262219, 393264, 917573 },
{ "Talking Doll", 262209, 393943, 917587 },
{ "Cookie Fortunes", 262263, 393266, 917520 },
{ "Garbage Can Lid", 262311, 393259, 917597 },
{ "Chewing Gum", 262210, 393267, 917522 },
{ "Ladder", 262155, 393258, 917598 },
{ "Disco Light", 262342, 393260, 917594 },
{ "Magazine Cover", 262185, 393261, 917517 },
{ "Matches", 262159, 393232, 917516 },
{ "Opera Lessons", 262293, 393731, 917600 },
{ "Pizza Card", 262239, 393262, 917526 },
{ "Toilet Plunger", 262282, 393257, 917555 },
{ "Black Velvet Poster", 262258, 393269, 917527 },
{ "Red Spray Paint", 262297, 393254, 917531 },
{ "Remote Control", 262161, 393255, 917595 },
{ "Sparkplug", 262294, 393256, 917532 },
{ "Tape Recorder", 262328, 393827, 917584 },
{ "Wacky Putty", 262228, 393559, 917537 },
{ "Wrench", 262175, 393422, 917530 }
};
BaseMenu *DuckmanMenuSystem::createAddRemoveInventoryMenu() {
BaseMenu *menu = new BaseMenu(this, 0x00120002, 0, 0, 0, 17, 1);
menu->addText("Add/Remove Inventory");
menu->addText("-----------------");
for (int i = 0; i < 21; i++) {
menu->addMenuItem(new MenuItem(kDebugInventoryItems[i].name, new MenuActionInventoryAddRemove(this, _vm, i)));
}
menu->addMenuItem(new MenuItem("Back", new MenuActionLeaveMenu(this)));
return menu;
}
int DuckmanMenuSystem::convertRootMenuId(uint32 menuId) {
switch (menuId) {
case 0x180001:
return kDuckmanMainMenu;
case 0x180002:
return kDuckmanPauseMenu;
case 0x180003:
return kDuckmanDebugMenu;
case 0x180004:
return kDuckmanDebugPauseMenu;
case 0x180005:
return kDuckmanSaveCompleteMenu;
/*
case 0x180006: // save game failed menu
*/
case 0x180007: // load game failed menu
return kDuckmanLoadGameFailedMenu;
case 0x180008:
return kDuckmanMainMenuDemo;
default:
error("DuckmanMenuSystem() Menu ID %08X not found", menuId);
}
}
bool DuckmanMenuSystem::initMenuCursor() {
bool cursorInitialVisibleFlag = false;
Control *cursorControl = _vm->getObjectControl(Illusions::CURSOR_OBJECT_ID);
if (cursorControl) {
if (cursorControl->_flags & 1)
cursorInitialVisibleFlag = false;
cursorControl->appearActor();
} else {
Common::Point pos = _vm->getNamedPointPosition(0x70001);
_vm->_controls->placeActor(0x50001, pos, 0x60001, Illusions::CURSOR_OBJECT_ID, 0);
cursorControl = _vm->getObjectControl(Illusions::CURSOR_OBJECT_ID);
}
return cursorInitialVisibleFlag;
}
int DuckmanMenuSystem::getGameState() {
return _vm->_cursor._gameState;
}
void DuckmanMenuSystem::setMenuCursorNum(int cursorNum) {
Control *mouseCursor = _vm->getObjectControl(Illusions::CURSOR_OBJECT_ID);
_vm->setCursorActorIndex(5, cursorNum, 0);
mouseCursor->startSequenceActor(0x60001, 2, 0);
}
void DuckmanMenuSystem::setGameState(int gameState) {
_vm->_cursor._gameState = gameState;
}
void DuckmanMenuSystem::playSoundEffect(int sfxId) {
_vm->playSoundEffect(sfxId);
}
MenuActionInventoryAddRemove::MenuActionInventoryAddRemove(BaseMenuSystem *menuSystem, IllusionsEngine_Duckman *vm, uint choiceIndex)
: BaseMenuAction(menuSystem), _choiceIndex(choiceIndex), _vm(vm) {
}
void MenuActionInventoryAddRemove::execute() {
if (_vm->_scriptResource->_properties.get(kDebugInventoryItems[_choiceIndex].propertyId)) {
if (_vm->_cursor._objectId == kDebugInventoryItems[_choiceIndex].objectId) {
_vm->stopCursorHoldingObject();
}
_vm->_scriptResource->_properties.set(kDebugInventoryItems[_choiceIndex].propertyId, false);
} else {
_vm->startCursorHoldingObject(kDebugInventoryItems[_choiceIndex].objectId,
kDebugInventoryItems[_choiceIndex].sequenceId);
_vm->_scriptResource->_properties.set(kDebugInventoryItems[_choiceIndex].propertyId, true);
}
_menuSystem->leaveMenu();
}
MenuActionUpdateSlider::MenuActionUpdateSlider(BaseMenuSystem *menuSystem, BaseMenu *baseMenu, SliderActionType type, IllusionsEngine_Duckman *vm)
: BaseMenuAction(menuSystem), menu(baseMenu), _type(type), _vm(vm) {
_menuItem = nullptr;
}
void MenuActionUpdateSlider::execute() {
assert(_menuItem);
Common::String text = _menuItem->getText();
Common::Point point = _menuItem->getMouseClickPoint();
int offset = 0;
_menuSystem->calcMenuItemTextPositionAtPoint(point, offset);
int newSliderValue = calcNewSliderValue(offset);
debug(0, "item text: %s, (%d, %d), New slider value: %d", text.c_str(), point.x, point.y, newSliderValue);
setSliderValue(newSliderValue);
}
int MenuActionUpdateSlider::calcNewSliderValue(int newOffset) {
Common::String text = _menuItem->getText();
int newSliderValue = 0;
int start = 0;
int end = 0;
int currentPosition = 0;
for (uint i = 0; i < text.size(); i++) {
switch (text[i]) {
case '{' : start = i; break;
case '}' : end = i; break;
case '|' : currentPosition = i; break;
default: break;
}
}
if (newOffset >= start && newOffset <= end) {
if (newOffset == start) {
newSliderValue = 0;
} else if (newOffset == end) {
newSliderValue = 15;
} else {
newSliderValue = newOffset - (start + 1);
}
return newSliderValue;
}
return currentPosition - start - 1;
}
void MenuActionUpdateSlider::setSliderValue(uint8 newValue) {
int start = 0;
Common::String text = _menuItem->getText();
for (uint i = 0; i < text.size(); i++) {
switch (text[i]) {
case '{' : start = i; break;
case '|' : text.setChar('~', i); break;
default: break;
}
}
text.setChar('|', start + newValue + 1);
_menuItem->setText(text);
_menuSystem->redrawMenuText(menu);
switch (_type) {
case SFX : _vm->_soundMan->setSfxVolume(newValue * (256/15)); break;
case MUSIC : _vm->_soundMan->setMusicVolume(newValue * (256/15)); break;
case VOICE : _vm->_soundMan->setSpeechVolume(newValue * (256/15)); break;
case TEXT_DURATION : _vm->setSubtitleDuration(newValue * (256/15)); break;
default: break;
}
}
MenuActionResetOptionSliders::MenuActionResetOptionSliders(BaseMenuSystem *menuSystem,
MenuActionUpdateSlider *sfxSlider,
MenuActionUpdateSlider *musicSlider,
MenuActionUpdateSlider *speechSlider,
MenuActionUpdateSlider *textDurationSlider)
: BaseMenuAction(menuSystem), _sfxSlider(sfxSlider), _musicSlider(musicSlider),
_speechSlider(speechSlider), _textDurationSlider(textDurationSlider) {
}
void MenuActionResetOptionSliders::execute() {
_sfxSlider->setSliderValue(11);
_musicSlider->setSliderValue(11);
_speechSlider->setSliderValue(15);
_textDurationSlider->setSliderValue(0);
}
} // End of namespace Illusions

View File

@@ -0,0 +1,135 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_DUCKMAN_MENUSYSTEM_DUCKMAN_H
#define ILLUSIONS_DUCKMAN_MENUSYSTEM_DUCKMAN_H
#include "illusions/menusystem.h"
namespace Illusions {
enum SliderActionType {
SFX,
MUSIC,
VOICE,
TEXT_DURATION
};
enum {
kDuckmanMainMenu,
kDuckmanMainMenuDemo,
kDuckmanLoadGameMenu,
kDuckmanLoadGameFailedMenu,
kDuckmanOptionsMenu,
kDuckmanPauseMenu,
kDuckmanDebugMenu,
kDuckmanDebugPauseMenu,
kDuckmanAddRemoveInventoryMenu,
kDuckmanQueryQuitMenu,
kDuckmanQueryRestartMenu,
kDuckmanSaveCompleteMenu,
kDuckmanLastMenuIndex
};
class IllusionsEngine_Duckman;
class MenuActionUpdateSlider;
class DuckmanMenuSystem : public BaseMenuSystem {
public:
DuckmanMenuSystem(IllusionsEngine_Duckman *vm);
~DuckmanMenuSystem() override;
void runMenu(MenuChoiceOffsets menuChoiceOffsets, int16 *menuChoiceOffset,
uint32 menuId, uint32 duration, uint timeOutMenuChoiceIndex, uint32 menuCallerThreadId);
public://protected:
IllusionsEngine_Duckman *_vm;
BaseMenu *_menus[kDuckmanLastMenuIndex];
void clearMenus();
void freeMenus();
BaseMenu *getMenuById(int menuId) override;
BaseMenu *createMenuById(int menuId);
BaseMenu *createMainMenu();
BaseMenu *createMainMenuDemo();
BaseMenu *createLoadGameMenu();
BaseMenu *createLoadGameFailedMenu();
BaseMenu *createOptionsMenu();
BaseMenu *createPauseMenu();
BaseMenu *createQueryRestartMenu();
BaseMenu *createQueryQuitMenu();
BaseMenu *createSaveCompleteMenu();
BaseMenu *createDebugMenu();
BaseMenu *createDebugPauseMenu();
BaseMenu *createAddRemoveInventoryMenu();
int convertRootMenuId(uint32 menuId);
bool initMenuCursor() override;
int getGameState() override;
void setGameState(int gameState) override;
void setMenuCursorNum(int cursorNum) override;
void playSoundEffect(int sfxId) override;
private:
MenuItem *createOptionsSliderMenuItem(MenuActionUpdateSlider **action, const Common::String &text,
SliderActionType type, BaseMenu *baseMenu);
};
class MenuActionInventoryAddRemove : public BaseMenuAction {
public:
MenuActionInventoryAddRemove(BaseMenuSystem *menuSystem, IllusionsEngine_Duckman *vm, uint choiceIndex);
void execute() override;
protected:
IllusionsEngine_Duckman *_vm;
int _choiceIndex;
};
class MenuActionUpdateSlider : public BaseMenuAction {
public:
MenuActionUpdateSlider(BaseMenuSystem *menuSystem, BaseMenu *baseMenu, SliderActionType type, IllusionsEngine_Duckman *vm);
void setMenuItem(MenuItem *menuItem) {
_menuItem = menuItem;
}
void execute() override;
void setSliderValue(uint8 newValue);
protected:
IllusionsEngine_Duckman *_vm;
SliderActionType _type;
MenuItem *_menuItem;
BaseMenu *menu;
int calcNewSliderValue(int newOffset);
};
class MenuActionResetOptionSliders : public BaseMenuAction {
public:
MenuActionResetOptionSliders(BaseMenuSystem *menuSystem,
MenuActionUpdateSlider *sfxSlider,
MenuActionUpdateSlider *musicSlider,
MenuActionUpdateSlider *speechSlider,
MenuActionUpdateSlider *textDurationSlider
);
void execute() override;
protected:
MenuActionUpdateSlider *_sfxSlider;
MenuActionUpdateSlider *_musicSlider;
MenuActionUpdateSlider *_speechSlider;
MenuActionUpdateSlider *_textDurationSlider;
};
} // End of namespace Illusions
#endif // ILLUSIONS_DUCKMAN_MENUSYSTEM_DUCKMAN_H

View File

@@ -0,0 +1,130 @@
/* 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/>.
*
*/
#include "illusions/duckman/illusions_duckman.h"
#include "illusions/duckman/propertytimers.h"
#include "illusions/resources/scriptresource.h"
#include "illusions/time.h"
#include "illusions/updatefunctions.h"
#include "engines/util.h"
namespace Illusions {
// PropertyTimers
PropertyTimers::PropertyTimers(IllusionsEngine_Duckman *vm) {
_vm = vm;
_propertyTimersActive = false;
_propertyTimersPaused = false;
}
PropertyTimers::~PropertyTimers() {
}
void PropertyTimers::addPropertyTimer(uint32 propertyId) {
PropertyTimer *propertyTimer;
if (findPropertyTimer(propertyId, propertyTimer) || findPropertyTimer(0, propertyTimer)) {
propertyTimer->_propertyId = propertyId;
propertyTimer->_startTime = 0;
propertyTimer->_duration = 0;
propertyTimer->_endTime = 0;
}
}
void PropertyTimers::setPropertyTimer(uint32 propertyId, uint32 duration) {
PropertyTimer *propertyTimer;
if (findPropertyTimer(propertyId, propertyTimer)) {
propertyTimer->_startTime = getCurrentTime();
propertyTimer->_duration = duration;
propertyTimer->_endTime = duration + propertyTimer->_startTime;
}
_vm->_scriptResource->_properties.set(propertyId, false);
if (!_propertyTimersActive) {
_vm->_updateFunctions->add(29, _vm->getCurrentScene(), new Common::Functor1Mem<uint, int, PropertyTimers>
(this, &PropertyTimers::updatePropertyTimers));
_propertyTimersActive = true;
}
}
void PropertyTimers::removePropertyTimer(uint32 propertyId) {
PropertyTimer *propertyTimer;
if (findPropertyTimer(propertyId, propertyTimer))
propertyTimer->_propertyId = 0;
_vm->_scriptResource->_properties.set(propertyId, true);
}
bool PropertyTimers::findPropertyTimer(uint32 propertyId, PropertyTimer *&propertyTimer) {
for (uint i = 0; i < kPropertyTimersCount; ++i) {
if (_propertyTimers[i]._propertyId == propertyId) {
propertyTimer = &_propertyTimers[i];
return true;
}
}
return false;
}
int PropertyTimers::updatePropertyTimers(uint flags) {
int result = kUFNext;
uint32 currTime = getCurrentTime();
if (_vm->_pauseCtr <= 0) {
if (_propertyTimersPaused) {
for (uint i = 0; i < kPropertyTimersCount; ++i) {
PropertyTimer &propertyTimer = _propertyTimers[i];
propertyTimer._startTime = currTime;
propertyTimer._endTime = currTime + propertyTimer._duration;
}
_propertyTimersPaused = false;
}
if (flags & 1) {
_propertyTimersActive = false;
_propertyTimersPaused = false;
result = kUFTerminate;
} else {
bool timersActive = false;
for (uint i = 0; i < kPropertyTimersCount; ++i) {
PropertyTimer &propertyTimer = _propertyTimers[i];
if (propertyTimer._propertyId) {
timersActive = true;
if (!_vm->_scriptResource->_properties.get(propertyTimer._propertyId) &&
isTimerExpired(propertyTimer._startTime, propertyTimer._endTime))
_vm->_scriptResource->_properties.set(propertyTimer._propertyId, true);
}
}
if (!timersActive) {
_propertyTimersActive = false;
_propertyTimersPaused = false;
result = kUFTerminate;
}
}
} else {
if (!_propertyTimersPaused) {
for (uint i = 0; i < kPropertyTimersCount; ++i) {
PropertyTimer &propertyTimer = _propertyTimers[i];
propertyTimer._duration -= getDurationElapsed(propertyTimer._startTime, propertyTimer._endTime);
}
_propertyTimersPaused = true;
}
result = kUFNext;
}
return result;
}
} // End of namespace Illusions

View File

@@ -0,0 +1,62 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_DUCKMAN_PROPERTYTIMERS_H
#define ILLUSIONS_DUCKMAN_PROPERTYTIMERS_H
#include "illusions/illusions.h"
#include "common/algorithm.h"
#include "common/stack.h"
namespace Illusions {
class IllusionsEngine_Duckman;
struct PropertyTimer {
uint32 _propertyId;
uint32 _startTime;
uint32 _duration;
uint32 _endTime;
PropertyTimer() : _propertyId(0) {}
};
const uint kPropertyTimersCount = 6;
class PropertyTimers {
public:
PropertyTimers(IllusionsEngine_Duckman *vm);
~PropertyTimers();
private:
IllusionsEngine_Duckman *_vm;
PropertyTimer _propertyTimers[kPropertyTimersCount];
bool _propertyTimersActive;
bool _propertyTimersPaused;
public:
void addPropertyTimer(uint32 propertyId);
void setPropertyTimer(uint32 propertyId, uint32 duration);
void removePropertyTimer(uint32 propertyId);
bool findPropertyTimer(uint32 propertyId, PropertyTimer *&propertyTimer);
int updatePropertyTimers(uint flags);
};
} // End of namespace Illusions
#endif // ILLUSIONS_ILLUSIONS_H

View File

@@ -0,0 +1,868 @@
/* 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/>.
*
*/
#include "illusions/duckman/illusions_duckman.h"
#include "illusions/duckman/scriptopcodes_duckman.h"
#include "illusions/duckman/duckman_dialog.h"
#include "illusions/duckman/menusystem_duckman.h"
#include "illusions/actor.h"
#include "illusions/camera.h"
#include "illusions/dictionary.h"
#include "illusions/input.h"
#include "illusions/menusystem.h"
#include "illusions/resources/scriptresource.h"
#include "illusions/resources/talkresource.h"
#include "illusions/screen.h"
#include "illusions/scriptstack.h"
#include "illusions/sound.h"
#include "illusions/specialcode.h"
#include "illusions/threads/scriptthread.h"
namespace Illusions {
// ScriptOpcodes_Duckman
ScriptOpcodes_Duckman::ScriptOpcodes_Duckman(IllusionsEngine_Duckman *vm)
: ScriptOpcodes(vm), _vm(vm) {
initOpcodes();
}
ScriptOpcodes_Duckman::~ScriptOpcodes_Duckman() {
freeOpcodes();
}
typedef Common::Functor2Mem<ScriptThread*, OpCall&, void, ScriptOpcodes_Duckman> ScriptOpcodeI;
#define OPCODE(op, func) \
_opcodes[op] = new ScriptOpcodeI(this, &ScriptOpcodes_Duckman::func); \
_opcodeNames[op] = #func;
void ScriptOpcodes_Duckman::initOpcodes() {
// First clear everything
for (uint i = 0; i < 256; ++i) {
_opcodes[i] = nullptr;
}
// Register opcodes
OPCODE(1, opNop);
OPCODE(2, opSuspend);
OPCODE(3, opYield);
OPCODE(4, opTerminate);
OPCODE(5, opJump);
OPCODE(6, opStartScriptThread);
OPCODE(7, opStartTimerThread);
OPCODE(8, opRerunThreads);
OPCODE(9, opNotifyThread);
OPCODE(10, opSuspendThread);
// 11-15 unused
OPCODE(16, opLoadResource);
OPCODE(17, opUnloadResource);
OPCODE(18, opEnterScene18);
OPCODE(19, opUnloadResourcesBySceneId);
OPCODE(20, opChangeScene);
OPCODE(21, opResumeFromSavegame);
OPCODE(22, opStartModalScene);
OPCODE(23, opExitModalScene);
OPCODE(24, opEnterScene24);
OPCODE(25, opLeaveScene24);
OPCODE(26, opEnterDebugger);
OPCODE(27, opLeaveDebugger);
OPCODE(28, opDumpCurrentSceneFiles);
// 29-31 unused
OPCODE(32, opPanCenterObject);
OPCODE(33, opPanTrackObject);
OPCODE(34, opPanToObject);
OPCODE(35, opPanToNamedPoint);
OPCODE(36, opPanToPoint);
OPCODE(37, opPanStop);
OPCODE(38, opStartFade);
OPCODE(39, opSetDisplay);
OPCODE(40, opSetCameraBounds);
// 41-47 unused
OPCODE(48, opSetProperty);
OPCODE(49, opPlaceActor);
OPCODE(50, opFaceActor);
OPCODE(51, opFaceActorToObject);
OPCODE(52, opStartSequenceActor);
OPCODE(53, opStartSequenceActorAtPosition);
OPCODE(54, opStartMoveActor);
OPCODE(55, opStartMoveActorToObject);
OPCODE(56, opStartTalkThread);
OPCODE(57, opAppearActor);
OPCODE(58, opDisappearActor);
OPCODE(59, opActivateObject);
OPCODE(60, opDeactivateObject);
OPCODE(61, opSetDefaultSequence);
// 62-63 unused
OPCODE(64, opStopCursorHoldingObject);
OPCODE(65, opStartCursorHoldingObject);
OPCODE(66, opPlayVideo);
// 67-68 unused
OPCODE(69, opRunSpecialCode);
OPCODE(70, opPause);
OPCODE(71, opUnpause);
OPCODE(72, opStartSound);
OPCODE(73, opStartSoundAtPosition);
// 74 unused
OPCODE(75, opStopSound);
OPCODE(76, opStartMidiMusic);
OPCODE(77, opStopMidiMusic);
OPCODE(78, opFadeMidiMusic);
// 79 unused
OPCODE(80, opAddMenuChoice);
OPCODE(81, opDisplayMenu);
OPCODE(82, opSwitchMenuChoice);
OPCODE(83, opQuitGame);
OPCODE(84, opResetGame);
OPCODE(85, opLoadGame);
OPCODE(86, opSaveGame);
OPCODE(87, opDeactivateButton);
OPCODE(88, opActivateButton);
// 89-95 unused
OPCODE(96, opIncBlockCounter);
OPCODE(97, opClearBlockCounter);
// 98-99 unused
OPCODE(100, opStackPushRandom);
OPCODE(101, opStackSwitchRandom);
// 102-103 unused
OPCODE(104, opJumpIf);
OPCODE(105, opIsPrevSceneId);
OPCODE(106, opNot);
OPCODE(107, opAnd);
OPCODE(108, opOr);
OPCODE(109, opGetProperty);
OPCODE(110, opCompareBlockCounter);
// 111 unused
OPCODE(112, opAddDialogItem);
OPCODE(113, opStartDialog);
OPCODE(114, opJumpToDialogChoice);
OPCODE(115, opSetBlockCounter115);
OPCODE(116, opSetBlockCounter116);
OPCODE(117, opSetBlockCounter117);
OPCODE(118, opSetBlockCounter118);
// 119-125 unused
OPCODE(126, opDebug126);
OPCODE(127, opDebug127);
}
#undef OPCODE
void ScriptOpcodes_Duckman::freeOpcodes() {
for (uint i = 0; i < 256; ++i) {
delete _opcodes[i];
}
}
// Opcodes
void ScriptOpcodes_Duckman::opNop(ScriptThread *scriptThread, OpCall &opCall) {
}
void ScriptOpcodes_Duckman::opSuspend(ScriptThread *scriptThread, OpCall &opCall) {
opCall._result = kTSSuspend;
}
void ScriptOpcodes_Duckman::opYield(ScriptThread *scriptThread, OpCall &opCall) {
opCall._result = kTSYield;
}
void ScriptOpcodes_Duckman::opTerminate(ScriptThread *scriptThread, OpCall &opCall) {
opCall._result = kTSTerminate;
}
void ScriptOpcodes_Duckman::opJump(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(jumpOffs);
opCall._deltaOfs += jumpOffs;
}
void ScriptOpcodes_Duckman::opStartScriptThread(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(threadId);
_vm->startScriptThread(threadId, opCall._threadId);
}
void ScriptOpcodes_Duckman::opStartTimerThread(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(isAbortable);
ARG_INT16(duration);
ARG_INT16(maxDuration);
if (maxDuration)
duration += _vm->getRandom(maxDuration);
//duration = 1;//DEBUG Speeds up things
//duration = 5;
//debug("duration: %d", duration);
if (isAbortable)
_vm->startAbortableTimerThread(duration, opCall._callerThreadId);
else
_vm->startTimerThread(duration, opCall._callerThreadId);
}
void ScriptOpcodes_Duckman::opRerunThreads(ScriptThread *scriptThread, OpCall &opCall) {
_vm->_rerunThreads = true;
}
void ScriptOpcodes_Duckman::opNotifyThread(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(threadId);
_vm->_threads->notifyId(threadId);
_vm->_threads->notifyTimerThreads(threadId);
}
void ScriptOpcodes_Duckman::opSuspendThread(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(threadId);
_vm->_threads->suspendId(threadId);
_vm->_threads->suspendTimerThreads(threadId);
}
void ScriptOpcodes_Duckman::opLoadResource(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(resourceId);
uint32 sceneId = _vm->getCurrentScene();
_vm->_resSys->loadResource(resourceId, sceneId, opCall._threadId);
_vm->notifyThreadId(opCall._threadId);
}
void ScriptOpcodes_Duckman::opUnloadResource(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(resourceId);
_vm->_resSys->unloadResourceById(resourceId);
}
void ScriptOpcodes_Duckman::opEnterScene18(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(sceneId);
_vm->enterScene(sceneId, 0);
}
void ScriptOpcodes_Duckman::opUnloadResourcesBySceneId(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(sceneId);
_vm->_resSys->unloadResourcesBySceneId(sceneId);
}
//static uint dsceneId = 0, dthreadId = 0;
//static uint dsceneId = 0x00010008, dthreadId = 0x00020029;//Beginning in Jac
//static uint dsceneId = 0x0001000A, dthreadId = 0x00020043;//Home front
//static uint dsceneId = 0x0001000E, dthreadId = 0x0002007C;
//static uint dsceneId = 0x00010012, dthreadId = 0x0002009D;//Paramount
//static uint dsceneId = 0x00010020, dthreadId = 0x00020112;//Xmas
//static uint dsceneId = 0x00010021, dthreadId = 0x00020113;
//static uint dsceneId = 0x00010022, dthreadId = 0x00020114;
//static uint dsceneId = 0x0001002D, dthreadId = 0x00020141;
//static uint dsceneId = 0x00010033, dthreadId = 0x000201A4;//Chinese
//static uint dsceneId = 0x00010036, dthreadId = 0x000201B5;
//static uint dsceneId = 0x00010039, dthreadId = 0x00020089;//Map
//static uint dsceneId = 0x0001003D, dthreadId = 0x000201E0;
//static uint dsceneId = 0x0001004B, dthreadId = 0x0002029B;
//static uint dsceneId = 0x0001005B, dthreadId = 0x00020341;
//static uint dsceneId = 0x00010010, dthreadId = 0x0002008A;
//static uint dsceneId = 0x10002, dthreadId = 0x20001;//Debug menu, not supported
//static uint dsceneId = 0x10035, dthreadId = 0x000201B4; // Starship Enterprise (outside)
//static uint dsceneId = 0x10044, dthreadId = 0x000202B8; // Starship Enterprise
//static uint dsceneId = 0x00010039, dthreadId = 0x00020089; // Map
//static uint dsceneId = 0x00010052, dthreadId = 0x00020347; // Credits
void ScriptOpcodes_Duckman::opChangeScene(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(sceneId);
ARG_UINT32(threadId);
_vm->_input->discardAllEvents();
debug(1, "changeScene(%08X, %08X)", sceneId, threadId);
//DEBUG
#if 0
if (dsceneId) {
sceneId = dsceneId;
threadId = dthreadId;
dsceneId = 0;
}
#endif
if (_vm->_scriptResource->_properties.get(31)) {
_vm->changeScene(0x10002, 0x20001, opCall._callerThreadId);
} else {
_vm->changeScene(sceneId, threadId, opCall._callerThreadId);
}
}
void ScriptOpcodes_Duckman::opResumeFromSavegame(ScriptThread *scriptThread, OpCall &opCall) {
_vm->resumeFromSavegame(opCall._callerThreadId);
}
void ScriptOpcodes_Duckman::opStartModalScene(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(sceneId);
ARG_UINT32(threadId);
_vm->_input->discardAllEvents();
_vm->enterPause(_vm->getCurrentScene(), opCall._callerThreadId);
_vm->_talkItems->pauseBySceneId(_vm->getCurrentScene());
_vm->enterScene(sceneId, threadId);
opCall._result = kTSSuspend;
}
void ScriptOpcodes_Duckman::opExitModalScene(ScriptThread *scriptThread, OpCall &opCall) {
_vm->_input->discardAllEvents();
if (_vm->_scriptResource->_properties.get(0x000E0027)) {
// NOTE This would switch to the debug menu which is not currently supported
_vm->startScriptThread2(0x10002, 0x20001, 0);
opCall._result = kTSTerminate;
} else {
_vm->dumpCurrSceneFiles(_vm->getCurrentScene(), opCall._callerThreadId);
_vm->exitScene();
_vm->leavePause(_vm->getCurrentScene(), opCall._callerThreadId);
_vm->_talkItems->unpauseBySceneId(_vm->getCurrentScene());
}
}
void ScriptOpcodes_Duckman::opEnterScene24(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(sceneId);
_vm->_input->discardAllEvents();
_vm->enterPause(_vm->getCurrentScene(), opCall._callerThreadId);
_vm->enterScene(sceneId, 0);
}
void ScriptOpcodes_Duckman::opLeaveScene24(ScriptThread *scriptThread, OpCall &opCall) {
_vm->_input->discardAllEvents();
_vm->dumpCurrSceneFiles(_vm->getCurrentScene(), opCall._callerThreadId);
_vm->exitScene();
_vm->leavePause(_vm->getCurrentScene(), opCall._callerThreadId);
}
void ScriptOpcodes_Duckman::opEnterDebugger(ScriptThread *scriptThread, OpCall &opCall) {
// Used for debugging purposes in the original engine
// This is not supported and only reachable by code not implemented here!
//error("ScriptOpcodes_Duckman::opEnterDebugger() Debugger function called");
_vm->_controls->disappearActors();
// TODO more logic needed here
}
void ScriptOpcodes_Duckman::opLeaveDebugger(ScriptThread *scriptThread, OpCall &opCall) {
// See opEnterDebugger
//error("ScriptOpcodes_Duckman::opLeaveDebugger() Debugger function called");
_vm->_controls->appearActors();
// TODO more logic needed here
}
void ScriptOpcodes_Duckman::opDumpCurrentSceneFiles(ScriptThread *scriptThread, OpCall &opCall) {
_vm->dumpCurrSceneFiles(_vm->getCurrentScene(), opCall._callerThreadId);
}
void ScriptOpcodes_Duckman::opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(speed);
ARG_UINT32(objectId);
_vm->_camera->panCenterObject(objectId, speed);
}
void ScriptOpcodes_Duckman::opPanTrackObject(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(objectId);
_vm->_camera->panTrackObject(objectId);
}
void ScriptOpcodes_Duckman::opPanToObject(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(speed);
ARG_UINT32(objectId);
Control *control = _vm->_dict->getObjectControl(objectId);
Common::Point pos = control->getActorPosition();
_vm->_camera->panToPoint(pos, speed, opCall._threadId);
}
void ScriptOpcodes_Duckman::opPanToNamedPoint(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(speed);
ARG_UINT32(namedPointId);
Common::Point pos = _vm->getNamedPointPosition(namedPointId);
_vm->_camera->panToPoint(pos, speed, opCall._threadId);
}
void ScriptOpcodes_Duckman::opPanToPoint(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(speed);
ARG_INT16(x);
ARG_INT16(y);
_vm->_camera->panToPoint(Common::Point(x, y), speed, opCall._threadId);
}
void ScriptOpcodes_Duckman::opPanStop(ScriptThread *scriptThread, OpCall &opCall) {
_vm->_camera->stopPan();
}
void ScriptOpcodes_Duckman::opStartFade(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(duration);
ARG_INT16(minValue);
ARG_INT16(maxValue);
ARG_INT16(firstIndex);
ARG_INT16(lastIndex);
_vm->startFader(duration, minValue, maxValue, firstIndex, lastIndex, opCall._threadId);
}
void ScriptOpcodes_Duckman::opSetDisplay(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(flag);
_vm->_screen->setDisplayOn(flag != 0);
}
void ScriptOpcodes_Duckman::opSetCameraBounds(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_INT16(x1);
ARG_INT16(y1);
ARG_INT16(x2);
ARG_INT16(y2);
_vm->_camera->setBounds(Common::Point(x1, y1), Common::Point(x2, y2));
}
void ScriptOpcodes_Duckman::opSetProperty(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(value);
ARG_UINT32(propertyId);
_vm->_scriptResource->_properties.set(propertyId, value != 0);
}
void ScriptOpcodes_Duckman::opPlaceActor(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(objectId);
ARG_UINT32(sequenceId);
ARG_UINT32(namedPointId);
Common::Point pos = _vm->getNamedPointPosition(namedPointId);
uint32 actorTypeId = _vm->getObjectActorTypeId(objectId);
_vm->_controls->placeActor(actorTypeId, pos, sequenceId, objectId, opCall._threadId);
}
void ScriptOpcodes_Duckman::opFaceActor(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(facing);
ARG_UINT32(objectId);
Control *control = _vm->_dict->getObjectControl(objectId);
control->faceActor(facing);
}
void ScriptOpcodes_Duckman::opFaceActorToObject(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(objectId2);
ARG_UINT32(objectId1);
Control *control1 = _vm->_dict->getObjectControl(objectId1);
Control *control2 = _vm->_dict->getObjectControl(objectId2);
Common::Point pos1 = control1->getActorPosition();
Common::Point pos2 = control2->getActorPosition();
uint facing;
if (_vm->calcPointDirection(pos1, pos2, facing))
control1->faceActor(facing);
}
void ScriptOpcodes_Duckman::opStartSequenceActor(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(objectId);
ARG_UINT32(sequenceId);
Control *control = _vm->_dict->getObjectControl(objectId);
control->startSequenceActor(sequenceId, 2, opCall._threadId);
}
void ScriptOpcodes_Duckman::opStartSequenceActorAtPosition(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(objectId);
ARG_UINT32(sequenceId);
ARG_UINT32(namedPointId);
Common::Point pos = _vm->getNamedPointPosition(namedPointId);
Control *control = _vm->_dict->getObjectControl(objectId);
control->setActorPosition(pos);
control->startSequenceActor(sequenceId, 2, opCall._threadId);
}
void ScriptOpcodes_Duckman::opStartMoveActor(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(objectId);
ARG_UINT32(sequenceId);
ARG_UINT32(namedPointId);
Control *control = _vm->_dict->getObjectControl(objectId);
Common::Point pos = _vm->getNamedPointPosition(namedPointId);
control->startMoveActor(sequenceId, pos, opCall._callerThreadId, opCall._threadId);
}
void ScriptOpcodes_Duckman::opStartMoveActorToObject(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(objectId1);
ARG_UINT32(objectId2);
ARG_UINT32(sequenceId);
Control *control1 = _vm->_dict->getObjectControl(objectId1);
if (!control1) {
warning("opStartMoveActorToObject: Control1 not found for objectId: %08X", objectId1);
return;
}
Common::Point pos;
if (objectId2 == 0x40003) {
pos = _vm->_cursor._position;
} else {
Control *control2 = _vm->_dict->getObjectControl(objectId2);
pos = control2->_feetPt;
if (control2->_actor) {
pos.x += control2->_actor->_position.x;
pos.y += control2->_actor->_position.y;
}
}
control1->startMoveActor(sequenceId, pos, opCall._callerThreadId, opCall._threadId);
}
void ScriptOpcodes_Duckman::opStartTalkThread(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(objectId);
ARG_UINT32(talkId);
ARG_UINT32(sequenceId1);
ARG_UINT32(sequenceId2);
_vm->startTalkThread(objectId, talkId, sequenceId1, sequenceId2, opCall._threadId);
}
void ScriptOpcodes_Duckman::opAppearActor(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(objectId);
Control *control = _vm->_dict->getObjectControl(objectId);
if (!control) {
Common::Point pos = _vm->getNamedPointPosition(0x70001);
_vm->_controls->placeActor(0x50001, pos, 0x60001, objectId, 0);
control = _vm->_dict->getObjectControl(objectId);
}
control->appearActor();
}
void ScriptOpcodes_Duckman::opDisappearActor(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(objectId);
Control *control = _vm->_dict->getObjectControl(objectId);
control->disappearActor();
}
void ScriptOpcodes_Duckman::opActivateObject(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(objectId);
Control *control = _vm->_dict->getObjectControl(objectId);
if (control)
control->activateObject();
}
void ScriptOpcodes_Duckman::opDeactivateObject(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(objectId);
Control *control = _vm->_dict->getObjectControl(objectId);
control->deactivateObject();
}
void ScriptOpcodes_Duckman::opSetDefaultSequence(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(objectId);
ARG_UINT32(defaultSequenceId);
ARG_UINT32(sequenceId);
Control *control = _vm->_dict->getObjectControl(objectId);
control->_actor->_defaultSequences.set(sequenceId, defaultSequenceId);
}
void ScriptOpcodes_Duckman::opStopCursorHoldingObject(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(flags);
_vm->stopCursorHoldingObject();
if (!(flags & 1))
_vm->playSoundEffect(7);
}
void ScriptOpcodes_Duckman::opStartCursorHoldingObject(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(flags);
ARG_UINT32(objectId);
ARG_UINT32(sequenceId);
_vm->startCursorHoldingObject(objectId, sequenceId);
if (!(flags & 1))
_vm->playSoundEffect(6);
}
void ScriptOpcodes_Duckman::opPlayVideo(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(videoId);
#if 1 // NOTE DEBUG Set to 0 to skip videos
_vm->playVideo(videoId, opCall._threadId);
#else
//DEBUG Resume calling thread, later done by the video player
_vm->notifyThreadId(opCall._threadId);
#endif
}
void ScriptOpcodes_Duckman::opRunSpecialCode(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(specialCodeId);
_vm->_specialCode->run(specialCodeId, opCall);
}
void ScriptOpcodes_Duckman::opPause(ScriptThread *scriptThread, OpCall &opCall) {
_vm->pause(opCall._callerThreadId);
}
void ScriptOpcodes_Duckman::opUnpause(ScriptThread *scriptThread, OpCall &opCall) {
_vm->unpause(opCall._callerThreadId);
}
void ScriptOpcodes_Duckman::opStartSound(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(volume);
ARG_UINT32(soundEffectId);
_vm->_soundMan->playSound(soundEffectId, volume, 0);
}
void ScriptOpcodes_Duckman::opStartSoundAtPosition(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(volume);
ARG_UINT32(soundEffectId);
ARG_UINT32(namedPointId);
Common::Point pos = _vm->getNamedPointPosition(namedPointId);
int16 pan = _vm->convertPanXCoord(pos.x);
_vm->_soundMan->playSound(soundEffectId, volume, pan);
}
void ScriptOpcodes_Duckman::opStopSound(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(soundEffectId);
_vm->_soundMan->stopSound(soundEffectId);
}
void ScriptOpcodes_Duckman::opStartMidiMusic(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(musicId);
_vm->_soundMan->playMidiMusic(musicId);
}
void ScriptOpcodes_Duckman::opStopMidiMusic(ScriptThread *scriptThread, OpCall &opCall) {
_vm->_soundMan->stopMidiMusic();
}
void ScriptOpcodes_Duckman::opFadeMidiMusic(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(duration);
ARG_INT16(finalVolume);
_vm->_soundMan->fadeMidiMusic(finalVolume, duration, opCall._threadId);
}
void ScriptOpcodes_Duckman::opAddMenuChoice(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_INT16(jumpOffs);
ARG_INT16(endMarker);
_vm->_stack->push(endMarker);
_vm->_stack->push(jumpOffs);
}
void ScriptOpcodes_Duckman::opDisplayMenu(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(timeOutDuration);
ARG_UINT32(menuId);
ARG_UINT32(timeOutMenuChoiceIndex);
MenuChoiceOffsets menuChoiceOffsets;
// Load menu choices from the stack
do {
int16 choiceOffs = _vm->_stack->pop();
menuChoiceOffsets.push_back(choiceOffs);
} while (_vm->_stack->pop() == 0);
_vm->_menuSystem->runMenu(menuChoiceOffsets, &_vm->_menuChoiceOfs,
menuId, timeOutDuration, timeOutMenuChoiceIndex,
opCall._callerThreadId);
//DEBUG Resume calling thread, later done by the video player
//_vm->notifyThreadId(opCall._callerThreadId);
}
void ScriptOpcodes_Duckman::opSwitchMenuChoice(ScriptThread *scriptThread, OpCall &opCall) {
opCall._deltaOfs += _vm->_menuChoiceOfs;
}
void ScriptOpcodes_Duckman::opQuitGame(ScriptThread *scriptThread, OpCall &opCall) {
_vm->quitGame();
}
void ScriptOpcodes_Duckman::opResetGame(ScriptThread *scriptThread, OpCall &opCall) {
_vm->reset();
_vm->_soundMan->stopMidiMusic();
_vm->_soundMan->clearMidiMusicQueue();
}
void ScriptOpcodes_Duckman::opLoadGame(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_INT16(bankNum)
ARG_INT16(slotNum)
bool success = _vm->loadSavegameFromScript(slotNum, opCall._callerThreadId);
_vm->_stack->push(success ? 1 : 0);
}
void ScriptOpcodes_Duckman::opSaveGame(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_INT16(bankNum)
ARG_INT16(slotNum)
bool success = _vm->saveSavegameFromScript(slotNum, opCall._callerThreadId);
_vm->_stack->push(success ? 1 : 0);
}
void ScriptOpcodes_Duckman::opDeactivateButton(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(button)
_vm->_input->deactivateButton(button);
}
void ScriptOpcodes_Duckman::opActivateButton(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(button)
_vm->_input->activateButton(button);
}
void ScriptOpcodes_Duckman::opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(index);
byte value = _vm->_scriptResource->_blockCounters.get(index) + 1;
if (value <= 63)
_vm->_scriptResource->_blockCounters.set(index, value);
}
void ScriptOpcodes_Duckman::opClearBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(index);
_vm->_scriptResource->_blockCounters.set(index, 0);
}
void ScriptOpcodes_Duckman::opStackPushRandom(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(maxValue);
_vm->_stack->push(_vm->getRandom(maxValue) + 1);
}
void ScriptOpcodes_Duckman::opStackSwitchRandom(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_INT16(rvalue);
ARG_INT16(jumpOffs);
int16 lvalue = _vm->_stack->peek();
if (lvalue < rvalue) {
_vm->_stack->pop();
opCall._deltaOfs += jumpOffs;
}
}
void ScriptOpcodes_Duckman::opJumpIf(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(jumpOffs);
int16 value = _vm->_stack->pop();
if (value == 0)
opCall._deltaOfs += jumpOffs;
}
void ScriptOpcodes_Duckman::opIsPrevSceneId(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(sceneId);
_vm->_stack->push(_vm->getPrevScene() == sceneId ? 1 : 0);
}
void ScriptOpcodes_Duckman::opNot(ScriptThread *scriptThread, OpCall &opCall) {
int16 value = _vm->_stack->pop();
_vm->_stack->push(value != 0 ? 0 : 1);
}
void ScriptOpcodes_Duckman::opAnd(ScriptThread *scriptThread, OpCall &opCall) {
int16 value1 = _vm->_stack->pop();
int16 value2 = _vm->_stack->pop();
_vm->_stack->push(value1 & value2);
}
void ScriptOpcodes_Duckman::opOr(ScriptThread *scriptThread, OpCall &opCall) {
int16 value1 = _vm->_stack->pop();
int16 value2 = _vm->_stack->pop();
_vm->_stack->push(value1 | value2);
}
void ScriptOpcodes_Duckman::opGetProperty(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(propertyId)
bool value = _vm->_scriptResource->_properties.get(propertyId);
_vm->_stack->push(value ? 1 : 0);
}
void ScriptOpcodes_Duckman::opCompareBlockCounter(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(index);
ARG_INT16(compareOp);
ARG_INT16(rvalue);
int16 lvalue = _vm->_scriptResource->_blockCounters.get(index);
bool compareResult = false;
switch (compareOp) {
case 1:
compareResult = lvalue == rvalue;
break;
case 2:
compareResult = lvalue != rvalue;
break;
case 3:
compareResult = lvalue < rvalue;
break;
case 4:
compareResult = lvalue > rvalue;
break;
case 5:
compareResult = lvalue >= rvalue;
break;
case 6:
compareResult = lvalue <= rvalue;
break;
default:
break;
}
_vm->_stack->push(compareResult ? 1 : 0);
}
void ScriptOpcodes_Duckman::opAddDialogItem(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_INT16(index);
ARG_INT16(choiceJumpOffs);
ARG_UINT32(sequenceId);
if (index && (_vm->_scriptResource->_blockCounters.getC0(index) & 0x40))
_vm->_dialogSys->addDialogItem(choiceJumpOffs, sequenceId);
}
void ScriptOpcodes_Duckman::opStartDialog(ScriptThread *scriptThread, OpCall &opCall) {
ARG_SKIP(2);
ARG_UINT32(actorTypeId);
_vm->_dialogSys->startDialog(&_vm->_menuChoiceOfs, actorTypeId, opCall._callerThreadId);
}
void ScriptOpcodes_Duckman::opJumpToDialogChoice(ScriptThread *scriptThread, OpCall &opCall) {
opCall._deltaOfs += _vm->_menuChoiceOfs;
}
void ScriptOpcodes_Duckman::opSetBlockCounter115(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(index);
if (_vm->_scriptResource->_blockCounters.getC0(index) & 0x80)
_vm->_scriptResource->_blockCounters.set(index, 0);
_vm->_scriptResource->_blockCounters.setC0(index, 0x40);
}
void ScriptOpcodes_Duckman::opSetBlockCounter116(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(index);
if (!(_vm->_scriptResource->_blockCounters.getC0(index) & 0x80))
_vm->_scriptResource->_blockCounters.setC0(index, 0x40);
}
void ScriptOpcodes_Duckman::opSetBlockCounter117(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(index);
_vm->_scriptResource->_blockCounters.setC0(index, 0);
}
void ScriptOpcodes_Duckman::opSetBlockCounter118(ScriptThread *scriptThread, OpCall &opCall) {
ARG_INT16(index);
_vm->_scriptResource->_blockCounters.setC0(index, 0x40);
}
void ScriptOpcodes_Duckman::opDebug126(ScriptThread *scriptThread, OpCall &opCall) {
// NOTE Prints some debug text
debug(1, "[DBG126] %s", (char*)opCall._code);
}
void ScriptOpcodes_Duckman::opDebug127(ScriptThread *scriptThread, OpCall &opCall) {
// NOTE Prints some debug text
debug(1, "[DBG127] %s", (char*)opCall._code);
}
} // End of namespace Illusions

View File

@@ -0,0 +1,164 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_DUCKMAN_SCRIPTOPCODES_DUCKMAN_H
#define ILLUSIONS_DUCKMAN_SCRIPTOPCODES_DUCKMAN_H
#include "illusions/scriptopcodes.h"
#include "common/func.h"
namespace Illusions {
class IllusionsEngine_Duckman;
class ScriptThread;
class ScriptOpcodes_Duckman : public ScriptOpcodes {
public:
ScriptOpcodes_Duckman(IllusionsEngine_Duckman *vm);
~ScriptOpcodes_Duckman() override;
void initOpcodes() override;
void freeOpcodes() override;
protected:
IllusionsEngine_Duckman *_vm;
// Opcodes
void opNop(ScriptThread *scriptThread, OpCall &opCall);
void opSuspend(ScriptThread *scriptThread, OpCall &opCall);
void opYield(ScriptThread *scriptThread, OpCall &opCall);
void opTerminate(ScriptThread *scriptThread, OpCall &opCall);
void opJump(ScriptThread *scriptThread, OpCall &opCall);
void opStartScriptThread(ScriptThread *scriptThread, OpCall &opCall);
void opNotifyThread(ScriptThread *scriptThread, OpCall &opCall);
void opSuspendThread(ScriptThread *scriptThread, OpCall &opCall);
void opLoadResource(ScriptThread *scriptThread, OpCall &opCall);
void opUnloadResource(ScriptThread *scriptThread, OpCall &opCall);
void opStartTimerThread(ScriptThread *scriptThread, OpCall &opCall);
void opRerunThreads(ScriptThread *scriptThread, OpCall &opCall);
void opEnterScene18(ScriptThread *scriptThread, OpCall &opCall);
void opUnloadResourcesBySceneId(ScriptThread *scriptThread, OpCall &opCall);
void opChangeScene(ScriptThread *scriptThread, OpCall &opCall);
void opResumeFromSavegame(ScriptThread *scriptThread, OpCall &opCall);
void opStartModalScene(ScriptThread *scriptThread, OpCall &opCall);
void opExitModalScene(ScriptThread *scriptThread, OpCall &opCall);
void opEnterScene24(ScriptThread *scriptThread, OpCall &opCall);
void opLeaveScene24(ScriptThread *scriptThread, OpCall &opCall);
void opEnterDebugger(ScriptThread *scriptThread, OpCall &opCall);
void opLeaveDebugger(ScriptThread *scriptThread, OpCall &opCall);
void opDumpCurrentSceneFiles(ScriptThread *scriptThread, OpCall &opCall);
void opPanCenterObject(ScriptThread *scriptThread, OpCall &opCall);
void opPanTrackObject(ScriptThread *scriptThread, OpCall &opCall);
void opPanToObject(ScriptThread *scriptThread, OpCall &opCall);
void opPanToNamedPoint(ScriptThread *scriptThread, OpCall &opCall);
void opPanToPoint(ScriptThread *scriptThread, OpCall &opCall);
void opPanStop(ScriptThread *scriptThread, OpCall &opCall);
void opStartFade(ScriptThread *scriptThread, OpCall &opCall);
void opSetDisplay(ScriptThread *scriptThread, OpCall &opCall);
void opSetCameraBounds(ScriptThread *scriptThread, OpCall &opCall);
void opSetProperty(ScriptThread *scriptThread, OpCall &opCall);
void opPlaceActor(ScriptThread *scriptThread, OpCall &opCall);
void opFaceActor(ScriptThread *scriptThread, OpCall &opCall);
void opFaceActorToObject(ScriptThread *scriptThread, OpCall &opCall);
void opStartSequenceActor(ScriptThread *scriptThread, OpCall &opCall);
void opStartSequenceActorAtPosition(ScriptThread *scriptThread, OpCall &opCall);
void opStartMoveActor(ScriptThread *scriptThread, OpCall &opCall);
void opStartMoveActorToObject(ScriptThread *scriptThread, OpCall &opCall);
void opStartTalkThread(ScriptThread *scriptThread, OpCall &opCall);
void opAppearActor(ScriptThread *scriptThread, OpCall &opCall);
void opDisappearActor(ScriptThread *scriptThread, OpCall &opCall);
void opActivateObject(ScriptThread *scriptThread, OpCall &opCall);
void opDeactivateObject(ScriptThread *scriptThread, OpCall &opCall);
void opSetDefaultSequence(ScriptThread *scriptThread, OpCall &opCall);
void opStopCursorHoldingObject(ScriptThread *scriptThread, OpCall &opCall);
void opStartCursorHoldingObject(ScriptThread *scriptThread, OpCall &opCall);
void opPlayVideo(ScriptThread *scriptThread, OpCall &opCall);
void opRunSpecialCode(ScriptThread *scriptThread, OpCall &opCall);
void opPause(ScriptThread *scriptThread, OpCall &opCall);
void opUnpause(ScriptThread *scriptThread, OpCall &opCall);
void opStartSound(ScriptThread *scriptThread, OpCall &opCall);
void opStartSoundAtPosition(ScriptThread *scriptThread, OpCall &opCall);
void opStopSound(ScriptThread *scriptThread, OpCall &opCall);
void opStartMidiMusic(ScriptThread *scriptThread, OpCall &opCall);
void opStopMidiMusic(ScriptThread *scriptThread, OpCall &opCall);
void opFadeMidiMusic(ScriptThread *scriptThread, OpCall &opCall);
void opAddMenuChoice(ScriptThread *scriptThread, OpCall &opCall);
void opDisplayMenu(ScriptThread *scriptThread, OpCall &opCall);
void opSwitchMenuChoice(ScriptThread *scriptThread, OpCall &opCall);
void opQuitGame(ScriptThread *scriptThread, OpCall &opCall);
void opResetGame(ScriptThread *scriptThread, OpCall &opCall);
void opLoadGame(ScriptThread *scriptThread, OpCall &opCall);
void opSaveGame(ScriptThread *scriptThread, OpCall &opCall);
void opDeactivateButton(ScriptThread *scriptThread, OpCall &opCall);
void opActivateButton(ScriptThread *scriptThread, OpCall &opCall);
void opIncBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
void opClearBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
void opStackPushRandom(ScriptThread *scriptThread, OpCall &opCall);
void opStackSwitchRandom(ScriptThread *scriptThread, OpCall &opCall);
void opJumpIf(ScriptThread *scriptThread, OpCall &opCall);
void opIsPrevSceneId(ScriptThread *scriptThread, OpCall &opCall);
void opNot(ScriptThread *scriptThread, OpCall &opCall);
void opAnd(ScriptThread *scriptThread, OpCall &opCall);
void opOr(ScriptThread *scriptThread, OpCall &opCall);
void opGetProperty(ScriptThread *scriptThread, OpCall &opCall);
void opCompareBlockCounter(ScriptThread *scriptThread, OpCall &opCall);
void opAddDialogItem(ScriptThread *scriptThread, OpCall &opCall);
void opStartDialog(ScriptThread *scriptThread, OpCall &opCall);
void opJumpToDialogChoice(ScriptThread *scriptThread, OpCall &opCall);
void opSetBlockCounter115(ScriptThread *scriptThread, OpCall &opCall);
void opSetBlockCounter116(ScriptThread *scriptThread, OpCall &opCall);
void opSetBlockCounter117(ScriptThread *scriptThread, OpCall &opCall);
void opSetBlockCounter118(ScriptThread *scriptThread, OpCall &opCall);
void opDebug126(ScriptThread *scriptThread, OpCall &opCall);
void opDebug127(ScriptThread *scriptThread, OpCall &opCall);
#if 0
void opStartTempScriptThread(ScriptThread *scriptThread, OpCall &opCall);
void opSetThreadSceneId(ScriptThread *scriptThread, OpCall &opCall);
void opEndTalkThreads(ScriptThread *scriptThread, OpCall &opCall);
void opEnterScene(ScriptThread *scriptThread, OpCall &opCall);
void opEnterCloseUpScene(ScriptThread *scriptThread, OpCall &opCall);
void opExitCloseUpScene(ScriptThread *scriptThread, OpCall &opCall);
void opSetActorToNamedPoint(ScriptThread *scriptThread, OpCall &opCall);
void opSetSelectSfx(ScriptThread *scriptThread, OpCall &opCall);
void opSetMoveSfx(ScriptThread *scriptThread, OpCall &opCall);
void opSetDenySfx(ScriptThread *scriptThread, OpCall &opCall);
void opSetAdjustUpSfx(ScriptThread *scriptThread, OpCall &opCall);
void opSetAdjustDnSfx(ScriptThread *scriptThread, OpCall &opCall);
void opIsCurrentSceneId(ScriptThread *scriptThread, OpCall &opCall);
void opIsActiveSceneId(ScriptThread *scriptThread, OpCall &opCall);
void opStackPop(ScriptThread *scriptThread, OpCall &opCall);
void opStackDup(ScriptThread *scriptThread, OpCall &opCall);
void opLoadSpecialCodeModule(ScriptThread *scriptThread, OpCall &opCall);
void opStopActor(ScriptThread *scriptThread, OpCall &opCall);
void opSetActorUsePan(ScriptThread *scriptThread, OpCall &opCall);
void opStartAbortableThread(ScriptThread *scriptThread, OpCall &opCall);
void opKillThread(ScriptThread *scriptThread, OpCall &opCall);
void opSetSceneIdThreadId(ScriptThread *scriptThread, OpCall &opCall);
void opStackPush0(ScriptThread *scriptThread, OpCall &opCall);
void opSetFontId(ScriptThread *scriptThread, OpCall &opCall);
void opAddMenuKey(ScriptThread *scriptThread, OpCall &opCall);
void opChangeSceneAll(ScriptThread *scriptThread, OpCall &opCall);
#endif
};
} // End of namespace Illusions
#endif // ILLUSIONS_DUCKMAN_SCRIPTOPCODES_DUCKMAN_H

View File

@@ -0,0 +1,78 @@
/* 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/>.
*
*/
#include "illusions/resourcesystem.h"
#include "illusions/fileresourcereader.h"
#include "illusions/illusions.h"
#include "common/file.h"
#include "common/str.h"
namespace Illusions {
byte *ResourceReaderFileReader::readResource(uint32 sceneId, uint32 resId, uint32 &dataSize) {
debug("ResourceReaderFileReader::readResource(%08X, %08X)", sceneId, resId);
Common::Path filename = buildResourceFilename(resId);
Common::File fd;
if (!fd.open(filename))
error("Resource::loadData() Could not open %s for reading", filename.toString().c_str());
dataSize = fd.size();
byte *data = (byte*)malloc(dataSize);
fd.read(data, dataSize);
return data;
}
Common::Path ResourceReaderFileReader::buildResourceFilename(uint32 resId) {
const char *ext = getResourceExtension(resId);
return Common::Path(Common::String::format("%08X%s", resId, ext));
}
const char *ResourceReaderFileReader::getResourceExtension(uint32 resId) {
// TODO Make constants
switch (ResourceTypeId(resId)) {
case 0x00060000:
case 0x00100000:
// ActorResource
return ".act";
case 0x00080000:
// SoundGroupResource
return ".sg";
case 0x000D0000:
// ScriptResource
return ".scr";
case 0x000F0000:
// TalkResource
return ".tlk";
case 0x00110000:
// BackgroundResource
return ".bg";
case 0x00120000:
// FontResource
return ".fnt";
case 0x00170000:
// SpecialCode
return "";
default:
return "";
}
}
} // End of namespace Illusions

View File

@@ -0,0 +1,40 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_FILERESOURCEREADER_H
#define ILLUSIONS_FILERESOURCEREADER_H
#include "illusions/illusions.h"
#include "illusions/resourcereader.h"
namespace Illusions {
class ResourceReaderFileReader : public BaseResourceReader {
public:
byte *readResource(uint32 sceneId, uint32 resId, uint32 &dataSize) override;
protected:
Common::Path buildResourceFilename(uint32 resId);
const char *getResourceExtension(uint32 resId);
};
} // End of namespace Illusions
#endif // ILLUSIONS_FILERESOURCEREADER_H

View File

@@ -0,0 +1,72 @@
/* 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/>.
*
*/
#include "illusions/illusions.h"
#include "illusions/fixedpoint.h"
namespace Illusions {
FixedPoint16 floatToFixed(float value) {
return value * 65536.0;
}
float fixedToFloat(FixedPoint16 value) {
return value / 65536.0;
}
FixedPoint16 fixedMul(FixedPoint16 a, FixedPoint16 b) {
return ((float)a * b) / 65536.0;
}
FixedPoint16 fixedDiv(FixedPoint16 a, FixedPoint16 b) {
return ((float)a / b) * 65536.0;
}
int16 fixedTrunc(FixedPoint16 value) {
// CHECKME Not sure if this correct
int16 result = (value >> 16) & 0xFFFF;
if ((value & 0xFFFF) >= 0x8000)
++result;
return result;
}
FixedPoint16 fixedDistance(FixedPoint16 x1, FixedPoint16 y1, FixedPoint16 x2, FixedPoint16 y2) {
float xd = fixedToFloat(x1) - fixedToFloat(x2);
float yd = fixedToFloat(y1) - fixedToFloat(y2);
if (xd != 0.0f || yd != 0.0f)
return floatToFixed(sqrt(xd * xd + yd * yd));
return 0;
}
FixedPoint16 fixedAtan(FixedPoint16 value) {
//return floatToFixed(atan2(1.0, fixedToFloat(value)));
return floatToFixed(atan(fixedToFloat(value)));
}
FixedPoint16 fixedCos(FixedPoint16 value) {
return floatToFixed(cos(fixedToFloat(value)));
}
FixedPoint16 fixedSin(FixedPoint16 value) {
return floatToFixed(sin(fixedToFloat(value)));
}
} // End of namespace Illusions

View File

@@ -0,0 +1,43 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_FIXEDPOINT_H
#define ILLUSIONS_FIXEDPOINT_H
#include "common/rect.h"
namespace Illusions {
typedef int32 FixedPoint16;
FixedPoint16 floatToFixed(float value);
float fixedToFloat(FixedPoint16 value);
FixedPoint16 fixedMul(FixedPoint16 a, FixedPoint16 b);
FixedPoint16 fixedDiv(FixedPoint16 a, FixedPoint16 b);
int16 fixedTrunc(FixedPoint16 value);
FixedPoint16 fixedDistance(FixedPoint16 x1, FixedPoint16 y1, FixedPoint16 x2, FixedPoint16 y2);
FixedPoint16 fixedAtan(FixedPoint16 value);
FixedPoint16 fixedCos(FixedPoint16 value);
FixedPoint16 fixedSin(FixedPoint16 value);
} // End of namespace Illusions
#endif // ILLUSIONS_FIXEDPOINT_H

View File

@@ -0,0 +1,103 @@
/* 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/>.
*
*/
#include "illusions/gamarchive.h"
namespace Illusions {
GamArchive::GamArchive(const char *filename)
: _fd(nullptr), _groupCount(0), _groups(nullptr) {
_fd = new Common::File();
if (!_fd->open(filename))
error("GamArchive::GamArchive() Could not open %s", filename);
loadDictionary();
}
GamArchive::~GamArchive() {
delete[] _groups;
delete _fd;
}
byte *GamArchive::readResource(uint32 sceneId, uint32 resId, uint32 &dataSize) {
const GamFileEntry *fileEntry = getGroupFileEntry(sceneId, resId);
_fd->seek(fileEntry->_fileOffset);
dataSize = fileEntry->_fileSize;
byte *data = (byte*)malloc(dataSize);
_fd->read(data, dataSize);
return data;
}
void GamArchive::loadDictionary() {
_groupCount = _fd->readUint32LE();
_groups = new GamGroupEntry[_groupCount];
uint32 *groupOffsets = new uint32[_groupCount];
for (uint i = 0; i < _groupCount; ++i) {
_groups[i]._id = _fd->readUint32LE();
groupOffsets[i] = _fd->readUint32LE();
}
for (uint i = 0; i < _groupCount; ++i) {
_fd->seek(groupOffsets[i]);
uint32 fileCount = _fd->readUint32LE();
_groups[i]._fileCount = fileCount;
_groups[i]._files = new GamFileEntry[fileCount];
debug(8, "Group %08X, fileCount: %d", _groups[i]._id, _groups[i]._fileCount);
for (uint j = 0; j < fileCount; ++j) {
_groups[i]._files[j]._id = _fd->readUint32LE();
_groups[i]._files[j]._fileOffset = _fd->readUint32LE();
_groups[i]._files[j]._fileSize = _fd->readUint32LE();
debug(8, " %08X, %08X, %d", _groups[i]._files[j]._id, _groups[i]._files[j]._fileOffset, _groups[i]._files[j]._fileSize);
}
}
delete[] groupOffsets;
}
const GamGroupEntry *GamArchive::getGroupEntry(uint32 sceneId) {
for (uint i = 0; i < _groupCount; ++i) {
if (_groups[i]._id == sceneId)
return &_groups[i];
}
return nullptr;
}
const GamFileEntry *GamArchive::getFileEntry(const GamGroupEntry *groupEntry, uint32 resId) {
for (uint i = 0; i < groupEntry->_fileCount; ++i) {
if (groupEntry->_files[i]._id == resId)
return &groupEntry->_files[i];
}
return nullptr;
}
const GamFileEntry *GamArchive::getGroupFileEntry(uint32 sceneId, uint32 resId) {
const GamGroupEntry *groupEntry = getGroupEntry(sceneId);
if (!groupEntry)
error("GamArchive::getFileEntry() Group %08X not found", sceneId);
const GamFileEntry *fileEntry = getFileEntry(groupEntry, resId);
if (!fileEntry)
error("GamArchive::getFileEntry() File %08X in group %08X not found", resId, sceneId);
return fileEntry;
}
} // End of namespace Illusions

View File

@@ -0,0 +1,64 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_GAMARCHIVE_H
#define ILLUSIONS_GAMARCHIVE_H
#include "illusions/illusions.h"
#include "common/file.h"
namespace Illusions {
struct GamFileEntry {
uint32 _id;
uint32 _fileOffset;
uint32 _fileSize;
};
struct GamGroupEntry {
uint32 _id;
uint _fileCount;
GamFileEntry *_files;
GamGroupEntry() : _fileCount(0), _files(0) {
}
~GamGroupEntry() {
delete[] _files;
}
};
class GamArchive {
public:
GamArchive(const char *filename);
~GamArchive();
byte *readResource(uint32 sceneId, uint32 resId, uint32 &dataSize);
protected:
Common::File *_fd;
uint _groupCount;
GamGroupEntry *_groups;
void loadDictionary();
const GamGroupEntry *getGroupEntry(uint32 sceneId);
const GamFileEntry *getFileEntry(const GamGroupEntry *groupEntry, uint32 resId);
const GamFileEntry *getGroupFileEntry(uint32 sceneId, uint32 resId);
};
} // End of namespace Illusions
#endif // ILLUSIONS_GAMARCHIVE_H

View File

@@ -0,0 +1,75 @@
/* 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/>.
*
*/
#include "illusions/gamestate.h"
namespace Illusions {
GameState::GameState()
: _writeBufferSize(0), _writeBuffer(nullptr), _readStream(nullptr) {
}
GameState::~GameState() {
free(_writeBuffer);
}
bool GameState::readState(uint32 &sceneId, uint32 &threadId) {
sceneId = _readStream->readUint32LE();
threadId = _readStream->readUint32LE();
return readStateInternal(_readStream);
}
void GameState::writeState(uint32 sceneId, uint32 threadId) {
Common::WriteStream *writeStream = newWriteStream();
writeStream->writeUint32LE(sceneId);
writeStream->writeUint32LE(threadId);
writeStateInternal(writeStream);
delete writeStream;
}
void GameState::read(Common::ReadStream *in) {
uint32 size = in->readUint32LE();
_readStream = in->readStream(size);
}
void GameState::write(Common::WriteStream *out) {
out->writeUint32LE(_writeBufferSize);
out->write(_writeBuffer, _writeBufferSize);
}
void GameState::deleteReadStream() {
delete _readStream;
_readStream = nullptr;
}
Common::WriteStream *GameState::newWriteStream() {
if (_writeBufferSize == 0 || !_writeBuffer) {
_writeBufferSize = calcWriteBufferSize();
_writeBuffer = (byte*)malloc(_writeBufferSize);
}
return new Common::MemoryWriteStream(_writeBuffer, _writeBufferSize);
}
uint32 GameState::calcWriteBufferSize() {
return calcWriteBufferSizeInternal() + 4 + 4;
}
} // End of namespace Illusions

View File

@@ -0,0 +1,52 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_GAMESTATE_H
#define ILLUSIONS_GAMESTATE_H
#include "common/file.h"
#include "common/memstream.h"
namespace Illusions {
class GameState {
public:
GameState();
virtual ~GameState();
bool readState(uint32 &sceneId, uint32 &threadId);
void writeState(uint32 sceneId, uint32 threadId);
void read(Common::ReadStream *in);
void write(Common::WriteStream *out);
void deleteReadStream();
protected:
uint32 _writeBufferSize;
byte *_writeBuffer;
Common::SeekableReadStream *_readStream;
Common::WriteStream *newWriteStream();
uint32 calcWriteBufferSize();
virtual uint32 calcWriteBufferSizeInternal() = 0;
virtual bool readStateInternal(Common::ReadStream *in) = 0;
virtual void writeStateInternal(Common::WriteStream *out) = 0;
};
} // End of namespace Illusions
#endif // ILLUSIONS_GAMESTATE_H

View File

@@ -0,0 +1,43 @@
/* 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/>.
*
*/
#include "illusions/resourcesystem.h"
#include "illusions/gamresourcereader.h"
#include "illusions/gamarchive.h"
#include "illusions/illusions.h"
#include "common/file.h"
#include "common/str.h"
namespace Illusions {
ResourceReaderGamArchive::ResourceReaderGamArchive(const char *filename) {
_gamArchive = new GamArchive(filename);
}
ResourceReaderGamArchive::~ResourceReaderGamArchive() {
delete _gamArchive;
}
byte *ResourceReaderGamArchive::readResource(uint32 sceneId, uint32 resId, uint32 &dataSize) {
return _gamArchive->readResource(sceneId, resId, dataSize);
}
} // End of namespace Illusions

View File

@@ -0,0 +1,43 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_RESOURCEREADER_GAMARCHIVE_H
#define ILLUSIONS_RESOURCEREADER_GAMARCHIVE_H
#include "illusions/illusions.h"
#include "illusions/resourcereader.h"
namespace Illusions {
class GamArchive;
class ResourceReaderGamArchive : public BaseResourceReader {
public:
ResourceReaderGamArchive(const char *filename);
~ResourceReaderGamArchive() override;
byte *readResource(uint32 sceneId, uint32 resId, uint32 &dataSize) override;
protected:
GamArchive *_gamArchive;
};
} // End of namespace Illusions
#endif // ILLUSIONS_RESOURCEREADER_GAMARCHIVE_H

View File

@@ -0,0 +1,81 @@
/* 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/>.
*
*/
#include "illusions/graphics.h"
namespace Illusions {
// WidthHeight
void WidthHeight::load(Common::SeekableReadStream &stream) {
_width = stream.readSint16LE();
_height = stream.readSint16LE();
debug(5, "WidthHeight::load() _width: %d; _height: %d",
_width, _height);
}
// SurfInfo
void SurfInfo::load(Common::SeekableReadStream &stream) {
_pixelSize = stream.readUint32LE();
_dimensions.load(stream);
debug(5, "SurfInfo::load() _pixelSize: %d",
_pixelSize);
}
// NamedPoint
void NamedPoint::load(Common::SeekableReadStream &stream) {
_namedPointId = stream.readUint32LE();
loadPoint(stream, _pt);
}
// NamedPoints
bool NamedPoints::findNamedPoint(uint32 namedPointId, Common::Point &pt) {
for (auto &namedPoint : _namedPoints) {
if (namedPoint._namedPointId == namedPointId) {
pt = namedPoint._pt;
return true;
}
}
return false;
}
void NamedPoints::load(uint count, Common::SeekableReadStream &stream) {
_namedPoints.reserve(count);
for (uint i = 0; i < count; ++i) {
NamedPoint namedPoint;
namedPoint.load(stream);
_namedPoints.push_back(namedPoint);
debug(0, "namedPoint(%08X, %d, %d)", namedPoint._namedPointId, namedPoint._pt.x, namedPoint._pt.y);
}
}
void loadPoint(Common::SeekableReadStream &stream, Common::Point &pt) {
pt.x = stream.readSint16LE();
pt.y = stream.readSint16LE();
debug(0, "loadPoint() x: %d; y: %d", pt.x, pt.y);
}
} // End of namespace Illusions

View File

@@ -0,0 +1,73 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_GRAPHICS_H
#define ILLUSIONS_GRAPHICS_H
#include "common/array.h"
#include "common/rect.h"
#include "common/stream.h"
namespace Illusions {
struct WidthHeight {
int16 _width, _height;
void load(Common::SeekableReadStream &stream);
WidthHeight() : _width(0), _height(0) {}
};
struct SurfInfo {
uint32 _pixelSize;
WidthHeight _dimensions;
void load(Common::SeekableReadStream &stream);
};
struct WRect {
Common::Point _topLeft;
Common::Point _bottomRight;
};
struct RGB {
byte r, g, b;
};
struct NamedPoint {
uint32 _namedPointId;
Common::Point _pt;
void load(Common::SeekableReadStream &stream);
};
class NamedPoints {
public:
bool findNamedPoint(uint32 namedPointId, Common::Point &pt);
void load(uint count, Common::SeekableReadStream &stream);
protected:
typedef Common::Array<NamedPoint> Items;
typedef Items::iterator ItemsIterator;
Items _namedPoints;
};
void loadPoint(Common::SeekableReadStream &stream, Common::Point &pt);
} // End of namespace Illusions
#endif // ILLUSIONS_GRAPHICS_H

View File

@@ -0,0 +1,363 @@
/* 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/>.
*
*/
#include "illusions/illusions.h"
#include "illusions/actor.h"
#include "illusions/camera.h"
#include "illusions/cursor.h"
#include "illusions/dictionary.h"
#include "illusions/resources/fontresource.h"
#include "illusions/graphics.h"
#include "illusions/input.h"
#include "illusions/resources/actorresource.h"
#include "illusions/resources/backgroundresource.h"
#include "illusions/resources/scriptresource.h"
#include "illusions/resources/soundresource.h"
#include "illusions/resources/talkresource.h"
#include "illusions/resourcesystem.h"
#include "illusions/screen.h"
#include "illusions/screentext.h"
#include "illusions/sound.h"
#include "illusions/specialcode.h"
#include "illusions/thread.h"
#include "illusions/time.h"
#include "illusions/updatefunctions.h"
#include "illusions/threads/talkthread.h"
#include "audio/audiostream.h"
#include "video/video_decoder.h"
#include "video/avi_decoder.h"
#include "common/config-manager.h"
#include "common/debug-channels.h"
#include "common/error.h"
#include "common/fs.h"
#include "common/timer.h"
#include "engines/util.h"
#include "graphics/cursorman.h"
#include "graphics/font.h"
#include "graphics/fontman.h"
#include "graphics/surface.h"
namespace Illusions {
char *debugW2I(uint16 *wstr) {
static char buf[65];
char *p = buf;
uint i = 0;
while (*wstr != 0 && i < sizeof(buf) - 1) {
*p++ = (byte)*wstr;
wstr++;
i++;
}
*p = 0;
return buf;
}
void swapBytesInWideString(byte *wstr) {
#if defined(SCUMM_BIG_ENDIAN)
for (byte *ptr = wstr; *ptr != 0; ptr += 2) {
WRITE_UINT16(ptr, SWAP_BYTES_16(READ_UINT16(ptr)));
}
#endif
}
IllusionsEngine::IllusionsEngine(OSystem *syst, const IllusionsGameDescription *gd) :
Engine(syst), _gameDescription(gd), _specialCode(nullptr) {
_random = new Common::RandomSource("illusions");
_rerunThreads = false;
_isSaveAllowed = true; // TODO
_resumeFromSavegameRequested = false;
_savegameSlotNum = -1;
_savegameSceneId = 0;
_savegameThreadId = 0;
_nextTempThreadId = 0;
Engine::syncSoundSettings();
}
IllusionsEngine::~IllusionsEngine() {
delete _random;
}
void IllusionsEngine::updateEvents() {
Common::Event event;
while (_eventMan->pollEvent(event)) {
_input->processEvent(event);
switch (event.type) {
case Common::EVENT_QUIT:
quitGame();
break;
default:
break;
}
}
}
void IllusionsEngine::runUpdateFunctions() {
_updateFunctions->update();
}
Common::Point *IllusionsEngine::getObjectActorPositionPtr(uint32 objectId) {
Control *control = getObjectControl(objectId);
if (control && control->_actor)
return &control->_actor->_position;
return nullptr;
}
uint32 IllusionsEngine::getElapsedUpdateTime() {
uint32 result = 0;
uint32 currTime = getCurrentTime();
if (_resGetCtr <= 0 ) {
if (_unpauseControlActorFlag) {
_unpauseControlActorFlag = false;
result = 0;
} else {
result = currTime - _lastUpdateTime;
}
_lastUpdateTime = currTime;
} else {
result = _resGetTime - _lastUpdateTime;
_lastUpdateTime = _resGetTime;
}
return result;
}
int IllusionsEngine::updateActors(uint flags) {
uint32 deltaTime = getElapsedUpdateTime();
for (Controls::ItemsIterator it = _controls->_controls.begin(); it != _controls->_controls.end(); ++it) {
Control *control = *it;
if (control->_pauseCtr == 0 && control->_actor && control->_actor->_controlRoutine)
control->_actor->runControlRoutine(control, deltaTime);
}
return kUFNext;
}
int IllusionsEngine::updateSequences(uint flags) {
for (Controls::ItemsIterator it = _controls->_controls.begin(); it != _controls->_controls.end(); ++it) {
Control *control = *it;
if (control->_pauseCtr == 0 && control->_actor && control->_actor->_seqCodeIp) {
control->sequenceActor();
}
}
return kUFNext;
}
int IllusionsEngine::updateGraphics(uint flags) {
Common::Point panPoint(0, 0);
uint32 currTime = getCurrentTime();
_camera->update(currTime);
updateFader();
BackgroundInstance *backgroundItem = _backgroundInstances->findActiveBackgroundInstance();
if (backgroundItem) {
BackgroundResource *bgRes = backgroundItem->_bgRes;
for (uint i = 0; i < bgRes->_bgInfosCount; ++i) {
BgInfo *bgInfo = &bgRes->_bgInfos[i];
uint32 priority = getPriorityFromBase(bgInfo->_priorityBase);
_screen->_drawQueue->insertSurface(backgroundItem->_surfaces[i],
bgInfo->_surfInfo._dimensions, backgroundItem->_panPoints[i], priority);
if (bgInfo->_flags & 1)
panPoint = backgroundItem->_panPoints[i];
}
}
for (Controls::ItemsIterator it = _controls->_controls.begin(); it != _controls->_controls.end(); ++it) {
Control *control = *it;
Actor *actor = control->_actor;
if (control->_pauseCtr == 0 && actor && (actor->_flags & Illusions::ACTOR_FLAG_IS_VISIBLE) && !(actor->_flags & Illusions::ACTOR_FLAG_200)) {
Common::Point drawPosition = control->calcPosition(panPoint);
if (actor->_flags & Illusions::ACTOR_FLAG_2000) {
Frame *frame = &(*actor->_frames)[actor->_frameIndex - 1];
_screen->_decompressQueue->insert(&actor->_drawFlags, frame->_flags,
frame->_surfInfo._pixelSize, frame->_surfInfo._dimensions,
frame->_compressedPixels, actor->_surface);
actor->_flags &= ~Illusions::ACTOR_FLAG_2000;
}
if (actor->_surfInfo._dimensions._width && actor->_surfInfo._dimensions._height) {
uint32 priority = control->getDrawPriority();
_screen->_drawQueue->insertSprite(&actor->_drawFlags, actor->_surface,
actor->_surfInfo._dimensions, drawPosition, control->_position,
priority, actor->_scale, actor->_spriteFlags);
}
}
}
if (_screenText->_surface) {
uint32 priority = getGameId() == kGameIdDuckman ? getPriorityFromBase(19) : getPriorityFromBase(99);
_screen->_drawQueue->insertTextSurface(_screenText->_surface, _screenText->_dimensions,
_screenText->_position, priority);
}
return kUFNext;
}
int IllusionsEngine::updateSoundMan(uint flags) {
_soundMan->update();
return kUFNext;
}
int IllusionsEngine::updateSprites(uint flags) {
_screen->updateSprites();
_screenPalette->updatePalette();
return kUFNext;
}
int IllusionsEngine::getRandom(int max) {
return _random->getRandomNumber(max - 1);
}
int IllusionsEngine::convertPanXCoord(int16 x) {
int16 diff = x - _camera->getCurrentPan().x;
int16 absX = ABS(diff);
int newX = 0;
if (absX < 160) {
newX = (diff << 7) / 320;
} else if (diff < 0) {
newX = -64;
} else {
newX = 64;
}
debug(1, "convertPanXCoord %d %d -> %d", diff, x, newX);
return newX;
}
bool IllusionsEngine::calcPointDirection(Common::Point &srcPt, Common::Point &dstPt, uint &facing) {
facing = 0;
uint xd = 0, yd = 0;
if (srcPt.x < dstPt.x)
xd = 0x40;
else if (srcPt.x > dstPt.x)
xd = 0x04;
else
xd = 0x00;
if (srcPt.y < dstPt.y)
yd = 0x01;
else if (srcPt.y > dstPt.y)
yd = 0x10;
else
yd = 0x00;
if (!xd && !yd)
facing = 0;
else if (!yd && xd)
facing = xd;
else if (yd && !xd)
facing = yd;
else if (xd == 0x04 && yd == 0x01)
facing = 0x02;
else if (xd == 0x40 && yd == 0x01)
facing = 0x80;
else if (xd == 0x04 && yd == 0x10)
facing = 0x08;
else if (xd == 0x40 && yd == 0x10)
facing = 0x20;
return facing != 0;
}
bool IllusionsEngine::isSoundActive() {
// TODO
return true;
}
void IllusionsEngine::setCurrFontId(uint32 fontId) {
_fontId = fontId;
}
bool IllusionsEngine::checkActiveTalkThreads() {
return _threads->isActiveThread(kMsgQueryTalkThreadActive);
}
void IllusionsEngine::setTextDuration(int kind, uint32 duration) {
_field8 = kind;
switch (_field8) {
case 1:
case 2:
_fieldA = 0;
break;
case 3:
case 4:
_fieldA = duration;
break;
default:
break;
}
}
uint32 IllusionsEngine::clipTextDuration(uint32 duration) {
switch (_field8) {
case 2:
if (duration == 0)
duration = 240;
break;
case 3:
if (duration < _fieldA)
duration = _fieldA;
break;
case 4:
if (duration > _fieldA)
duration = _fieldA;
break;
default:
break;
}
return duration;
}
void IllusionsEngine::getDefaultTextDimensions(WidthHeight &dimensions) {
dimensions = _defaultTextDimensions;
}
void IllusionsEngine::setDefaultTextDimensions(WidthHeight &dimensions) {
_defaultTextDimensions = dimensions;
}
void IllusionsEngine::getDefaultTextPosition(Common::Point &position) {
position = _defaultTextPosition;
}
void IllusionsEngine::setDefaultTextPosition(Common::Point &position) {
_defaultTextPosition = position;
}
FramesList *IllusionsEngine::findActorSequenceFrames(Sequence *sequence) {
return _actorInstances->findSequenceFrames(sequence);
}
void IllusionsEngine::setSubtitleDuration(uint16 duration) {
_subtitleDuration = duration;
ConfMan.setInt("talkspeed", _subtitleDuration);
ConfMan.flushToDisk();
}
uint16 IllusionsEngine::getSubtitleDuration() {
return (uint16)_subtitleDuration;
}
} // End of namespace Illusions

View File

@@ -0,0 +1,251 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_ILLUSIONS_H
#define ILLUSIONS_ILLUSIONS_H
#include "illusions/graphics.h"
#include "audio/mixer.h"
#include "audio/decoders/aiff.h"
#include "common/array.h"
#include "common/events.h"
#include "common/file.h"
#include "common/memstream.h"
#include "common/random.h"
#include "common/str.h"
#include "common/substream.h"
#include "common/system.h"
#include "engines/engine.h"
#include "graphics/surface.h"
#include "illusions/detection.h"
namespace Illusions {
enum ILLUSIONSAction {
kActionNone,
kActionCursorUp,
kActionCursorDown,
kActionCursorLeft,
kActionCursorRight,
kActionInventory,
kActionAbort,
kActionSkip,
kActionCheatMode
};
char *debugW2I(uint16 *wstr);
void swapBytesInWideString(byte * wstr);
#define ILLUSIONS_SAVEGAME_VERSION 0
class ResourceSystem;
class BaseResourceReader;
struct SurfInfo;
class ActorInstanceList;
struct ActorType;
class BackgroundInstanceList;
class BackgroundResource;
class Camera;
class Control;
class Controls;
class Cursor;
class Dictionary;
struct Fader;
class FramesList;
class Input;
class Screen;
class ScreenText;
class ScriptOpcodes;
class ScriptResource;
class ScriptStack;
struct Sequence;
class SoundMan;
class SpecialCode;
class TalkInstanceList;
class ThreadList;
class UpdateFunctions;
class GameState;
class ScreenPaletteBase;
class IllusionsEngine : public Engine {
public:
IllusionsEngine(OSystem *syst, const IllusionsGameDescription *gd);
~IllusionsEngine() override;
const Common::String getTargetName() { return _targetName; }
private:
const IllusionsGameDescription *_gameDescription;
Graphics::PixelFormat _pixelFormat;
public:
Common::RandomSource *_random;
Dictionary *_dict;
ResourceSystem *_resSys;
BaseResourceReader *_resReader;
UpdateFunctions *_updateFunctions;
GameState *_gameState;
void updateEvents();
Screen *_screen;
ScreenPaletteBase *_screenPalette;
ScreenText *_screenText;
Input *_input;
ActorInstanceList *_actorInstances;
BackgroundInstanceList *_backgroundInstances;
Camera *_camera;
Controls *_controls;
TalkInstanceList *_talkItems;
ScriptOpcodes *_scriptOpcodes;
SpecialCode *_specialCode;
ThreadList *_threads;
SoundMan *_soundMan;
uint32 _nextTempThreadId;
bool _doScriptThreadInit;
ScriptStack *_stack;
ScriptResource *_scriptResource;
bool _rerunThreads;
Fader *_fader;
int _pauseCtr;
int _resGetCtr;
uint32 _resGetTime;
bool _unpauseControlActorFlag;
uint32 _lastUpdateTime;
int _resumeFromSavegameRequested;
int _savegameSlotNum;
Common::String _savegameDescription;
uint32 _savegameSceneId;
uint32 _savegameThreadId;
uint32 _fontId;
int _field8;
uint32 _fieldA;
uint32 _subtitleDuration;
WidthHeight _defaultTextDimensions;
Common::Point _defaultTextPosition;
int16 _menuChoiceOfs;
int getGameId() const;
Common::Language getGameLanguage() const;
void runUpdateFunctions();
int updateActors(uint flags);
int updateSequences(uint flags);
int updateGraphics(uint flags);
int updateSoundMan(uint flags);
int updateSprites(uint flags);
uint32 getElapsedUpdateTime();
Common::Point *getObjectActorPositionPtr(uint32 objectId);
int getRandom(int max);
int convertPanXCoord(int16 x);
bool calcPointDirection(Common::Point &srcPt, Common::Point &dstPt, uint &facing);
bool isSoundActive();
virtual void updateFader() {};
virtual void clearFader() {};
virtual void pauseFader() {};
virtual void unpauseFader() {};
virtual bool isVideoPlaying() { return false; }
void setCurrFontId(uint32 fontId);
bool checkActiveTalkThreads();
void setTextDuration(int kind, uint32 duration);
uint32 clipTextDuration(uint32 duration);
void getDefaultTextDimensions(WidthHeight &dimensions);
void setDefaultTextDimensions(WidthHeight &dimensions);
void getDefaultTextPosition(Common::Point &position);
void setDefaultTextPosition(Common::Point &position);
uint16 getSubtitleDuration();
void setSubtitleDuration(uint16 duration);
FramesList *findActorSequenceFrames(Sequence *sequence);
virtual void setDefaultTextCoords() = 0;
virtual void loadSpecialCode(uint32 resId) = 0;
virtual void unloadSpecialCode(uint32 resId) = 0;
virtual void notifyThreadId(uint32 &threadId) = 0;
virtual bool testMainActorFastWalk(Control *control) = 0;
virtual bool testMainActorCollision(Control *control) = 0;
virtual Control *getObjectControl(uint32 objectId) = 0;
virtual Common::Point getNamedPointPosition(uint32 namedPointId) = 0;
virtual uint32 getPriorityFromBase(int16 priority) = 0;
virtual uint32 getPrevScene() = 0;
virtual uint32 getCurrentScene() = 0;
virtual bool isCursorObject(uint32 actorTypeId, uint32 objectId) = 0;
virtual void setCursorControlRoutine(Control *control) = 0;
virtual void placeCursorControl(Control *control, uint32 sequenceId) = 0;
virtual void setCursorControl(Control *control) = 0;
virtual void showCursor() = 0;
virtual void hideCursor() = 0;
virtual void startScriptThreadSimple(uint32 threadId, uint32 callingThreadId) = 0;
virtual uint32 startTempScriptThread(byte *scriptCodeIp, uint32 callingThreadId,
uint32 value8, uint32 valueC, uint32 value10) = 0;
// Savegame API
enum kReadSaveHeaderError {
kRSHENoError = 0,
kRSHEInvalidType = 1,
kRSHEInvalidVersion = 2,
kRSHEIoError = 3
};
struct SaveHeader {
Common::String description;
uint32 version;
byte gameID;
uint32 flags;
uint32 saveDate;
uint32 saveTime;
uint32 playTime;
Graphics::Surface *thumbnail;
};
bool _isSaveAllowed;
bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override { return _isSaveAllowed; }
bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override { return _isSaveAllowed; }
Common::Error loadGameState(int slot) override;
Common::Error saveGameState(int slot, const Common::String &desc, bool isAutosave = false) override;
Common::Error removeGameState(int slot);
bool savegame(const char *filename, const char *description);
bool loadgame(const char *filename);
bool existsSavegame(int num);
static Common::String getSavegameFilename(const Common::String &target, int num);
static kReadSaveHeaderError readSaveHeader(Common::SeekableReadStream *in, SaveHeader &header, bool skipThumbnail = true);
};
} // End of namespace Illusions
#endif // ILLUSIONS_ILLUSIONS_H

263
engines/illusions/input.cpp Normal file
View File

@@ -0,0 +1,263 @@
/* 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/>.
*
*/
#include "illusions/input.h"
#include "common/system.h"
namespace Illusions {
// KeyMap
void KeyMap::addKey(Common::CustomEventType action) {
add(action, MOUSE_NONE);
}
void KeyMap::addMouseButton(int mouseButton) {
add(kActionNone, mouseButton);
}
void KeyMap::add(Common::CustomEventType action, int mouseButton) {
KeyMapping keyMapping;
keyMapping._action = action;
keyMapping._mouseButton = mouseButton;
keyMapping._down = false;
push_back(keyMapping);
}
// InputEvent
InputEvent::InputEvent() : _bitMask(0) {
}
InputEvent& InputEvent::setBitMask(uint bitMask) {
_bitMask = bitMask;
return *this;
}
InputEvent& InputEvent::addKey(Common::CustomEventType action) {
_keyMap.addKey(action);
return *this;
}
InputEvent& InputEvent::addMouseButton(int mouseButton) {
_keyMap.addMouseButton(mouseButton);
return *this;
}
uint InputEvent::handle(Common::CustomEventType action, int mouseButton, bool down) {
uint newKeys = 0;
for (auto &keyMapping : _keyMap) {
if ((keyMapping._action != kActionNone && keyMapping._action == action) ||
(keyMapping._mouseButton != MOUSE_NONE && keyMapping._mouseButton == mouseButton)) {
if (down && !keyMapping._down) {
newKeys |= _bitMask;
keyMapping._down = true;
} else if (!down)
keyMapping._down = false;
}
}
return newKeys;
}
// Input
const uint kAllButtons = 0xFFFF;
static const char kCheatCode[] = "gosanta";
Input::Input() {
_buttonStates = 0;
_newButtons = 0;
_buttonsDown = 0;
_newKeys = 0;
_enabledButtons = kAllButtons;
_cursorPos.x = 0;
_cursorPos.y = 0;
_prevCursorPos.x = 0;
_prevCursorPos.y = 0;
_cursorMovedByKeyboard = false;
_cheatCodeIndex = 0;
}
void Input::processEvent(Common::Event event) {
switch (event.type) {
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
handleAction(event.customType, MOUSE_NONE, true);
break;
case Common::EVENT_CUSTOM_ENGINE_ACTION_END:
handleAction(event.customType, MOUSE_NONE, false);
break;
case Common::EVENT_KEYDOWN:
handleKey(event.kbd.keycode, MOUSE_NONE, true);
break;
case Common::EVENT_KEYUP:
handleKey(event.kbd.keycode, MOUSE_NONE, false);
break;
case Common::EVENT_MOUSEMOVE:
_cursorMovedByKeyboard = false;
_cursorPos.x = event.mouse.x;
_cursorPos.y = event.mouse.y;
break;
case Common::EVENT_LBUTTONDOWN:
handleMouseButton(MOUSE_LEFT_BUTTON, true);
break;
case Common::EVENT_LBUTTONUP:
handleMouseButton(MOUSE_LEFT_BUTTON, false);
break;
case Common::EVENT_RBUTTONDOWN:
handleMouseButton(MOUSE_RIGHT_BUTTON, true);
break;
case Common::EVENT_RBUTTONUP:
handleMouseButton(MOUSE_RIGHT_BUTTON, false);
break;
default:
break;
}
}
bool Input::pollEvent(uint evt) {
return pollButton(_inputEvents[evt].getBitMask());
}
bool Input::hasNewEvents() {
return lookNewButtons(kAllButtons);
}
void Input::discardEvent(uint evt) {
discardButtons(_inputEvents[evt].getBitMask());
}
void Input::discardAllEvents() {
discardButtons(kAllButtons);
}
void Input::activateButton(uint bitMask) {
_enabledButtons |= bitMask;
_buttonStates &= ~bitMask;
}
void Input::deactivateButton(uint bitMask) {
_enabledButtons &= ~bitMask;
}
Common::Point Input::getCursorPosition() {
return _cursorPos;
}
void Input::setCursorPosition(Common::Point mousePos) {
_prevCursorPos = _cursorPos = mousePos;
}
Common::Point Input::getCursorDelta() {
Common::Point deltaPos;
deltaPos.x = _prevCursorPos.x - _cursorPos.x;
deltaPos.y = _prevCursorPos.y - _cursorPos.y;
_prevCursorPos = _cursorPos;
return deltaPos;
}
InputEvent& Input::setInputEvent(uint evt, uint bitMask) {
InputEvent& inputEvent = _inputEvents[evt];
return inputEvent.setBitMask(bitMask);
}
void Input::handleAction(Common::CustomEventType action, int mouseButton, bool down) {
switch (action) {
case kActionCursorUp:
moveCursorByKeyboard(0, -4);
break;
case kActionCursorDown:
moveCursorByKeyboard(0, 4);
break;
case kActionCursorRight:
moveCursorByKeyboard(4, 0);
break;
case kActionCursorLeft:
moveCursorByKeyboard(-4, 0);
break;
default:
break;
}
for (uint i = 0; i < kEventMax; ++i) {
_newKeys |= _inputEvents[i].handle(action, mouseButton, down);
}
uint prevButtonStates = _buttonStates;
_buttonStates |= _newKeys;
_newKeys = 0;
_newButtons = ~prevButtonStates & _buttonStates;
}
void Input::handleKey(Common::KeyCode key, int mouseButton, bool down) {
if (!down && !isCheatModeActive()) {
if (_cheatCodeIndex < 7 && key == kCheatCode[_cheatCodeIndex]) {
_cheatCodeIndex++;
} else {
_cheatCodeIndex = 0;
}
}
}
void Input::handleMouseButton(int mouseButton, bool down) {
if (down)
_buttonsDown |= mouseButton;
else
_buttonsDown &= ~mouseButton;
handleAction(kActionNone, mouseButton, down);
}
bool Input::pollButton(uint bitMask) {
if (lookButtonStates(bitMask)) {
_buttonStates &= ~bitMask;
return true;
}
return false;
}
bool Input::lookButtonStates(uint bitMask) {
return (bitMask & (_buttonStates & _enabledButtons)) != 0;
}
bool Input::lookNewButtons(uint bitMask) {
return (bitMask & (_newButtons & _enabledButtons)) != 0;
}
void Input::setButtonState(uint bitMask) {
_buttonStates |= _enabledButtons & bitMask;
}
void Input::discardButtons(uint bitMask) {
_buttonStates &= ~bitMask;
}
void Input::moveCursorByKeyboard(int deltaX, int deltaY) {
_cursorMovedByKeyboard = true;
_cursorPos.x = CLIP(_cursorPos.x + deltaX, 0, g_system->getWidth() - 1);
_cursorPos.y = CLIP(_cursorPos.y + deltaY, 0, g_system->getHeight() - 1);
}
bool Input::isCheatModeActive() {
return _cheatCodeIndex == 7;
}
void Input::setCheatModeActive(bool active) {
_cheatCodeIndex = active ? 7 : 0;
}
} // End of namespace Illusions

116
engines/illusions/input.h Normal file
View File

@@ -0,0 +1,116 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_INPUT_H
#define ILLUSIONS_INPUT_H
#include "common/array.h"
#include "common/events.h"
#include "common/keyboard.h"
#include "common/rect.h"
#include "illusions/illusions.h"
namespace Illusions {
enum {
MOUSE_NONE = 0,
MOUSE_LEFT_BUTTON = 1,
MOUSE_RIGHT_BUTTON = 2
};
enum {
kEventLeftClick = 0,
kEventRightClick = 1,
kEventInventory = 2,
kEventAbort = 3,
kEventSkip = 4,
kEventF1 = 5,
kEventUp = 6,
kEventDown = 7,
kEventMax
};
struct KeyMapping {
Common::CustomEventType _action;
int _mouseButton;
bool _down;
};
class KeyMap : public Common::Array<KeyMapping> {
public:
void addKey(Common::CustomEventType action);
void addMouseButton(int mouseButton);
protected:
void add(Common::CustomEventType action, int mouseButton);
};
class InputEvent {
public:
InputEvent();
InputEvent& setBitMask(uint bitMask);
InputEvent& addKey(Common::CustomEventType action);
InputEvent& addMouseButton(int mouseButton);
uint handle(Common::CustomEventType action, int mouseButton, bool down);
uint getBitMask() const { return _bitMask; }
protected:
uint _bitMask;
KeyMap _keyMap;
};
class Input {
public:
Input();
void processEvent(Common::Event event);
bool pollEvent(uint evt);
bool hasNewEvents();
void discardEvent(uint evt);
void discardAllEvents();
bool pollButton(uint bitMask);
void activateButton(uint bitMask);
void deactivateButton(uint bitMask);
Common::Point getCursorPosition();
void setCursorPosition(Common::Point mousePos);
Common::Point getCursorDelta();
InputEvent& setInputEvent(uint evt, uint bitMask);
bool isCursorMovedByKeyboard() const { return _cursorMovedByKeyboard; }
bool isCheatModeActive();
void setCheatModeActive(bool active);
protected:
uint _cheatCodeIndex;
uint _buttonStates, _newButtons, _buttonsDown;
uint _enabledButtons;
uint _newKeys;
Common::Point _cursorPos, _prevCursorPos;
InputEvent _inputEvents[kEventMax];
bool _cursorMovedByKeyboard;
void handleAction(Common::CustomEventType Action, int mouseButton, bool down);
void handleKey(Common::KeyCode key, int mouseButton, bool down);
void handleMouseButton(int mouseButton, bool down);
void discardButtons(uint bitMask);
bool lookButtonStates(uint bitMask);
bool lookNewButtons(uint bitMask);
void setButtonState(uint bitMask);
void moveCursorByKeyboard(int deltaX, int deltaY);
};
} // End of namespace Illusions
#endif // ILLUSIONS_INPUT_H

View File

@@ -0,0 +1,715 @@
/* 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/>.
*
*/
#include "illusions/menusystem.h"
#include "illusions/illusions.h"
#include "illusions/dictionary.h"
#include "illusions/input.h"
#include "illusions/screen.h"
#include "illusions/screentext.h"
#include "illusions/thread.h"
#include "illusions/time.h"
#include "common/config-manager.h"
#include "common/translation.h"
#include "gui/saveload.h"
namespace Illusions {
// MenuItem
MenuItem::MenuItem(const Common::String &text, BaseMenuAction *action)
: _text(text), _action(action) {
}
MenuItem::~MenuItem() {
delete _action;
}
void MenuItem::executeAction(const Common::Point &point) {
_mouseClickPoint = point;
_action->execute();
}
// BaseMenu
BaseMenu::BaseMenu(BaseMenuSystem *menuSystem, uint32 fontId, byte backgroundColor, byte borderColor, byte textColor, byte fieldE,
uint defaultMenuItemIndex)
: _menuSystem(menuSystem), _fontId(fontId), _backgroundColor(backgroundColor), _borderColor(borderColor), _textColor(textColor), _fieldE(fieldE),
_defaultMenuItemIndex(defaultMenuItemIndex)
{
}
BaseMenu::~BaseMenu() {
for (auto *menuItem : _menuItems) {
delete menuItem;
}
}
void BaseMenu::addText(const Common::String &text) {
_text.push_back(text);
}
void BaseMenu::addMenuItem(MenuItem *menuItem) {
_menuItems.push_back(menuItem);
}
uint BaseMenu::getHeaderLinesCount() {
return _text.size();
}
const Common::String& BaseMenu::getHeaderLine(uint index) {
return _text[index];
}
uint BaseMenu::getMenuItemsCount() {
return _menuItems.size();
}
MenuItem *BaseMenu::getMenuItem(uint index) {
return _menuItems[index];
}
void BaseMenu::enterMenu() {
// Empty, implemented if necessary by the inherited class when the menu is entered
}
// BaseMenuSystem
BaseMenuSystem::BaseMenuSystem(IllusionsEngine *vm)
: _vm(vm), _isTimeOutEnabled(false), _menuChoiceOffset(nullptr) {
}
BaseMenuSystem::~BaseMenuSystem() {
}
void BaseMenuSystem::playSoundEffect13() {
playSoundEffect(13);
}
void BaseMenuSystem::playSoundEffect14() {
playSoundEffect(14);
}
void BaseMenuSystem::selectMenuChoiceIndex(uint choiceIndex) {
debug(0, "choiceIndex: %d", choiceIndex);
debug(0, "_menuChoiceOffset: %p", (void*)_menuChoiceOffset);
if (choiceIndex > 0 && _menuChoiceOffset) {
*_menuChoiceOffset = _menuChoiceOffsets[choiceIndex - 1];
debug(0, "*_menuChoiceOffset: %04X", *_menuChoiceOffset);
}
_vm->_threads->notifyId(_menuCallerThreadId);
_menuCallerThreadId = 0;
closeMenu();
}
void BaseMenuSystem::leaveMenu() {
playSoundEffect13();
if (!_menuStack.empty())
leaveSubMenu();
else
closeMenu();
}
void BaseMenuSystem::enterSubMenu(BaseMenu *menu) {
_menuStack.push(_activeMenu);
activateMenu(menu);
_hoveredMenuItemIndex = _hoveredMenuItemIndex3;
_hoveredMenuItemIndex2 = _hoveredMenuItemIndex3;
setMouseCursorToMenuItem(_hoveredMenuItemIndex);
placeActorHoverBackground();
placeActorTextColorRect();
}
void BaseMenuSystem::leaveSubMenu() {
_activeMenu = _menuStack.pop();
_field54 = _activeMenu->_field2C18;
_menuLinesCount = _activeMenu->getHeaderLinesCount();
_hoveredMenuItemIndex = 1;
_vm->_screenText->removeText();
_vm->_screenText->removeText();
activateMenu(_activeMenu);
_hoveredMenuItemIndex = _hoveredMenuItemIndex3;
_hoveredMenuItemIndex2 = _hoveredMenuItemIndex3;
setMouseCursorToMenuItem(_hoveredMenuItemIndex);
initActorHoverBackground();
placeActorTextColorRect();
}
void BaseMenuSystem::enterSubMenuById(int menuId) {
BaseMenu *menu = getMenuById(menuId);
enterSubMenu(menu);
}
uint BaseMenuSystem::getQueryConfirmationChoiceIndex() const {
return _queryConfirmationChoiceIndex;
}
void BaseMenuSystem::setQueryConfirmationChoiceIndex(uint queryConfirmationChoiceIndex) {
_queryConfirmationChoiceIndex = queryConfirmationChoiceIndex;
}
void BaseMenuSystem::setMouseCursorToMenuItem(int menuItemIndex) {
Common::Point mousePos;
if (calcMenuItemMousePos(menuItemIndex, mousePos))
setMousePos(mousePos);
}
void BaseMenuSystem::calcMenuItemRect(uint menuItemIndex, WRect &rect) {
FontResource *font = _vm->_dict->findFont(_activeMenu->_fontId);
int charHeight = font->getCharHeight() + font->getLineIncr();
_vm->_screenText->getTextInfoPosition(rect._topLeft);
if (_activeMenu->_backgroundColor) {
rect._topLeft.y += 4;
rect._topLeft.x += 4;
}
rect._topLeft.y += charHeight * (menuItemIndex + _menuLinesCount - 1);
WidthHeight textInfoDimensions;
_vm->_screenText->getTextInfoDimensions(textInfoDimensions);
rect._bottomRight.x = rect._topLeft.x + textInfoDimensions._width;
rect._bottomRight.y = rect._topLeft.y + charHeight;
}
bool BaseMenuSystem::calcMenuItemMousePos(uint menuItemIndex, Common::Point &pt) {
if (menuItemIndex < _hoveredMenuItemIndex3 || menuItemIndex >= _hoveredMenuItemIndex3 + _menuItemCount)
return false;
WRect rect;
calcMenuItemRect(menuItemIndex - _hoveredMenuItemIndex3 + 1, rect);
pt.x = rect._topLeft.x;
pt.y = rect._topLeft.y + (rect._bottomRight.y - rect._topLeft.y) / 2;
return true;
}
bool BaseMenuSystem::calcMenuItemIndexAtPoint(Common::Point pt, uint &menuItemIndex) {
WRect rect;
calcMenuItemRect(1, rect);
uint index = _hoveredMenuItemIndex3 + (pt.y - rect._topLeft.y) / (rect._bottomRight.y - rect._topLeft.y);
if (pt.y < rect._topLeft.y || pt.x < rect._topLeft.x || pt.x > rect._bottomRight.x ||
index > _field54 || index > _hoveredMenuItemIndex3 + _menuItemCount - 1)
return false;
menuItemIndex = index;
return true;
}
void BaseMenuSystem::setMousePos(Common::Point &mousePos) {
_vm->_input->setCursorPosition(mousePos);
Control *mouseCursor = _vm->getObjectControl(Illusions::CURSOR_OBJECT_ID);
mouseCursor->_actor->_position = mousePos;
}
void BaseMenuSystem::activateMenu(BaseMenu *menu) {
_activeMenu = menu;
// TODO: Run menu enter callback if necessary
_menuLinesCount = menu->getHeaderLinesCount();
menu->_field2C18 = menu->getMenuItemsCount();
_hoveredMenuItemIndex3 = 1;
_field54 = menu->_field2C18;
uint v2 = drawMenuText(menu);
if (menu->_field2C18 <= v2)
_menuItemCount = menu->_field2C18;
else
_menuItemCount = v2;
}
void BaseMenuSystem::initActorHoverBackground() {
Control *v0 = _vm->getObjectControl(0x4013E);
if (!v0) {
WidthHeight dimensions;
dimensions._width = 300;
dimensions._height = 15;
if (_vm->getGameId() == kGameIdBBDOU) {
_vm->_controls->placeSequenceLessActor(0x4013E, Common::Point(0, 0), dimensions, 91);
} else {
_vm->_controls->placeSequenceLessActor(0x4013E, Common::Point(0, 0), dimensions, 18);
}
v0 = _vm->getObjectControl(0x4013E);
v0->_flags |= 8;
}
placeActorHoverBackground();
v0->appearActor();
}
void BaseMenuSystem::placeActorHoverBackground() {
Control *v0 = _vm->getObjectControl(0x4013E);
v0->fillActor(0);
WidthHeight textInfoDimensions;
_vm->_screenText->getTextInfoDimensions(textInfoDimensions);
if (_activeMenu->_backgroundColor && _activeMenu->_borderColor != _activeMenu->_backgroundColor)
textInfoDimensions._width -= 6;
WidthHeight frameDimensions;
v0->getActorFrameDimensions(frameDimensions);
FontResource *font = _vm->_dict->findFont(_activeMenu->_fontId);
int charHeight = font->getCharHeight() + font->getLineIncr();
if (frameDimensions._height < charHeight)
charHeight = frameDimensions._height;
v0->drawActorRect(Common::Rect(textInfoDimensions._width - 1, charHeight), _activeMenu->_fieldE);
updateActorHoverBackground();
}
void BaseMenuSystem::updateActorHoverBackground() {
Control *v0 = _vm->getObjectControl(0x4013E);
WRect rect;
calcMenuItemRect(_hoveredMenuItemIndex2 - _hoveredMenuItemIndex3 + 1, rect);
v0->setActorPosition(rect._topLeft);
}
void BaseMenuSystem::hideActorHoverBackground() {
Control *v0 = _vm->getObjectControl(0x4013E);
if (v0)
v0->disappearActor();
}
void BaseMenuSystem::initActorTextColorRect() {
Control *v0 = _vm->getObjectControl(0x40143);
if (!v0) {
WidthHeight dimensions;
if (_vm->getGameId() == kGameIdBBDOU) {
dimensions._width = 420;
dimensions._height = 180;
_vm->_controls->placeSequenceLessActor(0x40143, Common::Point(0, 0), dimensions, 90);
} else {
dimensions._width = 300;
dimensions._height = 180;
_vm->_controls->placeSequenceLessActor(0x40143, Common::Point(0, 0), dimensions, 17);
}
v0 = _vm->getObjectControl(0x40143);
v0->_flags |= 8;
}
placeActorTextColorRect();
v0->appearActor();
}
void BaseMenuSystem::placeActorTextColorRect() {
Control *v0 = _vm->getObjectControl(0x40143);
v0->fillActor(0);
Common::Point textInfoPosition;
WidthHeight textInfoDimensions;
_vm->_screenText->getTextInfoPosition(textInfoPosition);
_vm->_screenText->getTextInfoDimensions(textInfoDimensions);
if (_activeMenu->_backgroundColor && _activeMenu->_borderColor != _activeMenu->_backgroundColor) {
textInfoDimensions._width -= 2;
textInfoDimensions._height -= 6;
}
v0->setActorPosition(textInfoPosition);
v0->drawActorRect(Common::Rect(textInfoDimensions._width - 1, textInfoDimensions._height - 1), _activeMenu->_textColor);
}
void BaseMenuSystem::hideActorTextColorRect() {
Control *v0 = _vm->getObjectControl(0x40143);
if (v0)
v0->disappearActor();
}
void BaseMenuSystem::openMenu(BaseMenu *menu) {
_isActive = true;
_menuStack.clear();
_cursorInitialVisibleFlag = initMenuCursor();
_savedCursorPos = _vm->_input->getCursorPosition();
_savedGameState = getGameState();
Control *cursorControl = _vm->getObjectControl(Illusions::CURSOR_OBJECT_ID);
_savedCursorActorIndex = cursorControl->_actor->_actorIndex;
_savedCursorSequenceId = cursorControl->_actor->_sequenceId;
setMenuCursorNum(1);
if (_vm->getGameId() == kGameIdDuckman) {
setGameState(4);
} else if (_vm->getGameId() == kGameIdBBDOU) {
setGameState(3);
}
activateMenu(menu);
_hoveredMenuItemIndex = _hoveredMenuItemIndex3;
_hoveredMenuItemIndex2 = _hoveredMenuItemIndex3;
setMouseCursorToMenuItem(_hoveredMenuItemIndex);
initActorHoverBackground();
initActorTextColorRect();
_vm->_input->discardAllEvents();
}
void BaseMenuSystem::closeMenu() {
while (!_menuStack.empty()) {
_vm->_screenText->removeText();
_menuStack.pop();
}
_vm->_screenText->removeText();
hideActorHoverBackground();
hideActorTextColorRect();
Control *mouseCursor = _vm->getObjectControl(Illusions::CURSOR_OBJECT_ID);
setGameState(_savedGameState);
mouseCursor->_actor->_actorIndex = _savedCursorActorIndex;
mouseCursor->_actor->_position = _savedCursorPos;
setMousePos(_savedCursorPos);
mouseCursor->startSequenceActor(_savedCursorSequenceId, 2, 0);
if (_cursorInitialVisibleFlag)
mouseCursor->disappearActor();
_vm->_input->discardAllEvents();
_isActive = false;
}
void BaseMenuSystem::handleClick(uint menuItemIndex, const Common::Point &mousePos) {
debug(0, "BaseMenuSystem::handleClick() menuItemIndex: %d click point: (%d, %d)", menuItemIndex, mousePos.x, mousePos.y);
if (menuItemIndex == 0) {
playSoundEffect14();
return;
}
MenuItem *menuItem = _activeMenu->getMenuItem(menuItemIndex - 1);
menuItem->executeAction(mousePos);
}
uint BaseMenuSystem::drawMenuText(BaseMenu *menu) {
MenuTextBuilder *menuTextBuilder = new MenuTextBuilder();
uint lineCount = 0;
for (uint i = 0; i < menu->getHeaderLinesCount(); ++i) {
menuTextBuilder->appendString(menu->getHeaderLine(i));
menuTextBuilder->appendNewLine();
}
for (uint i = _hoveredMenuItemIndex3; i <= _field54; ++i) {
menuTextBuilder->appendString(menu->getMenuItem(i - 1)->getText());
if (i + 1 <= menu->getMenuItemsCount())
menuTextBuilder->appendNewLine();
++lineCount;
}
menuTextBuilder->finalize();
uint16 *text = menuTextBuilder->getText();
Common::Point textPt;
int16 v9 = 0;
if (menu->_backgroundColor)
v9 = 4;
textPt.x = v9;
textPt.y = v9;
uint flags = TEXT_FLAG_LEFT_ALIGN;
if (menu->_backgroundColor != menu->_borderColor)
flags |= TEXT_FLAG_BORDER_DECORATION;
WidthHeight dimensions;
if (_vm->getGameId() == kGameIdDuckman) {
dimensions._width = 300;
dimensions._height = 180;
} else if (_vm->getGameId() == kGameIdBBDOU) {
dimensions._width = 580;
dimensions._height = 420;
}
uint16 *outTextPtr;
if (!_vm->_screenText->insertText(text, menu->_fontId, dimensions, textPt, flags, menu->_backgroundColor, menu->_borderColor, 0xFF, 0xFF, 0xFF, outTextPtr)) {
--lineCount;
for ( ; *outTextPtr; ++outTextPtr) {
if (*outTextPtr == 13)
--lineCount;
}
}
delete menuTextBuilder;
return lineCount;
}
void BaseMenuSystem::update(Control *cursorControl) {
Common::Point mousePos = _vm->_input->getCursorPosition();
setMousePos(mousePos);
uint newHoveredMenuItemIndex;
bool resetTimeOut = false;
if (calcMenuItemIndexAtPoint(mousePos, newHoveredMenuItemIndex)) {
if (newHoveredMenuItemIndex != _hoveredMenuItemIndex) {
if (_hoveredMenuItemIndex == 0)
initActorHoverBackground();
_hoveredMenuItemIndex = newHoveredMenuItemIndex;
_hoveredMenuItemIndex2 = newHoveredMenuItemIndex;
setMenuCursorNum(2);
updateActorHoverBackground();
resetTimeOut = true;
}
} else if (_hoveredMenuItemIndex != 0) {
setMenuCursorNum(1);
hideActorHoverBackground();
_hoveredMenuItemIndex = 0;
resetTimeOut = true;
}
if (_vm->_input->hasNewEvents())
resetTimeOut = true;
if (_vm->_input->pollEvent(kEventLeftClick)) {
handleClick(_hoveredMenuItemIndex, mousePos);
} else if (_vm->_input->pollEvent(kEventAbort) && _activeMenu->_defaultMenuItemIndex) {
handleClick(_activeMenu->_defaultMenuItemIndex, mousePos);
} else if (_vm->_input->pollEvent(kEventUp)) {
// TODO handleUpKey();
if (_hoveredMenuItemIndex == 1) {
_hoveredMenuItemIndex = _activeMenu->getMenuItemsCount();
} else {
_hoveredMenuItemIndex--;
}
setMouseCursorToMenuItem(_hoveredMenuItemIndex);
_hoveredMenuItemIndex2 = _hoveredMenuItemIndex;
updateActorHoverBackground();
playSoundEffect(0xC);
} else if (_vm->_input->pollEvent(kEventDown)) {
// TODO handleDownKey();
if (_hoveredMenuItemIndex == _activeMenu->getMenuItemsCount()) {
_hoveredMenuItemIndex = 1;
} else {
_hoveredMenuItemIndex++;
}
setMouseCursorToMenuItem(_hoveredMenuItemIndex);
_hoveredMenuItemIndex2 = _hoveredMenuItemIndex;
updateActorHoverBackground();
playSoundEffect(0xC);
}
updateTimeOut(resetTimeOut);
}
void BaseMenuSystem::setTimeOutDuration(uint32 duration, uint timeOutMenuChoiceIndex) {
if (duration > 0) {
_isTimeOutEnabled = true;
_isTimeOutReached = false;
_timeOutDuration = duration;
_timeOutMenuChoiceIndex = timeOutMenuChoiceIndex;
_timeOutStartTime = getCurrentTime();
_timeOutEndTime = duration + _timeOutStartTime;
} else {
_isTimeOutEnabled = false;
}
}
void BaseMenuSystem::setMenuCallerThreadId(uint32 menuCallerThreadId) {
_menuCallerThreadId = menuCallerThreadId;
}
void BaseMenuSystem::setMenuChoiceOffsets(MenuChoiceOffsets menuChoiceOffsets, int16 *menuChoiceOffset) {
_menuChoiceOffsets = menuChoiceOffsets;
_menuChoiceOffset = menuChoiceOffset;
}
void BaseMenuSystem::setSavegameSlotNum(int slotNum) {
_vm->_savegameSlotNum = slotNum;
}
void BaseMenuSystem::setSavegameDescription(Common::String desc) {
_vm->_savegameDescription = desc;
}
void BaseMenuSystem::updateTimeOut(bool resetTimeOut) {
if (!_isTimeOutEnabled)
return;
if (_menuStack.empty()) {
if (_isTimeOutReached) {
resetTimeOut = true;
_isTimeOutReached = false;
}
} else if (!_isTimeOutReached) {
_isTimeOutReached = true;
}
if (!_isTimeOutReached) {
if (resetTimeOut) {
_timeOutStartTime = getCurrentTime();
_timeOutEndTime = _timeOutDuration + _timeOutStartTime;
} else if (isTimerExpired(_timeOutStartTime, _timeOutEndTime)) {
_isTimeOutEnabled = false;
selectMenuChoiceIndex(_timeOutMenuChoiceIndex);
}
}
}
void BaseMenuSystem::redrawMenuText(BaseMenu *menu) {
_vm->_screenText->removeText();
drawMenuText(menu);
}
bool BaseMenuSystem::calcMenuItemTextPositionAtPoint(Common::Point pt, int &offset) {
uint menuItemIndex;
if (!calcMenuItemIndexAtPoint(pt, menuItemIndex)) {
return false;
}
WRect rect;
MenuItem *menuItem = _activeMenu->getMenuItem(menuItemIndex - 1);
calcMenuItemRect(menuItemIndex, rect);
int x = pt.x - rect._topLeft.x;
Common::String text = menuItem->getText();
FontResource *font = _vm->_dict->findFont(_activeMenu->_fontId);
int curX = 0;
for (uint i = 0; i < text.size(); i++) {
int16 w = font->getCharInfo(text[i])->_width;
if (x >= curX && x <= curX + w) {
offset = i;
return true;
}
curX = curX + w;
}
return false;
}
// MenuTextBuilder
MenuTextBuilder::MenuTextBuilder() : _pos(0) {
}
void MenuTextBuilder::appendString(const Common::String &value) {
for (uint i = 0; i < value.size(); ++i) {
_text[_pos++] = value[i];
}
}
void MenuTextBuilder::appendNewLine() {
_text[_pos++] = '\r';
}
void MenuTextBuilder::finalize() {
_text[_pos] = '\0';
}
// BaseMenuAction
BaseMenuAction::BaseMenuAction(BaseMenuSystem *menuSystem)
: _menuSystem(menuSystem) {
}
// MenuActionEnterMenu
MenuActionEnterMenu::MenuActionEnterMenu(BaseMenuSystem *menuSystem, int menuId)
: BaseMenuAction(menuSystem), _menuId(menuId) {
}
void MenuActionEnterMenu::execute() {
_menuSystem->enterSubMenuById(_menuId);
}
// MenuActionLeaveMenu
MenuActionLeaveMenu::MenuActionLeaveMenu(BaseMenuSystem *menuSystem)
: BaseMenuAction(menuSystem) {
}
void MenuActionLeaveMenu::execute() {
_menuSystem->leaveMenu();
}
// MenuActionReturnChoice
MenuActionReturnChoice::MenuActionReturnChoice(BaseMenuSystem *menuSystem, uint choiceIndex)
: BaseMenuAction(menuSystem), _choiceIndex(choiceIndex) {
}
void MenuActionReturnChoice::execute() {
_menuSystem->playSoundEffect13();
_menuSystem->selectMenuChoiceIndex(_choiceIndex);
}
// MenuActionEnterQueryMenu
MenuActionEnterQueryMenu::MenuActionEnterQueryMenu(BaseMenuSystem *menuSystem, int menuId, uint confirmationChoiceIndex)
: BaseMenuAction(menuSystem), _menuId(menuId), _confirmationChoiceIndex(confirmationChoiceIndex) {
}
void MenuActionEnterQueryMenu::execute() {
_menuSystem->setQueryConfirmationChoiceIndex(_confirmationChoiceIndex);
_menuSystem->enterSubMenuById(_menuId);
}
// MenuActionLoadGame
MenuActionLoadGame::MenuActionLoadGame(BaseMenuSystem *menuSystem, uint choiceIndex)
: BaseMenuAction(menuSystem), _choiceIndex(choiceIndex) {
}
void MenuActionLoadGame::execute() {
GUI::SaveLoadChooser *dialog;
Common::String desc;
int slot;
dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false);
slot = dialog->runModalWithCurrentTarget();
delete dialog;
if (slot >= 0) {
_menuSystem->setSavegameSlotNum(slot);
_menuSystem->selectMenuChoiceIndex(_choiceIndex);
}
}
// MenuActionSaveGame
MenuActionSaveGame::MenuActionSaveGame(BaseMenuSystem *menuSystem, uint choiceIndex)
: BaseMenuAction(menuSystem), _choiceIndex(choiceIndex) {
}
void MenuActionSaveGame::execute() {
GUI::SaveLoadChooser *dialog;
Common::String desc;
int slot;
dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
slot = dialog->runModalWithCurrentTarget();
desc = dialog->getResultString();
delete dialog;
if (slot >= 0) {
_menuSystem->setSavegameSlotNum(slot);
_menuSystem->setSavegameDescription(desc);
_menuSystem->selectMenuChoiceIndex(_choiceIndex);
}
}
} // End of namespace Illusions

View File

@@ -0,0 +1,253 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_MENUSYSTEM_H
#define ILLUSIONS_MENUSYSTEM_H
#include "illusions/actor.h"
#include "illusions/graphics.h"
#include "illusions/resources/fontresource.h"
#include "common/array.h"
#include "common/rect.h"
#include "common/stack.h"
#include "common/str.h"
#include "graphics/surface.h"
namespace Illusions {
class IllusionsEngine;
class BaseMenuSystem;
class BaseMenuAction;
const uint kMenuTextSize = 4096;
class MenuItem {
public:
MenuItem(const Common::String &text, BaseMenuAction *action);
~MenuItem();
void executeAction(const Common::Point &point);
const Common::String& getText() const { return _text; }
void setText(const Common::String &text) { _text = text; }
const Common::Point& getMouseClickPoint() { return _mouseClickPoint; };
protected:
Common::String _text;
BaseMenuAction *_action;
Common::Point _mouseClickPoint;
};
class BaseMenu {
public:
BaseMenu(BaseMenuSystem *menuSystem, uint32 fontId, byte backgroundColor, byte borderColor, byte textColor, byte fieldE,
uint defaultMenuItemIndex);
virtual ~BaseMenu();
void addText(const Common::String &text);
void addMenuItem(MenuItem *menuItem);
uint getHeaderLinesCount();
const Common::String& getHeaderLine(uint index);
uint getMenuItemsCount();
MenuItem *getMenuItem(uint index);
virtual void enterMenu();
public://protected://TODO
typedef Common::Array<MenuItem*> MenuItems;
BaseMenuSystem *_menuSystem;
uint32 _fontId;
byte _backgroundColor, _borderColor, _textColor, _fieldE;
uint _field2C18;
uint _defaultMenuItemIndex;
Common::Array<Common::String> _text;
MenuItems _menuItems;
};
class MenuStack : public Common::Stack<BaseMenu*> {
};
typedef Common::Array<int16> MenuChoiceOffsets;
class BaseMenuSystem {
public:
BaseMenuSystem(IllusionsEngine *vm);
virtual ~BaseMenuSystem();
void playSoundEffect13();
void playSoundEffect14();
void selectMenuChoiceIndex(uint choiceIndex);
void leaveMenu();
void enterSubMenu(BaseMenu *menu);
void leaveSubMenu();
void enterSubMenuById(int menuId);
uint getQueryConfirmationChoiceIndex() const;
void setQueryConfirmationChoiceIndex(uint queryConfirmationChoiceIndex);
bool isActive() const { return _isActive; }
void openMenu(BaseMenu *menu);
void closeMenu();
void handleClick(uint menuItemIndex, const Common::Point &mousePos);
uint drawMenuText(BaseMenu *menu);
void redrawMenuText(BaseMenu *menu);
void update(Control *cursorControl);
void setTimeOutDuration(uint32 duration, uint timeOutMenuChoiceIndex);
void setMenuCallerThreadId(uint32 menuCallerThreadId);
void setMenuChoiceOffsets(MenuChoiceOffsets menuChoiceOffsets, int16 *menuChoiceOffset);
void setSavegameSlotNum(int slotNum);
void setSavegameDescription(Common::String desc);
bool calcMenuItemTextPositionAtPoint(Common::Point pt, int &offset);
virtual bool initMenuCursor() = 0;
virtual int getGameState() = 0;
virtual void setGameState(int gameState) = 0;
virtual void setMenuCursorNum(int cursorNum) = 0;
protected:
IllusionsEngine *_vm;
MenuStack _menuStack;
uint32 _menuCallerThreadId;
bool _isTimeOutEnabled;
bool _isTimeOutReached;
uint32 _timeOutDuration;
uint _timeOutMenuChoiceIndex;
uint32 _timeOutStartTime;
uint32 _timeOutEndTime;
Common::Point _savedCursorPos;
bool _cursorInitialVisibleFlag;
int _savedGameState;
int _savedCursorActorIndex;
int _savedCursorSequenceId;
bool _isActive;
MenuChoiceOffsets _menuChoiceOffsets;
int16 *_menuChoiceOffset;
uint _queryConfirmationChoiceIndex;
uint _field54;
uint _menuLinesCount;
uint _menuItemCount;
uint _hoveredMenuItemIndex;
uint _hoveredMenuItemIndex2;
uint _hoveredMenuItemIndex3;
BaseMenu *_activeMenu;
void setMouseCursorToMenuItem(int menuItemIndex);
void calcMenuItemRect(uint menuItemIndex, WRect &rect);
bool calcMenuItemMousePos(uint menuItemIndex, Common::Point &pt);
bool calcMenuItemIndexAtPoint(Common::Point pt, uint &menuItemIndex);
void setMousePos(Common::Point &mousePos);
void activateMenu(BaseMenu *menu);
void updateTimeOut(bool resetTimeOut);
void initActorHoverBackground();
void placeActorHoverBackground();
void updateActorHoverBackground();
void hideActorHoverBackground();
void initActorTextColorRect();
void placeActorTextColorRect();
void hideActorTextColorRect();
virtual BaseMenu *getMenuById(int menuId) = 0;
virtual void playSoundEffect(int sfxId) = 0;
};
class MenuTextBuilder {
public:
MenuTextBuilder();
void appendString(const Common::String &value);
void appendNewLine();
void finalize();
uint16 *getText() { return _text; }
protected:
uint16 _text[kMenuTextSize];
uint _pos;
};
// Menu actions
class BaseMenuAction {
public:
BaseMenuAction(BaseMenuSystem *menuSystem);
virtual ~BaseMenuAction() {}
virtual void execute() = 0;
protected:
BaseMenuSystem *_menuSystem;
};
// Type 1: Enter a submenu
class MenuActionEnterMenu : public BaseMenuAction {
public:
MenuActionEnterMenu(BaseMenuSystem *menuSystem, int menuId);
void execute() override;
protected:
int _menuId;
};
// Type 4: Leave a submenu or the whole menu if on the main menu level
class MenuActionLeaveMenu : public BaseMenuAction {
public:
MenuActionLeaveMenu(BaseMenuSystem *menuSystem);
void execute() override;
};
// Type 5: Return a menu choice index and exit the menu
class MenuActionReturnChoice : public BaseMenuAction {
public:
MenuActionReturnChoice(BaseMenuSystem *menuSystem, uint choiceIndex);
void execute() override;
protected:
int _choiceIndex;
};
// Type 8: Return a menu choice index and exit the menu after displaying a query message
class MenuActionEnterQueryMenu : public BaseMenuAction {
public:
MenuActionEnterQueryMenu(BaseMenuSystem *menuSystem, int menuId, uint confirmationChoiceIndex);
void execute() override;
protected:
int _menuId;
uint _confirmationChoiceIndex;
};
class MenuActionLoadGame : public BaseMenuAction {
public:
MenuActionLoadGame(BaseMenuSystem *menuSystem, uint choiceIndex);
void execute() override;
protected:
uint _choiceIndex;
};
class MenuActionSaveGame : public BaseMenuAction {
public:
MenuActionSaveGame(BaseMenuSystem *menuSystem, uint choiceIndex);
void execute() override;
protected:
uint _choiceIndex;
};
} // End of namespace Illusions
#endif // ILLUSIONS_MENUSYSTEM_H

View File

@@ -0,0 +1,233 @@
/* 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/>.
*
*/
#include "illusions/illusions.h"
#include "illusions/bbdou/illusions_bbdou.h"
#include "illusions/duckman/illusions_duckman.h"
#include "backends/keymapper/action.h"
#include "backends/keymapper/keymapper.h"
#include "backends/keymapper/standard-actions.h"
#include "common/config-manager.h"
#include "common/translation.h"
#include "engines/advancedDetector.h"
#include "common/savefile.h"
#include "common/system.h"
#include "base/plugins.h"
#include "graphics/thumbnail.h"
#include "illusions/detection.h"
namespace Illusions {
int IllusionsEngine::getGameId() const {
return _gameDescription->gameId;
}
Common::Language IllusionsEngine::getGameLanguage() const {
return _gameDescription->desc.language;
}
} // End of namespace Illusions
class IllusionsMetaEngine : public AdvancedMetaEngine<Illusions::IllusionsGameDescription> {
public:
const char *getName() const override {
return "illusions";
}
bool hasFeature(MetaEngineFeature f) const override;
Common::Error createInstance(OSystem *syst, Engine **engine, const Illusions::IllusionsGameDescription *desc) const override;
int getMaximumSaveSlot() const override;
SaveStateList listSaves(const char *target) const override;
SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const override;
bool removeSaveState(const char *target, int slot) const override;
Common::KeymapArray initKeymaps(const char *target) const override;
};
bool IllusionsMetaEngine::hasFeature(MetaEngineFeature f) const {
return
(f == kSupportsListSaves) ||
(f == kSupportsDeleteSave) ||
(f == kSupportsLoadingDuringStartup) ||
(f == kSavesSupportMetaInfo) ||
(f == kSavesSupportThumbnail) ||
(f == kSimpleSavesNames) ||
(f == kSavesSupportCreationDate);
}
bool IllusionsMetaEngine::removeSaveState(const char *target, int slot) const {
Common::String fileName = Common::String::format("%s.%03d", target, slot);
return g_system->getSavefileManager()->removeSavefile(fileName);
}
Common::KeymapArray IllusionsMetaEngine::initKeymaps(const char *target) const {
using namespace Common;
using namespace Illusions;
Keymap *engineKeyMap = new Keymap(Keymap::kKeymapTypeGame, "illusions-default", _("Default keymappings"));
Keymap *gameKeyMap = new Keymap(Keymap::kKeymapTypeGame, "game-shortcuts", _("Game keymappings"));
Common::Action *act;
act = new Action(kStandardActionLeftClick, _("Left click"));
act->setLeftClickEvent();
act->addDefaultInputMapping("MOUSE_LEFT");
act->addDefaultInputMapping("JOY_A");
act->addDefaultInputMapping("RETURN");
engineKeyMap->addAction(act);
act = new Action(kStandardActionRightClick, _("Right click"));
act->setRightClickEvent();
act->addDefaultInputMapping("MOUSE_RIGHT");
act->addDefaultInputMapping("JOY_B");
act->addDefaultInputMapping("BACKSPACE");
engineKeyMap->addAction(act);
act = new Action("CRSRUP", _("Move cursor up"));
act->setCustomEngineActionEvent(kActionCursorUp);
act->addDefaultInputMapping("JOY_UP");
act->addDefaultInputMapping("UP");
act->allowKbdRepeats();
gameKeyMap->addAction(act);
act = new Action("CRSRDOWN", _("Move cursor down"));
act->setCustomEngineActionEvent(kActionCursorDown);
act->addDefaultInputMapping("JOY_DOWN");
act->addDefaultInputMapping("DOWN");
act->allowKbdRepeats();
gameKeyMap->addAction(act);
act = new Action("CRSRLEFT", _("Move cursor left"));
act->setCustomEngineActionEvent(kActionCursorLeft);
act->addDefaultInputMapping("JOY_LEFT");
act->addDefaultInputMapping("LEFT");
act->allowKbdRepeats();
gameKeyMap->addAction(act);
act = new Action("CRSRRIGHT", _("Move cursor right"));
act->setCustomEngineActionEvent(kActionCursorRight);
act->addDefaultInputMapping("JOY_RIGHT");
act->addDefaultInputMapping("RIGHT");
act->allowKbdRepeats();
gameKeyMap->addAction(act);
act = new Action("ABORT", _("Abort"));
act->setCustomEngineActionEvent(kActionAbort);
act->addDefaultInputMapping("ESCAPE");
act->addDefaultInputMapping("JOY_X");
gameKeyMap->addAction(act);
act = new Action("SKIP", _("Skip"));
act->setCustomEngineActionEvent(kActionSkip);
act->addDefaultInputMapping("SPACE");
gameKeyMap->addAction(act);
act = new Action("INVENTORY", _("Open inventory"));
act->setCustomEngineActionEvent(kActionInventory);
act->addDefaultInputMapping("TAB");
act->addDefaultInputMapping("JOY_Y");
gameKeyMap->addAction(act);
act = new Action("ENCHEAT", _("Enable cheat mode"));
act->setCustomEngineActionEvent(kActionCheatMode);
act->addDefaultInputMapping("F1");
act->addDefaultInputMapping("JOY_LEFT_SHOULDER");
gameKeyMap->addAction(act);
KeymapArray keymaps(2);
keymaps[0] = engineKeyMap;
keymaps[1] = gameKeyMap;
return keymaps;
}
int IllusionsMetaEngine::getMaximumSaveSlot() const {
return 999;
}
SaveStateList IllusionsMetaEngine::listSaves(const char *target) const {
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
Illusions::IllusionsEngine::SaveHeader header;
Common::String pattern = target;
pattern += ".???";
Common::StringArray filenames;
filenames = saveFileMan->listSavefiles(pattern.c_str());
SaveStateList saveList;
for (const auto &file : filenames) {
// Obtain the last 3 digits of the filename, since they correspond to the save slot
int slotNum = atoi(file.c_str() + file.size() - 3);
if (slotNum >= 0 && slotNum <= 999) {
Common::InSaveFile *in = saveFileMan->openForLoading(file.c_str());
if (in) {
if (Illusions::IllusionsEngine::readSaveHeader(in, header) == Illusions::IllusionsEngine::kRSHENoError) {
saveList.push_back(SaveStateDescriptor(this, slotNum, header.description));
}
delete in;
}
}
}
Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
SaveStateDescriptor IllusionsMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
Common::String filename = Illusions::IllusionsEngine::getSavegameFilename(target, slot);
Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(filename.c_str());
if (in) {
Illusions::IllusionsEngine::SaveHeader header;
Illusions::IllusionsEngine::kReadSaveHeaderError error;
error = Illusions::IllusionsEngine::readSaveHeader(in, header, false);
delete in;
if (error == Illusions::IllusionsEngine::kRSHENoError) {
SaveStateDescriptor desc(this, slot, header.description);
desc.setThumbnail(header.thumbnail);
desc.setSaveDate(header.saveDate & 0xFFFF, (header.saveDate >> 16) & 0xFF, (header.saveDate >> 24) & 0xFF);
desc.setSaveTime((header.saveTime >> 16) & 0xFF, (header.saveTime >> 8) & 0xFF);
desc.setPlayTime(header.playTime * 1000);
return desc;
}
}
return SaveStateDescriptor();
}
Common::Error IllusionsMetaEngine::createInstance(OSystem *syst, Engine **engine, const Illusions::IllusionsGameDescription *gd) const {
switch (gd->gameId) {
case Illusions::kGameIdBBDOU:
*engine = new Illusions::IllusionsEngine_BBDOU(syst, gd);
break;
case Illusions::kGameIdDuckman:
*engine = new Illusions::IllusionsEngine_Duckman(syst, gd);
break;
default:
return Common::kUnsupportedGameidError;
}
return Common::kNoError;
}
#if PLUGIN_ENABLED_DYNAMIC(ILLUSIONS)
REGISTER_PLUGIN_DYNAMIC(ILLUSIONS, PLUGIN_TYPE_ENGINE, IllusionsMetaEngine);
#else
REGISTER_PLUGIN_STATIC(ILLUSIONS, PLUGIN_TYPE_ENGINE, IllusionsMetaEngine);
#endif

View File

@@ -0,0 +1,82 @@
MODULE := engines/illusions
MODULE_OBJS := \
actor.o \
bbdou/bbdou_bubble.o \
bbdou/bbdou_cursor.o \
bbdou/bbdou_credits.o \
bbdou/bbdou_credits_staticdata.o \
bbdou/bbdou_foodctl.o \
bbdou/bbdou_inventory.o \
bbdou/bbdou_menukeys.o \
bbdou/bbdou_specialcode.o \
bbdou/bbdou_videoplayer.o \
bbdou/gamestate_bbdou.o \
bbdou/bbdou_triggerfunctions.o \
bbdou/illusions_bbdou.o \
bbdou/menusystem_bbdou.o \
bbdou/scriptopcodes_bbdou.o \
camera.o \
console.o \
cursor.o \
dictionary.o \
duckman/duckman_credits.o \
duckman/duckman_dialog.o \
duckman/duckman_inventory.o \
duckman/duckman_screenshakereffects.o \
duckman/duckman_specialcode.o \
duckman/duckman_videoplayer.o \
duckman/gamestate_duckman.o \
duckman/illusions_duckman.o \
duckman/menusystem_duckman.o \
duckman/propertytimers.o \
duckman/scriptopcodes_duckman.o \
fileresourcereader.o \
fixedpoint.o \
gamarchive.o \
gamestate.o \
gamresourcereader.o \
graphics.o \
illusions.o \
input.o \
metaengine.o \
menusystem.o \
pathfinder.o \
resources/actorresource.o \
resources/backgroundresource.o \
resources/fontresource.o \
resources/genericresource.o \
resources/midiresource.o \
resources/scriptresource.o \
resources/soundresource.o \
resources/talkresource.o \
resourcesystem.o \
saveload.o \
screen.o \
screentext.o \
scriptstack.o \
scriptopcodes.o \
sequenceopcodes.o \
sound.o \
specialcode.o \
textdrawer.o \
threads/abortablethread.o \
threads/causethread_duckman.o \
threads/scriptthread.o \
threads/talkthread.o \
threads/talkthread_duckman.o \
threads/timerthread.o \
thread.o \
time.o \
updatefunctions.o
# This module can be built as a plugin
ifeq ($(ENABLE_ILLUSIONS), DYNAMIC_PLUGIN)
PLUGIN := 1
endif
# Include common rules
include $(srcdir)/rules.mk
# Detection objects
DETECT_OBJS += $(MODULE)/detection.o

View File

@@ -0,0 +1,370 @@
/* 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/>.
*
*/
#include "illusions/illusions.h"
#include "illusions/pathfinder.h"
#include "camera.h"
namespace Illusions {
PointArray *PathFinder::findPath(Camera *camera, Common::Point sourcePt, Common::Point destPt,
PointArray *walkPoints, PathLines *walkRects, WidthHeight bgDimensions) {
Common::Point cameraPt = camera->getScreenOffset();
_screenRect.p0 = cameraPt;
_screenRect.p1.x = cameraPt.x + 320; //TODO fix me get screen dimensions here.
_screenRect.p1.y = cameraPt.y + 200;
_walkPoints = walkPoints;
_walkRects = walkRects;
_bgDimensions = bgDimensions;
return findPathInternal(sourcePt, destPt);
}
PointArray *PathFinder::findPathInternal(Common::Point sourcePt, Common::Point destPt) {
PathLine line;
PointArray *foundPath = new PointArray();
line.p0 = sourcePt;
line.p1 = destPt;
if (_walkRects && _walkPoints && isLineBlocked(line)) {
Common::Point nextStartPt = sourcePt, outPt;
if (!findValidDestLine(destPt)) {
findValidDestPt(destPt);
line.p1 = destPt;
}
_pathBytes = (byte*)calloc(1, _walkPoints->size());
bool done = false;
while (!done) {
line.p0 = nextStartPt;
if (!isLineBlocked(line)) {
foundPath->push_back(destPt);
done = true;
} else {
if (foundPath->size() < _walkPoints->size() + 2 && findClosestPt(nextStartPt, outPt, destPt)) {
foundPath->push_back(outPt);
nextStartPt = outPt;
} else {
if (foundPath->size() == 0)
foundPath->push_back(sourcePt);
done = true;
}
}
}
free(_pathBytes);
postProcess(sourcePt, foundPath);
} else {
foundPath->push_back(destPt);
}
return foundPath;
}
void PathFinder::postProcess(Common::Point sourcePt, PointArray *foundPath) {
// For each three points A, B and C, removes B if the line between A and C is not blocked
for (uint index = 0; index + 2 < foundPath->size(); ++index) {
PathLine line;
line.p0 = index == 0 ? sourcePt : (*foundPath)[index - 1];
line.p1 = (*foundPath)[index + 1];
if (!isLineBlocked(line)) {
debug("remove point");
foundPath->remove_at(index);
}
}
}
bool PathFinder::isLineBlocked(PathLine &line) {
for (uint i = 0; i < _walkRects->size(); ++i) {
if (calcLineStatus(line, (*_walkRects)[i], nullptr) != 3)
return true;
}
return false;
}
int PathFinder::calcLineDistance(PathLine &line) {
int16 deltaX = line.p0.x - line.p1.x;
int16 deltaY = line.p0.y - line.p1.y;
if (deltaX != 0 || deltaY != 0)
return sqrt(deltaX * deltaX + deltaY * deltaY);
return 0;
}
bool PathFinder::findClosestPt(Common::Point &sourcePt, Common::Point &closestPt, Common::Point &destPt) {
PathLine sourceLine, destLine;
uint minIndex = 0;
int minDistance = 0xFFFF;
sourceLine.p0 = sourcePt;
destLine.p1 = destPt;
for (uint i = 0; i < _walkPoints->size(); ++i) {
sourceLine.p1 = (*_walkPoints)[i];
destLine.p0 = (*_walkPoints)[i];
if (!_pathBytes[i] && !isLineBlocked(sourceLine)) {
int currDistance = calcLineDistance(destLine);
if (currDistance <= minDistance) {
minDistance = currDistance;
minIndex = i + 1;
}
}
}
if (minIndex) {
closestPt = (*_walkPoints)[minIndex - 1];
_pathBytes[minIndex - 1] = 1;
return true;
}
return false;
}
bool PathFinder::findValidDestLine(Common::Point &destPt) {
PathLine destLine;
destLine.p0 = destPt;
for (uint i = 0; i < _walkPoints->size(); ++i) {
destLine.p1 = (*_walkPoints)[i];
if (!isLineBlocked(destLine))
return true;
}
return false;
}
void PathFinder::findValidDestPt(Common::Point &destPt) {
Common::Point minPt, outPt, deltaPt;
int minDistance = 0xFFFF, currDistance;
PathLine destLine;
for (uint i = 0; i < _walkRects->size(); ++i) {
PathLine currRect = (*_walkRects)[i];
//TODO fix this hack. Used here to get xmas tree scene to work.
if (currRect.p1.x > _screenRect.p1.x) {
currRect.p1.x = _screenRect.p1.x;
}
if (currRect.p0.x < _screenRect.p0.x) {
currRect.p0.x = _screenRect.p0.x;
}
WidthHeight rectDimensions = calcRectDimensions(currRect);
adjustRectDimensions(rectDimensions);
clipLineToBg(destPt, rectDimensions, destLine);
if (calcLineStatus(destLine, currRect, &outPt) == 3) {
destLine.p0 = destPt;
destLine.p1 = currRect.p0;
currDistance = calcLineDistance(destLine);
if (currDistance < minDistance) {
minDistance = currDistance;
minPt = currRect.p0;
}
destLine.p0 = destPt;
destLine.p1 = currRect.p1;
currDistance = calcLineDistance(destLine);
if (currDistance < minDistance) {
minDistance = currDistance;
minPt = currRect.p1;
}
} else {
destLine.p0 = destPt;
destLine.p1 = outPt;
currDistance = calcLineDistance(destLine);
if (currDistance < minDistance) {
minDistance = currDistance;
minPt = outPt;
}
}
}
findDeltaPt(minPt, deltaPt);
destPt.x = deltaPt.x + minPt.x;
destPt.y = deltaPt.y + minPt.y;
}
WidthHeight PathFinder::calcRectDimensions(PathLine &rect) {
WidthHeight dimensions;
dimensions._width = rect.p1.x - rect.p0.x;
dimensions._height = rect.p1.y - rect.p0.y;
swapDimensions(dimensions);
return dimensions;
}
void PathFinder::adjustRectDimensions(WidthHeight &dimensions) {
dimensions._width = ABS(dimensions._height) * (dimensions._width < 0 ? -1 : 1);
dimensions._height = ABS(dimensions._width) * (dimensions._height < 0 ? -1 : 1);
if (dimensions._width)
dimensions._width = -dimensions._width;
else
dimensions._height = -dimensions._height;
swapDimensions(dimensions);
}
void PathFinder::swapDimensions(WidthHeight &dimensions) {
if (dimensions._width < 0) {
dimensions._width = -dimensions._width;
dimensions._height = -dimensions._height;
} else if (dimensions._width == 0)
dimensions._height = abs(dimensions._height);
else if (dimensions._height == 0)
dimensions._width = abs(dimensions._width);
}
void PathFinder::clipLineToBg(Common::Point &destPt, WidthHeight &rectDimensions, PathLine &outDestLine) {
if (rectDimensions._height == 0) {
outDestLine.p0.x = 0;
outDestLine.p0.y = destPt.y;
outDestLine.p1.x = _bgDimensions._width;
outDestLine.p1.y = destPt.y;
} else if (rectDimensions._width == 0) {
outDestLine.p0.y = 0;
outDestLine.p0.x = destPt.x;
outDestLine.p1.x = destPt.x;
outDestLine.p1.y = _bgDimensions._height;
} else {
outDestLine.p0 = destPt;
outDestLine.p1.x = destPt.x + rectDimensions._width;
outDestLine.p1.y = destPt.y + rectDimensions._height;
int16 y1 = destPt.y + (rectDimensions._height * -destPt.x / rectDimensions._width);
int16 y2 = destPt.y + (rectDimensions._height * (_bgDimensions._width - destPt.x) / rectDimensions._width);
int16 x1 = destPt.x + (rectDimensions._width * -destPt.y / rectDimensions._height);
int16 x2 = destPt.x + (rectDimensions._width * (_bgDimensions._height - destPt.y) / rectDimensions._height);
if (ABS(rectDimensions._height) <= ABS(rectDimensions._width)) {
outDestLine.p0.y = 0;
outDestLine.p0.x = _bgDimensions._width;
if (x1 < 0 || _bgDimensions._width < x1)
outDestLine.p0.y = y2;
else
outDestLine.p0.x = x1;
outDestLine.p1.x = 0;
outDestLine.p1.y = _bgDimensions._height;
if (x2 < 0 || _bgDimensions._width < x2)
outDestLine.p1.y = y1;
else
outDestLine.p1.x = x2;
} else {
outDestLine.p0.y = 0;
outDestLine.p0.x = 0;
if (x1 < 0 || _bgDimensions._width < x1)
outDestLine.p0.y = y1;
else
outDestLine.p0.x = x1;
outDestLine.p1.x = _bgDimensions._width;
outDestLine.p1.y = _bgDimensions._height;
if (x2 < 0 || _bgDimensions._width < x2)
outDestLine.p1.y = y2;
else
outDestLine.p1.x = x2;
}
}
}
void PathFinder::findDeltaPt(Common::Point pt, Common::Point &outDeltaPt) {
static const struct { int16 x, y; } kDeltaPoints[] = {
{ 0, -4}, {0, 4}, {-4, 0}, { 4, 0}, {-3, -3}, {3, 3}, {-3, 3}, { 3, -3},
{-2, -4}, {2, 4}, {-2, 4}, { 2, -4}, {-4, -2}, {4, 2}, {-4, 2}, { 4, -2},
{-1, -4}, {1, 4}, {-1, 4}, { 1, -4}, {-4, -1}, {4, 1}, {-4, 1}, { 4, -1},
{-2, -3}, {2, 3}, {-2, 3}, { 2, -3}, {-3, -2}, {3, 2}, {-3, 2}, { 3, -2}
};
Common::Point testPt;
for (uint i = 0; i < 32; ++i) {
testPt.x = pt.x + kDeltaPoints[i].x;
testPt.y = pt.y + kDeltaPoints[i].y;
if (findValidDestLine(testPt)) {
outDeltaPt.x = kDeltaPoints[i].x;
outDeltaPt.y = kDeltaPoints[i].y;
break;
}
}
}
/**
* returns true if line is contained within rect.
*/
bool PathFinder::isLineWithinRectangle(PathLine &line, PathLine &rect) {
return line.p0.x <= rect.p1.x && line.p1.x >= rect.p0.x &&
line.p0.y <= rect.p1.y && line.p1.y >= rect.p0.y;
}
/**
* flip line coordinates so it starts top left and finishes bottom right
*/
void PathFinder::swapLine(PathLine &line, PathLine &outLine) {
if (line.p1.x <= line.p0.x) {
outLine.p1.x = line.p0.x;
outLine.p0.x = line.p1.x;
} else {
outLine.p0.x = line.p0.x;
outLine.p1.x = line.p1.x;
}
if (line.p1.y <= line.p0.y) {
outLine.p1.y = line.p0.y;
outLine.p0.y = line.p1.y;
} else {
outLine.p0.y = line.p0.y;
outLine.p1.y = line.p1.y;
}
}
int PathFinder::calcLineStatus(PathLine &sourceLine, PathLine &destRect, Common::Point *outPoint) {
PathLine sourceLine1, destRect1;
swapLine(sourceLine, sourceLine1);
swapLine(destRect, destRect1);
if (!isLineWithinRectangle(sourceLine1, destRect1))
return 3;
int sourceDeltaX = sourceLine.p1.x - sourceLine.p0.x;
int sourceDeltaY = sourceLine.p1.y - sourceLine.p0.y;
int destDeltaX = destRect.p0.x - destRect.p1.x;
int destDeltaY = destRect.p0.y - destRect.p1.y;
int sdDeltaX = sourceLine.p0.x - destRect.p0.x;
int sdDeltaY = sourceLine.p0.y - destRect.p0.y;
int delta1 = destDeltaY * sdDeltaX - destDeltaX * sdDeltaY;
int delta2 = sourceDeltaY * destDeltaX - sourceDeltaX * destDeltaY;
int delta3 = sourceDeltaX * sdDeltaY - sourceDeltaY * sdDeltaX;
if ((delta2 <= 0 && (delta1 > 0 || delta2 > delta1)) ||
(delta2 > 0 && (delta1 < 0 || delta2 < delta1)) ||
(delta2 <= 0 && (delta3 > 0 || delta2 > delta3)) ||
(delta2 > 0 && (delta3 < 0 || delta2 < delta3)))
return 3;
if (!outPoint)
return 1;
if (delta2 == 0)
return 2;
int v15 = sourceDeltaX * delta1, v18 = sourceDeltaY * delta1;
int v16 = 0;
int v17 = 0;
if ((v15 >= 0 && delta2 >= 0) || (v15 < 0 && delta2 < 0)) {
v16 = delta2 / 2;
v17 = delta2 / 2;
} else if ((v15 < 0 && delta2 >= 0) || (v15 >= 0 && delta2 < 0)) {
v17 = delta2 / 2;
v16 = delta2 / -2;
}
outPoint->x = sourceLine.p0.x + (v15 + v16) / delta2;
if ((v18 >= 0 && delta2 < 0) || (v18 < 0 && delta2 >= 0))
v17 = -v17;
outPoint->y = sourceLine.p0.y + (v18 + v17) / delta2;
return 1;
}
} // End of namespace Illusions

View File

@@ -0,0 +1,68 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_PATHFINDER_H
#define ILLUSIONS_PATHFINDER_H
#include "illusions/graphics.h"
#include "common/array.h"
#include "common/list.h"
#include "common/rect.h"
namespace Illusions {
struct PathLine {
Common::Point p0, p1;
};
typedef Common::Array<PathLine> PathLines;
typedef Common::Array<Common::Point> PointArray;
class PathFinder {
public:
PointArray *findPath(Camera *camera, Common::Point sourcePt, Common::Point destPt,
PointArray *walkPoints, PathLines *walkRects, WidthHeight bgDimensions);
protected:
PathLine _screenRect;
PointArray *_walkPoints;
PathLines *_walkRects;
WidthHeight _bgDimensions;
byte *_pathBytes;
PointArray *findPathInternal(Common::Point sourcePt, Common::Point destPt);
void postProcess(Common::Point sourcePt, PointArray *foundPath);
bool isLineBlocked(PathLine &line);
int calcLineDistance(PathLine &line);
bool findClosestPt(Common::Point &sourcePt, Common::Point &closestPt, Common::Point &destPt);
bool findValidDestLine(Common::Point &destPt);
void findValidDestPt(Common::Point &destPt);
WidthHeight calcRectDimensions(PathLine &rect);
void adjustRectDimensions(WidthHeight &dimensions);
void swapDimensions(WidthHeight &dimensions);
void clipLineToBg(Common::Point &destPt, WidthHeight &rectDimensions, PathLine &outDestLine);
void findDeltaPt(Common::Point pt, Common::Point &outDeltaPt);
bool isLineWithinRectangle(PathLine &line, PathLine &rect);
void swapLine(PathLine &line, PathLine &outLine);
int calcLineStatus(PathLine &sourceLine, PathLine &destRect, Common::Point *outPoint);
};
} // End of namespace Illusions
#endif // ILLUSIONS_PATHFINDER_H

View File

@@ -0,0 +1,27 @@
/* 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/>.
*
*/
#include "illusions/resourcereader.h"
#include "illusions/illusions.h"
namespace Illusions {
} // End of namespace Illusions

View File

@@ -0,0 +1,37 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_RESOURCEREADER_H
#define ILLUSIONS_RESOURCEREADER_H
#include "illusions/illusions.h"
namespace Illusions {
class BaseResourceReader {
public:
virtual ~BaseResourceReader() {}
virtual byte *readResource(uint32 sceneId, uint32 resId, uint32 &dataSize) = 0;
};
} // End of namespace Illusions
#endif // ILLUSIONS_RESOURCEREADER_H

View File

@@ -0,0 +1,312 @@
/* 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/>.
*
*/
#include "illusions/actor.h"
#include "illusions/illusions.h"
#include "illusions/resources/actorresource.h"
#include "illusions/dictionary.h"
namespace Illusions {
// ActorResourceLoader
void ActorResourceLoader::load(Resource *resource) {
resource->_instance = _vm->_actorInstances->createActorInstance(resource);
}
bool ActorResourceLoader::isFlag(int flag) {
return
flag == kRlfLoadFile;
}
// Frame
void Frame::load(byte *dataStart, Common::SeekableReadStream &stream) {
_flags = stream.readUint16LE();
stream.skip(2); // Skip padding
uint32 pointsConfigOffs = stream.readUint32LE();
_surfInfo.load(stream);
uint32 compressedPixelsOffs = stream.readUint32LE();
_compressedPixels = dataStart + compressedPixelsOffs;
_pointsConfig = dataStart + pointsConfigOffs;
debug(5, "Frame::load() compressedPixelsOffs: %08X",
compressedPixelsOffs);
}
// Sequence
void Sequence::load(byte *dataStart, Common::SeekableReadStream &stream) {
_sequenceId = stream.readUint32LE();
_unk4 = stream.readUint32LE();
uint32 sequenceCodeOffs = stream.readUint32LE();
_sequenceCode = dataStart + sequenceCodeOffs;
debug(5, "Sequence::load() _sequenceId: %08X; _unk4: %d; sequenceCodeOffs: %08X",
_sequenceId, _unk4, sequenceCodeOffs);
}
// ActorType
void ActorType::load(byte *dataStart, Common::SeekableReadStream &stream) {
_actorTypeId = stream.readUint32LE();
_surfInfo.load(stream);
uint32 pointsConfigOffs = stream.readUint32LE();
uint namedPointsCount = stream.readUint16LE();
stream.skip(2); // Skip padding
uint32 namedPointsOffs = stream.readUint32LE();
_color.r = stream.readByte();
_color.g = stream.readByte();
_color.b = stream.readByte();
stream.readByte(); // Skip padding
_scale = stream.readByte();
_priority = stream.readByte();
_value1E = stream.readUint16LE();
_pathWalkPointsIndex = stream.readUint16LE();
_scaleLayerIndex = stream.readUint16LE();
_pathWalkRectIndex = stream.readUint16LE();
_priorityLayerIndex = stream.readUint16LE();
_regionLayerIndex = stream.readUint16LE();
_flags = stream.readUint16LE();
_pointsConfig = dataStart + pointsConfigOffs;
stream.seek(namedPointsOffs);
_namedPoints.load(namedPointsCount, stream);
debug(5, "ActorType::load() _actorTypeId: %08X; _color(%d,%d,%d); _scale: %d; _priority: %d; _value1E: %d",
_actorTypeId, _color.r, _color.g, _color.b, _scale, _priority, _value1E);
debug(5, "ActorType::load() _pathWalkPointsIndex: %d; _scaleLayerIndex: %d; _pathWalkRectIndex: %d",
_pathWalkPointsIndex, _scaleLayerIndex, _pathWalkRectIndex);
debug(5, "ActorType::load() _priorityLayerIndex: %d; _regionLayerIndex: %d; _flags: %04X",
_priorityLayerIndex, _regionLayerIndex,_flags);
}
// ActorResource
ActorResource::ActorResource() {
}
ActorResource::~ActorResource() {
}
void ActorResource::load(Resource *resource) {
byte *data = resource->_data;
uint32 dataSize = resource->_dataSize;
Common::MemoryReadStream stream(data, dataSize, DisposeAfterUse::NO);
_totalSize = stream.readUint32LE();
// Load actor types
stream.seek(0x06);
uint actorTypesCount = stream.readUint16LE();
stream.seek(0x10);
uint32 actorTypesOffs = stream.readUint32LE();
_actorTypes.reserve(actorTypesCount);
for (uint i = 0; i < actorTypesCount; ++i) {
ActorType actorType;
stream.seek(actorTypesOffs + i * 0x2C);
actorType.load(data, stream);
_actorTypes.push_back(actorType);
}
// Load sequences
stream.seek(0x08);
uint sequencesCount = stream.readUint16LE();
stream.seek(0x14);
uint32 sequencesOffs = stream.readUint32LE();
stream.seek(sequencesOffs);
_sequences.reserve(sequencesCount);
for (uint i = 0; i < sequencesCount; ++i) {
Sequence sequence;
sequence.load(data, stream);
_sequences.push_back(sequence);
}
// Load frames
stream.seek(0x0A);
uint framesCount = stream.readUint16LE();
stream.seek(0x18);
uint32 framesOffs = stream.readUint32LE();
stream.seek(framesOffs);
_frames.reserve(framesCount);
for (uint i = 0; i < framesCount; ++i) {
Frame frame;
frame.load(data, stream);
_frames.push_back(frame);
}
// Load named points
if (resource->_gameId == kGameIdBBDOU) {
// The count isn't stored explicitly so calculate it
uint namedPointsCount = (actorTypesOffs - 0x20) / 8;
stream.seek(0x20);
_namedPoints.load(namedPointsCount, stream);
}
debug(1, "ActorResource(%08X) framesCount: %d", resource->_resId, framesCount);
}
bool ActorResource::containsSequence(Sequence *sequence) {
for (uint i = 0; i < _sequences.size(); ++i) {
if (sequence == &_sequences[i])
return true;
}
return false;
}
bool ActorResource::findNamedPoint(uint32 namedPointId, Common::Point &pt) {
return _namedPoints.findNamedPoint(namedPointId, pt);
}
// ActorInstance
ActorInstance::ActorInstance(IllusionsEngine *vm)
: _vm(vm) {
}
void ActorInstance::load(Resource *resource) {
_actorResource = new ActorResource();
_actorResource->load(resource);
_sceneId = resource->_sceneId;
_pauseCtr = 0;
initActorTypes(resource->_gameId);
}
void ActorInstance::unload() {
if (_pauseCtr <= 0)
unregisterResources();
_vm->_actorInstances->removeActorInstance(this);
delete _actorResource;
}
void ActorInstance::pause() {
++_pauseCtr;
if (_pauseCtr == 1)
unregisterResources();
}
void ActorInstance::unpause() {
--_pauseCtr;
if (_pauseCtr == 0)
registerResources();
}
void ActorInstance::initActorTypes(int gameId) {
for (uint i = 0; i < _actorResource->_actorTypes.size(); ++i) {
ActorType *actorType = &_actorResource->_actorTypes[i];
ActorType *actorType2 = _vm->_dict->findActorType(actorType->_actorTypeId);
if (actorType2) {
actorType->_surfInfo._dimensions._width = MAX(actorType->_surfInfo._dimensions._width,
actorType2->_surfInfo._dimensions._width);
actorType->_surfInfo._dimensions._height = MAX(actorType->_surfInfo._dimensions._height,
actorType2->_surfInfo._dimensions._height);
if (actorType->_color.r == 255 && actorType->_color.g == 255 && actorType->_color.b == 255)
actorType->_color = actorType2->_color;
if (actorType->_value1E == 0)
actorType->_value1E = actorType2->_value1E;
}
_vm->_dict->addActorType(actorType->_actorTypeId, actorType);
}
for (uint i = 0; i < _actorResource->_sequences.size(); ++i) {
Sequence *sequence = &_actorResource->_sequences[i];
_vm->_dict->addSequence(sequence->_sequenceId, sequence);
if (gameId == kGameIdDuckman && sequence->_sequenceId == 0x60101) {
// TODO check that this is the correct location for this logic.
_vm->_controls->placeActor(0x50023, Common::Point(0,0), sequence->_sequenceId, 0x400d7, 0);
}
}
}
void ActorInstance::registerResources() {
for (uint i = 0; i < _actorResource->_actorTypes.size(); ++i) {
ActorType *actorType = &_actorResource->_actorTypes[i];
_vm->_dict->addActorType(actorType->_actorTypeId, actorType);
}
for (uint i = 0; i < _actorResource->_sequences.size(); ++i) {
Sequence *sequence = &_actorResource->_sequences[i];
_vm->_dict->addSequence(sequence->_sequenceId, sequence);
}
}
void ActorInstance::unregisterResources() {
for (uint i = 0; i < _actorResource->_actorTypes.size(); ++i) {
_vm->_dict->removeActorType(_actorResource->_actorTypes[i]._actorTypeId);
}
for (uint i = 0; i < _actorResource->_sequences.size(); ++i) {
_vm->_dict->removeSequence(_actorResource->_sequences[i]._sequenceId);
}
}
// ActorInstanceList
ActorInstanceList::ActorInstanceList(IllusionsEngine *vm)
: _vm(vm) {
}
ActorInstanceList::~ActorInstanceList() {
}
ActorInstance *ActorInstanceList::createActorInstance(Resource *resource) {
ActorInstance *actorInstance = new ActorInstance(_vm);
actorInstance->load(resource);
_items.push_back(actorInstance);
return actorInstance;
}
void ActorInstanceList::removeActorInstance(ActorInstance *actorInstance) {
_items.remove(actorInstance);
}
void ActorInstanceList::pauseBySceneId(uint32 sceneId) {
for (auto &it : _items) {
if (it->_sceneId == sceneId)
it->pause();
}
}
void ActorInstanceList::unpauseBySceneId(uint32 sceneId) {
for (auto &it : _items) {
if (it->_sceneId == sceneId)
it->unpause();
}
}
FramesList *ActorInstanceList::findSequenceFrames(Sequence *sequence) {
for (auto &actorInstance : _items) {
if (actorInstance->_pauseCtr <= 0 && actorInstance->_actorResource->containsSequence(sequence))
return &actorInstance->_actorResource->_frames;
}
return nullptr;
}
ActorInstance *ActorInstanceList::findActorByResource(ActorResource *actorResource) {
for (auto &it : _items) {
if (it->_actorResource == actorResource)
return (it);
}
return nullptr;
}
bool ActorInstanceList::findNamedPoint(uint32 namedPointId, Common::Point &pt) {
for (auto &actorInstance : _items) {
if (actorInstance->_pauseCtr == 0 && actorInstance->_actorResource->findNamedPoint(namedPointId, pt))
return true;
}
return false;
}
} // End of namespace Illusions

View File

@@ -0,0 +1,132 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_ACTORRESOURCE_H
#define ILLUSIONS_ACTORRESOURCE_H
#include "illusions/graphics.h"
#include "illusions/resourcesystem.h"
#include "graphics/surface.h"
namespace Illusions {
class IllusionsEngine;
class ActorResourceLoader : public BaseResourceLoader {
public:
ActorResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
~ActorResourceLoader() override {}
void load(Resource *resource) override;
bool isFlag(int flag) override;
protected:
IllusionsEngine *_vm;
};
struct Frame {
uint16 _flags;
byte *_pointsConfig;
SurfInfo _surfInfo;
byte *_compressedPixels;
void load(byte *dataStart, Common::SeekableReadStream &stream);
};
struct Sequence {
uint32 _sequenceId;
uint32 _unk4;
byte *_sequenceCode;
void load(byte *dataStart, Common::SeekableReadStream &stream);
};
struct ActorType {
uint32 _actorTypeId;
SurfInfo _surfInfo;
byte *_pointsConfig;
NamedPoints _namedPoints;
RGB _color;
byte _scale;
byte _priority;
int16 _value1E;
uint16 _pathWalkPointsIndex;
uint16 _scaleLayerIndex;
uint16 _pathWalkRectIndex;
uint16 _priorityLayerIndex;
uint16 _regionLayerIndex;
uint16 _flags;
void load(byte *dataStart, Common::SeekableReadStream &stream);
};
class FramesList : public Common::Array<Frame> {
};
class ActorResource {
public:
ActorResource();
~ActorResource();
void load(Resource *resource);
bool containsSequence(Sequence *sequence);
bool findNamedPoint(uint32 namedPointId, Common::Point &pt);
public:
uint32 _totalSize;
Common::Array<ActorType> _actorTypes;
Common::Array<Sequence> _sequences;
FramesList _frames;
NamedPoints _namedPoints;
};
class ActorInstance : public ResourceInstance {
public:
ActorInstance(IllusionsEngine *vm);
void load(Resource *resource) override;
void unload() override;
void pause() override;
void unpause() override;
public:
IllusionsEngine *_vm;
uint32 _sceneId;
int _pauseCtr;
ActorResource *_actorResource;
protected:
void initActorTypes(int gameId);
void registerResources();
void unregisterResources();
};
class ActorInstanceList {
public:
ActorInstanceList(IllusionsEngine *vm);
~ActorInstanceList();
ActorInstance *createActorInstance(Resource *resource);
void removeActorInstance(ActorInstance *actorInstance);
void pauseBySceneId(uint32 sceneId);
void unpauseBySceneId(uint32 sceneId);
FramesList *findSequenceFrames(Sequence *sequence);
ActorInstance *findActorByResource(ActorResource *actorResource);
bool findNamedPoint(uint32 namedPointId, Common::Point &pt);
protected:
typedef Common::List<ActorInstance*> Items;
typedef Items::iterator ItemsIterator;
IllusionsEngine *_vm;
Items _items;
};
} // End of namespace Illusions
#endif // ILLUSIONS_ACTORRESOURCE_H

View File

@@ -0,0 +1,641 @@
/* 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/>.
*
*/
#include "illusions/illusions.h"
#include "illusions/resources/backgroundresource.h"
#include "illusions/actor.h"
#include "illusions/camera.h"
#include "illusions/dictionary.h"
#include "illusions/resources/actorresource.h"
#include "illusions/screen.h"
#include "illusions/sequenceopcodes.h"
#include "common/str.h"
namespace Illusions {
// BackgroundResourceLoader
void BackgroundResourceLoader::load(Resource *resource) {
resource->_instance = _vm->_backgroundInstances->createBackgroundInstance(resource);
}
bool BackgroundResourceLoader::isFlag(int flag) {
return
flag == kRlfLoadFile;
}
// TileMap
void TileMap::load(byte *dataStart, Common::SeekableReadStream &stream) {
_width = stream.readSint16LE();
_height = stream.readSint16LE();
stream.skip(4); // Unknown
uint32 mapOffs = stream.pos();
_map = dataStart + mapOffs;
debug(0, "TileMap::load() _width: %d; _height: %d",
_width, _height);
}
// BgInfo
void BgInfo::load(byte *dataStart, Common::SeekableReadStream &stream) {
_flags = stream.readUint32LE();
uint16 unknown = stream.readUint16LE(); // TODO Unknown
_priorityBase = stream.readSint16LE();
_surfInfo.load(stream);
loadPoint(stream, _panPoint);
uint32 tileMapOffs = stream.readUint32LE();
uint32 tilePixelsOffs = stream.readUint32LE();
stream.seek(tileMapOffs);
_tileMap.load(dataStart, stream);
_tilePixels = dataStart + tilePixelsOffs;
debug(0, "BgInfo::load() _flags: %08X; unknown: %04X; _priorityBase: %d; tileMapOffs: %08X; tilePixelsOffs: %08X",
_flags, unknown, _priorityBase, tileMapOffs, tilePixelsOffs);
}
// PriorityLayer
void PriorityLayer::load(byte *dataStart, Common::SeekableReadStream &stream) {
_width = stream.readUint16LE();
_height = stream.readUint16LE();
uint32 mapOffs = stream.readUint32LE();
uint32 valuesOffs = stream.readUint32LE();
_map = dataStart + mapOffs;
_mapWidth = READ_LE_UINT16(_map + 0);
_mapHeight = READ_LE_UINT16(_map + 2);
_map += 8;
_values = dataStart + valuesOffs;
debug(0, "PriorityLayer::load() _width: %d; _height: %d; mapOffs: %08X; valuesOffs: %08X; _mapWidth: %d; _mapHeight: %d",
_width, _height, mapOffs, valuesOffs, _mapWidth, _mapHeight);
}
int PriorityLayer::getPriority(Common::Point pos) {
pos.x = CLIP<int16>(pos.x, 0, _width - 1);
pos.y = CLIP<int16>(pos.y, 0, _height - 1);
const int16 tx = pos.x / 32, sx = pos.x % 32;
const int16 ty = pos.y / 8, sy = pos.y % 8;
uint16 mapIndex = READ_LE_UINT16(_map + 2 * (tx + ty * _mapWidth)) - 1;
return _values[mapIndex * 32 * 8 + sx + sy * 32];
}
// ScaleLayer
void ScaleLayer::load(byte *dataStart, Common::SeekableReadStream &stream) {
_height = stream.readUint16LE();
stream.skip(2);
uint32 valuesOffs = stream.readUint32LE();
_values = dataStart + valuesOffs;
debug(0, "ScaleLayer::load() _height: %d; valuesOffs: %08X",
_height, valuesOffs);
}
int ScaleLayer::getScale(Common::Point pos) {
pos.y = CLIP<int16>(pos.y, 0, _height - 1);
return _values[pos.y];
}
// RegionLayer
void RegionLayer::load(byte *dataStart, Common::SeekableReadStream &stream) {
_unk = stream.readUint32LE();
uint32 regionSequenceIdsOffs = stream.readUint32LE();
_width = stream.readUint16LE();
_height = stream.readUint16LE();
uint32 mapOffs = stream.readUint32LE();
uint32 valuesOffs = stream.readUint32LE();
_regionSequenceIds = dataStart + regionSequenceIdsOffs;
_map = dataStart + mapOffs;
_values = dataStart + valuesOffs;
_mapWidth = READ_LE_UINT16(_map + 0);
_mapHeight = READ_LE_UINT16(_map + 2);
_map += 8;
debug(1, "RegionLayer::load() %d; regionSequenceIdsOffs: %08X; _width: %d; _height: %d; mapOffs: %08X; valuesOffs: %08X",
_unk, regionSequenceIdsOffs, _width, _height, mapOffs, valuesOffs);
}
int RegionLayer::getRegionIndex(Common::Point pos) {
pos.x = CLIP<int16>(pos.x, 0, _width - 1);
pos.y = CLIP<int16>(pos.y, 0, _height - 1);
const int16 tx = pos.x / 32, sx = pos.x % 32;
const int16 ty = pos.y / 8, sy = pos.y % 8;
uint16 mapIndex = READ_LE_UINT16(_map + 2 * (tx + ty * _mapWidth)) - 1;
return _values[mapIndex * 32 * 8 + sx + sy * 32];
}
uint32 RegionLayer::getRegionSequenceId(int regionIndex) {
return READ_LE_UINT32(_regionSequenceIds + 4 * regionIndex);
}
// Palette
void Palette::load(byte *dataStart, Common::SeekableReadStream &stream) {
_count = stream.readUint16LE();
_unk = stream.readUint16LE();
uint32 paletteOffs = stream.readUint32LE();
_palette = dataStart + paletteOffs;
}
// BackgroundObject
void BackgroundObject::load(byte *dataStart, Common::SeekableReadStream &stream) {
_objectId = stream.readUint32LE();
_flags = stream.readUint16LE();
_priority = stream.readUint16LE();
uint32 pointsConfigOffs = stream.readUint32LE();
_pointsConfig = dataStart + pointsConfigOffs;
debug(0, "BackgroundObject::load() _objectId: %08X; _flags: %04X; _priority: %d; pointsConfigOffs: %08X",
_objectId, _flags, _priority, pointsConfigOffs);
}
// PathWalkPoints
void PathWalkPoints::load(byte *dataStart, Common::SeekableReadStream &stream) {
_points = new PointArray();
uint count = stream.readUint32LE();
uint32 pointsOffs = stream.readUint32LE();
_points->reserve(count);
stream.seek(pointsOffs);
for (uint i = 0; i < count; ++i) {
Common::Point pt;
loadPoint(stream, pt);
_points->push_back(pt);
}
debug(0, "PathWalkPoints::load() count: %d; pointsOffs: %08X",
count, pointsOffs);
}
// PathWalkRects
void PathWalkRects::load(byte *dataStart, Common::SeekableReadStream &stream) {
_rects = new PathLines();
uint count = stream.readUint32LE();
uint32 rectsOffs = stream.readUint32LE();
_rects->reserve(count);
stream.seek(rectsOffs);
for (uint i = 0; i < count; ++i) {
PathLine rect;
loadPoint(stream, rect.p0);
loadPoint(stream, rect.p1);
_rects->push_back(rect);
}
debug(0, "PathWalkRects::load() count: %d; rectsOffs: %08X",
count, rectsOffs);
}
// BackgroundResource
BackgroundResource::BackgroundResource()
: _bgInfos(nullptr), _scaleLayers(nullptr), _priorityLayers(nullptr), _regionLayers(nullptr),
_regionSequences(nullptr), _backgroundObjects(nullptr), _pathWalkPoints(nullptr),
_pathWalkRects(nullptr), _palettes(nullptr) {
}
BackgroundResource::~BackgroundResource() {
delete[] _bgInfos;
delete[] _scaleLayers;
delete[] _priorityLayers;
delete[] _regionLayers;
delete[] _regionSequences;
delete[] _backgroundObjects;
delete[] _pathWalkPoints;
delete[] _pathWalkRects;
delete[] _palettes;
}
void BackgroundResource::load(byte *data, uint32 dataSize) {
Common::MemoryReadStream stream(data, dataSize, DisposeAfterUse::NO);
stream.seek(8);
_paletteIndex = stream.readUint16LE();
// Load background pixels
stream.seek(0x0A);
_bgInfosCount = stream.readUint16LE();
_bgInfos = new BgInfo[_bgInfosCount];
stream.seek(0x20);
uint32 bgInfosOffs = stream.readUint32LE();
for (uint i = 0; i < _bgInfosCount; ++i) {
stream.seek(bgInfosOffs + i * 0x1C);
_bgInfos[i].load(data, stream);
}
// Load scale layers
stream.seek(0x10);
_scaleLayersCount = stream.readUint16LE();
_scaleLayers = new ScaleLayer[_scaleLayersCount];
stream.seek(0x2C);
uint32 scaleLayersOffs = stream.readUint32LE();
debug(0, "_scaleLayersCount: %d", _scaleLayersCount);
for (uint i = 0; i < _scaleLayersCount; ++i) {
stream.seek(scaleLayersOffs + i * 8);
_scaleLayers[i].load(data, stream);
}
// Load priority layers
stream.seek(0x14);
_priorityLayersCount = stream.readUint16LE();
_priorityLayers = new PriorityLayer[_priorityLayersCount];
stream.seek(0x34);
uint32 priorityLayersOffs = stream.readUint32LE();
debug(1, "_priorityLayersCount: %d", _priorityLayersCount);
for (uint i = 0; i < _priorityLayersCount; ++i) {
stream.seek(priorityLayersOffs + i * 12);
_priorityLayers[i].load(data, stream);
}
// Load region layers
stream.seek(0x16);
_regionLayersCount = stream.readUint16LE();
_regionLayers = new RegionLayer[_regionLayersCount];
stream.seek(0x38);
uint32 regionLayersOffs = stream.readUint32LE();
debug(1, "_regionLayersCount: %d", _regionLayersCount);
for (uint i = 0; i < _regionLayersCount; ++i) {
stream.seek(regionLayersOffs + i * 20);
_regionLayers[i].load(data, stream);
}
// Load region sequences
stream.seek(0x1E);
_regionSequencesCount = stream.readUint16LE();
_regionSequences = new Sequence[_regionSequencesCount];
stream.seek(0x48);
uint32 regionSequencesOffs = stream.readUint32LE();
stream.seek(regionSequencesOffs);
for (uint i = 0; i < _regionSequencesCount; ++i) {
_regionSequences[i].load(data, stream);
}
// Load background objects
stream.seek(0x1C);
_backgroundObjectsCount = stream.readUint16LE();
_backgroundObjects = new BackgroundObject[_backgroundObjectsCount];
stream.seek(0x44);
uint32 backgroundObjectsOffs = stream.readUint32LE();
debug(0, "_backgroundObjectsCount: %d", _backgroundObjectsCount);
for (uint i = 0; i < _backgroundObjectsCount; ++i) {
stream.seek(backgroundObjectsOffs + i * 12);
_backgroundObjects[i].load(data, stream);
}
// Load path walk points
stream.seek(0x0E);
_pathWalkPointsCount = stream.readUint16LE();
debug(1, "_pathWalkPointsCount: %d", _pathWalkPointsCount);
_pathWalkPoints = new PathWalkPoints[_pathWalkPointsCount];
stream.seek(0x28);
uint32 pathWalkPointsOffs = stream.readUint32LE();
for (uint i = 0; i < _pathWalkPointsCount; ++i) {
stream.seek(pathWalkPointsOffs + i * 8);
_pathWalkPoints[i].load(data, stream);
}
// Load path walk rects
stream.seek(0x12);
_pathWalkRectsCount = stream.readUint16LE();
debug(1, "_pathWalkRectsCount: %d", _pathWalkRectsCount);
_pathWalkRects = new PathWalkRects[_pathWalkRectsCount];
stream.seek(0x30);
uint32 pathWalkRectsOffs = stream.readUint32LE();
for (uint i = 0; i < _pathWalkRectsCount; ++i) {
stream.seek(pathWalkRectsOffs + i * 8);
_pathWalkRects[i].load(data, stream);
}
// Load named points
stream.seek(0xC);
uint namedPointsCount = stream.readUint16LE();
stream.seek(0x24);
uint32 namedPointsOffs = stream.readUint32LE();
stream.seek(namedPointsOffs);
_namedPoints.load(namedPointsCount, stream);
// Load palettes
stream.seek(0x18);
_palettesCount = stream.readUint16LE();
_palettes = new Palette[_palettesCount];
stream.seek(0x3C);
uint32 palettesOffs = stream.readUint32LE();
debug(0, "_palettesCount: %d", _palettesCount);
for (uint i = 0; i < _palettesCount; ++i) {
stream.seek(palettesOffs + i * 8);
_palettes[i].load(data, stream);
}
}
int BackgroundResource::findMasterBgIndex() {
int index = 1;
while (!(_bgInfos[index - 1]._flags & 1)) { // TODO check if this is correct
++index;
}
return index;
}
PriorityLayer *BackgroundResource::getPriorityLayer(uint index) {
return &_priorityLayers[index];
}
ScaleLayer *BackgroundResource::getScaleLayer(uint index) {
return &_scaleLayers[index];
}
RegionLayer *BackgroundResource::getRegionLayer(uint index) {
return &_regionLayers[index];
}
PathWalkPoints *BackgroundResource::getPathWalkPoints(uint index) {
return &_pathWalkPoints[index];
}
PathWalkRects *BackgroundResource::getPathWalkRects(uint index) {
return &_pathWalkRects[index];
}
Palette *BackgroundResource::getPalette(uint index) {
return &_palettes[index];
}
bool BackgroundResource::findNamedPoint(uint32 namedPointId, Common::Point &pt) {
return _namedPoints.findNamedPoint(namedPointId, pt);
}
// BackgroundInstance
BackgroundInstance::BackgroundInstance(IllusionsEngine *vm)
: _vm(vm), _sceneId(0), _pauseCtr(0), _bgRes(nullptr), _savedPalette(nullptr) {
}
BackgroundInstance::~BackgroundInstance() {
delete[] _savedPalette;
}
void BackgroundInstance::load(Resource *resource) {
debug(1, "BackgroundResourceLoader::load() Loading background %08X from %s...", resource->_resId, resource->_filename.c_str());
BackgroundResource *backgroundResource = new BackgroundResource();
backgroundResource->load(resource->_data, resource->_dataSize);
_bgRes = backgroundResource;
_sceneId = resource->_sceneId;
initSurface();
// Insert background objects
for (uint i = 0; i < backgroundResource->_backgroundObjectsCount; ++i) {
_vm->_controls->placeBackgroundObject(&backgroundResource->_backgroundObjects[i]);
}
registerResources();
_vm->clearFader();
int index = _bgRes->findMasterBgIndex();
_vm->_camera->set(_bgRes->_bgInfos[index - 1]._panPoint, _bgRes->_bgInfos[index - 1]._surfInfo._dimensions);
if (_bgRes->_palettesCount > 0) {
Palette *palette = _bgRes->getPalette(_bgRes->_paletteIndex - 1);
_vm->_screenPalette->setPalette(palette->_palette, 1, palette->_count);
}
}
void BackgroundInstance::unload() {
debug(1, "BackgroundInstance::unload()");
freeSurface();
unregisterResources();
delete _bgRes;
_vm->_backgroundInstances->removeBackgroundInstance(this);
_vm->setDefaultTextCoords();
}
void BackgroundInstance::pause() {
++_pauseCtr;
if (_pauseCtr <= 1) {
unregisterResources();
_vm->setDefaultTextCoords();
_vm->_camera->getActiveState(_savedCameraState);
_savedPalette = new byte[1024];
_vm->_screenPalette->getPalette(_savedPalette);
freeSurface();
}
}
void BackgroundInstance::unpause() {
--_pauseCtr;
if (_pauseCtr <= 0) {
registerResources();
initSurface();
_vm->_screenPalette->setPalette(_savedPalette, 1, 256);
delete[] _savedPalette;
_savedPalette = nullptr;
_vm->clearFader();
_vm->_camera->setActiveState(_savedCameraState);
_vm->_backgroundInstances->refreshPan();
}
}
void BackgroundInstance::registerResources() {
for (uint i = 0; i < _bgRes->_regionSequencesCount; ++i) {
Sequence *sequence = &_bgRes->_regionSequences[i];
_vm->_dict->addSequence(sequence->_sequenceId, sequence);
}
}
void BackgroundInstance::unregisterResources() {
for (uint i = 0; i < _bgRes->_regionSequencesCount; ++i) {
Sequence *sequence = &_bgRes->_regionSequences[i];
_vm->_dict->removeSequence(sequence->_sequenceId);
}
}
void BackgroundInstance::initSurface() {
for (uint i = 0; i < kMaxBackgroundItemSurfaces; ++i) {
_surfaces[i] = nullptr;
}
for (uint i = 0; i < _bgRes->_bgInfosCount; ++i) {
BgInfo *bgInfo = &_bgRes->_bgInfos[i];
_panPoints[i] = bgInfo->_panPoint;
_surfaces[i] = _vm->_screen->allocSurface(bgInfo->_surfInfo);
drawTiles(_surfaces[i], bgInfo->_tileMap, bgInfo->_tilePixels);
#if 0
if (_bgRes->_pathWalkRectsCount > 0) {
PathLines *pl = _bgRes->_pathWalkRects->_rects;
for (int j=0; j < pl->size(); j++) {
PathLine pathLine = (*pl)[j];
debug(0, "walk path rect line[%d]. (%d,%d)->(%d,%d)", j, pathLine.p0.x, pathLine.p0.y, pathLine.p1.x, pathLine.p1.y);
_surfaces[i]->drawLine(pathLine.p0.x, pathLine.p0.y, pathLine.p1.x, pathLine.p1.y, 5);
}
}
#endif
}
}
void BackgroundInstance::freeSurface() {
for (uint i = 0; i < _bgRes->_bgInfosCount; ++i) {
if (_surfaces[i]) {
_surfaces[i]->free();
delete _surfaces[i];
_surfaces[i] = nullptr;
}
}
}
void BackgroundInstance::drawTiles(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels) {
switch (_vm->getGameId()) {
case kGameIdDuckman:
drawTiles8(surface, tileMap, tilePixels);
break;
case kGameIdBBDOU:
drawTiles16(surface, tileMap, tilePixels);
break;
default:
break;
}
}
void BackgroundInstance::drawTiles8(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels) {
const int kTileWidth = 32;
const int kTileHeight = 8;
const int kTileSize = kTileWidth * kTileHeight;
uint tileMapIndex = 0;
for (int tileY = 0; tileY < tileMap._height; ++tileY) {
int tileDestY = tileY * kTileHeight;
int tileDestH = MIN(kTileHeight, surface->h - tileDestY);
for (int tileX = 0; tileX < tileMap._width; ++tileX) {
int tileDestX = tileX * kTileWidth;
int tileDestW = MIN(kTileWidth, surface->w - tileDestX);
uint16 tileIndex = READ_LE_UINT16(tileMap._map + 2 * tileMapIndex);
++tileMapIndex;
byte *src = tilePixels + (tileIndex - 1) * kTileSize;
byte *dst = (byte*)surface->getBasePtr(tileDestX, tileDestY);
for (int h = 0; h < tileDestH; ++h) {
memcpy(dst, src, tileDestW);
dst += surface->pitch;
src += kTileWidth;
}
}
}
}
void BackgroundInstance::drawTiles16(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels) {
const int kTileWidth = 32;
const int kTileHeight = 8;
const int kTileSize = kTileWidth * kTileHeight * 2;
uint tileMapIndex = 0;
for (int tileY = 0; tileY < tileMap._height; ++tileY) {
int tileDestY = tileY * kTileHeight;
int tileDestH = MIN(kTileHeight, surface->h - tileDestY);
for (int tileX = 0; tileX < tileMap._width; ++tileX) {
int tileDestX = tileX * kTileWidth;
int tileDestW = MIN(kTileWidth, surface->w - tileDestX);
uint16 tileIndex = READ_LE_UINT16(tileMap._map + 2 * tileMapIndex);
++tileMapIndex;
byte *src = tilePixels + (tileIndex - 1) * kTileSize;
byte *dst = (byte*)surface->getBasePtr(tileDestX, tileDestY);
for (int h = 0; h < tileDestH; ++h) {
for (int w = 0; w < tileDestW; ++w) {
uint16 pixel = READ_LE_UINT16(src + w * 2);
WRITE_LE_UINT16(dst + w * 2, pixel);
}
dst += surface->pitch;
src += kTileWidth * 2;
}
}
}
}
// BackgroundInstanceList
BackgroundInstanceList::BackgroundInstanceList(IllusionsEngine *vm)
: _vm(vm) {
}
BackgroundInstanceList::~BackgroundInstanceList() {
}
BackgroundInstance *BackgroundInstanceList::createBackgroundInstance(Resource *resource) {
BackgroundInstance *backgroundInstance = new BackgroundInstance(_vm);
backgroundInstance->load(resource);
_items.push_back(backgroundInstance);
return backgroundInstance;
}
void BackgroundInstanceList::removeBackgroundInstance(BackgroundInstance *backgroundInstance) {
_items.remove(backgroundInstance);
}
void BackgroundInstanceList::pauseBySceneId(uint32 sceneId) {
for (auto &item : _items) {
if (item->_sceneId == sceneId)
item->pause();
}
}
void BackgroundInstanceList::unpauseBySceneId(uint32 sceneId) {
for (auto &item : _items) {
if (item->_sceneId == sceneId)
item->unpause();
}
}
BackgroundInstance *BackgroundInstanceList::findActiveBackgroundInstance() {
for (auto &item : _items) {
if (item->_pauseCtr == 0)
return item;
}
return nullptr;
}
BackgroundInstance *BackgroundInstanceList::findBackgroundByResource(BackgroundResource *backgroundResource) {
for (auto &item : _items) {
if (item->_bgRes == backgroundResource)
return item;
}
return nullptr;
}
BackgroundResource *BackgroundInstanceList::getActiveBgResource() {
BackgroundInstance *background = findActiveBackgroundInstance();
if (background)
return background->_bgRes;
return nullptr;
}
WidthHeight BackgroundInstanceList::getMasterBgDimensions() {
BackgroundInstance *backgroundInstance = findActiveBackgroundInstance();
int16 index = backgroundInstance->_bgRes->findMasterBgIndex();
return backgroundInstance->_bgRes->_bgInfos[index - 1]._surfInfo._dimensions;
}
void BackgroundInstanceList::refreshPan() {
BackgroundInstance *backgroundInstance = findActiveBackgroundInstance();
if (backgroundInstance) {
WidthHeight dimensions = getMasterBgDimensions();
_vm->_camera->refreshPan(backgroundInstance, dimensions);
}
}
bool BackgroundInstanceList::findActiveBackgroundNamedPoint(uint32 namedPointId, Common::Point &pt) {
BackgroundResource *backgroundResource = getActiveBgResource();
return backgroundResource ? backgroundResource->findNamedPoint(namedPointId, pt) : false;
}
} // End of namespace Illusions

View File

@@ -0,0 +1,233 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_BACKGROUNDRESOURCE_H
#define ILLUSIONS_BACKGROUNDRESOURCE_H
#include "illusions/camera.h"
#include "illusions/graphics.h"
#include "illusions/pathfinder.h"
#include "illusions/resourcesystem.h"
#include "graphics/surface.h"
#include "common/array.h"
#include "common/file.h"
#include "common/list.h"
#include "common/memstream.h"
#include "common/rect.h"
#include "common/substream.h"
#include "common/system.h"
namespace Illusions {
class IllusionsEngine;
struct Sequence;
class BackgroundResourceLoader : public BaseResourceLoader {
public:
BackgroundResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
~BackgroundResourceLoader() override {}
void load(Resource *resource) override;
bool isFlag(int flag) override;
protected:
IllusionsEngine *_vm;
};
struct TileMap {
int16 _width, _height;
//field_4 dd
byte *_map;
void load(byte *dataStart, Common::SeekableReadStream &stream);
};
struct BgInfo {
uint32 _flags;
//field_4 dw
int16 _priorityBase;
SurfInfo _surfInfo;
Common::Point _panPoint;
TileMap _tileMap;
byte *_tilePixels;
void load(byte *dataStart, Common::SeekableReadStream &stream);
};
class PriorityLayer {
public:
void load(byte *dataStart, Common::SeekableReadStream &stream);
int getPriority(Common::Point pos);
protected:
int16 _width, _height;
int16 _mapWidth, _mapHeight;
byte *_map, *_values;
};
class ScaleLayer {
public:
void load(byte *dataStart, Common::SeekableReadStream &stream);
int getScale(Common::Point pos);
protected:
int16 _height;
byte *_values;
};
class RegionLayer {
public:
void load(byte *dataStart, Common::SeekableReadStream &stream);
int getRegionIndex(Common::Point pos);
uint32 getRegionSequenceId(int regionIndex);
protected:
uint32 _unk;
byte *_regionSequenceIds;
int16 _width, _height;
int16 _mapWidth, _mapHeight;
byte *_map, *_values;
};
struct Palette {
uint16 _count;
uint16 _unk;
byte *_palette;
void load(byte *dataStart, Common::SeekableReadStream &stream);
};
struct BackgroundObject {
uint32 _objectId;
uint16 _flags;
int16 _priority;
byte *_pointsConfig;
void load(byte *dataStart, Common::SeekableReadStream &stream);
};
struct PathWalkPoints {
PointArray *_points;
PathWalkPoints() : _points(0) {}
~PathWalkPoints() { delete _points; }
void load(byte *dataStart, Common::SeekableReadStream &stream);
};
struct PathWalkRects {
PathLines *_rects;
PathWalkRects() : _rects(0) {}
~PathWalkRects() { delete _rects; }
void load(byte *dataStart, Common::SeekableReadStream &stream);
};
class BackgroundResource {
public:
BackgroundResource();
~BackgroundResource();
void load(byte *data, uint32 dataSize);
int findMasterBgIndex();
PriorityLayer *getPriorityLayer(uint index);
ScaleLayer *getScaleLayer(uint index);
RegionLayer *getRegionLayer(uint index);
PathWalkPoints *getPathWalkPoints(uint index);
PathWalkRects *getPathWalkRects(uint index);
Palette *getPalette(uint index);
bool findNamedPoint(uint32 namedPointId, Common::Point &pt);
public:
uint _paletteIndex;
uint _bgInfosCount;
BgInfo *_bgInfos;
uint _priorityLayersCount;
PriorityLayer *_priorityLayers;
uint _scaleLayersCount;
ScaleLayer *_scaleLayers;
uint _regionLayersCount;
RegionLayer *_regionLayers;
uint _regionSequencesCount;
Sequence *_regionSequences;
uint _backgroundObjectsCount;
BackgroundObject *_backgroundObjects;
uint _pathWalkPointsCount;
PathWalkPoints *_pathWalkPoints;
uint _pathWalkRectsCount;
PathWalkRects *_pathWalkRects;
NamedPoints _namedPoints;
uint _palettesCount;
Palette *_palettes;
};
const uint kMaxBackgroundItemSurfaces = 3;
class BackgroundInstance : public ResourceInstance {
public:
BackgroundInstance(IllusionsEngine *vm);
~BackgroundInstance();
void load(Resource *resource) override;
void unload() override;
void pause() override;
void unpause() override;
public:
IllusionsEngine *_vm;
uint32 _sceneId;
int _pauseCtr;
BackgroundResource *_bgRes;
Common::Point _panPoints[kMaxBackgroundItemSurfaces];
Graphics::Surface *_surfaces[kMaxBackgroundItemSurfaces];
CameraState _savedCameraState;
byte *_savedPalette;
void registerResources();
void unregisterResources();
void initSurface();
void freeSurface();
void drawTiles(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels);
void drawTiles8(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels);
void drawTiles16(Graphics::Surface *surface, TileMap &tileMap, byte *tilePixels);
};
class BackgroundInstanceList {
public:
BackgroundInstanceList(IllusionsEngine *vm);
~BackgroundInstanceList();
BackgroundInstance *createBackgroundInstance(Resource *resource);
void removeBackgroundInstance(BackgroundInstance *backgroundInstance);
void pauseBySceneId(uint32 sceneId);
void unpauseBySceneId(uint32 sceneId);
BackgroundInstance *findActiveBackgroundInstance();
BackgroundInstance *findBackgroundByResource(BackgroundResource *backgroundResource);
BackgroundResource *getActiveBgResource();
WidthHeight getMasterBgDimensions();
void refreshPan();
bool findActiveBackgroundNamedPoint(uint32 namedPointId, Common::Point &pt);
//protected:
public:
typedef Common::List<BackgroundInstance*> Items;
typedef Items::iterator ItemsIterator;
IllusionsEngine *_vm;
Items _items;
};
} // End of namespace Illusions
#endif // ILLUSIONS_BACKGROUNDRESOURCE_H

View File

@@ -0,0 +1,147 @@
/* 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/>.
*
*/
#include "illusions/illusions.h"
#include "illusions/resources/fontresource.h"
#include "illusions/dictionary.h"
namespace Illusions {
// FontResourceLoader
void FontResourceLoader::load(Resource *resource) {
FontInstance *fontInstance = new FontInstance(_vm);
fontInstance->load(resource);
resource->_instance = fontInstance;
}
bool FontResourceLoader::isFlag(int flag) {
return
flag == kRlfLoadFile;
}
// CharInfo
void CharInfo::load(byte *dataStart, Common::SeekableReadStream &stream) {
_width = stream.readUint16LE();
_field_2 = stream.readUint16LE();
uint32 pixelsOffs = stream.readUint32LE();
_pixels = dataStart + pixelsOffs;
debug(2, "CharInfo::load() _width: %d; _field_2: %d; pixelsOffs: %08X",
_width, _field_2, pixelsOffs);
}
// CharRange
CharRange::CharRange() : _charInfos(nullptr) {
}
CharRange::~CharRange() {
delete[] _charInfos;
}
void CharRange::load(byte *dataStart, Common::SeekableReadStream &stream) {
_firstChar = stream.readUint16LE();
_lastChar = stream.readUint16LE();
uint count = _lastChar - _firstChar + 1;
uint32 charInfosOffs = stream.readUint32LE();
_charInfos = new CharInfo[count];
for (uint i = 0; i < count; ++i) {
stream.seek(charInfosOffs + i * 8);
_charInfos[i].load(dataStart, stream);
}
debug(2, "CharRange::load() _firstChar: %d; _lastChar: %d; charInfosOffs: %08X",
_firstChar, _lastChar, charInfosOffs);
}
CharInfo *CharRange::getCharInfo(uint16 c) {
return &_charInfos[c - _firstChar];
}
bool CharRange::containsChar(uint16 c) {
return c >= _firstChar && c <= _lastChar;
}
// FontResource
FontResource::FontResource() : _charRanges(nullptr) {
}
FontResource::~FontResource() {
delete[] _charRanges;
}
void FontResource::load(Resource *resource) {
byte *data = resource->_data;
uint32 dataSize = resource->_dataSize;
Common::MemoryReadStream stream(data, dataSize, DisposeAfterUse::NO);
_totalSize = stream.readUint32LE();
_charHeight = stream.readUint16LE();
_defaultChar = stream.readUint16LE();
_colorIndex = stream.readUint16LE();
_lineIncr = stream.readUint16LE();
_widthC = stream.readUint16LE();
_charRangesCount = stream.readUint16LE();
uint32 charRangesOffs = stream.pos();
_charRanges = new CharRange[_charRangesCount];
for (uint i = 0; i < _charRangesCount; ++i) {
stream.seek(charRangesOffs + i * 8);
_charRanges[i].load(data, stream);
}
debug(2, "FontResource::load() _charHeight: %d; _defaultChar: %d; _colorIndex: %d; _lineIncr: %d; _widthC: %d; _charRangesCount: %d",
_charHeight, _defaultChar, _colorIndex, _lineIncr, _widthC, _charRangesCount);
}
CharInfo *FontResource::getCharInfo(uint16 c) {
for (uint i = 0; i < _charRangesCount; ++i) {
if (_charRanges[i].containsChar(c))
return _charRanges[i].getCharInfo(c);
}
return nullptr;
}
const Common::Rect FontResource::calculateRectForText(uint16 *text, uint textLength) {
int16 width = 0;
for (uint i = 0; i < textLength && *text; i++) {
width += getCharInfo(*text)->_width;
text++;
}
return Common::Rect(width, getCharHeight() + getLineIncr());
}
// FontInstance
FontInstance::FontInstance(IllusionsEngine *vm) : _vm(vm) {
}
void FontInstance::load(Resource *resource) {
_fontResource = new FontResource();
_fontResource->load(resource);
_resId = resource->_resId;
_vm->_dict->addFont(resource->_resId, _fontResource);
}
void FontInstance::unload() {
delete _fontResource;
_vm->_dict->removeFont(_resId);
}
} // End of namespace Illusions

View File

@@ -0,0 +1,95 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_FONTRESOURCE_H
#define ILLUSIONS_FONTRESOURCE_H
#include "illusions/graphics.h"
#include "illusions/resourcesystem.h"
namespace Illusions {
class IllusionsEngine;
class FontResourceLoader : public BaseResourceLoader {
public:
FontResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
~FontResourceLoader() override {}
void load(Resource *resource) override;
bool isFlag(int flag) override;
protected:
IllusionsEngine *_vm;
};
struct CharInfo {
int16 _width;
int16 _field_2;
byte *_pixels;
void load(byte *dataStart, Common::SeekableReadStream &stream);
};
struct CharRange {
CharRange();
~CharRange();
uint16 _firstChar;
uint16 _lastChar;
CharInfo *_charInfos;
void load(byte *dataStart, Common::SeekableReadStream &stream);
CharInfo *getCharInfo(uint16 c);
bool containsChar(uint16 c);
};
class FontResource {
public:
FontResource();
~FontResource();
void load(Resource *resource);
CharInfo *getCharInfo(uint16 c);
int16 getColorIndex() const { return _colorIndex; }
int16 getCharHeight() const { return _charHeight; }
int16 getLineIncr() const { return _lineIncr; }
const Common::Rect calculateRectForText(uint16 *text, uint textLength);
public:
uint32 _totalSize;
int16 _charHeight;
int16 _defaultChar;
int16 _colorIndex;
int16 _lineIncr;
int16 _widthC;
uint _charRangesCount;
CharRange *_charRanges;
CharRange *getCharRange(uint16 c);
};
class FontInstance : public ResourceInstance {
public:
FontInstance(IllusionsEngine *vm);
void load(Resource *resource) override;
void unload() override;
public:
IllusionsEngine *_vm;
FontResource *_fontResource;
uint32 _resId;
};
} // End of namespace Illusions
#endif // ILLUSIONS_FONTRESOURCE_H

View File

@@ -0,0 +1,39 @@
/* 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/>.
*
*/
#include "illusions/illusions.h"
#include "illusions/resources/genericresource.h"
#include "illusions/dictionary.h"
namespace Illusions {
// GenericResourceLoader
void GenericResourceLoader::load(Resource *resource) {
resource->_instance = nullptr;
}
bool GenericResourceLoader::isFlag(int flag) {
return
flag == kRlfLoadFile;
}
} // End of namespace Illusions

View File

@@ -0,0 +1,44 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_GENERICRESOURCE_H
#define ILLUSIONS_GENERICRESOURCE_H
#include "illusions/graphics.h"
#include "illusions/resourcesystem.h"
namespace Illusions {
class IllusionsEngine;
class GenericResourceLoader : public BaseResourceLoader {
public:
GenericResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
~GenericResourceLoader() override {}
void load(Resource *resource) override;
bool isFlag(int flag) override;
protected:
IllusionsEngine *_vm;
};
} // End of namespace Illusions
#endif // ILLUSIONS_GENERICRESOURCE_H

View File

@@ -0,0 +1,99 @@
/* 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/>.
*
*/
#include "illusions/illusions.h"
#include "illusions/resources/midiresource.h"
namespace Illusions {
// MidiGroupResourceLoader
void MidiGroupResourceLoader::load(Resource *resource) {
debug(1, "MidiGroupResourceLoader::load() Loading midi group %08X...", resource->_resId);
MidiGroupInstance *midiGroupInstance = new MidiGroupInstance(_vm);
midiGroupInstance->load(resource);
resource->_instance = midiGroupInstance;
}
bool MidiGroupResourceLoader::isFlag(int flag) {
return
flag == kRlfLoadFile/* ||
flag == kRlfFreeDataAfterLoad*/;
}
// MidiMusic
void MidiMusic::load(Common::SeekableReadStream &stream) {
_musicId = stream.readUint32LE();
_looping = stream.readUint16LE() != 0;
stream.skip(2 + 32 + 4); // Skip unused/unknown values
debug(1, "MidiMusic::load() _musicId: %08X; _looping: %d", _musicId, _looping);
}
// MidiGroupResource
MidiGroupResource::MidiGroupResource()
: _midiMusicCount(0), _midiMusic(nullptr) {
}
MidiGroupResource::~MidiGroupResource() {
delete[] _midiMusic;
}
void MidiGroupResource::load(byte *data, uint32 dataSize) {
Common::MemoryReadStream stream(data, dataSize, DisposeAfterUse::NO);
stream.skip(4);
_midiMusicCount = stream.readUint16LE();
stream.skip(2);
uint32 midiMusicOffs = stream.readUint32LE();
debug("_midiMusicCount: %d; midiMusicOffs: %08X", _midiMusicCount, midiMusicOffs);
_midiMusic = new MidiMusic[_midiMusicCount];
stream.seek(midiMusicOffs);
for (uint i = 0; i < _midiMusicCount; ++i) {
_midiMusic[i].load(stream);
}
}
// MidiGroupInstance
MidiGroupInstance::MidiGroupInstance(IllusionsEngine *vm)
: _vm(vm), _midiGroupResource(nullptr) {
}
void MidiGroupInstance::load(Resource *resource) {
_midiGroupResource = new MidiGroupResource();
_midiGroupResource->load(resource->_data, resource->_dataSize);
for (uint i = 0; i < _midiGroupResource->_midiMusicCount; ++i) {
// TODO
// SoundEffect *soundEffect = &_soundGroupResource->_soundEffects[i];
// _vm->_soundMan->loadSound(soundEffect->_soundEffectId, resource->_resId, soundEffect->_looping);
}
_resId = resource->_resId;
}
void MidiGroupInstance::unload() {
// _vm->_soundMan->unloadSounds(_resId);
delete _midiGroupResource;
}
} // End of namespace Illusions

View File

@@ -0,0 +1,71 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_MIDIRESOURCE_H
#define ILLUSIONS_MIDIRESOURCE_H
#include "illusions/graphics.h"
#include "illusions/resourcesystem.h"
namespace Illusions {
class IllusionsEngine;
class MidiGroupResourceLoader : public BaseResourceLoader {
public:
MidiGroupResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
~MidiGroupResourceLoader() override {}
void load(Resource *resource) override;
bool isFlag(int flag) override;
protected:
IllusionsEngine *_vm;
};
struct MidiMusic {
uint32 _musicId;
bool _looping;
void load(Common::SeekableReadStream &stream);
};
class MidiGroupResource {
public:
MidiGroupResource();
~MidiGroupResource();
void load(byte *data, uint32 dataSize);
public:
uint _midiMusicCount;
MidiMusic *_midiMusic;
};
class MidiGroupInstance : public ResourceInstance {
public:
MidiGroupInstance(IllusionsEngine *vm);
void load(Resource *resource) override;
void unload() override;
public:
IllusionsEngine *_vm;
MidiGroupResource *_midiGroupResource;
uint32 _resId;
};
} // End of namespace Illusions
#endif // ILLUSIONS_SOUNDRESOURCE_H

View File

@@ -0,0 +1,413 @@
/* 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/>.
*
*/
#include "illusions/illusions.h"
#include "illusions/resources/scriptresource.h"
namespace Illusions {
// ScriptResourceLoader
void ScriptResourceLoader::load(Resource *resource) {
ScriptInstance *scriptInstance = new ScriptInstance(_vm);
scriptInstance->load(resource);
resource->_instance = scriptInstance;
}
bool ScriptResourceLoader::isFlag(int flag) {
return
flag == kRlfLoadFile;
}
// Properties
Properties::Properties()
: _count(0), _properties(nullptr) {
}
void Properties::init(uint count, byte *properties) {
_count = count;
_properties = properties;
}
void Properties::clear() {
uint32 size = getSize();
for (uint32 i = 0; i < size; ++i) {
_properties[i] = 0;
}
}
bool Properties::get(uint32 propertyId) {
uint index;
byte mask;
getProperyPos(propertyId, index, mask);
return (_properties[index] & mask) != 0;
}
void Properties::set(uint32 propertyId, bool value) {
uint index;
byte mask;
getProperyPos(propertyId, index, mask);
if (value)
_properties[index] |= mask;
else
_properties[index] &= ~mask;
}
uint32 Properties::getSize() {
return (_count >> 3) + 1;
}
void Properties::writeToStream(Common::WriteStream *out) {
const uint32 size = getSize();
out->writeUint32LE(size);
out->write(_properties, size);
}
bool Properties::readFromStream(Common::ReadStream *in) {
uint32 size = in->readUint32LE();
if (size != getSize())
return false;
in->read(_properties, size);
return true;
}
void Properties::getProperyPos(uint32 propertyId, uint &index, byte &mask) {
propertyId &= 0xFFFF;
index = propertyId >> 3;
mask = 1 << (propertyId & 7);
}
// BlockCounters
BlockCounters::BlockCounters()
: _count(0), _blockCounters(nullptr) {
}
void BlockCounters::init(uint count, byte *blockCounters) {
_count = count;
_blockCounters = blockCounters;
}
void BlockCounters::clear() {
for (uint i = 0; i < _count; ++i) {
_blockCounters[i] = 0;
}
}
byte BlockCounters::get(uint index) {
return _blockCounters[index - 1] & 0x3F;
}
void BlockCounters::set(uint index, byte value) {
_blockCounters[index - 1] ^= (_blockCounters[index - 1] ^ value) & 0x3F;
}
byte BlockCounters::getC0(uint index) {
return _blockCounters[index - 1] & 0xC0;
}
void BlockCounters::setC0(uint index, byte value) {
byte oldValue = _blockCounters[index - 1] & 0x3F;
if (value & 0x80)
value = value & 0xBF;
_blockCounters[index - 1] = oldValue | (value & 0xC0);
}
uint32 BlockCounters::getSize() {
return _count;
}
void BlockCounters::writeToStream(Common::WriteStream *out) {
const uint32 size = getSize();
out->writeUint32LE(size);
out->write(_blockCounters, size);
}
bool BlockCounters::readFromStream(Common::ReadStream *in) {
uint32 size = in->readUint32LE();
if (size != getSize())
return false;
in->read(_blockCounters, size);
return true;
}
// TriggerCause
void TriggerCause::load(Common::SeekableReadStream &stream) {
_verbId = stream.readUint32LE();
_objectId2 = stream.readUint32LE();
_codeOffs = stream.readUint32LE();
debug(2, "TriggerCause::load() _verbId: %08X; _objectId2: %08X; _codeOffs: %08X",
_verbId, _objectId2, _codeOffs);
}
// TriggerObject
TriggerObject::TriggerObject()
: _causesCount(0), _causes(nullptr) {
}
TriggerObject::~TriggerObject() {
delete[] _causes;
}
void TriggerObject::load(byte *dataStart, Common::SeekableReadStream &stream) {
_objectId = stream.readUint32LE();
_causesCount = stream.readUint16LE();
stream.skip(2); // Skip padding
debug(2, "TriggerObject::load() _objectId: %08X; _causesCount: %d",
_objectId, _causesCount);
_causes = new TriggerCause[_causesCount];
for (uint i = 0; i < _causesCount; ++i) {
_causes[i].load(stream);
}
}
bool TriggerObject::findTriggerCause(uint32 verbId, uint32 objectId2, uint32 &codeOffs) {
if ((verbId & 0xFFFF0000) == 0) {
for (uint i = 0; i < _causesCount; ++i) {
if ((verbId == 7 && ((_causes[i]._verbId == 7 && _causes[i]._objectId2 == objectId2) || _causes[i]._verbId == 8)) ||
(verbId != 7 && verbId == _causes[i]._verbId)) {
codeOffs = _causes[i]._codeOffs;
return true;
}
}
} else {
for (uint i = 0; i < _causesCount; ++i) {
if (_causes[i]._verbId == verbId && _causes[i]._objectId2 == objectId2) {
codeOffs = _causes[i]._codeOffs;
return true;
}
}
}
return false;
}
void TriggerObject::fixupSceneInfosDuckman() {
for (uint i = 0; i < _causesCount; ++i) {
_causes[i]._verbId &= 0xFFFF;
}
}
// SceneInfo
SceneInfo::SceneInfo()
: _triggerObjectsCount(0), _triggerObjects(nullptr),
_resourcesCount(0), _resources(nullptr) {
}
SceneInfo::~SceneInfo() {
delete[] _triggerObjects;
delete[] _resources;
}
void SceneInfo::load(byte *dataStart, Common::SeekableReadStream &stream) {
_id = stream.readUint16LE();
_unk = stream.readUint16LE();
_name = (uint16 *)(dataStart + stream.pos());
swapBytesInWideString((byte *)_name);
stream.skip(128);
_triggerObjectsCount = stream.readUint16LE();
_resourcesCount = stream.readUint16LE();
debug(2, "\nSceneInfo::load() _id: %d; _unk: %d; _name: [%s]",
_id, _unk, debugW2I(_name));
uint32 triggerObjectsListOffs = stream.readUint32LE();
if (_resourcesCount > 0) {
_resources = new uint32[_resourcesCount];
for (uint i = 0; i < _resourcesCount; ++i) {
_resources[i] = stream.readUint32LE();
}
}
if (_triggerObjectsCount > 0) {
_triggerObjects = new TriggerObject[_triggerObjectsCount];
for (uint i = 0; i < _triggerObjectsCount; ++i) {
stream.seek(triggerObjectsListOffs + i * 4);
uint32 triggerObjectOffs = stream.readUint32LE();
stream.seek(triggerObjectOffs);
_triggerObjects[i].load(dataStart, stream);
}
}
}
bool SceneInfo::findTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs) {
TriggerObject *triggerObject = findTriggerObject(objectId);
if (triggerObject)
return triggerObject->findTriggerCause(verbId, objectId2, codeOffs);
return false;
}
void SceneInfo::getResources(uint &resourcesCount, uint32 *&resources) {
resourcesCount = _resourcesCount;
resources = _resources;
}
TriggerObject *SceneInfo::findTriggerObject(uint32 objectId) {
for (uint i = 0; i < _triggerObjectsCount; ++i) {
if (_triggerObjects[i]._objectId == objectId)
return &_triggerObjects[i];
}
return nullptr;
}
void SceneInfo::fixupSceneInfosDuckman() {
for (uint i = 0; i < _triggerObjectsCount; ++i) {
_triggerObjects[i].fixupSceneInfosDuckman();
}
}
// ScriptResource
ScriptResource::ScriptResource()
: _codeOffsets(nullptr), _sceneInfos(nullptr), _objectMap(nullptr) {
}
ScriptResource::~ScriptResource() {
delete[] _codeOffsets;
delete[] _sceneInfos;
delete[] _objectMap;
}
void ScriptResource::load(Resource *resource) {
_data = resource->_data;
_dataSize = resource->_dataSize;
Common::MemoryReadStream stream(_data, _dataSize, DisposeAfterUse::NO);
uint32 objectMapOffs = 0, sceneInfosOffs = 0;
_objectMapCount = 0;
if (resource->_gameId == kGameIdBBDOU) {
sceneInfosOffs = 0x18;
} else if (resource->_gameId == kGameIdDuckman) {
for (uint i = 0; i < 27; ++i) {
_soundIds[i] = stream.readUint32LE();
}
sceneInfosOffs = 0x8C;
}
stream.skip(4); // Skip unused
// Read item counts
uint propertiesCount = stream.readUint16LE();
uint blockCountersCount = stream.readUint16LE();
if (resource->_gameId == kGameIdDuckman)
_objectMapCount = stream.readUint16LE();
_codeCount = stream.readUint16LE();
_sceneInfosCount = stream.readUint16LE();
if (resource->_gameId == kGameIdDuckman)
stream.readUint16LE();//Unused?
// Read item offsets
uint32 propertiesOffs = stream.readUint32LE();
uint32 blockCountersOffs = stream.readUint32LE();
if (resource->_gameId == kGameIdDuckman)
objectMapOffs = stream.readUint32LE(); //TODO Is this needed for BBDOU?
uint32 codeTblOffs = stream.readUint32LE();
debug(2, "ScriptResource::load() propertiesCount: %d; blockCountersCount: %d; _codeCount: %d; _sceneInfosCount: %d; _objectMapCount: %d",
propertiesCount, blockCountersCount, _codeCount, _sceneInfosCount, _objectMapCount);
debug(2, "ScriptResource::load() propertiesOffs: %08X; blockCountersOffs: %08X; codeTblOffs: %08X; objectMapOffs: %08X",
propertiesOffs, blockCountersOffs, codeTblOffs, objectMapOffs);
// Init properties
_properties.init(propertiesCount, _data + propertiesOffs);
// Init blockcounters
_blockCounters.init(blockCountersCount, _data + blockCountersOffs);
_codeOffsets = new uint32[_codeCount];
stream.seek(codeTblOffs);
for (uint i = 0; i < _codeCount; ++i) {
_codeOffsets[i] = stream.readUint32LE();
}
_sceneInfos = new SceneInfo[_sceneInfosCount];
for (uint i = 0; i < _sceneInfosCount; ++i) {
stream.seek(sceneInfosOffs + i * 4);
uint32 sceneInfoOffs = stream.readUint32LE();
stream.seek(sceneInfoOffs);
_sceneInfos[i].load(_data, stream);
}
if (_objectMapCount > 0) {
_objectMap = new uint32[_objectMapCount];
stream.seek(objectMapOffs);
for (uint i = 0; i < _objectMapCount; ++i) {
_objectMap[i] = stream.readUint32LE();
stream.skip(4);
}
}
if (resource->_gameId == kGameIdDuckman) {
stream.seek(0x6C);
_mainActorObjectId = stream.readUint32LE();
} else if (resource->_gameId == kGameIdBBDOU) {
stream.seek(0);
_mainActorObjectId = stream.readUint32LE();
}
if (resource->_gameId == kGameIdDuckman)
fixupSceneInfosDuckman();
}
byte *ScriptResource::getThreadCode(uint32 threadId) {
return _data + _codeOffsets[(threadId & 0xFFFF) - 1];
}
byte *ScriptResource::getCode(uint32 codeOffs) {
return _data + codeOffs;
}
SceneInfo *ScriptResource::getSceneInfo(uint32 index) {
if (index > 0 && index <= _sceneInfosCount)
return &_sceneInfos[index - 1];
return nullptr;
}
uint32 ScriptResource::getObjectActorTypeId(uint32 objectId) {
return _objectMap[(objectId & 0xFFFF) - 1];
}
void ScriptResource::fixupSceneInfosDuckman() {
for (uint i = 0; i < _sceneInfosCount; ++i) {
_sceneInfos[i].fixupSceneInfosDuckman();
}
}
// ScriptInstance
ScriptInstance::ScriptInstance(IllusionsEngine *vm)
: _vm(vm) {
}
void ScriptInstance::load(Resource *resource) {
_vm->_scriptResource = new ScriptResource();
_vm->_scriptResource->load(resource);
}
void ScriptInstance::unload() {
delete _vm->_scriptResource;
_vm->_scriptResource = nullptr;
}
} // End of namespace Illusions

View File

@@ -0,0 +1,152 @@
/* 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/>.
*
*/
#ifndef ILLUSIONS_SCRIPTRESOURCE_H
#define ILLUSIONS_SCRIPTRESOURCE_H
#include "illusions/resourcesystem.h"
#include "common/file.h"
namespace Illusions {
class IllusionsEngine;
class ScriptResourceLoader : public BaseResourceLoader {
public:
ScriptResourceLoader(IllusionsEngine *vm) : _vm(vm) {}
~ScriptResourceLoader() override {}
void load(Resource *resource) override;
bool isFlag(int flag) override;
protected:
IllusionsEngine *_vm;
};
class Properties {
public:
Properties();
void init(uint count, byte *properties);
void clear();
bool get(uint32 propertyId);
void set(uint32 propertyId, bool value);
uint32 getSize();
void writeToStream(Common::WriteStream *out);
bool readFromStream(Common::ReadStream *in);
public:
uint _count;
byte *_properties;
void getProperyPos(uint32 propertyId, uint &index, byte &mask);
};
class BlockCounters {
public:
BlockCounters();
void init(uint count, byte *blockCounters);
void clear();
byte get(uint index);
void set(uint index, byte value);
byte getC0(uint index);
void setC0(uint index, byte value);
uint32 getSize();
void writeToStream(Common::WriteStream *out);
bool readFromStream(Common::ReadStream *in);
public:
uint _count;
byte *_blockCounters;
};
struct TriggerCause {
uint32 _verbId;
uint32 _objectId2;
uint32 _codeOffs;
void load(Common::SeekableReadStream &stream);
};
class TriggerObject {
public:
TriggerObject();
~TriggerObject();
void load(byte *dataStart, Common::SeekableReadStream &stream);
bool findTriggerCause(uint32 verbId, uint32 objectId2, uint32 &codeOffs);
void fixupSceneInfosDuckman();
public:
uint32 _objectId;
uint _causesCount;
TriggerCause *_causes;
};
class SceneInfo {
public:
SceneInfo();
~SceneInfo();
void load(byte *dataStart, Common::SeekableReadStream &stream);
bool findTriggerCause(uint32 verbId, uint32 objectId2, uint32 objectId, uint32 &codeOffs);
void getResources(uint &resourcesCount, uint32 *&resources);
void fixupSceneInfosDuckman();
protected:
uint16 _id;
uint16 _unk;
uint16 *_name;
uint _triggerObjectsCount;
TriggerObject *_triggerObjects;
uint _resourcesCount;
uint32 *_resources;
TriggerObject *findTriggerObject(uint32 objectId);
};
class ScriptResource {
public:
ScriptResource();
~ScriptResource();
void load(Resource *resource);
byte *getThreadCode(uint32 threadId);
byte *getCode(uint32 codeOffs);
SceneInfo *getSceneInfo(uint32 index);
uint32 getObjectActorTypeId(uint32 objectId);
uint32 getMainActorObjectId() const { return _mainActorObjectId; }
public:
byte *_data;
uint32 _dataSize;
Properties _properties;
BlockCounters _blockCounters;
uint _codeCount;
uint32 *_codeOffsets;
uint _sceneInfosCount;
SceneInfo *_sceneInfos;
// Duckman specific
uint32 _soundIds[27];
uint _objectMapCount;
uint32 *_objectMap;
uint32 _mainActorObjectId;
void fixupSceneInfosDuckman();
};
class ScriptInstance : public ResourceInstance {
public:
ScriptInstance(IllusionsEngine *vm);
void load(Resource *resource) override;
void unload() override;
public:
IllusionsEngine *_vm;
};
} // End of namespace Illusions
#endif // ILLUSIONS_ACTORRESOURCE_H

Some files were not shown because too many files have changed in this diff Show More