Initial commit
This commit is contained in:
580
engines/hypno/scene.cpp
Normal file
580
engines/hypno/scene.cpp
Normal file
@@ -0,0 +1,580 @@
|
||||
/* 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"
|
||||
|
||||
#include "backends/keymapper/keymapper.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::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
|
||||
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;
|
||||
}
|
||||
|
||||
disableGameKeymaps();
|
||||
keymapper->getKeymap("cutscene")->setEnabled(true);
|
||||
|
||||
while (g_system->getEventManager()->pollEvent(event)) {
|
||||
mousePos = g_system->getEventManager()->getMousePos();
|
||||
// Events
|
||||
switch (event.type) {
|
||||
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
|
||||
if (event.customType == kActionSkipCutscene) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
keymapper->getKeymap("cutscene")->setEnabled(false);
|
||||
enableGameKeymaps();
|
||||
|
||||
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 (!_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();
|
||||
}
|
||||
|
||||
if (!isMusicActive() && !scene->music.empty()) {
|
||||
playMusic(scene->music, scene->musicRate);
|
||||
}
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user