mirror of
https://github.com/libretro/scummvm.git
synced 2024-11-27 11:20:40 +00:00
573 lines
15 KiB
C++
573 lines
15 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "common/events.h"
|
|
|
|
#include "hypno/grammar.h"
|
|
#include "hypno/hypno.h"
|
|
|
|
namespace Hypno {
|
|
|
|
extern int parse_mis(const char *);
|
|
|
|
const char *sceneVariables[] = {
|
|
"GS_NONE",
|
|
"GS_SCTEXT",
|
|
"GS_AMBIENT",
|
|
"GS_MUSIC",
|
|
"GS_VOLUME",
|
|
"GS_MOUSESPEED",
|
|
"GS_MOUSEON",
|
|
"GS_LEVELCOMPLETE",
|
|
"GS_LEVELWON",
|
|
"GS_CHEATS",
|
|
"GS_SWITCH0",
|
|
"GS_SWITCH1",
|
|
"GS_SWITCH2",
|
|
"GS_SWITCH3",
|
|
"GS_SWITCH4",
|
|
"GS_SWITCH5",
|
|
"GS_SWITCH6",
|
|
"GS_SWITCH7",
|
|
"GS_SWITCH8",
|
|
"GS_SWITCH9",
|
|
"GS_SWITCH10",
|
|
"GS_SWITCH11",
|
|
"GS_SWITCH12",
|
|
"GS_COMBATJSON",
|
|
"GS_COMBATLEVEL",
|
|
"GS_PUZZLELEVEL",
|
|
nullptr
|
|
};
|
|
|
|
void HypnoEngine::loadSceneLevel(const Common::String ¤t, const Common::String &next, const Common::String &prefix) {
|
|
debugC(1, kHypnoDebugParser, "Parsing %s", current.c_str());
|
|
Common::Path name = convertPath(current);
|
|
|
|
Common::File test;
|
|
if (!test.open(name))
|
|
error("Failed to open %s", name.toString().c_str());
|
|
|
|
const uint32 fileSize = test.size();
|
|
char *buf = (char *)malloc(fileSize + 1);
|
|
test.read(buf, fileSize);
|
|
test.close();
|
|
buf[fileSize] = '\0';
|
|
debugC(1, kHypnoDebugParser, "%s", buf);
|
|
parse_mis(buf);
|
|
Scene *level = new Scene();
|
|
level->prefix = prefix;
|
|
level->levelIfWin = next;
|
|
level->hots = *g_parsedHots;
|
|
_levels[name.toString('/')] = level;
|
|
free(buf);
|
|
}
|
|
|
|
void HypnoEngine::loadSceneLevel(const char *buf, const Common::String &name, const Common::String &next, const Common::String &prefix) {
|
|
debugC(1, kHypnoDebugParser, "Parsing %s", name.c_str());
|
|
debugC(1, kHypnoDebugParser, "%s", buf);
|
|
parse_mis(buf);
|
|
Scene *level = new Scene();
|
|
level->prefix = prefix;
|
|
level->levelIfWin = next;
|
|
level->hots = *g_parsedHots;
|
|
_levels[name] = level;
|
|
}
|
|
|
|
void HypnoEngine::resetSceneState() {
|
|
uint32 i = 0;
|
|
while (sceneVariables[i]) {
|
|
// Preserve difficulty level variables
|
|
if (sceneVariables[i] != Common::String("GS_COMBATLEVEL") && sceneVariables[i] != Common::String("GS_PUZZLELEVEL"))
|
|
_sceneState[sceneVariables[i]] = 0;
|
|
i++;
|
|
}
|
|
_intros.clear();
|
|
}
|
|
|
|
bool HypnoEngine::checkSceneCompleted() {
|
|
return _sceneState["GS_LEVELCOMPLETE"] || _sceneState["GS_LEVELWON"];
|
|
}
|
|
|
|
bool HypnoEngine::checkLevelWon() {
|
|
return _sceneState["GS_LEVELWON"];
|
|
}
|
|
|
|
// Hotspots
|
|
|
|
void HypnoEngine::clickedHotspot(Common::Point mousePos) {
|
|
Hotspots *hots = stack.back();
|
|
Hotspot selected(MakeHotspot);
|
|
bool found = false;
|
|
int rs = 100000000;
|
|
int cs = 0;
|
|
for (Hotspots::const_iterator it = hots->begin(); it != hots->end(); ++it) {
|
|
const Hotspot h = *it;
|
|
cs = h.rect.width() * h.rect.height();
|
|
if (h.rect.contains(mousePos)) {
|
|
if (cs < rs) {
|
|
selected = h;
|
|
found = true;
|
|
rs = cs;
|
|
}
|
|
}
|
|
}
|
|
if (selected.type == MakeMenu) {
|
|
if (isDemo()) {
|
|
_nextLevel = "sixdemo/mis/demo.mis";
|
|
resetSceneState();
|
|
} else // TODO: remove when proper escape to main menu is implemented
|
|
openMainMenuDialog();
|
|
return;
|
|
}
|
|
|
|
if (!found)
|
|
return;
|
|
|
|
if (selected.smenu) {
|
|
if (selected.smenu->empty())
|
|
error("Invalid menu selected");
|
|
_nextHotsToAdd = selected.smenu;
|
|
}
|
|
|
|
_videosPlaying.clear();
|
|
_nextParallelVideoToPlay.clear();
|
|
_nextSequentialVideoToPlay.clear();
|
|
|
|
bool cont = true;
|
|
for (Actions::const_iterator itt = selected.actions.begin(); itt != selected.actions.end() && cont; ++itt) {
|
|
Action *action = *itt;
|
|
switch (action->type) {
|
|
case ChangeLevelAction:
|
|
runChangeLevel((ChangeLevel *)action);
|
|
break;
|
|
|
|
case EscapeAction:
|
|
runEscape();
|
|
break;
|
|
|
|
case CutsceneAction:
|
|
runCutscene((Cutscene *)action);
|
|
break;
|
|
|
|
case PlayAction:
|
|
runPlay((Play *)action);
|
|
break;
|
|
|
|
case SoundAction:
|
|
runSound((Sound *)action);
|
|
break;
|
|
|
|
case WalNAction:
|
|
runWalN((WalN *)action);
|
|
break;
|
|
|
|
case GlobalAction:
|
|
cont = runGlobal((Global *)action);
|
|
break;
|
|
|
|
case TalkAction:
|
|
runTalk((Talk *)action);
|
|
break;
|
|
|
|
case SaveAction:
|
|
runSave((Save *)action);
|
|
break;
|
|
|
|
case LoadAction:
|
|
runLoad((Load *)action);
|
|
break;
|
|
|
|
case LoadCheckpointAction:
|
|
runLoadCheckpoint((LoadCheckpoint *)action);
|
|
break;
|
|
|
|
case QuitAction:
|
|
runQuit((Quit *)action);
|
|
break;
|
|
|
|
case AmbientAction:
|
|
runAmbient((Ambient *)action);
|
|
break;
|
|
|
|
case PaletteAction:
|
|
runPalette((Palette *)action);
|
|
break;
|
|
|
|
case SwapPointerAction:
|
|
runSwapPointer((SwapPointer *)action);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool HypnoEngine::hoverHotspot(Common::Point mousePos) {
|
|
Hotspots *hots = stack.back();
|
|
Hotspot selected(MakeHotspot);
|
|
bool found = false;
|
|
int rs = 100000000;
|
|
for (Hotspots::const_iterator it = hots->begin(); it != hots->end(); ++it) {
|
|
const Hotspot h = *it;
|
|
if (h.type != MakeHotspot)
|
|
continue;
|
|
|
|
int cs = h.rect.width() * h.rect.height();
|
|
if (h.rect.contains(mousePos)) {
|
|
if (cs < rs) {
|
|
selected = h;
|
|
found = true;
|
|
rs = cs;
|
|
}
|
|
}
|
|
}
|
|
if (found) {
|
|
for (Actions::const_iterator itt = selected.actions.begin(); itt != selected.actions.end(); ++itt) {
|
|
Action *action = *itt;
|
|
switch (action->type) {
|
|
case MiceAction:
|
|
runMice((Mice *)action);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Common::String HypnoEngine::findNextLevel(const Transition *trans) { error("Function \"%s\" not implemented", __FUNCTION__); }
|
|
|
|
void HypnoEngine::runTransition(Transition *trans) {
|
|
Common::String nextLevel = findNextLevel(trans);
|
|
if (!trans->frameImage.empty()) {
|
|
// This is only used in Wetlands, and therefore, resolution should be 320x200
|
|
changeScreenMode("320x200");
|
|
debugC(1, kHypnoDebugScene, "Rendering %s frame in transaction", trans->frameImage.c_str());
|
|
loadImage(trans->frameImage, 0, 0, false, true, trans->frameNumber);
|
|
drawScreen();
|
|
Common::String *ptr = new Common::String(nextLevel);
|
|
if (!startAlarm(2 * 1000000, ptr)) // 2 seconds
|
|
error("Failed to install alarm");
|
|
} else
|
|
_nextLevel = nextLevel;
|
|
}
|
|
|
|
void HypnoEngine::runScene(Scene *scene) {
|
|
changeScreenMode(scene->resolution);
|
|
_refreshConversation = false;
|
|
Common::Event event;
|
|
Common::Point mousePos;
|
|
Common::List<uint32> videosToRemove;
|
|
bool enableLoopingVideos = true;
|
|
int32 lastCountdown = 0;
|
|
// These variables are always resetted
|
|
_sceneState["GS_LEVELCOMPLETE"] = 0;
|
|
_sceneState["GS_LEVELWON"] = 0;
|
|
|
|
stack.clear();
|
|
_nextHotsToAdd = &scene->hots;
|
|
defaultCursor();
|
|
|
|
while (!shouldQuit() && _nextLevel.empty()) {
|
|
|
|
if (_timerStarted && _videosPlaying.empty() && !_nextHotsToRemove) {
|
|
|
|
if (lastCountdown == _countdown) {
|
|
} else if (_countdown > 0) {
|
|
uint32 c = 251; // red
|
|
if (stack.size() > 0)
|
|
runMenu(stack.back());
|
|
uint32 minutes = _countdown / 60;
|
|
uint32 seconds = _countdown % 60;
|
|
drawString("console", Common::String::format("TIME: %d:%d", minutes, seconds), 80, 10, 60, c);
|
|
drawScreen();
|
|
} else {
|
|
assert(!scene->levelIfLose.empty());
|
|
_nextLevel = scene->levelIfLose;
|
|
debugC(1, kHypnoDebugScene, "Finishing level with timeout and jumping to %s", _nextLevel.c_str());
|
|
resetSceneState();
|
|
removeTimers();
|
|
_defaultCursorIdx = 0;
|
|
continue;
|
|
}
|
|
lastCountdown = _countdown;
|
|
}
|
|
|
|
while (g_system->getEventManager()->pollEvent(event)) {
|
|
mousePos = g_system->getEventManager()->getMousePos();
|
|
// Events
|
|
switch (event.type) {
|
|
case Common::EVENT_KEYDOWN:
|
|
if (event.kbd.keycode == Common::KEYCODE_ESCAPE) {
|
|
for (Videos::iterator it = _videosPlaying.begin(); it != _videosPlaying.end(); ++it) {
|
|
if (it->decoder) {
|
|
skipVideo(*it);
|
|
if (it->scaled) {
|
|
runMenu(stack.back());
|
|
drawScreen();
|
|
}
|
|
}
|
|
}
|
|
_videosPlaying.clear();
|
|
|
|
if (!_conversation.empty())
|
|
_refreshConversation = true;
|
|
}
|
|
|
|
break;
|
|
|
|
case Common::EVENT_QUIT:
|
|
case Common::EVENT_RETURN_TO_LAUNCHER:
|
|
break;
|
|
|
|
case Common::EVENT_RBUTTONDOWN:
|
|
if (stack.empty())
|
|
break;
|
|
if (!_conversation.empty()) {
|
|
rightClickedConversation(mousePos);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case Common::EVENT_LBUTTONDOWN:
|
|
if (stack.empty())
|
|
break;
|
|
if (!_conversation.empty()) {
|
|
leftClickedConversation(mousePos);
|
|
break;
|
|
}
|
|
if (!_nextHotsToAdd && !_nextHotsToRemove /*&& _videosPlaying.empty()*/) {
|
|
clickedHotspot(mousePos);
|
|
drawScreen();
|
|
}
|
|
break;
|
|
|
|
case Common::EVENT_MOUSEMOVE:
|
|
// Reset cursor to default
|
|
// changeCursor("default");
|
|
// The following functions will return true
|
|
// if the cursor is changed
|
|
|
|
if (!_conversation.empty() && !hoverConversation(mousePos))
|
|
defaultCursor();
|
|
|
|
if (stack.empty() || !_conversation.empty() || !_videosPlaying.empty())
|
|
break;
|
|
|
|
if (!hoverHotspot(mousePos))
|
|
defaultCursor();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (_refreshConversation && !_conversation.empty() &&
|
|
_nextSequentialVideoToPlay.empty() &&
|
|
_nextParallelVideoToPlay.empty() &&
|
|
_videosPlaying.empty()) {
|
|
showConversation();
|
|
runMenu(stack.back(), true);
|
|
drawScreen();
|
|
_refreshConversation = false;
|
|
}
|
|
|
|
// Movies
|
|
if (!_nextParallelVideoToPlay.empty()) {
|
|
for (Videos::iterator it = _nextParallelVideoToPlay.begin(); it != _nextParallelVideoToPlay.end(); ++it) {
|
|
playVideo(*it);
|
|
if (it->loop)
|
|
_videosLooping.push_back(*it);
|
|
else
|
|
_videosPlaying.push_back(*it);
|
|
}
|
|
_nextParallelVideoToPlay.clear();
|
|
}
|
|
|
|
if (!_nextSequentialVideoToPlay.empty() && _videosPlaying.empty()) {
|
|
MVideo *it = _nextSequentialVideoToPlay.begin();
|
|
playVideo(*it);
|
|
if (it->loop)
|
|
_videosLooping.push_back(*it);
|
|
else
|
|
_videosPlaying.push_back(*it);
|
|
_nextSequentialVideoToPlay.remove_at(0);
|
|
}
|
|
|
|
for (Videos::iterator it = _videosLooping.begin(); it != _videosLooping.end(); ++it) {
|
|
if (it->decoder && _conversation.empty()) {
|
|
if (it->decoder->endOfVideo()) {
|
|
if (it->loop && enableLoopingVideos) {
|
|
it->decoder->rewind();
|
|
it->decoder->start();
|
|
}
|
|
} else if (it->decoder->needsUpdate()) {
|
|
updateScreen(*it);
|
|
}
|
|
}
|
|
}
|
|
|
|
uint32 i = 0;
|
|
videosToRemove.clear();
|
|
for (Videos::iterator it = _videosPlaying.begin(); it != _videosPlaying.end(); ++it) {
|
|
|
|
if (it->decoder) {
|
|
if (it->decoder->endOfVideo()) {
|
|
if (it->scaled ||
|
|
( it->decoder->getWidth() == _screenW
|
|
&& it->decoder->getHeight() == _screenH
|
|
&& it->decoder->getCurFrame() > 0)) {
|
|
runMenu(stack.back());
|
|
drawScreen();
|
|
}
|
|
it->decoder->close();
|
|
delete it->decoder;
|
|
it->decoder = nullptr;
|
|
videosToRemove.push_back(i);
|
|
} else if (it->decoder->needsUpdate()) {
|
|
updateScreen(*it);
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
if (!videosToRemove.empty()) {
|
|
|
|
for (Common::List<uint32>::iterator it = videosToRemove.begin(); it != videosToRemove.end(); ++it) {
|
|
debugC(1, kHypnoDebugScene, "removing %d from %d size", *it, _videosPlaying.size());
|
|
_videosPlaying.remove_at(*it);
|
|
}
|
|
debugC(1, kHypnoDebugScene, "Something to play: %d", _videosPlaying.size());
|
|
// Nothing else to play
|
|
if (_videosPlaying.empty() && _nextSequentialVideoToPlay.empty() && !checkSceneCompleted()) {
|
|
if (!_conversation.empty())
|
|
_refreshConversation = true;
|
|
}
|
|
}
|
|
|
|
if (checkSceneCompleted() || checkLevelWon()) {
|
|
if (!checkLevelWon() && stack.size() > 1) {
|
|
debugC(1, kHypnoDebugScene, "Executing escape instead of ending the scene");
|
|
runEscape();
|
|
_sceneState["GS_LEVELCOMPLETE"] = 0;
|
|
continue;
|
|
}
|
|
|
|
// Make sure all the videos are played before we finish
|
|
enableLoopingVideos = false;
|
|
if (_conversation.empty() &&
|
|
_videosPlaying.empty() &&
|
|
_nextSequentialVideoToPlay.empty() &&
|
|
_nextParallelVideoToPlay.empty()) {
|
|
|
|
if (_nextLevel.empty()) {
|
|
assert(!scene->levelIfWin.empty());
|
|
_nextLevel = scene->levelIfWin;
|
|
}
|
|
|
|
if (checkLevelWon()) {
|
|
debugC(1, kHypnoDebugScene, "Resetting level variables");
|
|
resetSceneState();
|
|
_checkpoint = _nextLevel;
|
|
_defaultCursorIdx = 0;
|
|
}
|
|
_sceneState["GS_LEVELCOMPLETE"] = 0;
|
|
|
|
debugC(1, kHypnoDebugScene, "Finishing level and jumping to %s", _nextLevel.c_str());
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (_music.empty() && !scene->music.empty() && _videosPlaying.empty() && _nextSequentialVideoToPlay.empty()) {
|
|
_music = scene->music;
|
|
playSound(_music, 0, scene->musicRate);
|
|
}
|
|
|
|
if (!_videosPlaying.empty() || !_videosLooping.empty() || !_nextSequentialVideoToPlay.empty()) {
|
|
drawScreen();
|
|
continue;
|
|
}
|
|
|
|
if (_nextHotsToRemove) {
|
|
debugC(1, kHypnoDebugScene, "Removing a hotspot list!");
|
|
stack.pop_back();
|
|
runMenu(stack.back());
|
|
_nextHotsToRemove = nullptr;
|
|
drawScreen();
|
|
} else if (_nextHotsToAdd) {
|
|
debugC(1, kHypnoDebugScene, "Adding a hotspot list!");
|
|
stack.push_back(_nextHotsToAdd);
|
|
runMenu(stack.back());
|
|
_nextHotsToAdd = nullptr;
|
|
drawScreen();
|
|
}
|
|
|
|
g_system->updateScreen();
|
|
g_system->delayMillis(30);
|
|
}
|
|
|
|
// Deallocate videos
|
|
for (Videos::iterator it = _videosLooping.begin(); it != _videosLooping.end(); ++it) {
|
|
if (it->decoder)
|
|
skipVideo(*it);
|
|
}
|
|
|
|
for (Videos::iterator it = _videosPlaying.begin(); it != _videosPlaying.end(); ++it) {
|
|
if (it->decoder)
|
|
skipVideo(*it);
|
|
}
|
|
|
|
for (Videos::iterator it = _nextParallelVideoToPlay.begin(); it != _nextParallelVideoToPlay.end(); ++it) {
|
|
if (it->decoder)
|
|
skipVideo(*it);
|
|
}
|
|
|
|
for (Videos::iterator it = _nextSequentialVideoToPlay.begin(); it != _nextSequentialVideoToPlay.end(); ++it) {
|
|
if (it->decoder)
|
|
skipVideo(*it);
|
|
}
|
|
|
|
for (Videos::iterator it = _escapeSequentialVideoToPlay.begin(); it != _escapeSequentialVideoToPlay.end(); ++it) {
|
|
if (it->decoder)
|
|
skipVideo(*it);
|
|
}
|
|
|
|
_nextParallelVideoToPlay.clear();
|
|
_nextSequentialVideoToPlay.clear();
|
|
_escapeSequentialVideoToPlay.clear();
|
|
_conversation.clear();
|
|
|
|
if (!_keepTimerDuringScenes)
|
|
removeTimers();
|
|
}
|
|
|
|
void HypnoEngine::showConversation() { error("Function \"%s\" not implemented", __FUNCTION__); }
|
|
void HypnoEngine::endConversation() { error("Function \"%s\" not implemented", __FUNCTION__); }
|
|
void HypnoEngine::rightClickedConversation(const Common::Point &mousePos) { error("Function \"%s\" not implemented", __FUNCTION__); }
|
|
void HypnoEngine::leftClickedConversation(const Common::Point &mousePos) { error("Function \"%s\" not implemented", __FUNCTION__); }
|
|
bool HypnoEngine::hoverConversation(const Common::Point &mousePos) { error("Function \"%s\" not implemented", __FUNCTION__); }
|
|
|
|
} // End of namespace Hypno
|