scummvm/engines/mads/scene.cpp

503 lines
13 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "common/scummsys.h"
#include "mads/scene.h"
#include "mads/compression.h"
#include "mads/mads.h"
#include "mads/nebular/nebular_scenes.h"
namespace MADS {
Scene::Scene(MADSEngine *vm): _vm(vm), _action(_vm), _depthSurface(vm),
_dirtyAreas(_vm), _dynamicHotspots(vm), _interface(vm),
_kernelMessages(vm), _screenObjects(vm), _sequences(vm),
_sprites(vm), _spriteSlots(vm), _textDisplay(vm) {
_priorSceneId = 0;
_nextSceneId = 0;
_currentSceneId = 0;
_vocabBuffer = nullptr;
_sceneLogic = nullptr;
_sceneInfo = nullptr;
_animFlag = false;
_animVal1 = 0;
_depthStyle = 0;
_v1A = _v1C = 0;
_roomChanged = false;
_reloadSceneFlag = false;
_destFacing = 0;
_freeAnimationFlag = false;
_animation = nullptr;
_activeAnimation = nullptr;
_textSpacing = -1;
_verbList.push_back(VerbInit(VERB_LOOK, 2, 0));
_verbList.push_back(VerbInit(VERB_TAKE, 2, 0));
_verbList.push_back(VerbInit(VERB_PUSH, 2, 0));
_verbList.push_back(VerbInit(VERB_OPEN, 2, 0));
_verbList.push_back(VerbInit(VERB_PUT, 1, -1));
_verbList.push_back(VerbInit(VERB_TALKTO, 2, 0));
_verbList.push_back(VerbInit(VERB_GIVE, 1, 2));
_verbList.push_back(VerbInit(VERB_PULL, 2, 0));
_verbList.push_back(VerbInit(VERB_CLOSE, 2, 0));
_verbList.push_back(VerbInit(VERB_THROW, 1, 2));
}
Scene::~Scene() {
delete[] _vocabBuffer;
delete _sceneLogic;
delete _sceneInfo;
}
void Scene::clearVocab() {
freeVocab();
_activeVocabs.clear();
}
void Scene::freeVocab() {
delete[] _vocabBuffer;
_vocabBuffer = nullptr;
}
void Scene::addActiveVocab(int vocabId) {
if (activeVocabIndexOf(vocabId) == -1) {
assert(_activeVocabs.size() < 200);
_activeVocabs.push_back(vocabId);
}
}
int Scene::activeVocabIndexOf(int vocabId) {
for (uint i = 0; i < _activeVocabs.size(); ++i) {
if (_activeVocabs[i] == vocabId)
return i;
}
return -1;
}
void Scene::clearSequenceList() {
_sequences.clear();
}
void Scene::clearMessageList() {
_kernelMessages.clear();
_talkFont = "*FONTCONV.FF";
_textSpacing = -1;
}
void Scene::loadSceneLogic() {
delete _sceneLogic;
switch (_vm->getGameID()) {
case GType_RexNebular:
_sceneLogic = Nebular::SceneFactory::createScene(_vm);
break;
default:
error("Unknown game");
}
}
void Scene::loadScene(int sceneId, const Common::String &prefix, bool palFlag) {
// Store the previously active scene number and set the new one
_priorSceneId = _currentSceneId;
_currentSceneId = sceneId;
_v1 = 0;
if (palFlag)
_vm->_palette->resetGamePalette(18, 10);
_spriteSlots.clear(false);
_sequences.clear();
_kernelMessages.clear();
// TODO: palletteUsage reset? setPalette(_nullPalette);
_sceneInfo = SceneInfo::init(_vm);
_sceneInfo->load(_currentSceneId, _v1, Common::String(), _vm->_game->_v2 ? 17 : 16,
_depthSurface, _backgroundSurface);
// Initialise palette animation for the scene
initPaletteAnimation(_sceneInfo->_palAnimData, false);
// Copy over nodes
_nodes.clear();
for (uint i = 0; i < _sceneInfo->_nodes.size(); ++i)
_nodes.push_back(_sceneInfo->_nodes[i]);
// Load hotspots
loadHotspots();
// Load vocab
loadVocab();
// Load palette usage
_vm->_palette->_paletteUsage.load(1, 0xF);
// Load interface
int flags = _vm->_game->_v2 ? 0x4101 : 0x4100;
if (!_vm->_textWindowStill)
flags |= 0x200;
_animation = Animation::init(_vm, this);
MSurface surface;
_animation->load(surface, _interface, prefix, flags, nullptr, nullptr);
_vm->_palette->_paletteUsage.load(0);
_bandsRange = _sceneInfo->_yBandsEnd - _sceneInfo->_yBandsStart;
_scaleRange = _sceneInfo->_maxScale - _sceneInfo->_minScale;
_spriteSlots.clear(false);
_interfaceY = MADS_SCENE_HEIGHT;
_spritesCount = _sprites.size();
warning("TODO: sub_1EA80 / showMouse");
warning("TODO: inventory_anim_allocate");
}
void Scene::loadHotspots() {
File f(Resources::formatName(RESPREFIX_RM, _currentSceneId, ".HH"));
MadsPack madsPack(&f);
Common::SeekableReadStream *stream = madsPack.getItemStream(0);
int count = stream->readUint16LE();
delete stream;
stream = madsPack.getItemStream(1);
_hotspots.clear();
for (int i = 0; i < count; ++i)
_hotspots.push_back(Hotspot(*stream));
delete stream;
f.close();
}
void Scene::loadVocab() {
// Add all the verbs to the active vocab list
for (uint i = 0; i < _verbList.size(); ++i)
addActiveVocab(_verbList[i]._id);
// Load the vocabs for any object descriptions and custom actions
for (uint objIndex = 0; objIndex < _vm->_game->_objects.size(); ++objIndex) {
InventoryObject &io = _vm->_game->_objects[objIndex];
addActiveVocab(io._descId);
for (int vocabIndex = 0; vocabIndex <io._vocabCount; ++vocabIndex) {
addActiveVocab(io._vocabList[vocabIndex]._vocabId);
}
}
// Load scene hotspot list vocabs and verbs
for (uint i = 0; i < _hotspots.size(); ++i) {
addActiveVocab(_hotspots[i]._vocabId);
if (_hotspots[i]._verbId)
addActiveVocab(_hotspots[i]._verbId);
}
loadVocabStrings();
}
void Scene::loadVocabStrings() {
freeVocab();
File f("*VOCAB.DAT");
char *textStrings = new char[f.size()];
f.read(textStrings, f.size());
for (uint strIndex = 0; strIndex < _activeVocabs.size(); ++strIndex) {
const char *s = textStrings;
for (int vocabIndex = 0; vocabIndex < _activeVocabs[strIndex]; ++vocabIndex)
s += strlen(s) + 1;
_vocabStrings.push_back(s);
}
delete[] textStrings;
f.close();
}
void Scene::initPaletteAnimation(Common::Array<RGB4> &animData, bool animFlag) {
// Initialise the animation palette and ticks list
_animTicksList.clear();
_animPalData.clear();
for (uint i = 0; i < animData.size(); ++i) {
_animTicksList.push_back(_vm->_events->getFrameCounter());
_animPalData.push_back(animData[i]);
}
// Save the initial starting palette
Common::copy(&_vm->_palette->_mainPalette[0], &_vm->_palette->_mainPalette[PALETTE_SIZE],
&_vm->_palette->_savedPalette[0]);
// Calculate total
_animCount = 0;
for (uint i = 0; i < _animPalData.size(); ++i)
_animCount += _animPalData[i].r;
_animVal1 = (_animCount > 16) ? 3 : 0;
_animFlag = animFlag;
}
bool Scene::getDepthHighBits(const Common::Point &pt) {
if (_sceneInfo->_depthStyle == 2) {
return 0;
} else {
const byte *p = _depthSurface.getBasePtr(pt.x, pt.y);
return (*p & 0x70) >> 4;
}
}
void Scene::loop() {
_nextSceneId = _currentSceneId;
while (!_vm->shouldQuit() && !_reloadSceneFlag && _nextSceneId == _currentSceneId) {
// Handle drawing a game frame
doFrame();
if (_vm->_dialogs->_pendingDialog != DIALOG_NONE && !_vm->_game->_abortTimers
&& _vm->_game->_player._stepEnabled)
_reloadSceneFlag = true;
}
}
void Scene::doFrame() {
Player &player = _vm->_game->_player;
bool flag = false;
if (_action._selectedAction || !player._stepEnabled) {
_action.clear();
_action._selectedAction = 0;
}
if (!_vm->_game->_abortTimers && !player._unk3) {
if (_dynamicHotspots._changed)
_dynamicHotspots.refresh();
_screenObjects.check(player._stepEnabled && !_action._startWalkFlag &&
!_vm->_game->_abortTimers2);
}
if (_action._selectedAction && player._stepEnabled && !_action._startWalkFlag &&
!_vm->_game->_abortTimers && !player._unk3) {
_action.startAction();
if (_action._activeAction._verbId == Nebular::NOUN_LOOK_AT) {
_action._activeAction._verbId = VERB_LOOK;
_action._savedSelectedRow = false;
}
flag = true;
}
if (flag || (_vm->_game->_abortTimers && _vm->_game->_abortTimersMode == ABORTMODE_2)) {
doPreactions();
}
checkStartWalk();
if (!_vm->_game->_abortTimers2)
_vm->_events->_currentTimer = _vm->_events->getFrameCounter();
if ((_action._inProgress && !player._moving && !_action._startWalkFlag &&
player._newDirection == player._direction) ||
(_vm->_game->_abortTimers && _vm->_game->_abortTimersMode == ABORTMODE_0)) {
doAction();
}
if (_currentSceneId != _nextSceneId) {
_freeAnimationFlag = true;
} else {
doSceneStep();
checkKeyboard();
if (_currentSceneId != _nextSceneId) {
_freeAnimationFlag = true;
} else {
player.nextFrame();
// Cursor update code
CursorType cursorId = CURSOR_ARROW;
if (_action._v83338 == 1 && !_screenObjects._v7FECA &&
_screenObjects._category == CAT_HOTSPOT) {
int idx = _screenObjects._selectedObject - _interface._screenObjectsCount;
if (idx >= (int)_hotspots.size()) {
idx -= _hotspots.size();
_vm->_events->_newCursorId = _dynamicHotspots[idx]._cursor;
} else {
_vm->_events->_newCursorId = _hotspots[idx]._cursor;
}
cursorId = _vm->_events->_newCursorId == CURSOR_NONE ?
CURSOR_ARROW : _vm->_events->_newCursorId;
}
if (!player._stepEnabled)
cursorId = CURSOR_WAIT;
if (cursorId >= _vm->_events->_cursorSprites->getCount())
cursorId = (CursorType)_vm->_events->_cursorSprites->getCount();
_vm->_events->_newCursorId = cursorId;
if (cursorId != _vm->_events->_cursorId) {
_vm->_events->setCursor(cursorId);
}
if (!_vm->_game->_abortTimers) {
// Handle any active sequences
_sequences.tick();
// Handle any active animation
if (_activeAnimation)
_activeAnimation->update();
}
// If the debugget flag is set, show the mouse position
int mouseTextIndex = 0;
if (_vm->_debugger->_showMousePos) {
Common::Point pt = _vm->_events->mousePos();
Common::String msg = Common::String::format("(%d,%d)", pt.x, pt.y);
mouseTextIndex = _kernelMessages.add(Common::Point(5, 5),
0x203, 0, 0, 1, msg);
}
if (!_vm->_game->_abortTimers) {
if (_reloadSceneFlag || _currentSceneId != _nextSceneId)
_kernelMessages.reset();
_kernelMessages.update();
}
_vm->_game->_abortTimers2 = !_vm->_game->_abortTimers2;
warning("TODO: image_inter_list_call");
// Write any text needed by the interface
if (_vm->_game->_abortTimers2)
_interface.writeText();
// Draw any elements
drawElements(_vm->_game->_abortTimers2, _vm->_game->_abortTimers2);
// Handle message updates
if (_vm->_game->_abortTimers2) {
uint32 priorTime = _vm->_game->_priorFrameTimer;
uint32 newTime = _vm->_events->getFrameCounter();
_sequences.delay(newTime, priorTime);
_kernelMessages.delay(newTime, priorTime);
}
if (_vm->_debugger->_showMousePos)
// Mouse position display isn't persistent, so remove it
_kernelMessages.remove(mouseTextIndex);
warning("TODO: sub_1DA3E");
}
}
if (_vm->_game->_abortTimers2)
_animFlag = true;
_vm->_game->_abortTimers2 = 0;
if (_freeAnimationFlag) {
_activeAnimation->free();
_activeAnimation = nullptr;
}
// TODO: Verify correctness of frame wait
}
void Scene::drawElements(bool transitionFlag, bool surfaceFlag) {
// Draw any sprites
_spriteSlots.drawBackground();
// Process dirty areas
_textDisplay.setDirtyAreas();
// Merge any identified dirty areas
_dirtyAreas.merge(1, DIRTY_AREAS_SIZE);
// Copy dirty areas to the main display surface
_dirtyAreas.copy(&_vm->_screen, &_backgroundSurface, _posAdjust);
// Handle dirty areas for foreground objects
_spriteSlots.setDirtyAreas();
_textDisplay.setDirtyAreas2();
_dirtyAreas.merge(1, DIRTY_AREAS_SIZE);
// Draw foreground sprites
_spriteSlots.drawForeground(&_vm->_screen);
// Draw text elements onto the view
_textDisplay.draw(&_vm->_screen);
//
_vm->_screen.setPointer(&_vm->_screen);
_interface.setBounds(Common::Rect(_vm->_screen._offset.x, _vm->_screen._offset.y,
_vm->_screen._offset.x + _vm->_screen.w, _vm->_screen._offset.y + _vm->_screen.h));
if (transitionFlag) {
// Fading in the screen
_vm->_screen.transition(transitionFlag, surfaceFlag);
_vm->_sound->startQueuedCommands();
} else {
// Copy dirty areas to the screen
_dirtyAreas.copy(&_vm->_screen, &_backgroundSurface, _vm->_screen._offset);
}
warning("TODO: sub_115A2");
_spriteSlots.cleanUp();
_textDisplay.cleanUp();
}
void Scene::leftClick() {
warning("TODO: Scene::leftClick");
}
void Scene::doPreactions() {
warning("TODO: Scene::doPreactions");
}
void Scene::doAction() {
warning("TODO: Scene::doAction");
}
void Scene::checkStartWalk() {
if (_action._startWalkFlag && _action._walkFlag) {
_vm->_game->_player.setDest(_destPos, _destFacing);
_action._startWalkFlag = false;
}
}
void Scene::doSceneStep() {
warning("TODO: Scene::doSceneStep");
}
void Scene::checkKeyboard() {
warning("TODO: Scene::checkKeyboard");
}
void Scene::free() {
warning("TODO: Scene::free");
}
} // End of namespace MADS