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

1
engines/hypno/POTFILES Normal file
View File

@@ -0,0 +1 @@
engines/hypno/metaengine.cpp

255
engines/hypno/actions.cpp Normal file
View File

@@ -0,0 +1,255 @@
/* 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 {
// Actions
void HypnoEngine::runMenu(Hotspots *hs, bool only_menu) {
Hotspot *h = hs->begin();
assert(h->type == MakeMenu);
debugC(1, kHypnoDebugScene, "hotspot actions size: %d", h->actions.size());
for (Actions::const_iterator itt = h->actions.begin(); !only_menu && itt != h->actions.end(); ++itt) {
Action *action = *itt;
switch (action->type) {
case QuitAction:
runQuit((Quit *)action);
break;
case TimerAction:
runTimer((Timer *)action);
break;
case BackgroundAction:
runBackground((Background *)action);
break;
case OverlayAction:
runOverlay((Overlay *)action);
break;
case AmbientAction:
runAmbient((Ambient *)action);
break;
case IntroAction:
runIntro((Intro *)action);
break;
case PaletteAction:
runPalette((Palette *)action);
break;
default:
break;
}
// else if (typeid(*action) == typeid(Mice))
// runMice(h, (Mice*) action);
}
drawBackToMenu(h);
}
void HypnoEngine::drawBackToMenu(Hotspot *h) {}
void HypnoEngine::runBackground(Background *a) {
if (a->condition.size() > 0) {
bool condition = _sceneState[a->condition];
if (a->flag1 == "/NSTATE" || a->flag2 == "/NSTATE")
condition = !condition;
if (!condition)
return;
}
loadImage(a->path, a->origin.x, a->origin.y, false);
}
void HypnoEngine::runTimer(Timer *a) {
if (_timerStarted)
return; // Do not start another timer
uint32 delay = a->delay / 1000;
if (a->flag == "vus0")
_keepTimerDuringScenes = true;
debugC(1, kHypnoDebugScene, "Starting timer with %d secons", delay);
if (delay == 0 || !startCountdown(delay))
error("Failed to start countdown");
}
void HypnoEngine::runOverlay(Overlay *a) {
loadImage(a->path, a->origin.x, a->origin.y, false);
}
void HypnoEngine::runMice(Mice *a) {
changeCursor(a->path, a->index);
}
void HypnoEngine::runSwapPointer(SwapPointer *a) {
_defaultCursorIdx = a->index;
defaultCursor();
}
void HypnoEngine::runPalette(Palette *a) {
loadPalette(a->path);
}
void HypnoEngine::runEscape() {
_nextHotsToRemove = stack.back();
_nextSequentialVideoToPlay = _escapeSequentialVideoToPlay;
_escapeSequentialVideoToPlay.clear();
}
void HypnoEngine::runIntro(Intro *a) {
// Should not repeat the same
if (_intros.contains(a->path))
return;
_intros[a->path] = true;
MVideo v(a->path, Common::Point(0, 0), false, true, false);
disableCursor();
runIntro(v);
defaultCursor();
}
void HypnoEngine::runCutscene(Cutscene *a) {
stopSound();
defaultCursor();
MVideo v(a->path, Common::Point(0, 0), false, true, false);
disableCursor();
runIntro(v);
defaultCursor();
runMenu(stack.back());
drawScreen();
}
bool HypnoEngine::runGlobal(Global *a) {
debugC(1, kHypnoDebugScene, "Running global with command '%s' and variable '%s'", a->command.c_str(), a->variable.c_str());
if (a->command == "TURNON")
_sceneState[a->variable] = 1;
else if (a->command == "TURNOFF")
_sceneState[a->variable] = 0;
else if (a->command == "TOGGLE")
_sceneState[a->variable] = !_sceneState[a->variable];
else if (a->command == "CHECK") {
if (!_sceneState[a->variable]) // Clear any video to play
_nextSequentialVideoToPlay.clear();
return _sceneState[a->variable];
} else if (a->command == "NCHECK") {
if (_sceneState[a->variable]) // Clear any video to play
_nextSequentialVideoToPlay.clear();
return !_sceneState[a->variable];
} else if (a->command == "CLEAR") {
resetSceneState();
return true;
} else
error("Invalid command %s", a->command.c_str());
return true;
}
void HypnoEngine::runPlay(Play *a) {
if (a->condition.size() > 0 && !_sceneState[a->condition])
return;
if (a->flag == "/BITMAP")
loadImage(a->path, a->origin.x, a->origin.y, false);
else {
_nextSequentialVideoToPlay.push_back(MVideo(a->path, a->origin, false, false, false));
}
}
void HypnoEngine::runSound(Sound *a) {
playSound(a->path, 1);
}
void HypnoEngine::runAmbient(Ambient *a) {
if (a->flag == "/BITMAP") {
Graphics::Surface *frame = decodeFrame(a->path, a->frameNumber);
Graphics::Surface *sframe;
if (a->fullscreen)
sframe = frame->scale(_screenW, _screenH);
else
sframe = frame;
drawImage(*sframe, a->origin.x, a->origin.y, true);
if (a->fullscreen) {
frame->free();
delete frame;
}
sframe->free();
delete sframe;
} else {
bool loop = a->flag == "/LOOP";
if (loop) { // Avoid re-adding the same looping video
if (_intros.contains(a->path))
return;
_intros[a->path] = true;
}
_nextSequentialVideoToPlay.push_back(MVideo(a->path, a->origin, false, a->fullscreen, loop));
}
}
void HypnoEngine::runWalN(WalN *a) {
if (a->condition.size() > 0 && !_sceneState[a->condition])
return;
if (a->wn == "WAL0")
_nextSequentialVideoToPlay.push_back(MVideo(a->path, a->origin, false, false, false));
else if (a->wn == "WAL1")
_escapeSequentialVideoToPlay.push_back(MVideo(a->path, a->origin, false, false, false));
else
error("Invalid WALN command: %s", a->wn.c_str());
}
void HypnoEngine::runSave(Save *a) {
// TODO: enable this when saving in the main menu is available
// saveGameDialog();
}
void HypnoEngine::runLoad(Load *a) {
loadGameDialog();
}
void HypnoEngine::runLoadCheckpoint(LoadCheckpoint *a) {
if (_checkpoint.empty())
error("Invalid checkpoint!");
loadGame(_checkpoint, _score, _sceneState["GS_PUZZLELEVEL"], _sceneState["GS_COMBATLEVEL"]);
}
void HypnoEngine::runQuit(Quit *a) {
quitGame();
}
void HypnoEngine::runChangeLevel(ChangeLevel *a) {
debugC(1, kHypnoDebugScene, "Next level is '%s'", a->level.c_str());
_nextLevel = a->level;
}
void HypnoEngine::runTalk(Talk *a) {
// Recreate the items to allow modifications
Talk *n = new Talk(a);
_conversation.push_back(n);
_refreshConversation = true;
}
} // End of namespace Hypno

836
engines/hypno/arcade.cpp Normal file
View File

@@ -0,0 +1,836 @@
/* 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/tokenizer.h"
#include "common/events.h"
#include "graphics/cursorman.h"
#include "graphics/framelimiter.h"
#include "hypno/grammar.h"
#include "hypno/hypno.h"
#include "backends/keymapper/keymapper.h"
namespace Hypno {
extern int parse_arc(const char *);
void HypnoEngine::splitArcadeFile(const Common::String &filename, Common::String &arc, Common::String &list) {
debugC(1, kHypnoDebugParser, "Splitting %s", filename.c_str());
Common::File file;
if (!file.open(filename.c_str()))
error("Failed to open %s", filename.c_str());
while (!file.eos()) {
byte x = file.readByte();
byte p = arc.lastChar();
arc += x;
if (x == 'X' && p == '\n') {
while (!file.eos()) {
x = file.readByte();
if (x == 'Y' && list.size() > 0 && list[list.size()-1] == '\n')
break;
list += x;
}
break; // No need to keep parsing
}
}
file.close();
}
void HypnoEngine::parseArcadeShooting(const Common::String &prefix, const Common::String &filename, const Common::String &data) {
debugC(1, kHypnoDebugParser, "Parsing %s/%s", prefix.c_str(), filename.c_str());
parse_arc(data.c_str());
ArcadeShooting *arcade = new ArcadeShooting();
*arcade = *g_parsedArc;
_levels[filename] = (Level*) arcade;
g_parsedArc->clear();
}
SegmentShootsSequence HypnoEngine::parseShootList(const Common::String &filename, const Common::String &data) {
debugC(1, kHypnoDebugParser, "Parsing %s", filename.c_str());
debugC(1, kHypnoDebugParser, "%s", data.c_str());
// Preparsing
Common::String pdata;
Common::StringTokenizer lines(data, "\n");
Common::String t;
while (!lines.empty()) {
t = lines.nextToken();
if (t[0] == ';')
continue;
if (t.size() == 0)
continue;
pdata += "\n" + t;
}
Common::String n;
ShootInfo si;
si.timestamp = 0;
si.name = "";
SegmentShootsSequence seq;
// Patch to fix an issue in the parsing of the c3 level in Spiderman
if (filename == "c3.mi_" || filename == "c3h.mi_")
Common::replace(pdata, "92.B", "92,B");
// Parsing
pdata.trim();
pdata = "\n" + pdata;
if (pdata[1] == 'L') { // List of elements
SegmentShoots ss;
ss.segmentRepetition = 0;
Common::StringTokenizer tok(pdata, " ,.\n\t");
while (!tok.empty()) {
t = tok.nextToken();
while (t == "L") {
if (ss.segmentRepetition > 0)
seq.push_back(ss);
t = tok.nextToken();
ss.segmentRepetition = atoi(t.c_str());
ss.shootSequence.clear();
t = tok.nextToken();
}
n = tok.nextToken();
if (t == "Z") {
seq.push_back(ss);
break;
}
si.name = n;
si.timestamp = atoi(t.c_str());
if (si.timestamp == 0 && si.name != "0") // 0,0 is a special case
error("Error at parsing '%s' with timestamp: %s", n.c_str(), t.c_str());
ss.shootSequence.push_back(si);
debugC(1, kHypnoDebugParser, "%d -> %s", si.timestamp, si.name.c_str());
}
} else if (pdata[1] == 'S' ) { // Single element
SegmentShoots ss;
Common::StringTokenizer tok(pdata, " ,\t\r");
while (!tok.empty()) {
t = tok.nextToken();
if (t[0] == '\n')
continue;
n = tok.nextToken();
if (t == "Z")
break;
Common::replace(n, "\nS", "");
Common::replace(n, "\nZ\n", "");
Common::replace(n, "\nZ", "");
uint32 timestamp = atoi(t.c_str());
if (timestamp < si.timestamp) {
debugC(1, kHypnoDebugParser, "WARNING: stopping the sequence earlier than expected");
break;
}
si.name = n;
si.timestamp = timestamp;
if (si.timestamp == 0)
error("Error at parsing '%s' with timestamp: %s", n.c_str(), t.c_str());
ss.shootSequence.push_back(si);
debugC(1, kHypnoDebugParser, "%d -> %s", si.timestamp, si.name.c_str());
}
seq.push_back(ss);
} else
error("Invalid shoot sequence to parse: %c", pdata[1]);
return seq;
}
void HypnoEngine::loadArcadeLevel(const Common::String &arclevel, const Common::String &nextWin, const Common::String &nextLose, const Common::String &prefix) {
debugC(1, kHypnoDebugParser, "Parsing %s", arclevel.c_str());
Common::String arc;
Common::String list;
splitArcadeFile(arclevel, arc, list);
debugC(1, kHypnoDebugParser, "%s", arc.c_str());
parseArcadeShooting("", arclevel, arc);
ArcadeShooting *arcade = (ArcadeShooting *) _levels[arclevel];
arcade->shootSequence = parseShootList(arclevel, list);
arcade->prefix = prefix;
arcade->levelIfWin = nextWin;
arcade->levelIfLose = nextLose;
}
void HypnoEngine::drawPlayer() { error("Function \"%s\" not implemented", __FUNCTION__); }
void HypnoEngine::drawHealth() { error("Function \"%s\" not implemented", __FUNCTION__); }
void HypnoEngine::drawAmmo() {}
void HypnoEngine::drawShoot(const Common::Point &target) { error("Function \"%s\" not implemented", __FUNCTION__); }
void HypnoEngine::hitPlayer() { error("Function \"%s\" not implemented", __FUNCTION__); }
void HypnoEngine::missedTarget(Shoot *s, ArcadeShooting *arc) {}
void HypnoEngine::missNoTarget(ArcadeShooting *arc) {}
void HypnoEngine::runBeforeArcade(ArcadeShooting *arc) {}
void HypnoEngine::runAfterArcade(ArcadeShooting *arc) {}
void HypnoEngine::pressedKey(const int keycode) {}
void HypnoEngine::initSegment(ArcadeShooting *arc) { error("Function \"%s\" not implemented", __FUNCTION__); }
void HypnoEngine::findNextSegment(ArcadeShooting *arc) { error("Function \"%s\" not implemented", __FUNCTION__); }
byte *HypnoEngine::getTargetColor(Common::String name, int levelId) { error("Function \"%s\" not implemented", __FUNCTION__); }
bool HypnoEngine::availableObjectives() {
return (_objKillsRequired[_objIdx] > 0);
}
bool HypnoEngine::checkArcadeObjectives() {
debugC(1, kHypnoDebugArcade, "Checking objective %d (%d/%d)", _objIdx, _objKillsCount[_objIdx], _objKillsRequired[_objIdx]);
if (_objKillsRequired[_objIdx] > 0)
return (_objKillsCount[_objIdx] >= _objKillsRequired[_objIdx] && \
_objMissesCount[_objIdx] <= _objMissesAllowed[_objIdx]);
return true;
}
bool HypnoEngine::checkTransition(ArcadeTransitions &transitions, ArcadeShooting *arc) {
error("Function \"%s\" not implemented", __FUNCTION__);
}
void HypnoEngine::runArcade(ArcadeShooting *arc) {
_arcadeMode = arc->mode;
Common::Point mousePos;
Common::List<uint32> shootsToRemove;
// segment/shoots
Segments segments = arc->segments;
initSegment(arc);
// Transitions
_transitions = arc->transitions;
_levelId = arc->id;
_shootSound = arc->shootSound;
_hitSound = arc->hitSound;
_additionalSound = arc->additionalSound;
debugC(1, kHypnoDebugArcade, "Starting segment of type %x of size %d", segments[_segmentIdx].type, segments[_segmentIdx].size);
_shoots.clear();
_skipLevel = false;
_loseLevel = false;
_skipDefeatVideo = false;
_skipNextVideo = false;
_mask = nullptr;
_masks = nullptr;
if (arc->mouseBox == Common::Rect(0, 0, 0, 0))
error("Invalid or missing mouse box");
Common::Point offset;
Common::Point anchor = arc->anchor;
anchor.x = 0; // This is almost always zero, except when the screen starts at the middle
// We don't really need it
anchor.y = MAX(0, anchor.y - arc->mouseBox.bottom);
// Correct mouseBox
arc->mouseBox.moveTo(anchor.x, anchor.y);
_background = new MVideo(arc->backgroundVideo, anchor, false, false, false);
drawCursorArcade(mousePos);
playVideo(*_background);
if (!arc->maskVideo.empty()) {
_masks = new MVideo(arc->maskVideo, offset, false, false, false);
playVideo(*_masks);
_mask = _masks->decoder->decodeNextFrame();
}
float rate = _background->decoder->getFrameRate().toDouble();
if (rate < 10) {
debugC(1, kHypnoDebugArcade, "Used frame rate looks odd: %f, increasing x 10", rate);
_background->decoder->setRate(10.0);
}
_currentPalette = arc->backgroundPalette;
loadPalette(_currentPalette);
int firstFrame = segments[_segmentIdx].start;
if (firstFrame > 1) {
_background->decoder->forceSeekToFrame(firstFrame);
_masks->decoder->forceSeekToFrame(firstFrame);
segments[_segmentIdx].start = 1;
}
bool shootingPrimary = false;
bool shootingSecondary = false;
bool needsUpdate = true;
bool transition = false;
_objIdx = 0;
_objKillsCount[0] = 0;
_objKillsCount[1] = 0;
_objMissesCount[0] = 0;
_objMissesCount[1] = 0;
_objKillsRequired[0] = arc->objKillsRequired[0];
_objKillsRequired[1] = arc->objKillsRequired[1];
_objMissesAllowed[0] = arc->objMissesAllowed[0];
_objMissesAllowed[1] = arc->objMissesAllowed[1];
bool vsync = g_system->getFeatureState(OSystem::kFeatureVSync);
// Disable vsync for arcade sequences, since these require a fixed frame rate
g_system->beginGFXTransaction();
g_system->setFeatureState(OSystem::kFeatureVSync, false);
g_system->endGFXTransaction();
debugC(1, kHypnoDebugArcade, "Using frame delay: %d", arc->frameDelay);
Graphics::FrameLimiter limiter(g_system, 1000.0 / arc->frameDelay);
limiter.startFrame();
Common::Event event;
while (!shouldQuit()) {
if (_timerStarted) {
if (_countdown <= 0) {
_loseLevel = true;
debugC(1, kHypnoDebugArcade, "Finishing level (timeout)");
_timerStarted = false;
removeTimers();
}
}
needsUpdate = _background->decoder->needsUpdate();
while (g_system->getEventManager()->pollEvent(event)) {
mousePos = getPlayerPosition(false);
// Events
switch (event.type) {
case Common::EVENT_QUIT:
case Common::EVENT_RETURN_TO_LAUNCHER:
break;
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
pressedKey(event.customType);
if (event.customType == kActionPrimaryShoot)
if (clickedPrimaryShoot(mousePos))
shootingPrimary = true;
break;
case Common::EVENT_KEYDOWN:
pressedKey(event.kbd.keycode);
break;
case Common::EVENT_LBUTTONDOWN:
if (clickedPrimaryShoot(mousePos))
shootingPrimary = true;
break;
case Common::EVENT_RBUTTONDOWN:
if (clickedSecondaryShoot(mousePos))
shootingSecondary = true;
break;
case Common::EVENT_RBUTTONUP:
shootingSecondary = false;
break;
case Common::EVENT_MOUSEMOVE:
drawCursorArcade(mousePos);
if (mousePos.x >= arc->mouseBox.right-1) {
g_system->warpMouse(arc->mouseBox.right-1, mousePos.y);
} else if (mousePos.y < arc->mouseBox.top) { // Usually top is zero
g_system->warpMouse(mousePos.x, arc->mouseBox.top + 1);
} else if (mousePos.y >= arc->mouseBox.bottom-1) {
g_system->warpMouse(mousePos.x, arc->mouseBox.bottom-1);
} else if (mousePos.x <= 40 && offset.x < 0) {
for (Shoots::iterator it = _shoots.begin(); it != _shoots.end(); ++it) {
if (it->video && it->video->decoder)
it->video->position.x = it->video->position.x + 1;
}
offset.x = offset.x + 1;
needsUpdate = true;
} else if (mousePos.x >= 280 && offset.x > 320 - _background->decoder->getWidth()) {
for (Shoots::iterator it = _shoots.begin(); it != _shoots.end(); ++it) {
if (it->video && it->video->decoder)
it->video->position.x = it->video->position.x - 1;
}
offset.x = offset.x - 1;
needsUpdate = true;
}
_background->position.x = offset.x;
break;
default:
break;
}
}
if (needsUpdate) {
getPlayerPosition(true);
if (_background->decoder->getCurFrame() > firstFrame)
drawScreen();
updateScreen(*_background);
if (!arc->maskVideo.empty() && _masks->decoder->needsUpdate())
_mask = _masks->decoder->decodeNextFrame();
if (_additionalVideo && _additionalVideo->decoder->needsUpdate())
_additionalVideo->decoder->decodeNextFrame(); // only audio?
}
if (_health <= 0) {
skipVideo(*_background);
if (_skipDefeatVideo)
; // No video
else if (!arc->defeatNoEnergySecondVideo.empty() && transition) {
disableCursor();
MVideo video(arc->defeatNoEnergySecondVideo, Common::Point(0, 0), false, true, false);
runIntro(video);
} else if (!arc->defeatNoEnergyFirstVideo.empty()) {
disableCursor();
MVideo video(arc->defeatNoEnergyFirstVideo, Common::Point(0, 0), false, true, false);
runIntro(video);
}
assert(!arc->levelIfLose.empty());
_nextLevel = arc->levelIfLose;
debugC(1, kHypnoDebugArcade, "Losing level and jumping to %s", _nextLevel.c_str());
_lives = _lives - 1;
break;
}
if (!_transitions.empty()) {
transition = checkTransition(_transitions, arc);
}
if (_background->decoder && _background->decoder->getCurFrame() >= int(segments[_segmentIdx].start + segments[_segmentIdx].size - 2)) {
debugC(1, kHypnoDebugArcade, "Finished segment %d of type %x", _segmentIdx, segments[_segmentIdx].type);
// Clear shoots
/*for (Shoots::iterator it = _shoots.begin(); it != _shoots.end(); ++it) {
if (it->video && it->video->decoder)
skipVideo(*it->video);
delete it->video;
}
_shoots.clear();*/
findNextSegment(arc);
if (_segmentIdx >= segments.size())
error("Invalid segment %d", _segmentIdx);
debugC(1, kHypnoDebugArcade, "Starting segment %d of type %x at %d", _segmentIdx, segments[_segmentIdx].type, segments[_segmentIdx].start);
if (!segments[_segmentIdx].end) { // If it is not the end segment
_background->decoder->forceSeekToFrame(segments[_segmentIdx].start);
continue;
}
}
if (segments[_segmentIdx].end || _skipLevel || _loseLevel) {
skipVideo(*_background);
// Objectives
if (!checkArcadeObjectives() && !_skipLevel) {
if (!arc->defeatMissBossVideo.empty()) {
MVideo video(arc->defeatMissBossVideo, Common::Point(0, 0), false, true, false);
disableCursor();
runIntro(video);
}
assert(!arc->levelIfLose.empty());
_nextLevel = arc->levelIfLose;
_lives = _lives - 1;
_arcadeMode = "";
debugC(1, kHypnoDebugArcade, "Losing level (objectives) and jumping to %s", _nextLevel.c_str());
break;
}
if (!arc->nextLevelVideo.empty() && !_skipNextVideo) {
MVideo video(arc->nextLevelVideo, Common::Point(0, 0), false, true, false);
disableCursor();
runIntro(video);
}
assert(!arc->levelIfWin.empty());
_nextLevel = arc->levelIfWin;
_checkpoint = _nextLevel;
_arcadeMode = "";
_skipLevel = false;
debugC(1, kHypnoDebugArcade, "Wining level and jumping to %s", _nextLevel.c_str());
break;
}
if (_shootSequence.size() > 0) {
ShootInfo si = _shootSequence.front();
int idx = (int)segments[_segmentIdx].size * _segmentRepetition \
+ _background->decoder->getCurFrame() \
- (int)segments[_segmentIdx].start + 3;
//debug("%d %d", si.timestamp, idx);
if ((int)si.timestamp <= idx) {
_shootSequence.pop_front();
incEnemyTargets();
for (Shoots::iterator it = arc->shoots.begin(); it != arc->shoots.end(); ++it) {
if (it->name == si.name) {
Shoot s = *it;
s.startFrame = si.timestamp;
if (_masks) {
s.startFrame = 0;
_shoots.push_back(s);
} else if (it->animation == "NONE") {
byte *c = getTargetColor(it->name, _levelId);
assert(s.paletteSize == 1 || s.paletteSize == 0);
loadPalette(c, s.paletteOffset, s.paletteSize);
_shoots.push_back(s);
} else {
s.video = new MVideo(it->animation, offset + it->position, true, false, false);
playVideo(*s.video);
s.video->decoder->decodeNextFrame(); // Make sure the palette is loaded
if (s.attackFrames.size() == 0) {
uint32 lastFrame = s.bodyFrames.back().lastFrame();
s.attackFrames.push_back(lastFrame - 3);
}
s.lastFrame = s.bodyFrames[s.bodyFrames.size() - 1].lastFrame();
loadPalette(s.video->decoder->getPalette() + 3*s.paletteOffset, s.paletteOffset, s.paletteSize);
_shoots.push_back(s);
}
if (!s.noEnemySound) {
if (!s.enemySound.empty())
playSound(_soundPath + s.enemySound, 1, s.enemySoundRate);
else if (!arc->enemySound.empty())
playSound(_soundPath + arc->enemySound, 1, arc->enemySoundRate);
}
}
}
}
}
uint32 i = 0;
shootsToRemove.clear();
for (Shoots::iterator it = _shoots.begin(); it != _shoots.end(); ++it) {
if (it->video && it->video->decoder) {
int frame = it->video->decoder->getCurFrame();
if (it->attackFrames.size() > 0) {
uint32 attackFrame = it->attackFrames.front();
if (frame > 0 && frame >= (int)(attackFrame - 2) && !it->destroyed) {
if (!_infiniteHealthCheat)
_health = _health - it->attackWeight;
hitPlayer();
it->attackFrames.pop_front();
}
}
uint32 bodyLastFrame = it->bodyFrames[it->bodyFrames.size() - 1].lastFrame();
if (frame > 0 && frame >= (int)(bodyLastFrame - 3) && !it->destroyed) {
incTargetsMissed();
missedTarget(it, arc);
// No need to pop attackFrames or explosionFrames
skipVideo(*it->video);
shootsToRemove.push_back(i);
} else if (frame > 0 && frame >= (int)(it->lastFrame)) {
skipVideo(*it->video);
shootsToRemove.push_back(i);
} else if (it->video->decoder->needsUpdate() || needsUpdate) {
updateScreen(*it->video);
}
} else if (!it->video && it->bodyFrames.size() > 0) {
uint32 frame = _background->decoder->getCurFrame();
uint32 bodyLastFrame = it->bodyFrames[it->bodyFrames.size() - 1].lastFrame();
if (frame > it->startFrame && frame - it->startFrame >= bodyLastFrame)
if (!it->destroyed) {
incTargetsMissed();
missedTarget(it, arc);
shootsToRemove.push_back(i);
}
}
i++;
}
if (shootsToRemove.size() > 0) {
debugC(1, kHypnoDebugArcade, "Shoots to remove: %d", shootsToRemove.size());
Common::sort(shootsToRemove.begin(), shootsToRemove.end());
for (Common::List<uint32>::iterator it = shootsToRemove.reverse_begin(); it != shootsToRemove.end(); --it) {
debugC(1, kHypnoDebugArcade, "Removing %d from %d size", *it, _shoots.size());
delete _shoots[*it].video;
_shoots.remove_at(*it);
}
}
if (!isMusicActive() && !arc->music.empty()) {
playMusic(_soundPath + arc->music, arc->musicRate, arc->musicStereo);
}
if (needsUpdate) {
if (shootingPrimary) {
shootingPrimary = shoot(mousePos, arc, false);
} else if (shootingSecondary) {
shootingSecondary = shoot(mousePos, arc, true);
}
drawPlayer();
drawHealth();
drawAmmo();
}
limiter.delayBeforeSwap();
drawScreen();
limiter.startFrame();
}
g_system->beginGFXTransaction();
// Restore vsync state
g_system->setFeatureState(OSystem::kFeatureVSync, vsync);
g_system->endGFXTransaction();
// Deallocate shoots
for (Shoots::iterator it = _shoots.begin(); it != _shoots.end(); ++it) {
if (it->video && it->video->decoder)
skipVideo(*it->video);
delete it->video;
}
if (_background->decoder) {
skipVideo(*_background);
}
delete _background;
_background = nullptr;
if (_masks) {
skipVideo(*_masks);
delete _masks;
_mask = nullptr;
_masks = nullptr;
}
if (_additionalVideo) {
skipVideo(*_additionalVideo);
delete _additionalVideo;
_additionalVideo = nullptr;
}
_timerStarted = false;
removeTimers();
stopSound();
stopMusic();
}
Common::Point HypnoEngine::computeTargetPosition(const Common::Point &mousePos) {
return mousePos;
}
Common::Point HypnoEngine::getPlayerPosition(bool needsUpdate) {
return g_system->getEventManager()->getMousePos();
}
int HypnoEngine::detectTarget(const Common::Point &mousePos) {
int i = -1;
Common::Point target = computeTargetPosition(mousePos);
if (target.x >= _compositeSurface->w || target.y >= _compositeSurface->h)
return -1;
if (target.x < 0 || target.y < 0)
return -1;
for (Shoots::iterator it = _shoots.begin(); it != _shoots.end(); ++it) {
i++;
if (it->destroyed)
continue;
if (it->animation != "NONE" && !it->video->decoder)
continue;
uint32 c = _compositeSurface->getPixel(target.x, target.y);
if (c >= it->paletteOffset && c < it->paletteOffset + it->paletteSize) {
return i;
}
}
return -1;
}
void HypnoEngine::drawCursorArcade(const Common::Point &mousePos) {
int i = detectTarget(mousePos);
if (i >= 0)
changeCursor("target");
else
changeCursor("arcade");
}
bool HypnoEngine::clickedPrimaryShoot(const Common::Point &mousePos) { return true; }
bool HypnoEngine::shoot(const Common::Point &mousePos, ArcadeShooting *arc, bool secondary) {
incShotsFired();
int i = detectTarget(mousePos);
if (i < 0) {
missNoTarget(arc);
} else {
if (!_shoots[i].hitSound.empty())
playSound(_soundPath + _shoots[i].hitSound, 1);
incEnemyHits();
if (_shoots[i].timesToShoot > 1) {
_shoots[i].timesToShoot = _shoots[i].timesToShoot - 1;
// Redraw cursor
drawCursorArcade(mousePos);
goto end;
}
if (!_shoots[i].deathSound.empty())
playSound(_soundPath + _shoots[i].deathSound, 1);
incTargetsDestroyed();
incScore(_shoots[i].pointsToShoot);
incBonus(_shoots[i].pointsToShoot);
_shoots[i].destroyed = true;
if (_shoots[i].animation != "NONE") {
if (_shoots[i].deathPosition.x != 0 && _shoots[i].deathPosition.y != 0) {
Common::Point position = computeTargetPosition(mousePos);
_shoots[i].video->position = Common::Point(position.x, position.y) - _shoots[i].deathPosition;
}
int currentFrame = _shoots[i].video->decoder->getCurFrame();
uint32 explosionIdx;
for (explosionIdx = 0; explosionIdx < _shoots[i].bodyFrames.size(); explosionIdx++) {
if (int(_shoots[i].bodyFrames[explosionIdx].lastFrame()) >= currentFrame)
break;
}
if (explosionIdx > 0)
explosionIdx = explosionIdx - 1;
uint32 explosionStartFrame = _shoots[i].explosionFrames[explosionIdx].start;
uint32 explosionLastFrame = _shoots[i].explosionFrames[explosionIdx].lastFrame();
_objKillsCount[_objIdx] = _objKillsCount[_objIdx] + _shoots[i].objKillsCount;
_shoots[i].video->decoder->forceSeekToFrame(explosionStartFrame - 2);
_shoots[i].lastFrame = explosionLastFrame - 2;
} else {
if (!_shoots[i].explosionAnimation.empty()) {
Common::Point position = computeTargetPosition(mousePos);
_shoots[i].video = new MVideo(_shoots[i].explosionAnimation, position, true, false, false);
playVideo(*_shoots[i].video);
int w = _shoots[i].video->decoder->getWidth();
int h = _shoots[i].video->decoder->getHeight();
uint32 explosionLastFrame = _shoots[i].video->decoder->getFrameCount() - 1;
_shoots[i].video->position = Common::Point(position.x - w / 2, position.y - h / 2);
_shoots[i].lastFrame = explosionLastFrame - 1;
} else if (_objIdx == 0 && !arc->hitBoss1Video.empty()) {
_background->decoder->pauseVideo(true);
MVideo video(arc->hitBoss1Video, Common::Point(0, 0), false, true, false);
disableCursor();
runIntro(video);
// Should be currentPalette?
loadPalette(arc->backgroundPalette);
_background->decoder->pauseVideo(false);
updateScreen(*_background);
drawScreen();
} else if (_objIdx == 1 && !arc->hitBoss2Video.empty()) {
_background->decoder->pauseVideo(true);
MVideo video(arc->hitBoss2Video, Common::Point(0, 0), false, true, false);
runIntro(video);
// Should be currentPalette?
loadPalette(arc->backgroundPalette);
_background->decoder->pauseVideo(false);
updateScreen(*_background);
drawScreen();
drawCursorArcade(mousePos);
}
byte p[3] = {0x00, 0x00, 0x00}; // Always black?
assert(_shoots[i].paletteSize == 1 || _shoots[i].paletteSize == 0);
loadPalette((byte *) &p, _shoots[i].paletteOffset, _shoots[i].paletteSize);
_objKillsCount[_objIdx] = _objKillsCount[_objIdx] + _shoots[i].objKillsCount;
}
// Redraw cursor
drawCursorArcade(mousePos);
}
end:
if (secondary) {
if (_background->decoder->getCurFrame() % 2 == 0)
drawShoot(mousePos);
if (checkRButtonUp()) {
setRButtonUp(false);
return false;
}
return clickedSecondaryShoot(mousePos);
} else {
drawShoot(mousePos);
return false;
}
}
void HypnoEngine::incBonus(int inc) {
_bonus = _bonus + inc;
}
void HypnoEngine::incScore(int inc) {
_score = _score + inc;
}
void HypnoEngine::incLivesUsed() {
_stats.livesUsed++;
}
void HypnoEngine::incShotsFired() {
_stats.shootsFired++;
}
void HypnoEngine::incEnemyHits() {
_stats.enemyHits++;
}
void HypnoEngine::incEnemyTargets() {
_stats.enemyTargets++;
}
void HypnoEngine::incTargetsDestroyed() {
_stats.targetsDestroyed++;
}
void HypnoEngine::incTargetsMissed() {
_stats.targetsMissed++;
}
uint32 HypnoEngine::killRatio() {
if (_stats.enemyTargets == 0)
return 0;
return 100 * _stats.targetsDestroyed / _stats.enemyTargets;
}
uint32 HypnoEngine::accuracyRatio() {
if (_stats.shootsFired == 0)
return 0;
return 100 * _stats.enemyHits / _stats.shootsFired;
}
void HypnoEngine::incFriendliesEncountered() {
_stats.friendliesEncountered++;
}
void HypnoEngine::incInfoReceived() {
_stats.infoReceived++;
}
void HypnoEngine::resetStatistics() {
_stats = ArcadeStats();
_bonus = 0;
}
bool HypnoEngine::clickedSecondaryShoot(const Common::Point &mousePos) {
return false;
}
bool HypnoEngine::checkRButtonUp() {
return false;
}
void HypnoEngine::setRButtonUp(const bool val) {
return;
}
void HypnoEngine::disableGameKeymaps() {
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
keymapper->getKeymap("game-shortcuts")->setEnabled(false);
}
void HypnoEngine::enableGameKeymaps() {
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
keymapper->getKeymap("game-shortcuts")->setEnabled(true);
}
} // End of namespace Hypno

View File

@@ -0,0 +1,903 @@
/* 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 "hypno/grammar.h"
#include "hypno/hypno.h"
#include "common/events.h"
#include "backends/keymapper/keymapper.h"
namespace Hypno {
void BoyzEngine::runBeforeArcade(ArcadeShooting *arc) {
_checkpoint = _currentLevel;
_lastStats = _stats;
if (!_flashbackMode) {
// if we are flashback mode,
// then we are testing some level
// and we should not save
saveProfile(_name, int(arc->id));
}
if (arc->mode == "YM") {
assert(!arc->player.empty());
_playerFrames = decodeFrames(arc->player);
_playerFrameSep = 0;
Common::Rect healthBarBox(0, 3, 107, 18);
Common::Rect ammoBarBox(0, 20, 103, 34);
Common::Rect portraitBox(0, 40, 57, 94);
for (int i = 0; i < int(_playerFrames.size()); i++) {
_healthBar[i+1] = _playerFrames[i]->getSubArea(healthBarBox);
_ammoBar[i+1] = _playerFrames[i]->getSubArea(ammoBarBox);
_portrait[i+1] = _playerFrames[i]->getSubArea(portraitBox);
}
_playerFrameSep = _playerFrames.size();
_playerFrameIdx = -1;
} else {
uint32 r = 1 + _rnd->getRandomNumber(1);
arc->backgroundVideo = Common::String::format("c3/c35c0%ds.smk", r);
_playerFrameSep = 0;
_playerFrameIdx = -1;
if (arc->mode != "YS")
error("Invalid mode: %s", arc->mode.c_str());
}
if (!arc->beforeVideo.empty()) {
MVideo video(arc->beforeVideo, Common::Point(0, 0), false, true, false);
runIntro(video);
}
_currentScript = arc->script;
// Reload all weapons
for (Script::iterator it = _currentScript.begin(); it != _currentScript.end(); ++it) {
_ammo = _weaponMaxAmmo[it->cursor];
}
_currentActor = 0;
updateFromScript();
_shootsDestroyed.clear();
_health = _previousHealth;
_selectedCorrectBox = 0;
}
void BoyzEngine::runAfterArcade(ArcadeShooting *arc) {
for (int i = 0; i < int(_playerFrames.size()); i++) {
_playerFrames[i]->free();
delete _playerFrames[i];
}
_playerFrames.clear();
if (_flashbackMode) {
resetStatistics();
_nextLevel = "<select_t1>";
return;
}
if (_health <= 0) {
if (_arcadeMode == "YS")
return;
_stats = _lastStats;
disableCursor();
if (_levelId == 36 && !checkArcadeObjectives()) {
MVideo video("warnings/w09s.smk", Common::Point(0, 0), false, true, false);
runIntro(video);
} else {
if (getTerritory(_currentLevel) <= 4) {
MVideo video(_deathDay[_currentActor], Common::Point(0, 0), false, true, false);
runIntro(video);
} else {
MVideo video(_deathNight[_currentActor], Common::Point(0, 0), false, true, false);
runIntro(video);
}
}
return;
} else {
if (_levelId == 33 && checkArcadeObjectives()) {
MVideo video("c3/c33a02s.smk", Common::Point(0, 0), false, true, false);
runIntro(video);
defaultCursor();
waitForUserClick(1);
} else if (_levelId == 42) {
disableCursor();
MVideo video("c4/c4bro8s.smk", Common::Point(0, 0), false, true, false);
runIntro(video);
}
}
if (_currentLevel == lastLevelTerritory(_currentLevel)) {
int territory = getTerritory(_currentLevel) - 1;
showArcadeStats(territory, _stats);
// Merge current stats with the global ones
_globalStats.livesUsed = _stats.livesUsed + _globalStats.livesUsed;
_globalStats.shootsFired = _stats.shootsFired + _globalStats.shootsFired;
_globalStats.enemyHits = _stats.enemyHits + _globalStats.enemyHits;
_globalStats.enemyTargets = _stats.enemyTargets + _globalStats.enemyTargets;
_globalStats.targetsDestroyed = _stats.targetsDestroyed + _globalStats.targetsDestroyed;
_globalStats.targetsMissed = _stats.targetsMissed + _globalStats.targetsMissed;
_globalStats.friendliesEncountered = _stats.friendliesEncountered + _globalStats.friendliesEncountered;
_globalStats.infoReceived = _stats.infoReceived + _globalStats.infoReceived;
// If we are finishing the last level, show final stats
if (_currentLevel == "c59.mi_")
showArcadeStats(5, _globalStats);
// After that, we can reset the current stats
resetStatistics();
}
_previousHealth = _health;
_sceneState[Common::String::format("GS_SEQ_%d", _levelId)] = 1;
}
void BoyzEngine::showArcadeStats(int territory, const ArcadeStats &data) {
byte *palette;
Graphics::Surface *stats = decodeFrame("preload/stats.smk", territory, &palette);
loadPalette(palette, 0, 256);
drawImage(*stats, 0, 0, true);
stats->free();
delete stats;
free(palette);
uint32 enemiesAvailable = data.targetsDestroyed + data.targetsMissed;
drawString("scifi08.fgx", Common::String::format("%d", enemiesAvailable), 278, 41, 0, kHypnoColorWhiteOrBlue);
uint32 killRatio = enemiesAvailable > 0 ? 100 * data.targetsDestroyed / enemiesAvailable : 0;
drawString("scifi08.fgx", Common::String::format("%d%%", killRatio), 278, 56, 0, kHypnoColorWhiteOrBlue);
drawString("scifi08.fgx", Common::String::format("%d", data.shootsFired), 278, 79, 0, kHypnoColorWhiteOrBlue);
uint32 accuracyRatio = data.shootsFired > 0 ? 100 * data.enemyHits / data.shootsFired : 0;
drawString("scifi08.fgx", Common::String::format("%d%%", accuracyRatio), 278, 94, 0, kHypnoColorWhiteOrBlue);
drawString("scifi08.fgx", Common::String::format("%d", data.livesUsed), 278, 119, 0, kHypnoColorWhiteOrBlue);
drawString("scifi08.fgx", Common::String::format("%d", data.friendliesEncountered), 278, 144, 0, kHypnoColorWhiteOrBlue);
drawString("scifi08.fgx", Common::String::format("%d", data.infoReceived), 278, 159, 0, kHypnoColorWhiteOrBlue);
uint32 scorePercentage = (killRatio + accuracyRatio) / 2;
drawString("scifi08.fgx", Common::String::format("%d%%", scorePercentage), 278, 184, 0, kHypnoColorWhiteOrBlue);
bool cont = true;
while (!shouldQuit() && cont) {
Common::Event event;
while (g_system->getEventManager()->pollEvent(event)) {
switch (event.type) {
case Common::EVENT_KEYDOWN:
cont = false;
break;
default:
break;
}
}
drawScreen();
g_system->delayMillis(10);
}
}
void BoyzEngine::pressedKey(const int keycode) {
if (keycode == kActionSkipLevel) {
_skipLevel = true;
return;
} else if (keycode == kActionKillPlayer) { // Added for testing
_health = 0;
} else if (keycode == kActionPause) {
openMainMenuDialog();
}
}
void BoyzEngine::updateFromScript() {
if (_currentScript.size() > 0) {
ScriptInfo si = *_currentScript.begin();
//debug("%d %d %d", si.time, _background->decoder->getCurFrame(), si.actor);
if (!_background || int(si.time) <= _background->decoder->getCurFrame()) {
if (_currentActor != si.actor)
_ammo = _weaponMaxAmmo[si.cursor];
_currentActor = si.actor;
_currentMode = si.mode;
_currentWeapon = si.cursor;
_currentScript.pop_front();
if (_currentMode == NonInteractive)
changeCursor(_crosshairsInactive[_currentWeapon], _crosshairsPalette, true);
else
changeCursor(_crosshairsActive[_currentWeapon], _crosshairsPalette, true);
}
}
}
void BoyzEngine::drawCursorArcade(const Common::Point &mousePos) {
if (_currentMode == NonInteractive) {
return;
}
int i = detectTarget(mousePos);
if (i >= 0)
changeCursor(_crosshairsTarget[_currentWeapon], _crosshairsPalette, true);
else
changeCursor(_crosshairsActive[_currentWeapon], _crosshairsPalette, true);
}
void BoyzEngine::drawPlayer() {
updateFromScript();
if (_arcadeMode != "YS")
drawImage(_portrait[_currentActor], 0, 200 - _portrait[_currentActor].h, true);
}
void BoyzEngine::drawHealth() {
updateFromScript();
if(_arcadeMode == "YS")
return;
float w = float(_health) / float(_maxHealth);
if (w <= 0 || _healthBar[_currentActor].w - 3 <= 0 || _healthBar[_currentActor].h / 2 <= 0)
return;
Common::Rect healthBarBox(0, 0, int((_healthBar[_currentActor].w - 3) * w), _healthBar[_currentActor].h / 2);
uint32 c = kHypnoColorWhiteOrBlue; // white
_compositeSurface->fillRect(healthBarBox, c);
for (int i = 0; i < _maxHealth; i = i + 10) {
int x = (_healthBar[_currentActor].w - 3) * float(i) / float(_maxHealth);
_compositeSurface->drawLine(x, 2, x, 6, 0);
}
drawImage(_healthBar[_currentActor], 0, 0, true);
}
void BoyzEngine::drawAmmo() {
updateFromScript();
if(_arcadeMode == "YS")
return;
float w = float(_ammoBar[_currentActor].w) / float(_weaponMaxAmmo[_currentWeapon]);
Common::Rect ammoBarBox(320 - int(_ammo * w), 0, 320, _ammoBar[_currentActor].h / 2);
uint32 c = kHypnoColorGreen; // green
_compositeSurface->fillRect(ammoBarBox, c);
drawImage(_ammoBar[_currentActor], 320 - _ammoBar[_currentActor].w, 0, true);
for (int i = 1; i < _weaponMaxAmmo[_currentWeapon]; i++) {
int x = 320 - _ammoBar[_currentActor].w + int (i * w);
_compositeSurface->drawLine(x, 2, x, 6, 0);
}
}
void BoyzEngine::hitPlayer() {
if(_arcadeMode == "YS")
return; // Should never happen?
uint32 c = kHypnoColorRed; // red
_compositeSurface->fillRect(Common::Rect(0, 0, _screenW, _screenH), c);
drawScreen();
if (!_infiniteHealthCheat) {
_health = _health - 10;
}
if (!_hitSound.empty())
playSound(_soundPath + _hitSound, 1, 11025);
}
void BoyzEngine::drawShoot(const Common::Point &target) {}
void BoyzEngine::initSegment(ArcadeShooting *arc) {
_segmentShootSequenceOffset = 0;
_segmentShootSequenceMax = 0;
uint32 randomSegmentShootSequence = _segmentShootSequenceOffset + _rnd->getRandomNumber(_segmentShootSequenceMax);
SegmentShoots segmentShoots = arc->shootSequence[randomSegmentShootSequence];
_shootSequence = segmentShoots.shootSequence;
_segmentRepetitionMax = segmentShoots.segmentRepetition; // Usually zero
_segmentRepetition = 0;
_segmentOffset = 0;
_segmentIdx = _segmentOffset;
}
void BoyzEngine::findNextSegment(ArcadeShooting *arc) {
_segmentIdx = _segmentIdx + 1;
}
bool BoyzEngine::checkTransition(ArcadeTransitions &transitions, ArcadeShooting *arc) {
ArcadeTransition at = *transitions.begin();
int ttime = at.time;
if (_background->decoder->getCurFrame() > ttime) {
if (_background->decoder->getCurFrame() > ttime + 3) {
debugC(1, kHypnoDebugArcade, "Skipped transition of %d at %d", ttime, _background->decoder->getCurFrame());
} else if (at.video == "NONE") {
if (!at.palette.empty()) {
_background->decoder->pauseVideo(true);
_currentPalette = at.palette;
loadPalette(_currentPalette);
_background->decoder->pauseVideo(false);
drawPlayer();
updateScreen(*_background);
drawScreen();
} else if (!arc->additionalSound.empty())
playSound(arc->additionalSound, 1, arc->additionalSoundRate);
else if (_levelId == 36) {
if (!checkArcadeObjectives()) {
_health = 0;
// Not sure how to handle this
}
} else if (_levelId == 354) {
if (ttime == 70) {
incInfoReceived();
_background->decoder->pauseVideo(true);
MVideo video("tempdir/c35h4a1s.smk", Common::Point(0, 0), false, true, false);
disableCursor();
runIntro(video);
defaultCursor();
waitForUserClick(1);
_skipLevel = true;
}
} else if (_levelId == 51) {
if (_selectedCorrectBox == 0) {
_background->decoder->pauseVideo(true);
_background->decoder->forceSeekToFrame(ttime - 2);
_masks->decoder->forceSeekToFrame(ttime - 2);
const Graphics::Surface *frame = _background->decoder->decodeNextFrame();
Graphics::Surface *boxes = frame->convertTo(frame->format, _background->decoder->getPalette());
drawImage(*boxes, 0, 0, false);
drawScreen();
boxes->free();
delete boxes;
_selectedCorrectBox = pickABox();
if (_selectedCorrectBox == 1) {
_background->decoder->forceSeekToFrame(582);
_masks->decoder->forceSeekToFrame(582);
} else if (_selectedCorrectBox == -1) {
_background->decoder->forceSeekToFrame(525);
_masks->decoder->forceSeekToFrame(525);
} else
error("Invalid value for _selectedCorrectBox: %d", _selectedCorrectBox);
_background->decoder->pauseVideo(false);
updateScreen(*_background);
drawScreen();
} else if (_selectedCorrectBox == -1) {
_health = 0;
}
}
} else if (!at.video.empty()) {
_background->decoder->pauseVideo(true);
_masks->decoder->pauseVideo(true);
debugC(1, kHypnoDebugArcade, "Playing transition %s", at.video.c_str());
MVideo video(at.video, Common::Point(0, 0), false, true, false);
disableCursor();
runIntro(video);
if (!at.palette.empty())
_currentPalette = at.palette;
loadPalette(_currentPalette);
if (_levelId == 59) {
_background->decoder->forceSeekToFrame(97);
_masks->decoder->forceSeekToFrame(97);
}
_background->decoder->pauseVideo(false);
_masks->decoder->pauseVideo(false);
drawPlayer();
updateScreen(*_background);
drawScreen();
drawCursorArcade(g_system->getEventManager()->getMousePos());
} else if (!at.sound.empty()) {
playSound(at.sound, 1, at.soundRate, at.soundStereo);
} else if (at.jumpToTime > 0) {
_background->decoder->forceSeekToFrame(at.jumpToTime);
_masks->decoder->forceSeekToFrame(at.jumpToTime);
} else if (at.loseLevel) {
debugC(1, kHypnoDebugArcade, "Losing level in transition at %d", _background->decoder->getCurFrame());
_health = 0;
} else if (at.winLevel) {
debugC(1, kHypnoDebugArcade, "Wining level in transition at %d", _background->decoder->getCurFrame());
_skipLevel = true;
} else
error ("Invalid transition at %d", ttime);
transitions.pop_front();
return true;
}
return false;
}
int BoyzEngine::detectTarget(const Common::Point &mousePos) {
Common::Point target = computeTargetPosition(mousePos);
if (!_mask)
return -1;
uint32 m = _mask->getPixel(target.x, target.y);
if (m == 0)
return -1;
int i = 0;
for (Shoots::iterator it = _shoots.begin(); it != _shoots.end(); ++it) {
if (!it->bodyFrames.empty() && _background->decoder->getCurFrame() > int(it->bodyFrames.back().start)) {
i++;
continue; // This shoot is old!
}
if (m == it->paletteOffset && !_shoots[i].destroyed)
return i;
i++;
}
if (i == int(_shoots.size()))
return -1;
error("Invalid mask state (%d)!", m);
}
char BoyzEngine::selectDirection() {
Common::Event event;
Common::Rect button(252, 158, 315, 195);
Graphics::Surface *screen = _compositeSurface->convertTo(_compositeSurface->format, _background->decoder->getPalette());
Frames mapFrames = decodeFrames("c4/minemap.smk");
bool showMap = _sceneState["GS_MINEMAP"];
bool viewingMap = false;
if (showMap)
drawImage(*mapFrames[0], 0, 0, true);
while (!shouldQuit()) {
while (g_system->getEventManager()->pollEvent(event)) {
Common::Point mousePos = g_system->getEventManager()->getMousePos();
switch (event.type) {
case Common::EVENT_MOUSEMOVE:
if (showMap && button.contains(mousePos))
defaultCursor();
else if (!viewingMap && mousePos.x <= _screenW / 3)
changeCursor(_leftArrowPointer, _crosshairsPalette, true);
else if (!viewingMap && mousePos.x >= 2 * _screenW / 3)
changeCursor(_rightArrowPointer, _crosshairsPalette, true);
else if (!viewingMap)
changeCursor(_crossPointer, _crosshairsPalette, true);
break;
case Common::EVENT_LBUTTONDOWN:
if (showMap && button.contains(mousePos)) {
if (viewingMap) {
drawImage(*screen, 0, 0, false);
drawImage(*mapFrames[0], 0, 0, true);
} else {
drawImage(*mapFrames[1], 0, 0, true);
}
viewingMap = !viewingMap;
} else if (!viewingMap && mousePos.x <= _screenH / 2) {
screen->free();
delete screen;
return 'L';
} else if (!viewingMap) {
screen->free();
delete screen;
return 'R';
}
break;
default:
break;
}
}
drawScreen();
g_system->delayMillis(10);
}
screen->free();
delete screen;
return 0;
}
void BoyzEngine::waitForUserClick(uint32 timeout) {
Common::Event event;
bool cont = true;
Common::Rect button(252, 158, 315, 195);
while (!shouldQuit() && cont) {
while (g_system->getEventManager()->pollEvent(event)) {
Common::Point mousePos = g_system->getEventManager()->getMousePos();
switch (event.type) {
case Common::EVENT_QUIT:
case Common::EVENT_RETURN_TO_LAUNCHER:
cont = false;
break;
case Common::EVENT_LBUTTONDOWN:
if (button.contains(mousePos))
cont = false;
break;
default:
break;
}
}
drawScreen();
g_system->delayMillis(10);
}
}
int BoyzEngine::pickABox() {
Common::Event event;
Common::Rect correctBox(84, 14, 135, 66);
Common::Rect incorrectBoxes[6];
incorrectBoxes[0] = Common::Rect(15, 17, 77, 66);
incorrectBoxes[1] = Common::Rect(2, 69, 84, 92);
incorrectBoxes[2] = Common::Rect(74, 108, 242, 138);
incorrectBoxes[3] = Common::Rect(62, 134, 245, 160);
incorrectBoxes[4] = Common::Rect(59, 161, 239, 190);
incorrectBoxes[5] = Common::Rect(135, 29, 223, 101);
int i;
while (!shouldQuit()) {
while (g_system->getEventManager()->pollEvent(event)) {
Common::Point mousePos = g_system->getEventManager()->getMousePos();
switch (event.type) {
case Common::EVENT_MOUSEMOVE:
if (correctBox.contains(mousePos)) {
changeCursor(_crosshairsTarget[_currentWeapon], _crosshairsPalette, true);
break;
}
for (i = 0; i < 6; i++)
if (incorrectBoxes[i].contains(mousePos)) {
changeCursor(_crosshairsTarget[_currentWeapon], _crosshairsPalette, true);
break;
}
if (i == 6)
changeCursor(_crosshairsActive[_currentWeapon], _crosshairsPalette, true);
break;
case Common::EVENT_LBUTTONDOWN:
if (correctBox.contains(mousePos))
return 1;
for (i = 0; i < 6; i++)
if (incorrectBoxes[i].contains(mousePos))
return -1;
break;
default:
break;
}
}
drawScreen();
g_system->delayMillis(10);
}
return -1;
}
bool BoyzEngine::shoot(const Common::Point &mousePos, ArcadeShooting *arc, bool secondary) {
if (_currentMode == NonInteractive) {
return false;
}
if (!secondary && _currentWeapon > 0) {
if (_ammo == 0) {
if (!arc->noAmmoSound.empty())
playSound(_soundPath + arc->noAmmoSound, 1, arc->noAmmoSoundRate);
return false;
}
if (!_infiniteAmmoCheat)
_ammo--;
playSound(_soundPath + _weaponShootSound[_currentWeapon], 1);
incShotsFired();
}
int i = detectTarget(mousePos);
if (i < 0) {
missNoTarget(arc);
} else {
debugC(1, kHypnoDebugArcade, "Hit target %s", _shoots[i].name.c_str());
if (_shoots[i].nonHostile && secondary) {
playSound(_soundPath + _heySound[_currentActor], 1);
if (_shoots[i].isAnimal) {
playSound(_soundPath + _shoots[i].animalSound, 1);
return false;
}
// Not really killed, but counted as objective
_objKillsCount[_objIdx] = _objKillsCount[_objIdx] + _shoots[i].objKillsCount;
if (!_shoots[i].additionalVideo.empty()) {
incFriendliesEncountered();
incInfoReceived();
_background->decoder->pauseVideo(true);
MVideo video(_shoots[i].additionalVideo, Common::Point(0, 0), false, true, false);
disableCursor();
runIntro(video);
defaultCursor();
if (_shoots[i].waitForClickAfterInteraction > 0) {
waitForUserClick(_shoots[i].waitForClickAfterInteraction);
if (_shoots[i].name == "LILKID")
_sceneState["GS_MINEMAP"] = true;
else if (_shoots[i].name == "HO3") {
_sceneState["GS_C5MAP"] = true;
}
}
loadPalette(_currentPalette);
_background->decoder->pauseVideo(false);
// Skip the rest of the interaction
if (_shoots[i].explosionFrames[0].start == uint32(-1))
_skipLevel = true;
else {
_background->decoder->forceSeekToFrame(_shoots[i].explosionFrames[0].start + 3);
_masks->decoder->forceSeekToFrame(_shoots[i].explosionFrames[0].start + 3);
_shoots[i].destroyed = true;
_shootsDestroyed[_shoots[i].name] = true;
updateScreen(*_background);
drawScreen();
}
} else if (_shoots[i].interactionFrame > 0) {
incFriendliesEncountered();
incInfoReceived();
_background->decoder->forceSeekToFrame(_shoots[i].interactionFrame);
_masks->decoder->forceSeekToFrame(_shoots[i].interactionFrame);
if (!arc->missBoss2Video.empty()) {
_additionalVideo = new MVideo(arc->missBoss2Video, Common::Point(0, 0), true, false, false);
playVideo(*_additionalVideo);
}
//_shoots[i].lastFrame = _background->decoder->getFrameCount();
_shoots[i].destroyed = true;
_shootsDestroyed[_shoots[i].name] = true;
updateScreen(*_background);
drawScreen();
}
return false;
} else if (_shoots[i].nonHostile && !secondary) {
if (checkCup(_shoots[i].name))
return false;
if (!_shoots[i].hitSound.empty())
playSound(_soundPath + _shoots[i].hitSound, 1);
if (!_shoots[i].deathSound.empty())
playSound(_soundPath + _shoots[i].deathSound, 1);
if (!_shoots[i].additionalVideo.empty() || _shoots[i].interactionFrame > 0) // Only count the ones with info
incFriendliesEncountered();
uint32 idx = _shoots[i].warningVideoIdx;
if (idx > 0) {
Common::String filename = _warningVideosDay[idx];
_background->decoder->pauseVideo(true);
MVideo video(filename, Common::Point(0, 0), false, true, false);
disableCursor();
runIntro(video);
// Should be currentPalette?
loadPalette(arc->backgroundPalette);
_background->decoder->pauseVideo(false);
updateScreen(*_background);
drawScreen();
hitPlayer();
}
if (_shoots[i].explosionFrames.size() == 0)
return false;
debugC(1, kHypnoDebugArcade, "Jumping to %d", _shoots[i].explosionFrames[0].start - 3);
_background->decoder->forceSeekToFrame(_shoots[i].explosionFrames[0].start - 3);
_masks->decoder->forceSeekToFrame(_shoots[i].explosionFrames[0].start - 3);
if (_shoots[i].jumpToTimeAfterKilled == -1000) {
ArcadeTransition at("", 0, "", 0, _shoots[i].explosionFrames[0].lastFrame() - 1);
at.loseLevel = true;
_transitions.push_front(at);
}
return false;
} else if (!_shoots[i].nonHostile && secondary) {
if (_shoots[i].interactionFrame > 0) {
_background->decoder->forceSeekToFrame(_shoots[i].interactionFrame);
_masks->decoder->forceSeekToFrame(_shoots[i].interactionFrame);
_shoots[i].destroyed = true;
updateScreen(*_background);
drawScreen();
}
return false;
}
if (!_shoots[i].hitSound.empty())
playSound(_soundPath + _shoots[i].hitSound, 1);
incEnemyHits();
if (!_shoots[i].deathSound.empty())
playSound(_soundPath + _shoots[i].deathSound, 1);
if (_shoots[i].playInteractionAudio && !arc->missBoss2Video.empty()) {
incInfoReceived();
_additionalVideo = new MVideo(arc->missBoss2Video, Common::Point(0, 0), true, false, false);
playVideo(*_additionalVideo);
}
incTargetsDestroyed();
incScore(_shoots[i].pointsToShoot);
incBonus(_shoots[i].pointsToShoot);
_shoots[i].destroyed = true;
_objKillsCount[_objIdx] = _objKillsCount[_objIdx] + _shoots[i].objKillsCount;
_shootsDestroyed[_shoots[i].name] = true;
if (_shoots[i].name == "HELICOPTER") {
_background->decoder->pauseVideo(true);
MVideo video(arc->hitBoss2Video, Common::Point(0, 0), false, true, false);
disableCursor();
runIntro(video);
_skipLevel = true;
_skipNextVideo = true;
return false;
}
_background->decoder->forceSeekToFrame(_shoots[i].explosionFrames[0].start - 3);
_masks->decoder->forceSeekToFrame(_shoots[i].explosionFrames[0].start - 3);
debugC(1, kHypnoDebugArcade, "Jumping to: %d", _shoots[i].explosionFrames[0].start - 3);
changeCursor(_crosshairsActive[_currentWeapon], _crosshairsPalette, true);
if (_shoots[i].jumpToTimeAfterKilled > 0) {
ArcadeTransition at("", 0, "", 0, _shoots[i].explosionFrames[0].lastFrame() - 1);
at.jumpToTime = _shoots[i].jumpToTimeAfterKilled;
_transitions.push_front(at);
} else if ( _shoots[i].jumpToTimeAfterKilled == -1 ) {
ArcadeTransition at("", 0, "", 0, _shoots[i].explosionFrames[0].lastFrame() - 1);
at.winLevel = true;
_transitions.push_front(at);
}
}
return false;
}
void BoyzEngine::missedTarget(Shoot *s, ArcadeShooting *arc) {
debugC(1, kHypnoDebugArcade, "Missed target %s!", s->name.c_str());
if (!s->checkIfDestroyed.empty()) {
if (_shootsDestroyed.contains(s->checkIfDestroyed))
return; // Precondition was destroyed, so we ignore the missed shoot
}
if (s->nonHostile) {
if (!s->additionalVideo.empty() || s->interactionFrame > 0) // Only count the ones with info
incFriendliesEncountered();
_stats.targetsMissed--; // If the target was not hostile, it should *not* count as missed
}
if (s->name == "CAPTOR" || (s->name == "G1" && _currentLevel == "c354.mi_")) {
_background->decoder->pauseVideo(true);
MVideo video(_warningHostage, Common::Point(0, 0), false, true, false);
disableCursor();
runIntro(video);
hitPlayer();
if (_health > 0)
_skipLevel = true;
return;
} else if (s->name.hasPrefix("ALARM")) {
if (s->missedAnimation != uint32(-1) && uint32(_background->decoder->getCurFrame()) > s->missedAnimation)
return;
_background->decoder->pauseVideo(true);
int territory = getTerritory(_currentLevel);
Filename path;
if (territory <= 2)
path = "misc/alrm_brs.smk";
else if (territory <= 4)
path = "misc/alrm_mbs.smk";
else
path = "misc/alrm_c5s.smk";
disableCursor();
MVideo alarmVideo(path, Common::Point(0, 0), false, true, false);
runIntro(alarmVideo);
MVideo warningVideo(_warningAlarmVideos.front(), Common::Point(0, 0), false, true, false);
runIntro(warningVideo);
_health = 0;
return;
} else if (s->direction > 0) {
char selected = selectDirection();
defaultCursor();
if (selected == s->direction) {
int missedAnimation = s->missedAnimation;
debugC(1, kHypnoDebugArcade, "Jumping to: %d", missedAnimation);
_background->decoder->forceSeekToFrame(missedAnimation);
_masks->decoder->forceSeekToFrame(missedAnimation);
} else {
_background->decoder->forceSeekToFrame(s->explosionFrames[0].start - 3);
_masks->decoder->forceSeekToFrame(s->explosionFrames[0].start - 3);
if (s->jumpToTimeAfterKilled == -1000) {
ArcadeTransition at("", 0, "", 0, s->explosionFrames[0].lastFrame() - 1);
at.loseLevel = true;
_transitions.push_front(at);
}
}
return;
}
if (s->missedAnimation == 0) {
return;
} else if (s->missedAnimation == uint32(-1)) {
debugC(1, kHypnoDebugArcade, "Jumping to end of level");
_skipLevel = true;
} else if (s->missedAnimation == uint32(-1000)) {
if (_background->decoder->getCurFrame() > int(s->explosionFrames[0].start))
return; // Too late for this
_health = 0;
} else {
int missedAnimation = s->missedAnimation;
if (missedAnimation + 3 > int(_background->decoder->getFrameCount()) - 1) {
_skipLevel = true;
return;
}
if (_background->decoder->getCurFrame() > missedAnimation)
return; // Too late for this
debugC(1, kHypnoDebugArcade, "Jumping to: %d", missedAnimation);
_background->decoder->forceSeekToFrame(missedAnimation);
_masks->decoder->forceSeekToFrame(missedAnimation);
}
if (s->attackFrames.size() > 0 && s->attackFrames.front() == 0)
return;
if (!s->nonHostile)
hitPlayer();
}
bool BoyzEngine::checkCup(const Common::String &name) {
if (name == "CUP1") {
if (_background->path == "c3/c35c01s.smk") {
MVideo video("c3/c35c07s.smk", Common::Point(0, 0), false, true, false);
disableCursor();
runIntro(video);
_skipLevel = true;
_sceneState["GS_WONSHELLGAME"] = 1;
} else {
MVideo video("c3/c35c06s.smk", Common::Point(0, 0), false, true, false);
disableCursor();
runIntro(video);
_health = 0;
}
return true;
} else if (name == "CUP2") {
if (_background->path == "c3/c35c02s.smk") {
MVideo video("c3/c35c07s.smk", Common::Point(0, 0), false, true, false);
disableCursor();
runIntro(video);
_skipLevel = true;
_sceneState["GS_WONSHELLGAME"] = 1;
} else {
MVideo video("c3/c35c06s.smk", Common::Point(0, 0), false, true, false);
disableCursor();
runIntro(video);
_health = 0;
}
return true;
}
return false;
}
bool BoyzEngine::clickedSecondaryShoot(const Common::Point &mousePos) {
if (_currentMode == NonInteractive) {
return false;
}
Common::Rect ammoBarBox(320 - _ammoBar[_currentActor].w, 0, 320, _ammoBar[_currentActor].h);
if (ammoBarBox.contains(mousePos)) {
_ammo = _weaponMaxAmmo[_currentWeapon];
playSound(_soundPath + _weaponReloadSound[_currentWeapon], 1);
return false;
}
return true;
}
} // namespace Hypno

1142
engines/hypno/boyz/boyz.cpp Normal file

File diff suppressed because it is too large Load Diff

521
engines/hypno/boyz/hard.cpp Normal file
View File

@@ -0,0 +1,521 @@
/* 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/bitarray.h"
#include "gui/message.h"
#include "common/events.h"
#include "common/config-manager.h"
#include "common/savefile.h"
#include "hypno/hypno.h"
#include "backends/keymapper/keymapper.h"
namespace Hypno {
void BoyzEngine::runCode(Code *code) {
if (code->name == "<main_menu>")
runMainMenu(code);
else if (code->name == "<difficulty_menu>")
runDifficultyMenu(code);
else if (code->name == "<retry_menu>")
runRetryMenu(code);
else if (code->name == "<check_c3>")
runCheckC3(code);
else if (code->name == "<check_ho>")
runCheckHo(code);
else if (code->name == "<check_c5>")
runCheckC5(code);
else if (code->name == "<alarm_c5>")
runAlarmC5(code);
else if (code->name == "<credits>")
endCredits(code);
else
error("invalid hardcoded level: %s", code->name.c_str());
}
void BoyzEngine::runMainMenu(Code *code) {
resetSceneState();
resetStatistics();
_globalStats = ArcadeStats();
_flashbackMode = false;
Common::Event event;
byte *palette;
Graphics::Surface *menu = decodeFrame("preload/mainmenu.smk", 0, &palette);
loadPalette(palette, 0, 256);
free(palette);
drawImage(*menu, 0, 0, false);
_name.clear();
bool cont = true;
uint32 c = kHypnoColorWhiteOrBlue; // white
uint32 posY = 105;
Common::StringArray profiles = listProfiles();
for (Common::StringArray::iterator it = profiles.begin(); it != profiles.end(); ++it) {
drawString("block05.fgx", *it, 130, posY, 170, c);
posY = posY + 10;
if (posY >= 185)
break;
}
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
keymapper->getKeymap("game-shortcuts")->setEnabled(false);
g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
while (!shouldQuit() && cont) {
while (g_system->getEventManager()->pollEvent(event)) {
// Events
switch (event.type) {
case Common::EVENT_QUIT:
case Common::EVENT_RETURN_TO_LAUNCHER:
break;
case Common::EVENT_KEYDOWN:
if (event.kbd.keycode == Common::KEYCODE_BACKSPACE)
_name.deleteLastChar();
else if (event.kbd.keycode == Common::KEYCODE_RETURN && !_name.empty()) {
cont = false;
} else if (event.kbd.keycode == Common::KEYCODE_ESCAPE) {
if (runExitMenu())
quitGame();
disableCursor();
}
else if (Common::isAlpha(event.kbd.keycode)) {
playSound("sound/m_choice.raw", 1);
_name = _name + char(event.kbd.keycode - 32);
}
drawImage(*menu, 0, 0, false);
drawString("block05.fgx", _name, 130, 58, 170, c);
posY = 105;
for (Common::StringArray::iterator it = profiles.begin(); it != profiles.end(); ++it) {
drawString("block05.fgx", *it, 130, posY, 170, c);
posY = posY + 10;
if (posY >= 185)
break;
}
break;
default:
break;
}
}
drawScreen();
g_system->delayMillis(10);
}
g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
keymapper->getKeymap("game-shortcuts")->setEnabled(true);
menu->free();
delete menu;
if (shouldQuit())
return;
_name.toLowercase();
bool found = loadProfile(_name);
if (!found) {
_nextLevel = code->levelIfWin;
} else if (_unlockAllLevels) {
_nextLevel = "<select_t1>";
unlockAllLevels();
_flashbackMode = true;
}
assert(!_nextLevel.empty());
}
bool BoyzEngine::runExitMenu() {
changeCursor("crosshair");
bool quit = false;
Common::Event event;
byte *palette;
Graphics::Surface *menu = decodeFrame("preload/mainmenu.smk", 8, &palette);
loadPalette(palette, 0, 256);
free(palette);
drawImage(*menu, 0, 0, false);
Common::Rect yesBox(142, 87, 179, 102);
Common::Rect noBox(142, 104, 179, 119);
bool cont = true;
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
keymapper->getKeymap("exit-menu")->setEnabled(true);
while (!shouldQuit() && cont) {
while (g_system->getEventManager()->pollEvent(event)) {
Common::Point mousePos = g_system->getEventManager()->getMousePos();
// Events
switch (event.type) {
case Common::EVENT_QUIT:
case Common::EVENT_RETURN_TO_LAUNCHER:
break;
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
if (event.customType == kActionYes) {
quit = true;
cont = false;
} else if (event.customType == kActionNo) {
quit = false;
cont = false;
break;
}
break;
case Common::EVENT_LBUTTONDOWN:
if (yesBox.contains(mousePos)) {
quit = true;
cont = false;
break;
} else if (noBox.contains(mousePos)) {
quit = false;
cont = false;
break;
}
break;
default:
break;
}
}
drawScreen();
g_system->delayMillis(10);
}
keymapper->getKeymap("exit-menu")->setEnabled(false);
menu->free();
delete menu;
return quit;
}
void BoyzEngine::runDifficultyMenu(Code *code) {
changeCursor("crosshair");
_difficulty.clear();
Common::Rect chumpBox(121, 62, 199, 77);
Common::Rect punkBox(121, 81, 199, 96);
Common::Rect badAssBox(121, 100, 199, 115);
Common::Rect cancelBox(121, 138, 245, 153);
Common::Event event;
Common::Point mousePos;
byte *palette;
Graphics::Surface *menu = decodeFrame("preload/mainmenu.smk", 1, &palette);
loadPalette(palette, 0, 256);
free(palette);
drawImage(*menu, 0, 0, false);
bool cont = true;
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
keymapper->getKeymap("game-shortcuts")->setEnabled(false);
keymapper->getKeymap("difficulty-menu")->setEnabled(true);
while (!shouldQuit() && cont) {
while (g_system->getEventManager()->pollEvent(event)) {
mousePos = g_system->getEventManager()->getMousePos();
// Events
switch (event.type) {
case Common::EVENT_QUIT:
case Common::EVENT_RETURN_TO_LAUNCHER:
break;
case Common::EVENT_LBUTTONDOWN:
if (chumpBox.contains(mousePos)) {
_difficulty = "chump";
cont = false;
} else if (punkBox.contains(mousePos)) {
_difficulty = "punk";
cont = false;
} else if (badAssBox.contains(mousePos)) {
_difficulty = "bad ass";
cont = false;
} else if (cancelBox.contains(mousePos)) {
cont = false;
}
break;
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
if (event.customType == kActionDifficultyChump) {
_difficulty = "chump";
cont = false;
} else if (event.customType == kActionDifficultyPunk) {
_difficulty = "punk";
cont = false;
} else if (event.customType == kActionDifficultyBadass) {
_difficulty = "bad ass";
cont = false;
} else if (event.customType == kActionDifficultExit) {
cont = false;
}
break;
default:
break;
}
}
drawScreen();
g_system->delayMillis(10);
}
keymapper->getKeymap("difficulty-menu")->setEnabled(false);
keymapper->getKeymap("game-shortcuts")->setEnabled(true);
if (_difficulty.empty())
_nextLevel = "<main_menu>";
else {
saveProfile(_name, 0);
if (_unlockAllLevels) {
_nextLevel = "<select_t1>";
unlockAllLevels();
_flashbackMode = true;
} else
_nextLevel = code->levelIfWin;
}
menu->free();
delete menu;
}
void BoyzEngine::runRetryMenu(Code *code) {
incLivesUsed();
uint32 idx = _rnd->getRandomNumber(_deathVideo.size() - 1);
Filename filename = _deathVideo[idx];
MVideo video(filename, Common::Point(0, 0), false, true, false);
disableCursor();
runIntro(video);
changeCursor("crosshair");
Common::Rect retryMissionBox(73, 62, 245, 77);
Common::Rect restartTerritoryBox(73, 81, 245, 96);
Common::Rect restartMissionBox(73, 100, 245, 114);
Common::Rect quitBox(73, 119, 245, 133);
Common::Event event;
Common::Point mousePos;
byte *palette;
Graphics::Surface *menu = decodeFrame("preload/mainmenu.smk", 5, &palette);
loadPalette(palette, 0, 256);
free(palette);
drawImage(*menu, 0, 0, false);
bool cont = true;
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
keymapper->getKeymap("game-shortcuts")->setEnabled(false);
keymapper->getKeymap("retry-menu")->setEnabled(true);
while (!shouldQuit() && cont) {
while (g_system->getEventManager()->pollEvent(event)) {
mousePos = g_system->getEventManager()->getMousePos();
// Events
switch (event.type) {
case Common::EVENT_QUIT:
case Common::EVENT_RETURN_TO_LAUNCHER:
break;
case Common::EVENT_LBUTTONDOWN:
if (retryMissionBox.contains(mousePos)) {
_nextLevel = _checkpoint;
cont = false;
} else if (restartTerritoryBox.contains(mousePos)) {
// Restore initial health for the team
_health = _maxHealth;
_stats = _globalStats;
_nextLevel = firstLevelTerritory(_checkpoint);
cont = false;
} else if (restartMissionBox.contains(mousePos)) {
_nextLevel = "<main_menu>";
cont = false;
} else if (quitBox.contains(mousePos))
quitGame();
break;
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
if (event.customType == kActionRetry) {
_nextLevel = _checkpoint;
cont = false;
} else if (event.customType == kActionNewMission) {
_nextLevel = "<main_menu>";
cont = false;
} else if (event.customType == kActionRestart) {
// Restore initial health for the team
_health = _maxHealth;
_stats = _globalStats;
_nextLevel = firstLevelTerritory(_checkpoint);
cont = false;
} else if (event.customType == kActionQuit)
quitGame();
break;
default:
break;
}
}
drawScreen();
g_system->delayMillis(10);
}
keymapper->getKeymap("retry-menu")->setEnabled(false);
keymapper->getKeymap("game-shortcuts")->setEnabled(true);
menu->free();
delete menu;
}
void BoyzEngine::runAlarmC5(Code *code) {
MVideo video1("misc/alrm_c5s.smk", Common::Point(0, 0), false, true, false);
disableCursor();
runIntro(video1);
MVideo video2("preload/deathn4s.smk", Common::Point(0, 0), false, true, false);
disableCursor();
runIntro(video2);
_nextLevel = "<check_c5>";
}
void BoyzEngine::runCheckC5(Code *code) {
if (_sceneState["GS_C5MAP"]) {
if (!_sceneState["GS_MINEMAP_VIEWED"]) {
MVideo video("c5/c5_maps.smk", Common::Point(0, 0), false, true, false);
disableCursor();
runIntro(video);
defaultCursor();
waitForUserClick(1);
_sceneState["GS_MINEMAP_VIEWED"] = true;
}
}
Common::String nextLevel;
if (_sceneState["GS_SEQ_51"] &&
_sceneState["GS_SEQ_52"] &&\
_sceneState["GS_SEQ_53"]) {
MVideo video("c5/c5intrbs.smk", Common::Point(0, 0), false, true, false);
disableCursor();
runIntro(video);
nextLevel = "c54.mi_";
}
if (nextLevel.empty())
nextLevel = "<select_c5>";
_nextLevel = nextLevel;
saveProfile(_name, 531);
}
void BoyzEngine::runCheckC3(Code *code) {
Common::String nextLevel;
if (_sceneState["GS_SEQ_31"] && _sceneState["GS_SEQ_32"] &&\
_sceneState["GS_SEQ_33"] && _sceneState["GS_SEQ_34"] &&\
_sceneState["GS_HOTELDONE"]) {
nextLevel = "c36.mi_";
}
if (nextLevel.empty())
nextLevel = "<select_c3>";
_nextLevel = nextLevel;
saveProfile(_name, 3591);
}
void BoyzEngine::runCheckHo(Code *code) {
Common::String nextLevel;
if (_sceneState["GS_SEQ_351"] && _sceneState["GS_SEQ_352"] &&\
_sceneState["GS_SEQ_353"] && _sceneState["GS_SEQ_354"] &&\
_sceneState["GS_SEQ_355"]) {
_sceneState["GS_HOTELDONE"] = 1;
nextLevel = "<check_c3>";
}
if (nextLevel.empty())
nextLevel = "<select_ho>";
_nextLevel = nextLevel;
saveProfile(_name, 3592);
}
void BoyzEngine::endCredits(Code *code) {
_flashbackMode = true;
saveProfile(_name, 59);
showCredits();
_nextLevel = "<select_t1>";
}
void BoyzEngine::showCredits() {
MVideo c1("intro/sbcred1.smk", Common::Point(0, 0), false, true, false);
runIntro(c1);
MVideo c2("intro/sbcred2.smk", Common::Point(0, 0), false, true, false);
runIntro(c2);
}
int BoyzEngine::getTerritory(const Common::String &level) {
if (Common::matchString(level.c_str(), "c1#.mi_"))
return 1;
else if (Common::matchString(level.c_str(), "c2#.mi_"))
return 2;
else if (Common::matchString(level.c_str(), "c3#.mi_"))
return 3;
else if (Common::matchString(level.c_str(), "c3##.mi_"))
return 3;
else if (Common::matchString(level.c_str(), "c4#.mi_"))
return 4;
else if (Common::matchString(level.c_str(), "c5#.mi_"))
return 5;
else
error("Invalid territory for level %s", level.c_str());
}
Common::String BoyzEngine::firstLevelTerritory(const Common::String &level) {
if (Common::matchString(level.c_str(), "c1#.mi_"))
return "c19.mi_";
else if (Common::matchString(level.c_str(), "c2#.mi_"))
return "c21.mi_";
else if (Common::matchString(level.c_str(), "c3#.mi_"))
return "c31.mi_";
else if (Common::matchString(level.c_str(), "c3##.mi_"))
return "c31.mi_";
else if (Common::matchString(level.c_str(), "c4#.mi_"))
return "c41.mi_";
else if (Common::matchString(level.c_str(), "c5#.mi_"))
return "c51.mi_";
else
error("Invalid territory for level %s", level.c_str());
}
Common::String BoyzEngine::lastLevelTerritory(const Common::String &level) {
if (Common::matchString(level.c_str(), "c1#.mi_"))
return "c18.mi_";
else if (Common::matchString(level.c_str(), "c2#.mi_"))
return "c22.mi_";
else if (Common::matchString(level.c_str(), "c3#.mi_"))
return "c38.mi_";
else if (Common::matchString(level.c_str(), "c3##.mi_"))
return "c38.mi_";
else if (Common::matchString(level.c_str(), "c4#.mi_"))
return "c42.mi_";
else if (Common::matchString(level.c_str(), "c5#.mi_"))
return "c59.mi_";
else
error("Invalid territory for level %s", level.c_str());
}
} // End of namespace Hypno

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/>.
*
*/
#include "common/events.h"
#include "hypno/hypno.h"
namespace Hypno {
const char *sceneVariablesBoyz[] = {
"GS_NONE",
"GS_SCTEXT",
"GS_AMBIENT",
"GS_MUSIC",
"GS_VOLUME",
"GS_LEVELCOMPLETE",
"GS_LEVELWON",
"GS_HOLDMOUSE",
"GS_DIFFICULTY",
"GS_TERRITORY",
"GS_SECTOR",
"GS_HITPOINTS",
"GS_TERRITORY1_RAND",
"GS_C5MAP",
"GS_WONSHELLGAME",
"GS_C36_READY",
"GS_MINEMAP",
"GS_MINEMAP_VIEWED",
"GS_HOTELDONE",
"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_SEQ_11",
"GS_SEQ_12",
"GS_SEQ_13",
"GS_SEQ_14",
"GS_SEQ_15",
"GS_SEQ_16",
"GS_SEQ_17",
"GS_SEQ_18",
"GS_SEQ_19",
"GS_SEQ_21",
"GS_SEQ_22",
"GS_SEQ_31",
"GS_SEQ_32",
"GS_SEQ_33",
"GS_SEQ_34",
"GS_SEQ_35",
"GS_SEQ_351",
"GS_SEQ_352",
"GS_SEQ_353",
"GS_SEQ_354",
"GS_SEQ_355",
"GS_SEQ_36",
"GS_SEQ_41",
"GS_SEQ_42",
"GS_SEQ_51",
"GS_SEQ_52",
"GS_SEQ_53",
"GS_SEQ_54",
"GS_SEQ_55",
"GS_SEQ_56",
"GS_SEQ_57",
"GS_SEQ_58",
"GS_SEQ_59",
nullptr
};
void BoyzEngine::resetSceneState() {
uint32 i = 0;
while (sceneVariablesBoyz[i]) {
_sceneState[sceneVariablesBoyz[i]] = 0;
i++;
}
_intros.clear();
}
void BoyzEngine::loadSceneState(Common::SeekableReadStream *stream) {
uint32 i = 0;
while (sceneVariablesBoyz[i]) {
_sceneState[sceneVariablesBoyz[i]] = stream->readUint32LE();
i++;
}
}
void BoyzEngine::saveSceneState(Common::WriteStream *stream) {
uint32 i = 0;
while (sceneVariablesBoyz[i]) {
stream->writeUint32LE(_sceneState[sceneVariablesBoyz[i]]);
i++;
}
}
void BoyzEngine::unlockAllLevels() {
uint32 i = 0;
while (sceneVariablesBoyz[i]) {
if (Common::String(sceneVariablesBoyz[i]).hasPrefix("GS_SEQ_"))
_sceneState[sceneVariablesBoyz[i]] = true;
i++;
}
}
void BoyzEngine::runMenu(Hotspots *hs, bool only_menu) {
Hotspot *h = hs->begin();
assert(h->type == MakeMenu);
if (!h->background.empty()) {
loadImage(h->background, 0, 0, false, true, 1);
if (h->backgroundFrames.empty()) {
h->backgroundFrames = decodeFrames(h->background);
}
}
renderHighlights(hs);
}
void BoyzEngine::renderHighlights(Hotspots *hs) {
Hotspot *menu = hs->begin();
if (menu->type != MakeMenu || menu->background.empty())
return;
for (Hotspots::const_iterator it = hs->begin(); it != hs->end(); ++it) {
if (it->type == MakeMenu)
continue;
Highlight *hl;
for (Actions::const_iterator itt = it->actions.begin(); itt != it->actions.end(); ++itt) {
Action *action = *itt;
switch (action->type) {
case HighlightAction:
hl = (Highlight *)action;
assert(_sceneState.contains(hl->condition));
if (_sceneState[hl->condition]) {
Graphics::Surface sub = menu->backgroundFrames[0]->getSubArea(it->rect);
drawImage(sub, it->rect.left, it->rect.top, false);
}
break;
default:
break;
}
}
}
}
bool BoyzEngine::hoverHotspot(Common::Point mousePos) {
if (_rnd->getRandomBit())
return false; // Dirty trick to avoid updating the screen too often
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) {
Hotspot *menu = hots->begin();
if (menu->type == MakeMenu && !menu->background.empty()) { // Hihghlight
Graphics::Surface sub = menu->backgroundFrames[2]->getSubArea(selected.rect);
drawImage(*menu->backgroundFrames[1], 0, 0, false);
renderHighlights(hots);
drawImage(sub, selected.rect.left, selected.rect.top, false);
drawScreen();
}
return true;
}
return false;
}
} // End of namespace Hypno

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 hypno "Hypnotix Inc." yes "" "" ""

3
engines/hypno/credits.pl Normal file
View File

@@ -0,0 +1,3 @@
begin_section("Hypno");
add_person("Gustavo Grieco", "neuromancer", "");
end_section();

193
engines/hypno/cursors.cpp Normal file
View File

@@ -0,0 +1,193 @@
/* 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/rect.h"
#include "graphics/cursorman.h"
#include "hypno/hypno.h"
namespace Hypno {
static const byte MOUSECURSOR_SCI[] = {
1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0,
1, 3, 3, 1, 0, 0, 0, 0, 0, 0, 0,
1, 3, 3, 3, 1, 0, 0, 0, 0, 0, 0,
1, 3, 3, 3, 3, 1, 0, 0, 0, 0, 0,
1, 3, 3, 3, 3, 3, 1, 0, 0, 0, 0,
1, 3, 3, 3, 3, 3, 3, 1, 0, 0, 0,
1, 3, 3, 3, 3, 3, 3, 3, 1, 0, 0,
1, 3, 3, 3, 3, 3, 3, 3, 3, 1, 0,
1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1,
1, 3, 3, 3, 3, 3, 1, 0, 0, 0, 0,
1, 3, 1, 0, 1, 3, 3, 1, 0, 0, 0,
1, 1, 0, 0, 1, 3, 3, 1, 0, 0, 0,
0, 0, 0, 0, 0, 1, 3, 3, 1, 0, 0,
0, 0, 0, 0, 0, 1, 3, 3, 1, 0, 0,
0, 0, 0, 0, 0, 0, 1, 3, 3, 1, 0};
static const byte circleCursor[] = {
0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0,
0, 0, 2, 2, 0, 0, 0, 0, 0, 2, 2, 0, 0,
0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0,
0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0,
2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0,
0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0,
0, 0, 2, 2, 0, 0, 0, 0, 0, 2, 2, 0, 0,
0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0};
static const byte targetCursor[] = {
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
0, 0, 1, 0, 0, 2, 2, 2, 2, 2, 0, 0, 1, 0, 0,
0, 1, 0, 2, 2, 0, 0, 0, 0, 0, 2, 2, 0, 1, 0,
1, 0, 2, 0, 0, 1, 1, 1, 1, 1, 0, 0, 2, 0, 1,
1, 0, 2, 0, 1, 0, 0, 0, 0, 0, 1, 0, 2, 0, 1,
1, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 1,
1, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 1,
1, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 1,
1, 0, 2, 0, 1, 0, 0, 0, 0, 0, 1, 0, 2, 0, 1,
1, 0, 2, 0, 0, 1, 1, 1, 1, 1, 0, 0, 2, 0, 1,
0, 1, 0, 2, 2, 0, 0, 0, 0, 0, 2, 2, 0, 1, 0,
0, 0, 1, 0, 0, 2, 2, 2, 2, 2, 0, 0, 1, 0, 0,
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0};
static const byte crosshairCursor[] = {
0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0,
0, 0, 0, 2, 2, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0,
0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0,
0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0,
0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0,
2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2,
0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0,
0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0,
0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0,
0, 0, 0, 2, 2, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0,
0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0};
static const byte cursorPalette[] = {
0x00, 0x00, 0x00, // Transparent
0x00, 0x00, 0xff, // Blue
0xff, 0x00, 0x00, // Red
0xff, 0xff, 0xff // White
};
static const byte sciCursorPalette[] = {
0x00, 0x00, 0x00, // Transparent
0xff, 0xff, 0xff, // Black
0xff, 0x00, 0x00, // Red
0xff, 0xff, 0xff // White
};
struct CursorTable {
const char *name;
const void *buf;
int w;
int h;
int hotspotX;
int hotspotY;
};
static const CursorTable cursorTable[] = {
{"default", MOUSECURSOR_SCI, 11, 16, 0, 0},
{"arcade", circleCursor, 13, 11, 7, 5},
{"target", targetCursor, 15, 13, 8, 6},
{"crosshair", crosshairCursor, 15, 13, 8, 6},
{nullptr, nullptr, 0, 0, 0, 0}};
void HypnoEngine::disableCursor() {
CursorMan.showMouse(false);
}
void HypnoEngine::defaultCursor() {
if (!_defaultCursor.empty()) {
if (_defaultCursorIdx == uint32(-1))
changeCursor(_defaultCursor);
else
changeCursor(_defaultCursor, _defaultCursorIdx);
} else
changeCursor("default");
}
void HypnoEngine::changeCursor(const Common::String &cursor) {
const CursorTable *entry = cursorTable;
while (entry->name) {
if (cursor == entry->name)
break;
entry++;
}
assert(entry->name);
if (cursor == "default")
CursorMan.replaceCursorPalette(sciCursorPalette, 0, 3);
else
CursorMan.replaceCursorPalette(cursorPalette, 0, 3);
CursorMan.replaceCursor(entry->buf, entry->w, entry->h, entry->hotspotX, entry->hotspotY, 0);
CursorMan.showMouse(true);
}
Graphics::Surface *CursorCache::getCursor(const Common::String &cursor, uint32 n, byte **palette) {
if (cursor == _filename && n == _frame) {
*palette = _palette;
return _surface;
}
free(_palette);
_palette = nullptr;
if (_surface) {
_surface->free();
delete _surface;
_surface = nullptr;
}
_filename = cursor;
_frame = n;
_surface = _vm->decodeFrame(cursor, n, &_palette);
*palette = _palette;
return _surface;
}
void HypnoEngine::changeCursor(const Common::String &cursor, uint32 n, bool centerCursor) {
byte *palette;
Graphics::Surface *entry = _cursorCache->getCursor(cursor, n, &palette);
uint32 hotspotX = centerCursor ? entry->w / 2 : 0;
uint32 hotspotY = centerCursor ? entry->h / 2 : 0;
CursorMan.replaceCursor(*entry, hotspotX, hotspotY, 0, false);
CursorMan.replaceCursorPalette(palette, 0, 256);
CursorMan.showMouse(true);
}
void HypnoEngine::changeCursor(const Graphics::Surface &entry, byte *palette, bool centerCursor) {
uint32 hotspotX = centerCursor ? entry.w / 2 : 0;
uint32 hotspotY = centerCursor ? entry.h / 2 : 0;
CursorMan.replaceCursor(entry, hotspotX, hotspotY, 0, false);
CursorMan.replaceCursorPalette(palette, 0, 256);
CursorMan.showMouse(true);
}
} // End of namespace Hypno

322
engines/hypno/detection.cpp Normal file
View File

@@ -0,0 +1,322 @@
/* 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 "base/plugins.h"
#include "engines/advancedDetector.h"
#include "hypno/hypno.h"
#include "hypno/detection.h"
static const DebugChannelDef debugFlagList[] = {
{Hypno::kHypnoDebugMedia, "media", "Media debug channel"},
{Hypno::kHypnoDebugParser, "parser", "Parser debug channel"},
{Hypno::kHypnoDebugScene, "scene", "Scene debug channel"},
{Hypno::kHypnoDebugArcade, "arcade", "Arcade debug channel"},
DEBUG_CHANNEL_END};
namespace Hypno {
static const PlainGameDescriptor hypnoGames[] = {
{"sinistersix", "Marvel Comics Spider-Man: The Sinister Six"},
{"wetlands", "Wetlands"},
{"soldierboyz", "Soldier Boyz"},
{"teacher", "Bruce Coville's My Teacher Is an Alien"},
{nullptr, nullptr}};
static const ADGameDescription gameDescriptions[] = {
{
"sinistersix", // Demo from the US release
"Demo",
AD_ENTRY2s("DATA.Z", "2a9c7cf8920ec794482f0a5873102da5", 1285960,
"DCINE1.SMK", "1ff3db09d148e8dd8b56d2e87e7296b8", 493752),
Common::EN_USA,
Common::kPlatformDOS,
ADGF_DEMO,
GUIO1(GUIO_NOMIDI)
},
{
"sinistersix", // US release
nullptr,
AD_ENTRY2s("DATA.Z", "a1f71005a45e6ee454bb0bf3868dff54", 8766307,
"MISSIONS.LIB", "585704e26094cbaf14fbee90798e8d5d", 119945),
Common::EN_USA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOMIDI)
},
{
"sinistersix", // ES release
nullptr,
AD_ENTRY2s("SPIDER.EXE", "dbd912d6f6724c6d44775fc19cfa8ca0", 483871,
"MISSIONS.LIB", "585704e26094cbaf14fbee90798e8d5d", 119945),
Common::ES_ESP,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOMIDI)
},
{
"sinistersix", // DE release
nullptr,
AD_ENTRY2s("Setup1.Sax", "86b6ae45f45a8273ef3116be6bac01f5", 9591164,
"MISSIONS.LIB", "585704e26094cbaf14fbee90798e8d5d", 119945),
Common::DE_DEU,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOMIDI)
},
{
"sinistersix", // IT release
nullptr,
AD_ENTRY2s("DATA.Z", "8e1aa1ab39e38c4f1bf67c0b330b3991", 8740866,
"MISSIONS.LIB", "585704e26094cbaf14fbee90798e8d5d", 119945),
Common::IT_ITA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOMIDI)
},
{
"sinistersix", // HE release (CD, Installed)
nullptr,
AD_ENTRY2s("SPIDER.EXE", "dbd912d6f6724c6d44775fc19cfa8ca0", 483359,
"MISSIONS.LIB", "585704e26094cbaf14fbee90798e8d5d", 119945),
Common::HE_ISR,
Common::kPlatformDOS,
ADGF_UNSTABLE,
GUIO1(GUIO_NOMIDI)
},
{
"sinistersix", // HE release (CD, Not Installed)
nullptr,
AD_ENTRY2s("DATA.Z", "5068f15089ac05556c2f3f37e06c4f32", 8921748,
"MISSIONS.LIB", "585704e26094cbaf14fbee90798e8d5d", 119945),
Common::HE_ISR,
Common::kPlatformDOS,
ADGF_UNSTABLE,
GUIO1(GUIO_NOMIDI)
},
{
"wetlands", // Wetlands Demo PC Spiel (October 1995)
"EarlyDemo",
AD_ENTRY2s("wetlands.exe", "edc5b0c0caf3d5b01d344cb555d9a085", 641411,
"c61.mis", "11e384b3abe0f42995bb61566d877e45", 18497),
Common::EN_USA,
Common::kPlatformDOS,
ADGF_DEMO,
GUIO1(GUIO_NOMIDI)
},
{
"wetlands", // Wetlands Demo Disc (November 1995)
"Demo",
AD_ENTRY3s("wetlands.exe", "15a6b1b3819ef002438df340509b5373", 642231,
"wetdemo.exe", "15a6b1b3819ef002438df340509b5373", 458319,
"demo.exe", "15a6b1b3819ef002438df340509b5373", 533221),
Common::EN_USA,
Common::kPlatformDOS,
ADGF_DEMO,
GUIO1(GUIO_NOMIDI)
},
{
"wetlands", // Wetlands Demo from a Hebrew magazine
"DemoHebrew",
AD_ENTRY3s("wetlands.exe", "15a6b1b3819ef002438df340509b5373", 629503,
"wetdemo.exe", "15a6b1b3819ef002438df340509b5373", 458319,
"demo.exe", "15a6b1b3819ef002438df340509b5373", 533221),
Common::EN_USA,
Common::kPlatformDOS,
ADGF_DEMO,
GUIO1(GUIO_NOMIDI)
},
{
"wetlands", // Personal Computer World (UK) (May 1996) - Chapter 11 demo
"PCWDemo",
AD_ENTRY2s("wetlands.exe", "15a6b1b3819ef002438df340509b5373", 553355,
"missions.lib", "6ffa658f22a00b6e17d7f920fcc13578", 12469),
Common::EN_USA,
Common::kPlatformDOS,
ADGF_DEMO,
GUIO1(GUIO_NOMIDI)
},
{
"wetlands", // PC Gamer Disc 12 (November 1995) - Chapter 31 demo
"PCGDemo",
AD_ENTRY2s("wetlands.exe", "15a6b1b3819ef002438df340509b5373", 553355,
"missions.lib", "34b922fac8f64546c0690aa83f09e98e", 40891),
Common::EN_USA,
Common::kPlatformDOS,
ADGF_DEMO,
GUIO1(GUIO_NOMIDI)
},
{
"wetlands", // Génération 4 (FR) - Number 81 (October 1995) - Chapters 31/52 demo
"Gen4",
AD_ENTRY2s("wetlands.exe", "15a6b1b3819ef002438df340509b5373", 629503,
"missions.lib", "34b922fac8f64546c0690aa83f09e98e", 40891),
Common::EN_USA,
Common::kPlatformDOS,
ADGF_DEMO,
GUIO1(GUIO_NOMIDI)
},
{
"wetlands", // Might and Magic Trilogy CD (November 1995) - Chapters 31/52 demo
"M&MCD",
AD_ENTRY2s("wetlands.exe", "15a6b1b3819ef002438df340509b5373", 642231,
"missions.lib", "7e3e5b23ade5ef0df88e9d31f5d669e6", 10188),
Common::EN_USA,
Common::kPlatformDOS,
ADGF_DEMO,
GUIO1(GUIO_NOMIDI)
},
{
"wetlands", // Non Interactive: PC Review 49 (November 1995)
"NonInteractive",
AD_ENTRY2s("playsmks.exe", "edc5b0c0caf3d5b01d344cb555d9a085", 422607,
"wetmusic.81m", "0d99c63ce19633d09569b1fdcdff1505", 2833439),
Common::EN_USA,
Common::kPlatformDOS,
ADGF_DEMO,
GUIO1(GUIO_NOMIDI)
},
{
"wetlands", // Non Interactive: Joystick HS 7 (September 1995)
"NonInteractiveJoystick",
AD_ENTRY2s("playsmks.exe", "edc5b0c0caf3d5b01d344cb555d9a085", 422607,
"c44_22k.raw", "4b2279af59ce3049cc5177b0047e8447", 5247618),
Common::EN_USA,
Common::kPlatformDOS,
ADGF_DEMO,
GUIO1(GUIO_NOMIDI)
},
{
"wetlands", // Wetlands (US)
nullptr,
AD_ENTRY2s("wetlands.exe", "15a6b1b3819ef002438df340509b5373", 647447,
"missions.lib", "aeaaa8b26ab17e37f060334a311a3ff6", 309793),
Common::EN_USA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOMIDI)
},
{
"wetlands", // Wetlands 1.1 (US)
nullptr,
AD_ENTRY2s("wetlands.exe", "15a6b1b3819ef002438df340509b5373", 647411,
"missions.lib", "aeaaa8b26ab17e37f060334a311a3ff6", 309793),
Common::EN_USA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOMIDI)
},
{
"wetlands", // Wetlands (FR)
nullptr,
AD_ENTRY2s("wetlands.exe", "edc5b0c0caf3d5b01d344cb555d9a085", 629575,
"missions.lib", "aeaaa8b26ab17e37f060334a311a3ff6", 309793),
Common::FR_FRA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOMIDI)
},
{
"wetlands", // Wetlands (ES)
nullptr,
AD_ENTRY2s("wetlands.exe", "8d0f3630523da827bb25e665b7d3f879", 644055,
"missions.lib", "aeaaa8b26ab17e37f060334a311a3ff6", 309793),
Common::ES_ESP,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOMIDI)
},
{
"wetlands", // Wetlands (KO)
nullptr,
AD_ENTRY2s("wetlands.exe", "edc5b0c0caf3d5b01d344cb555d9a085", 360151,
"missions.lib", "aeaaa8b26ab17e37f060334a311a3ff6", 309793),
Common::KO_KOR,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOMIDI)
},
{
"soldierboyz", // Solidier Boyz (US)
nullptr,
AD_ENTRY2s("boyz.exe", "bac1d734f2606dbdd0816dfa7a5cf518", 263347,
"setup.exe", "bac1d734f2606dbdd0816dfa7a5cf518", 160740),
Common::EN_USA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOMIDI)
},
{
"teacher", // Bruce Coville's My Teacher Is an Alien Demo - PC Collector 10 (July 1997)
"Demo",
AD_ENTRY2s("teacher.exe", "7650ab104a21e2ca33a1d0d54a51e9d1", 258560,
"demomenu.smk", "abb06755ff1d345b11b0f2c2d42e5dc7", 2424),
Common::EN_USA,
Common::kPlatformWindows,
ADGF_UNSTABLE | ADGF_DEMO,
GUIO1(GUIO_NOMIDI)
},
AD_TABLE_END_MARKER
};
} // End of namespace Hypno
static const char *const directoryGlobs[] = {
"boyz",
"spider",
"wetlands",
"sixdemo",
"demo",
"factory",
"movie",
"c_misc",
"data",
"demo",
nullptr
};
class HypnoMetaEngineDetection : public AdvancedMetaEngineDetection<ADGameDescription> {
public:
HypnoMetaEngineDetection() : AdvancedMetaEngineDetection(Hypno::gameDescriptions, Hypno::hypnoGames) {
_guiOptions = GUIO6(GUIO_NOMIDI, GAMEOPTION_ORIGINAL_CHEATS, GAMEOPTION_INFINITE_HEALTH, GAMEOPTION_INFINITE_AMMO, GAMEOPTION_UNLOCK_ALL_LEVELS, GAMEOPTION_RESTORED_CONTENT);
_maxScanDepth = 3;
_directoryGlobs = directoryGlobs;
}
const char *getName() const override {
return "hypno";
}
const char *getEngineName() const override {
return "Hypno";
}
const char *getOriginalCopyright() const override {
return "Marvel Comics Spider-Man: The Sinister Six (C) Brooklyn Multimedia\n"
"Wetlands (C) Hypnotix, Inc.\n"
"Soldier Boyz (C) Hypnotix, Inc., Motion Picture Corporation of America Interactive";
}
const DebugChannelDef *getDebugChannels() const override {
return debugFlagList;
}
};
REGISTER_PLUGIN_STATIC(HYPNO_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, HypnoMetaEngineDetection);

31
engines/hypno/detection.h Normal file
View File

@@ -0,0 +1,31 @@
/* 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 HYPNO_DETECTION_H
#define HYPNO_DETECTION_H
#define GAMEOPTION_ORIGINAL_CHEATS GUIO_GAMEOPTIONS1
#define GAMEOPTION_INFINITE_HEALTH GUIO_GAMEOPTIONS2
#define GAMEOPTION_INFINITE_AMMO GUIO_GAMEOPTIONS3
#define GAMEOPTION_UNLOCK_ALL_LEVELS GUIO_GAMEOPTIONS4
#define GAMEOPTION_RESTORED_CONTENT GUIO_GAMEOPTIONS5
#endif

770
engines/hypno/grammar.h Normal file
View File

@@ -0,0 +1,770 @@
/* 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 HYPNO_GRAMMAR_H
#define HYPNO_GRAMMAR_H
#include "common/array.h"
#include "common/hash-ptr.h"
#include "common/hash-str.h"
#include "common/list.h"
#include "common/queue.h"
#include "common/rect.h"
#include "common/str.h"
#include "video/smk_decoder.h"
namespace Hypno {
typedef Common::String Filename;
typedef Common::List<Filename> Filenames;
class HypnoSmackerDecoder : public Video::SmackerDecoder {
public:
bool loadStream(Common::SeekableReadStream *stream) override;
protected:
uint32 getSignatureVersion(uint32 signature) const override;
};
class MVideo {
public:
MVideo(Filename, Common::Point, bool, bool, bool);
Filename path;
Common::Point position;
bool scaled;
bool transparent;
bool loop;
HypnoSmackerDecoder *decoder;
};
typedef Common::Array<MVideo> Videos;
enum HotspotType {
MakeMenu,
MakeHotspot
};
enum ActionType {
MiceAction,
TimerAction,
PaletteAction,
BackgroundAction,
HighlightAction,
OverlayAction,
EscapeAction,
SaveAction,
LoadAction,
LoadCheckpointAction,
QuitAction,
CutsceneAction,
PlayAction,
IntroAction,
AmbientAction,
WalNAction,
GlobalAction,
TalkAction,
SwapPointerAction,
SoundAction,
ChangeLevelAction
};
class Action {
public:
virtual ~Action() {} // needed to make Action polymorphic
ActionType type;
};
typedef Common::Array<Action *> Actions;
class Hotspot;
typedef Common::Array<Hotspot> Hotspots;
typedef Common::Array<Hotspots *> HotspotsStack;
typedef Common::Array<Graphics::Surface *> Frames;
class Hotspot {
public:
Hotspot(HotspotType type_, Common::Rect rect_ = Common::Rect(0, 0, 0, 0)) {
type = type_;
rect = rect_;
smenu = nullptr;
}
HotspotType type;
Common::String flags[3];
Common::Rect rect;
Common::String setting;
Filename background;
Frames backgroundFrames;
Actions actions;
Hotspots *smenu;
};
class Mice : public Action {
public:
Mice(Filename path_, uint32 index_) {
type = MiceAction;
path = path_;
index = index_;
}
Filename path;
uint32 index;
};
class SwapPointer : public Action {
public:
SwapPointer(uint32 index_) {
type = SwapPointerAction;
index = index_;
}
uint32 index;
};
class Timer : public Action {
public:
Timer(uint32 delay_, Common::String flag_) {
type = TimerAction;
delay = delay_;
flag = flag_;
}
uint32 delay;
Common::String flag;
};
class Palette : public Action {
public:
Palette(Filename path_) {
type = PaletteAction;
path = path_;
}
Filename path;
};
class Highlight : public Action {
public:
Highlight(Common::String condition_) {
type = HighlightAction;
condition = condition_;
}
Common::String condition;
};
class Background : public Action {
public:
Background(Filename path_, Common::Point origin_, Common::String condition_, Common::String flag1_, Common::String flag2_) {
type = BackgroundAction;
path = path_;
origin = origin_;
condition = condition_;
flag1 = flag1_;
flag2 = flag2_;
}
Filename path;
Common::Point origin;
Common::String condition;
Common::String flag1;
Common::String flag2;
};
class Overlay : public Action {
public:
Overlay(Filename path_, Common::Point origin_, Common::String flag_) {
type = OverlayAction;
path = path_;
origin = origin_;
flag = flag_;
}
Filename path;
Common::Point origin;
Common::String flag;
};
class Escape : public Action {
public:
Escape() {
type = EscapeAction;
}
};
class Save : public Action {
public:
Save() {
type = SaveAction;
}
};
class Load : public Action {
public:
Load() {
type = LoadAction;
}
};
class LoadCheckpoint : public Action {
public:
LoadCheckpoint() {
type = LoadCheckpointAction;
}
};
class Quit : public Action {
public:
Quit() {
type = QuitAction;
}
};
class Cutscene : public Action {
public:
Cutscene(Filename path_) {
type = CutsceneAction;
path = path_;
}
Filename path;
};
class Sound : public Action {
public:
Sound(Filename path_) {
type = SoundAction;
path = path_;
}
Filename path;
};
class Intro : public Action {
public:
Intro(Filename path_) {
type = IntroAction;
path = path_;
}
Filename path;
};
class Play : public Action {
public:
Play(Filename path_, Common::Point origin_, Common::String condition_, Common::String flag_) {
type = PlayAction;
path = path_;
origin = origin_;
condition = condition_;
flag = flag_;
}
Filename path;
Common::Point origin;
Common::String condition;
Common::String flag;
};
class Ambient : public Action {
public:
Ambient(Filename path_, Common::Point origin_, Common::String flag_) {
type = AmbientAction;
path = path_;
origin = origin_;
flag = flag_;
fullscreen = false;
frameNumber = 0;
}
Filename path;
Common::Point origin;
Common::String flag;
uint32 frameNumber;
bool fullscreen;
};
class WalN : public Action {
public:
WalN(Common::String wn_, Filename path_, Common::Point origin_, Common::String condition_, Common::String flag_) {
wn = wn_;
type = WalNAction;
path = path_;
origin = origin_;
condition = condition_;
flag = flag_;
}
Common::String wn;
Filename path;
Common::Point origin;
Common::String condition;
Common::String flag;
};
class Global : public Action {
public:
Global(Common::String variable_, Common::String command_) {
type = GlobalAction;
variable = variable_;
command = command_;
}
Common::String variable;
Common::String command;
};
class TalkCommand {
public:
Common::String command;
Common::String variable;
Filename path;
uint32 num;
Common::Point position;
};
typedef Common::Array<TalkCommand> TalkCommands;
class Talk : public Action {
public:
Talk() {
type = TalkAction;
boxPos = Common::Point(0, 0);
escape = false;
active = true;
}
Talk(Talk *t) {
*this = *t;
}
TalkCommands commands;
bool active;
bool escape;
Common::Point introPos;
Filename intro;
Common::Point boxPos;
Filename background;
Common::Point backgroundPos;
Common::Rect rect;
Filename second;
Common::Point secondPos;
};
class ChangeLevel : public Action {
public:
ChangeLevel(Filename level_) {
type = ChangeLevelAction;
level = level_;
}
Filename level;
};
enum LevelType {
TransitionLevel,
SceneLevel,
ArcadeLevel,
CodeLevel
};
class Level {
public:
Level() {
type = CodeLevel;
musicRate = 22050;
playMusicDuringIntro = false;
musicStereo = false;
}
virtual ~Level() {} // needed to make Level polymorphic
LevelType type;
Filenames intros;
Filename prefix;
Filename levelIfWin;
Filename levelIfLose;
bool playMusicDuringIntro;
Filename music;
uint32 musicRate;
bool musicStereo;
};
class Scene : public Level {
public:
Scene() {
type = SceneLevel;
resolution = "640x480";
}
Common::String resolution;
Hotspots hots;
};
class FrameInfo {
public:
FrameInfo(uint32 start_, uint32 length_) {
start = start_;
length = length_;
}
uint32 lastFrame() {
return start + length;
}
uint32 start;
uint32 length;
};
enum ScriptMode {
Interactive = 1,
NonInteractive,
};
class ScriptInfo {
public:
ScriptInfo(uint32 time_, uint32 mode_, uint32 actor_, uint32 cursor_) {
time = time_;
mode = ScriptMode(mode_);
actor = actor_;
cursor = cursor_;
}
uint32 time;
ScriptMode mode;
uint32 actor;
uint32 cursor;
};
typedef Common::List<ScriptInfo> Script;
class Shoot {
public:
Shoot() {
destroyed = false;
video = nullptr;
timesToShoot = 1;
pointsToShoot = 0;
attackWeight = 0;
paletteOffset = 0;
paletteSize = 0;
missedAnimation = 0;
objKillsCount = 0;
objMissesCount = 0;
animation = "NONE";
explosionAnimation = "";
startFrame = 0;
lastFrame = 1024;
interactionFrame = 0;
noEnemySound = false;
enemySoundRate = 22050;
isAnimal = false;
nonHostile = false;
playInteractionAudio = false;
animalSound = "";
jumpToTimeAfterKilled = 0;
warningVideoIdx = 0;
waitForClickAfterInteraction = 0;
direction = 0;
}
Common::String name;
Filename animation;
Filename startSound;
Common::Point position;
Common::Point deathPosition;
uint32 timesToShoot;
uint32 pointsToShoot;
uint32 attackWeight;
// Objectives
uint32 objKillsCount;
uint32 objMissesCount;
// Palette
uint32 paletteOffset;
uint32 paletteSize;
// Missed animation
uint32 missedAnimation;
// Sounds
Filename enemySound;
uint32 enemySoundRate;
Filename deathSound;
Filename hitSound;
Filename animalSound;
MVideo *video;
Common::List<uint32> attackFrames;
Common::Array<FrameInfo> bodyFrames;
Common::Array<FrameInfo> explosionFrames;
uint32 startFrame;
uint32 lastFrame;
uint32 interactionFrame;
Filename explosionAnimation;
Filename additionalVideo;
bool playInteractionAudio;
bool destroyed;
bool noEnemySound;
// Soldier Boyz specific
bool nonHostile;
bool isAnimal;
Common::String checkIfDestroyed;
int jumpToTimeAfterKilled;
char direction;
uint32 waitForClickAfterInteraction;
uint32 warningVideoIdx;
};
typedef Common::Array<Shoot> Shoots;
class ShootInfo {
public:
Common::String name;
uint32 timestamp;
};
typedef Common::List<ShootInfo> ShootSequence;
class SegmentShoots {
public:
SegmentShoots() {
segmentRepetition = 0;
}
ShootSequence shootSequence;
uint32 segmentRepetition;
};
typedef Common::Array<SegmentShoots> SegmentShootsSequence;
typedef Common::Array<Common::String> Sounds;
enum SegmentType {
Straight,
Select3,
TurnLeft3,
Straight3,
TurnRight3,
Select2,
TurnLeft2,
TurnRight2,
};
class Segment {
public:
Segment(byte type_, uint32 start_, uint32 size_) {
type = type_;
start = start_;
size = size_;
end = false;
}
byte type;
uint32 start;
uint32 size;
bool end;
};
typedef Common::Array<Segment> Segments;
class ArcadeTransition {
public:
ArcadeTransition(Filename video_, Filename palette_, Filename sound_, uint32 soundRate_, uint32 time_) {
video = video_;
palette = palette_;
sound = sound_;
soundRate = soundRate_;
soundStereo = false;
loseLevel = false;
winLevel = false;
selection = false;
jumpToTime = 0;
time = time_;
}
Filename video;
Filename palette;
Filename sound;
uint32 soundRate;
bool soundStereo;
bool loseLevel;
bool winLevel;
bool selection;
uint32 jumpToTime;
uint32 time;
};
typedef Common::List<ArcadeTransition> ArcadeTransitions;
class ArcadeShooting : public Level {
public:
ArcadeShooting() {
type = ArcadeLevel;
health = 100;
id = 0;
objKillsRequired[0] = 0;
objKillsRequired[1] = 0;
objMissesAllowed[0] = 0;
objMissesAllowed[1] = 0;
mouseBox = Common::Rect(0, 0, 320, 200);
frameDelay = 0;
targetSoundRate = 0; // TODO: unused
shootSoundRate = 0;
enemySoundRate = 0;
hitSoundRate = 0;
additionalSoundRate = 0;
noAmmoSoundRate = 0;
}
void clear() {
nextLevelVideo.clear();
postStatsVideo.clear();
backgroundVideo.clear();
transitions.clear();
maskVideo.clear();
player.clear();
shoots.clear();
intros.clear();
defeatNoEnergyFirstVideo.clear();
defeatMissBossVideo.clear();
defeatNoEnergySecondVideo.clear();
missBoss1Video.clear();
missBoss2Video.clear();
hitBoss1Video.clear();
hitBoss2Video.clear();
beforeVideo.clear();
briefingVideo.clear();
additionalVideo.clear();
additionalSound.clear();
noAmmoSound.clear();
segments.clear();
script.clear();
objKillsRequired[0] = 0;
objKillsRequired[1] = 0;
objMissesAllowed[0] = 0;
objMissesAllowed[1] = 0;
mouseBox = Common::Rect(0, 0, 320, 200);
anchor = Common::Point(0, 0);
targetSoundRate = 0;
shootSoundRate = 0;
enemySoundRate = 0;
hitSoundRate = 0;
noAmmoSoundRate = 0;
}
uint32 id;
uint32 frameDelay;
Common::String mode;
Common::Rect mouseBox;
Common::Point anchor;
ArcadeTransitions transitions;
Segments segments;
// Objectives
uint32 objKillsRequired [2];
uint32 objMissesAllowed [2];
// Script
Script script;
// Videos
Filename nextLevelVideo;
Filename postStatsVideo;
Filename defeatNoEnergyFirstVideo;
Filename defeatNoEnergySecondVideo;
Filename defeatMissBossVideo;
Filename hitBoss1Video;
Filename missBoss1Video;
Filename hitBoss2Video;
Filename missBoss2Video;
Filename beforeVideo;
Filename additionalVideo;
Filename briefingVideo;
Filename backgroundVideo;
Filename backgroundPalette;
Filename maskVideo;
Filename player;
int health;
Shoots shoots;
SegmentShootsSequence shootSequence;
// Sounds
Filename targetSound;
uint32 targetSoundRate;
Filename shootSound;
uint32 shootSoundRate;
Filename enemySound;
uint32 enemySoundRate;
Filename hitSound;
uint32 hitSoundRate;
Filename additionalSound;
uint32 additionalSoundRate;
Filename noAmmoSound;
uint32 noAmmoSoundRate;
};
class Transition : public Level {
public:
Transition(Common::String level) {
type = TransitionLevel;
nextLevel = level;
levelEasy = "";
levelHard = "";
frameNumber = 0;
frameImage = "";
}
Transition(Common::String easy, Common::String hard) {
type = TransitionLevel;
levelEasy = easy;
levelHard = hard;
frameNumber = 0;
frameImage = "";
}
Common::String nextLevel;
Common::String levelEasy;
Common::String levelHard;
Filename frameImage;
uint32 frameNumber;
};
class Code : public Level {
public:
Code(Common::String name_) {
type = CodeLevel;
name = name_;
}
Common::String name;
};
typedef Common::HashMap<Filename, Level*> Levels;
extern Hotspots *g_parsedHots;
extern ArcadeShooting *g_parsedArc;
class ArcadeStats {
public:
ArcadeStats() {
livesUsed = 0;
shootsFired = 0;
enemyHits = 0;
enemyTargets = 0;
targetsDestroyed = 0;
targetsMissed = 0;
friendliesEncountered = 0;
infoReceived = 0;
}
uint32 livesUsed;
uint32 shootsFired;
uint32 enemyHits;
uint32 enemyTargets;
uint32 targetsDestroyed;
uint32 targetsMissed;
uint32 friendliesEncountered;
uint32 infoReceived;
};
} // End of namespace Hypno
#endif

File diff suppressed because it is too large Load Diff

659
engines/hypno/grammar_arc.y Normal file
View File

@@ -0,0 +1,659 @@
/* 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/>.
*
*/
%require "3.0"
%defines "engines/hypno/tokens_arc.h"
%output "engines/hypno/grammar_arc.cpp"
%define api.prefix {HYPNO_ARC_}
%{
#include "common/array.h"
#include "hypno/hypno.h"
#undef yyerror
#define yyerror HYPNO_ARC_xerror
namespace Hypno {
Shoot *shoot = NULL;
}
extern int HYPNO_ARC_lex();
extern int HYPNO_ARC_lineno;
uint32 HYPNO_ARC_default_sound_rate = 0;
void HYPNO_ARC_xerror(const char *str) {
error("%s at line %d", str, HYPNO_ARC_lineno);
}
int HYPNO_ARC_wrap() {
return 1;
}
using namespace Hypno;
void parseSN(const char *sn, const char *path, const char *enc, const char *flag) {
uint32 sampleRate = 11025;
bool stereo = false;
if (Common::String("22K") == enc || Common::String("22k") == enc)
sampleRate = 22050;
else if (HYPNO_ARC_default_sound_rate > 0)
sampleRate = HYPNO_ARC_default_sound_rate;
if (Common::String("STEREO") == flag)
stereo = true;
if (Common::String("S0") == sn) {
g_parsedArc->music = path;
g_parsedArc->musicRate = sampleRate;
g_parsedArc->musicStereo = stereo;
} else if (Common::String("S1") == sn) {
g_parsedArc->shootSound = path;
g_parsedArc->shootSoundRate = sampleRate;
assert(!stereo);
} else if (Common::String("S2") == sn) {
g_parsedArc->hitSound = path;
g_parsedArc->hitSoundRate = sampleRate;
assert(!stereo);
} else if (Common::String("S4") == sn) {
g_parsedArc->enemySound = path;
g_parsedArc->enemySoundRate = sampleRate;
assert(!stereo);
} else if (Common::String("S5") == sn) {
g_parsedArc->additionalSound = path;
g_parsedArc->additionalSoundRate = sampleRate;
assert(!stereo);
} else if (Common::String("S7") == sn) {
g_parsedArc->noAmmoSound = path;
g_parsedArc->noAmmoSoundRate = sampleRate;
assert(!stereo);
} else if (Common::String("S8") == sn) {
g_parsedArc->additionalSound = path;
g_parsedArc->additionalSoundRate = sampleRate;
assert(!stereo);
}
debugC(1, kHypnoDebugParser, "SN %s", path);
}
%}
%union {
char *s; /* string value */
int i; /* integer value */
}
%token<s> NAME FILENAME BNTOK SNTOK YXTOK FNTOK ENCTOK ONTOK H12TOK
%token<i> NUM BYTE
// header
%token COMMENT ALTOK AVTOK ABTOK CTOK DTOK HTOK HETOK HLTOK HUTOK KNTOK RETTOK QTOK RESTOK
%token PTOK FTOK TTOK TATOK TPTOK TSTOK ATOK VTOK OTOK LTOK MTOK NTOK NRTOK NSTOK RTOK R0TOK R1TOK
%token ITOK I1TOK GTOK JTOK J0TOK KTOK UTOK ZTOK
// body
%token NONETOK A0TOK P0TOK WTOK
// end
%token XTOK
// bytes??
%token CB3TOK C02TOK
%type<s> enc flag
%%
start: YXTOK { g_parsedArc->mode = $1; free($1); } header ZTOK RETTOK body XTOK
| RETTOK start
;
header: hline header
| RETTOK header
| /* nothing */
;
hline: CTOK NUM {
g_parsedArc->id = $2;
HYPNO_ARC_default_sound_rate = 0;
debugC(1, kHypnoDebugParser, "C %d", $2); }
| FTOK NUM {
HYPNO_ARC_default_sound_rate = $2;
debugC(1, kHypnoDebugParser, "F %d", $2);
}
| DTOK NUM {
g_parsedArc->frameDelay = $2;
debugC(1, kHypnoDebugParser, "D %d", $2);
}
| PTOK NUM NUM { debugC(1, kHypnoDebugParser, "P %d %d", $2, $3); }
| ATOK NUM NUM { g_parsedArc->anchor = Common::Point($2, $3);
debugC(1, kHypnoDebugParser, "A %d %d", $2, $3);
}
| MTOK FILENAME {
debugC(1, kHypnoDebugParser, "M %s", $2);
g_parsedArc->maskVideo = $2;
free($2);
}
| UTOK NUM NUM NUM NUM {
debugC(1, kHypnoDebugParser, "U %d %d %d %d", $2, $3, $4, $5);
ScriptInfo si($2, $3, $4, $5);
g_parsedArc->script.push_back(si);
}
| VTOK NUM NUM {
debugC(1, kHypnoDebugParser, "V %d %d", $2, $3);
g_parsedArc->mouseBox = Common::Rect(0, 0, $2, $3);
}
| VTOK RESTOK {
debugC(1, kHypnoDebugParser, "V 320,200");
g_parsedArc->mouseBox = Common::Rect(0, 0, 320, 200);
}
| OTOK NUM NUM {
g_parsedArc->objKillsRequired[0] = $2;
g_parsedArc->objMissesAllowed[0] = $3;
debugC(1, kHypnoDebugParser, "O %d %d", $2, $3);
}
| ONTOK NUM NUM {
if (Common::String("O0") == $1) {
g_parsedArc->objKillsRequired[0] = $2;
g_parsedArc->objMissesAllowed[0] = $3;
} else if (Common::String("O1") == $1) {
g_parsedArc->objKillsRequired[1] = $2;
g_parsedArc->objMissesAllowed[1] = $3;
} else
error("Invalid objective: '%s'", $1);
debugC(1, kHypnoDebugParser, "ON %d %d", $2, $3);
free($1);
}
| ONTOK NUM {
if (Common::String("O0") == $1) {
g_parsedArc->objKillsRequired[0] = $2;
} else if (Common::String("O1") == $1) {
g_parsedArc->objKillsRequired[1] = $2;
} else
error("Invalid objective: '%s'", $1);
debugC(1, kHypnoDebugParser, "ON %d", $2);
free($1);
}
| TPTOK NONETOK NUM FILENAME {
ArcadeTransition at("NONE", $4, "", 0, $3);
g_parsedArc->transitions.push_back(at);
debugC(1, kHypnoDebugParser, "Tp %s %d %s", "NONE", $3, $4);
free($4);
}
| TSTOK FILENAME NUM NUM {
ArcadeTransition at($2, "", "", 0, $3);
at.selection = true;
g_parsedArc->transitions.push_back(at);
debugC(1, kHypnoDebugParser, "Ts %s %d %d", $2, $3, $4);
free($2);
}
| TPTOK FILENAME NUM FILENAME {
ArcadeTransition at($2, $4, "", 0, $3);
g_parsedArc->transitions.push_back(at);
debugC(1, kHypnoDebugParser, "Tp %s %d %s", $2, $3, $4);
free($2);
free($4);
}
| TATOK NUM FILENAME flag enc {
uint32 sampleRate = 11025;
bool stereo = false;
if (Common::String("22K") == $5 || Common::String("22k") == $5)
sampleRate = 22050;
if (Common::String("STEREO") == $4)
stereo = true;
ArcadeTransition at("", "", $3, sampleRate, $2);
at.soundStereo = stereo;
g_parsedArc->transitions.push_back(at);
debugC(1, kHypnoDebugParser, "Ta %d %s", $2, $3);
free($3);
free($4);
free($5);
}
| TTOK FILENAME NUM {
ArcadeTransition at($2, "", "", 0, $3);
g_parsedArc->transitions.push_back(at);
debugC(1, kHypnoDebugParser, "T %s %d", $2, $3);
free($2);
}
| TTOK NONETOK NUM {
ArcadeTransition at("NONE", "", "", 0, $3);
g_parsedArc->transitions.push_back(at);
debugC(1, kHypnoDebugParser, "T NONE %d", $3); }
| NTOK FILENAME {
g_parsedArc->backgroundVideo = $2;
debugC(1, kHypnoDebugParser, "N %s", $2);
free($2);
}
| NSTOK FILENAME {
g_parsedArc->backgroundVideo = $2;
debugC(1, kHypnoDebugParser, "N* %s", $2);
free($2);
}
| RTOK FILENAME {
g_parsedArc->backgroundPalette = $2;
debugC(1, kHypnoDebugParser, "R %s", $2);
free($2);
}
| ITOK FILENAME {
g_parsedArc->player = $2;
debugC(1, kHypnoDebugParser, "I %s", $2);
free($2);
}
| I1TOK FILENAME {
debugC(1, kHypnoDebugParser, "I1 %s", $2);
free($2);
}
| QTOK NUM NUM { debugC(1, kHypnoDebugParser, "Q %d %d", $2, $3); }
| BNTOK FILENAME {
if (Common::String("B0") == $1)
g_parsedArc->beforeVideo = $2;
else if (Common::String("B1") == $1)
g_parsedArc->additionalVideo = $2;
else if (Common::String("B2") == $1)
g_parsedArc->nextLevelVideo = $2;
else if (Common::String("B3") == $1)
g_parsedArc->defeatNoEnergyFirstVideo = $2;
else if (Common::String("B4") == $1)
g_parsedArc->defeatMissBossVideo = $2;
else if (Common::String("B5") == $1)
g_parsedArc->defeatNoEnergySecondVideo = $2;
else if (Common::String("B6") == $1)
g_parsedArc->hitBoss1Video = $2;
else if (Common::String("B7") == $1)
g_parsedArc->missBoss1Video = $2;
else if (Common::String("B8") == $1)
g_parsedArc->hitBoss2Video = $2;
else if (Common::String("B9") == $1)
g_parsedArc->missBoss2Video = $2;
else if (Common::String("BA") == $1)
g_parsedArc->briefingVideo = $2;
else if (Common::String("BB") == $1)
g_parsedArc->postStatsVideo = $2;
debugC(1, kHypnoDebugParser, "BN %s", $2);
free($1);
free($2);
}
| SNTOK FILENAME enc flag {
parseSN($1, $2, $3, $4);
free($1);
free($2);
free($3);
free($4);
}
| SNTOK FILENAME flag enc {
parseSN($1, $2, $4, $3);
free($1);
free($2);
free($3);
free($4);
}
| HETOK BYTE NUM NUM {
Segment segment($2, $4, $3);
segment.end = true;
g_parsedArc->segments.push_back(segment);
debugC(1, kHypnoDebugParser, "HE %x %d %d", $2, $3, $4);
}
| HLTOK BYTE NUM NUM {
Segment segment($2, $4, $3);
g_parsedArc->segments.push_back(segment);
debugC(1, kHypnoDebugParser, "HL %x %d %d", $2, $3, $4);
}
| HUTOK BYTE NUM NUM {
Segment segment($2, $4, $3);
g_parsedArc->segments.push_back(segment);
debugC(1, kHypnoDebugParser, "HU %x %d %d", $2, $3, $4);
}
| HTOK NAME NUM NUM {
assert(Common::String($2).size() == 1);
Segment segment($2[0], $4, $3);
g_parsedArc->segments.push_back(segment);
debugC(1, kHypnoDebugParser, "H %s %d %d", $2, $3, $4);
free($2);
}
| HTOK RTOK NUM NUM { // Workaround for BYTE == R
Segment segment('R', $4, $3);
g_parsedArc->segments.push_back(segment);
debugC(1, kHypnoDebugParser, "H R %d %d", $3, $4);
}
| HTOK ATOK NUM NUM { // Workaround for BYTE == A
Segment segment('A', $4, $3);
g_parsedArc->segments.push_back(segment);
debugC(1, kHypnoDebugParser, "H A %d %d", $3, $4);
}
| HTOK PTOK NUM NUM { // Workaround for BYTE == P
Segment segment('P', $4, $3);
g_parsedArc->segments.push_back(segment);
debugC(1, kHypnoDebugParser, "H P %d %d", $3, $4);
}
| HTOK LTOK NUM NUM { // Workaround for BYTE == P
Segment segment('L', $4, $3);
g_parsedArc->segments.push_back(segment);
debugC(1, kHypnoDebugParser, "H P %d %d", $3, $4);
}
| H12TOK BYTE NUM NUM {
Segment segment($2, $4, $3);
g_parsedArc->segments.push_back(segment);
debugC(1, kHypnoDebugParser, "HN %x %d %d", $2, $3, $4);
free($1);
}
| HTOK BYTE NUM NUM {
Segment segment($2, $4, $3);
g_parsedArc->segments.push_back(segment);
debugC(1, kHypnoDebugParser, "H %x %d %d", $2, $3, $4);
}
;
enc: ENCTOK { $$ = $1; }
| /* nothing */ { $$ = scumm_strdup(""); }
;
flag: NAME { $$ = $1; }
| /* nothing */ { $$ = scumm_strdup(""); }
;
body: bline body
| RETTOK body
| /* nothing */
;
bline: FNTOK FILENAME {
if (shoot) delete shoot;
shoot = new Shoot();
if (Common::String("F0") == $1)
shoot->animation = $2;
else if (Common::String("F4") == $1)
shoot->explosionAnimation = $2;
else if (Common::String("F6") == $1)
shoot->additionalVideo = $2;
debugC(1, kHypnoDebugParser, "FN %s", $2);
free($1);
free($2);
}
| AVTOK NUM {
assert($2 == 0);
shoot->nonHostile = true;
debugC(1, kHypnoDebugParser, "AV %d", $2);
}
| ALTOK NUM {
assert(g_parsedArc->shoots.size() > 0);
shoot->checkIfDestroyed = g_parsedArc->shoots.back().name;
debugC(1, kHypnoDebugParser, "AL %d", $2);
}
| ABTOK NUM {
assert($2 == 1);
shoot->playInteractionAudio = true;
debugC(1, kHypnoDebugParser, "AB %d", $2);
}
| DTOK LTOK {
shoot->direction = 'L';
debugC(1, kHypnoDebugParser, "D L"); }
| DTOK RTOK {
shoot->direction = 'R';
debugC(1, kHypnoDebugParser, "D R"); }
| J0TOK NUM {
assert($2 > 0);
shoot->warningVideoIdx = $2;
debugC(1, kHypnoDebugParser, "J0 %d", $2);
}
| FNTOK NONETOK {
if (shoot) delete shoot;
shoot = new Shoot();
shoot->animation = "NONE";
debugC(1, kHypnoDebugParser, "FN NONE");
free($1);
}
| FTOK FILENAME {
if (shoot) delete shoot;
shoot = new Shoot();
shoot->animation = $2;
debugC(1, kHypnoDebugParser, "FN %s", $2);
free($2);
}
| ITOK NAME {
shoot->name = $2;
debugC(1, kHypnoDebugParser, "I %s", $2);
free($2);
}
| ITOK BNTOK { // Workaround for NAME == B1
shoot->name = $2;
debugC(1, kHypnoDebugParser, "I %s", $2);
free($2);
}
| ITOK ATOK { // Workaround for NAME == A
shoot->name = "A";
debugC(1, kHypnoDebugParser, "I A");
}
| ITOK CTOK { // Workaround for NAME == C
shoot->name = "C";
debugC(1, kHypnoDebugParser, "I C");
}
| ITOK DTOK { // Workaround for NAME == D
shoot->name = "D";
debugC(1, kHypnoDebugParser, "I D");
}
| ITOK FTOK { // Workaround for NAME == F
shoot->name = "F";
debugC(1, kHypnoDebugParser, "I F");
}
| ITOK GTOK { // Workaround for NAME == G
shoot->name = "G";
debugC(1, kHypnoDebugParser, "I G");
}
| ITOK HTOK { // Workaround for NAME == H
shoot->name = "H";
debugC(1, kHypnoDebugParser, "I H");
}
| ITOK H12TOK { // Workaround for NAME == H1/H2
shoot->name = $2;
debugC(1, kHypnoDebugParser, "I %s", $2);
free($2);
}
| ITOK ITOK { // Workaround for NAME == I
shoot->name = "I";
debugC(1, kHypnoDebugParser, "I I");
}
| ITOK JTOK { // Workaround for NAME == J
shoot->name = "J";
debugC(1, kHypnoDebugParser, "I J");
}
| ITOK KTOK { // Workaround for NAME == K
shoot->name = "K";
debugC(1, kHypnoDebugParser, "I K");
}
| ITOK NTOK { // Workaround for NAME == N
shoot->name = "N";
debugC(1, kHypnoDebugParser, "I N");
}
| ITOK OTOK { // Workaround for NAME == O
shoot->name = "O";
debugC(1, kHypnoDebugParser, "I O");
}
| ITOK PTOK { // Workaround for NAME == P
shoot->name = "P";
debugC(1, kHypnoDebugParser, "I P");
}
| ITOK QTOK { // Workaround for NAME == Q
shoot->name = "Q";
debugC(1, kHypnoDebugParser, "I Q");
}
| ITOK RTOK { // Workaround for NAME == R
shoot->name = "R";
debugC(1, kHypnoDebugParser, "I R");
}
| ITOK SNTOK { // Workaround for NAME == S1
shoot->name = $2;
debugC(1, kHypnoDebugParser, "I %s", $2);
free($2);
}
| ITOK TTOK { // Workaround for NAME == T
shoot->name = "T";
debugC(1, kHypnoDebugParser, "I T");
}
| ITOK LTOK { // Workaround for NAME == L
shoot->name = "L";
debugC(1, kHypnoDebugParser, "I L");
}
| ITOK MTOK { // Workaround for NAME == M
shoot->name = "M";
debugC(1, kHypnoDebugParser, "I M");
}
| ITOK UTOK { // Workaround for NAME == U
shoot->name = "U";
debugC(1, kHypnoDebugParser, "I U");
}
| JTOK NUM {
debugC(1, kHypnoDebugParser, "J %d", $2);
}
| A0TOK NUM NUM {
shoot->position = Common::Point($2, $3);
debugC(1, kHypnoDebugParser, "A0 %d %d", $2, $3);
}
| RTOK NUM NUM {
shoot->objKillsCount = $2;
shoot->objMissesCount = $3;
debugC(1, kHypnoDebugParser, "R %d %d", $2, $3);
}
| R0TOK NUM NUM {
shoot->objKillsCount = $2;
shoot->objMissesCount = $3;
debugC(1, kHypnoDebugParser, "R0 %d %d", $2, $3);
}
| R1TOK NUM NUM {
debugC(1, kHypnoDebugParser, "R1 %d %d", $2, $3);
}
| BNTOK NUM NUM {
FrameInfo fi($3, $2);
shoot->bodyFrames.push_back(fi);
debugC(1, kHypnoDebugParser, "BN %d %d", $2, $3);
free($1);
}
| KNTOK NUM NUM {
FrameInfo fi($3, $2);
shoot->explosionFrames.push_back(fi);
debugC(1, kHypnoDebugParser, "KN %d %d", $2, $3);
}
| P0TOK NUM NUM {
shoot->paletteSize = $2;
shoot->paletteOffset = $3;
debugC(1, kHypnoDebugParser, "P0 %d %d", $2, $3); }
| OTOK NUM NUM {
if ($2 == 0 && $3 == 0)
error("Invalid O command (0, 0)");
shoot->deathPosition = Common::Point($2, $3);
debugC(1, kHypnoDebugParser, "O %d %d", $2, $3);
}
| CTOK NUM {
shoot->timesToShoot = $2;
debugC(1, kHypnoDebugParser, "C %d", $2);
}
| HTOK NUM {
shoot->attackFrames.push_back($2);
debugC(1, kHypnoDebugParser, "H %d", $2); }
| VTOK NUM { debugC(1, kHypnoDebugParser, "V %d", $2); }
| VTOK { debugC(1, kHypnoDebugParser, "V"); }
| WTOK NUM {
shoot->attackWeight = $2;
debugC(1, kHypnoDebugParser, "W %d", $2); }
| DTOK NUM {
shoot->pointsToShoot = $2;
debugC(1, kHypnoDebugParser, "D %d", $2);
}
| LTOK NUM NUM {
debugC(1, kHypnoDebugParser, "L %d %d", $2, $3);
}
| LTOK NUM {
debugC(1, kHypnoDebugParser, "L %d", $2);
FrameInfo fi($2-1, 0);
shoot->bodyFrames.push_back(fi);
}
| MTOK NUM { debugC(1, kHypnoDebugParser, "M %d", $2);
shoot->missedAnimation = $2;
}
| KTOK { debugC(1, kHypnoDebugParser, "K"); }
| KTOK NUM { debugC(1, kHypnoDebugParser, "K %d", $2);
FrameInfo fi($2, 1);
shoot->explosionFrames.push_back(fi);
}
| KTOK NUM NUM NUM {
assert($3 > $2);
FrameInfo fi($2, $3 - $2);
shoot->jumpToTimeAfterKilled = $4;
shoot->explosionFrames.push_back(fi);
debugC(1, kHypnoDebugParser, "K %d %d %d", $2, $3, $4);
}
| KTOK NUM NUM { debugC(1, kHypnoDebugParser, "K %d %d", $2, $3);
FrameInfo fi($2, 1);
shoot->explosionFrames.push_back(fi);
}
| SNTOK FILENAME enc {
if (Common::String("S0") == $1) {
shoot->enemySound = $2;
if (Common::String($3) == "11K")
shoot->enemySoundRate = 11025;
else
shoot->enemySoundRate = 22050;
} else if (Common::String("S1") == $1)
shoot->deathSound = $2;
else if (Common::String("S2") == $1)
shoot->hitSound = $2;
else if (Common::String("S4") == $1)
shoot->animalSound = $2;
debugC(1, kHypnoDebugParser, "SN %s", $2);
free($1);
free($2);
free($3);
}
| SNTOK {
debugC(1, kHypnoDebugParser, "SN");
free($1);
}
| GTOK { debugC(1, kHypnoDebugParser, "G"); }
| TTOK NUM NUM NUM {
shoot->interactionFrame = $2;
assert($3 == 0);
shoot->waitForClickAfterInteraction = $4;
debugC(1, kHypnoDebugParser, "T %d %d %d", $2, $3, $4);
}
| TTOK NUM {
shoot->interactionFrame = $2;
debugC(1, kHypnoDebugParser, "T %d", $2);
}
| TTOK {
shoot->isAnimal = true;
debugC(1, kHypnoDebugParser, "T");
}
| MTOK {
debugC(1, kHypnoDebugParser, "M");
}
| NTOK {
shoot->noEnemySound = true;
debugC(1, kHypnoDebugParser, "N"); }
| NRTOK {
debugC(1, kHypnoDebugParser, "NR"); }
| ZTOK {
g_parsedArc->shoots.push_back(*shoot);
//delete shoot;
//shoot = nullptr;
debugC(1, kHypnoDebugParser, "Z");
}
;

File diff suppressed because it is too large Load Diff

435
engines/hypno/grammar_mis.y Normal file
View File

@@ -0,0 +1,435 @@
/* 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/>.
*
*/
%require "3.0"
%defines "engines/hypno/tokens_mis.h"
%output "engines/hypno/grammar_mis.cpp"
%define api.prefix {HYPNO_MIS_}
%{
#include "common/array.h"
#include "hypno/hypno.h"
//#include <stdio.h>
#undef yyerror
#define yyerror HYPNO_MIS_xerror
extern int HYPNO_MIS_lex();
extern int yylineno;
namespace Hypno {
Common::Array<uint32> *smenu_idx = nullptr;
HotspotsStack *stack = nullptr;
Talk *talk_action = nullptr;
}
void HYPNO_MIS_xerror(const char *str) {
error("ERROR: %s", str);
}
int HYPNO_MIS_wrap() {
return 1;
}
using namespace Hypno;
%}
%union {
char *s; /* string value */
int i; /* integer value */
}
%token<s> NAME FILENAME FLAG GSSWITCH WALNTOK ENCTOK
%token<i> NUM
%token COMMENT HOTSTOK CUTSTOK BACKTOK INTRTOK RETTOK TIMETOK PALETOK BBOXTOK OVERTOK MICETOK SONDTOK PLAYTOK ENDTOK
%token MENUTOK SMENTOK ESCPTOK NRTOK AMBITOK SWPTTOK MPTRTOK
%token GLOBTOK TONTOK TOFFTOK
%token TALKTOK INACTOK FDTOK BOXXTOK ESCAPETOK SECONDTOK INTROTOK DEFAULTTOK
%token<s> PG PA PD PH PF PP PI PS
%token PE PL
%type<s> gsswitch flag mflag
%%
start: init lines
;
init: {
if (smenu_idx)
delete smenu_idx;
smenu_idx = new Common::Array<uint32>();
smenu_idx->push_back((uint32)-1);
if (stack)
delete stack;
stack = new Hypno::HotspotsStack();
stack->push_back(new Hotspots());
}
lines: line lines
| /* nothing */
;
line: MENUTOK mflag mflag mflag {
Hotspot hot(MakeMenu);
debugC(1, kHypnoDebugParser, "MENU %s %s", $2, $3);
hot.flags[0] = $2;
hot.flags[1] = $3;
hot.flags[2] = $4;
Hotspots *cur = stack->back();
cur->push_back(hot);
// We don't care about menus, only hotspots
int idx = smenu_idx->back();
idx++;
smenu_idx->pop_back();
smenu_idx->push_back(idx);
free($2);
free($3);
free($4);
}
| MENUTOK FILENAME {
Hotspot hot(MakeMenu);
debugC(1, kHypnoDebugParser, "MENU %s", $2);
hot.background = $2;
Hotspots *cur = stack->back();
cur->push_back(hot);
// We don't care about menus, only hotspots
int idx = smenu_idx->back();
idx++;
smenu_idx->pop_back();
smenu_idx->push_back(idx);
free($2);
}
| HOTSTOK BBOXTOK NUM NUM NUM NUM {
Hotspot hot(MakeHotspot, Common::Rect($3, $4, $5, $6));
debugC(1, kHypnoDebugParser, "HOTS %d.", hot.type);
Hotspots *cur = stack->back();
cur->push_back(hot);
}
| SMENTOK {
// This should always point to a hotspot
int idx = smenu_idx->back();
idx++;
smenu_idx->pop_back();
smenu_idx->push_back(idx);
Hotspots *cur = stack->back();
Hotspot *hot = &(*cur)[idx];
smenu_idx->push_back((uint32)-1);
hot->smenu = new Hotspots();
stack->push_back(hot->smenu);
debugC(1, kHypnoDebugParser, "SUBMENU");
}
| ESCPTOK {
Escape *a = new Escape();
Hotspots *cur = stack->back();
Hotspot *hot = &cur->back();
hot->actions.push_back(a);
debugC(1, kHypnoDebugParser, "ESC SUBMENU"); }
| TIMETOK NUM mflag {
Timer *a = new Timer($2, $3);
Hotspots *cur = stack->back();
Hotspot *hot = &cur->back();
hot->actions.push_back(a);
debugC(1, kHypnoDebugParser, "TIME %d %s", $2, $3);
free($3);
}
| SWPTTOK NUM {
SwapPointer *a = new SwapPointer($2);
Hotspots *cur = stack->back();
Hotspot *hot = &cur->back();
hot->actions.push_back(a);
debugC(1, kHypnoDebugParser, "SWPT %d", $2); }
| BACKTOK FILENAME NUM NUM gsswitch flag flag {
Background *a = new Background($2, Common::Point($3, $4), $5, $6, $7);
Hotspots *cur = stack->back();
Hotspot *hot = &cur->back();
hot->actions.push_back(a);
debugC(1, kHypnoDebugParser, "BACK");
free($2);
free($5);
free($6);
free($7);
}
| GLOBTOK GSSWITCH NAME {
Global *a = new Global($2, $3);
Hotspots *cur = stack->back();
Hotspot *hot = &cur->back();
hot->actions.push_back(a);
debugC(1, kHypnoDebugParser, "GLOB");
free($2);
free($3);
}
| AMBITOK FILENAME NUM NUM flag {
Ambient *a = new Ambient($2, Common::Point($3, $4), $5);
Hotspots *cur = stack->back();
Hotspot *hot = &cur->back();
hot->actions.push_back(a);
debugC(1, kHypnoDebugParser, "AMBI %d %d", $3, $4);
free($2);
free($5);
}
| PLAYTOK FILENAME NUM NUM gsswitch flag {
Play *a = new Play($2, Common::Point($3, $4), $5, $6);
Hotspots *cur = stack->back();
Hotspot *hot = &cur->back();
hot->actions.push_back(a);
debugC(1, kHypnoDebugParser, "PLAY %s.", $2);
free($2);
free($5);
free($6);
}
| SONDTOK FILENAME ENCTOK {
//Play *a = new Play($2, Common::Point($3, $4), $5, $6);
//Hotspots *cur = stack->back();
//Hotspot *hot = &cur->back();
//hot->actions.push_back(a);
debugC(1, kHypnoDebugParser, "SOND %s.", $2);
free($2);
free($3);
}
| SONDTOK FILENAME {
//Play *a = new Play($2, Common::Point($3, $4), $5, $6);
//Hotspots *cur = stack->back();
//Hotspot *hot = &cur->back();
//hot->actions.push_back(a);
debugC(1, kHypnoDebugParser, "SOND %s.", $2);
free($2);
}
| OVERTOK FILENAME NUM NUM flag {
Overlay *a = new Overlay($2, Common::Point($3, $4), $5);
Hotspots *cur = stack->back();
Hotspot *hot = &cur->back();
hot->actions.push_back(a);
free($2);
free($5);
}
| PALETOK FILENAME {
Palette *a = new Palette($2);
Hotspots *cur = stack->back();
Hotspot *hot = &cur->back();
hot->actions.push_back(a);
debugC(1, kHypnoDebugParser, "PALE");
free($2);
}
| INTRTOK FILENAME NUM NUM {
Intro *a = new Intro(Common::String("cine/") + $2);
Hotspots *cur = stack->back();
Hotspot *hot = &cur->back();
hot->actions.push_back(a);
debugC(1, kHypnoDebugParser, "INTRO %s %d %d", $2, $3, $4);
free($2);
}
| INTRTOK FILENAME {
Intro *a = new Intro(Common::String("cine/") + $2);
Hotspots *cur = stack->back();
Hotspot *hot = &cur->back();
hot->actions.push_back(a);
debugC(1, kHypnoDebugParser, "INTRO %s", $2);
free($2);
}
| CUTSTOK FILENAME {
Cutscene *a = new Cutscene($2);
Hotspots *cur = stack->back();
Hotspot *hot = &cur->back();
hot->actions.push_back(a);
debugC(1, kHypnoDebugParser, "CUTS %s", $2);
free($2);
}
| WALNTOK FILENAME NUM NUM gsswitch flag {
WalN *a = new WalN($1, $2, Common::Point($3, $4), $5, $6);
Hotspots *cur = stack->back();
Hotspot *hot = &cur->back();
hot->actions.push_back(a);
debugC(1, kHypnoDebugParser, "WALN %s %d %d", $2, $3, $4);
free($1);
free($2);
free($5);
free($6);
}
| MICETOK FILENAME NUM {
Mice *a = new Mice($2, $3-1);
Hotspots *cur = stack->back();
Hotspot *hot = &cur->back();
hot->actions.push_back(a);
free($2);
}
| MPTRTOK FILENAME NUM NUM NUM NUM NUM {
debugC(1, kHypnoDebugParser, "MPTR %s %d %d %d %d %d", $2, $3, $4, $5, $6, $7);
free($2);
}
| TALKTOK alloctalk talk {
Hotspots *cur = stack->back();
Hotspot *hot = &cur->back();
hot->actions.push_back(talk_action);
talk_action = nullptr;
debugC(1, kHypnoDebugParser, "TALK"); }
| ENDTOK anything RETTOK {
debugC(1, kHypnoDebugParser, "explicit END");
g_parsedHots = stack->back();
stack->pop_back();
smenu_idx->pop_back();
}
| RETTOK { debugC(1, kHypnoDebugParser, "implicit END"); }
;
anything: NAME anything { free($1); }
| // nothing
;
alloctalk: {
assert(talk_action == nullptr);
talk_action = new Talk();
talk_action->escape = false;
talk_action->active = true;
}
talk: INACTOK talk {
talk_action->active = false;
debugC(1, kHypnoDebugParser, "inactive"); }
| FDTOK talk { debugC(1, kHypnoDebugParser, "inactive"); }
| BACKTOK FILENAME NUM NUM gsswitch flag {
talk_action->background = $2;
talk_action->backgroundPos = Common::Point($3, $4);
debugC(1, kHypnoDebugParser, "BACK in TALK");
free($2);
free($5);
free($6);
}
| BOXXTOK NUM NUM {
talk_action->boxPos = Common::Point($2, $3);
debugC(1, kHypnoDebugParser, "BOXX %d %d", $2, $3); }
| ESCAPETOK {
talk_action->escape = true;
debugC(1, kHypnoDebugParser, "ESCAPE"); }
| SECONDTOK FILENAME NUM NUM flag {
talk_action->second = $2;
talk_action->secondPos = Common::Point($3, $4);
debugC(1, kHypnoDebugParser, "SECOND %s %d %d '%s'", $2, $3, $4, $5);
free($2);
free($5);
}
| INTROTOK FILENAME NUM NUM {
talk_action->intro = $2;
talk_action->introPos = Common::Point($3, $4);
debugC(1, kHypnoDebugParser, "INTRO %s %d %d", $2, $3, $4);
free($2);
}
| DEFAULTTOK FILENAME NUM NUM {
// Unsure how this is different from second
talk_action->second = $2;
talk_action->secondPos = Common::Point($3, $4);
debugC(1, kHypnoDebugParser, "DEFAULT %s %d %d", $2, $3, $4);
free($2);
}
| PG talk {
TalkCommand talk_cmd;
talk_cmd.command = "G";
talk_cmd.path = $1+2;
talk_action->commands.push_back(talk_cmd);
debugC(1, kHypnoDebugParser, "%s", $1);
free($1);
}
| PH talk {
debugC(1, kHypnoDebugParser, "%s", $1);
free($1);
}
| PF talk {
TalkCommand talk_cmd;
talk_cmd.command = "F";
talk_cmd.num = atoi($1+2)-1;
talk_action->commands.push_back(talk_cmd);
debugC(1, kHypnoDebugParser, "%s", $1);
free($1);
}
| PA talk {
TalkCommand talk_cmd;
talk_cmd.command = "A";
talk_cmd.num = atoi($1+2)-1;
talk_action->commands.push_back(talk_cmd);
debugC(1, kHypnoDebugParser, "|A%d", talk_cmd.num);
free($1);
}
| PD talk {
TalkCommand talk_cmd;
talk_cmd.command = "D";
talk_cmd.num = atoi($1+2)-1;
talk_action->commands.push_back(talk_cmd);
debugC(1, kHypnoDebugParser, "%s", $1);
free($1);
}
| PP NUM NUM flag talk {
TalkCommand talk_cmd;
talk_cmd.command = "P";
talk_cmd.path = $1+2;
talk_cmd.position = Common::Point($2, $3);
talk_action->commands.push_back(talk_cmd);
debugC(1, kHypnoDebugParser, "%s %d %d '%s'", $1, $2, $3, $4);
free($1);
free($4);
}
| PI NUM NUM talk {
TalkCommand talk_cmd;
talk_cmd.command = "I";
talk_cmd.path = $1+2;
talk_cmd.position = Common::Point($2, $3);
talk_action->commands.push_back(talk_cmd);
debugC(1, kHypnoDebugParser, "%s %d %d", $1, $2, $3);
free($1);
}
| PS talk {
TalkCommand talk_cmd;
talk_cmd.command = "S";
talk_cmd.variable = $1+2;
talk_action->commands.push_back(talk_cmd);
debugC(1, kHypnoDebugParser, "%s", $1);
free($1);
}
| PL talk {
TalkCommand talk_cmd;
talk_cmd.command = "L";
talk_action->commands.push_back(talk_cmd);
debugC(1, kHypnoDebugParser, "|L");
}
| PE { debugC(1, kHypnoDebugParser, "|E"); }
| /*nothing*/
;
mflag: NAME { $$ = $1; }
| /* nothing */ { $$ = scumm_strdup(""); }
;
flag: FLAG { $$ = $1; debugC(1, kHypnoDebugParser, "flag: %s", $1); }
| /* nothing */ { $$ = scumm_strdup(""); }
;
gsswitch: GSSWITCH { $$ = $1; debugC(1, kHypnoDebugParser, "switch %s", $1); }
| /* nothing */ { $$ = scumm_strdup(""); }
;

787
engines/hypno/hypno.cpp Normal file
View File

@@ -0,0 +1,787 @@
/* 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 "audio/audiostream.h"
#include "audio/decoders/raw.h"
#include "common/archive.h"
#include "common/config-manager.h"
#include "common/debug-channels.h"
#include "common/debug.h"
#include "common/error.h"
#include "common/events.h"
#include "common/file.h"
#include "common/memstream.h"
#include "common/savefile.h"
#include "common/str.h"
#include "common/system.h"
#include "common/substream.h"
#include "common/timer.h"
#include "engines/advancedDetector.h"
#include "engines/util.h"
#include "graphics/paletteman.h"
#include "image/bmp.h"
#include "hypno/grammar.h"
#include "hypno/hypno.h"
#include "backends/keymapper/keymapper.h"
namespace Hypno {
Hotspots *g_parsedHots;
ArcadeShooting *g_parsedArc;
HypnoEngine *g_hypno;
HypnoEngine::HypnoEngine(OSystem *syst, const ADGameDescription *gd)
: Engine(syst), _gameDescription(gd), _image(nullptr),
_compositeSurface(nullptr), _transparentColor(0),
_nextHotsToAdd(nullptr), _nextHotsToRemove(nullptr),
_levelId(0), _skipLevel(false), _health(0), _maxHealth(0),
_playerFrameIdx(0), _playerFrameSep(0), _refreshConversation(false),
_countdown(0), _timerStarted(false), _score(0), _bonus(0), _lives(0),
_defaultCursor(""), _defaultCursorIdx(0), _skipDefeatVideo(false),
_background(nullptr), _masks(nullptr), _useSubtitles(false),
_additionalVideo(nullptr), _ammo(0), _maxAmmo(0), _skipNextVideo(false),
_screenW(0), _screenH(0), // Every games initializes its own resolution
_keepTimerDuringScenes(false), _subtitles(nullptr) {
_rnd = new Common::RandomSource("hypno");
_checkpoint = "";
_cursorCache = new CursorCache(this);
if (gd->extra)
_variant = gd->extra;
else
_variant = "FullGame";
g_hypno = this;
g_parsedArc = new ArcadeShooting();
_language = Common::parseLanguage(ConfMan.get("language"));
_platform = Common::parsePlatform(ConfMan.get("platform"));
if (!Common::parseBool(ConfMan.get("cheats"), _cheatsEnabled))
error("Failed to parse bool from cheats options");
if (!Common::parseBool(ConfMan.get("infiniteHealth"), _infiniteHealthCheat))
error("Failed to parse bool from cheats options");
if (!Common::parseBool(ConfMan.get("infiniteAmmo"), _infiniteAmmoCheat))
error("Failed to parse bool from cheats options");
if (!Common::parseBool(ConfMan.get("unlockAllLevels"), _unlockAllLevels))
error("Failed to parse bool from cheats options");
if (!Common::parseBool(ConfMan.get("restored"), _restoredContentEnabled))
error("Failed to parse bool from restored options");
// Only enable if subtitles are available
if (!Common::parseBool(ConfMan.get("subtitles"), _useSubtitles))
warning("Failed to parse bool from subtitles options");
// Add quit level
Hotspot q(MakeMenu);
Action *a = new Quit();
q.actions.push_back(a);
Scene *quit = new Scene();
quit->hots.push_back(q);
quit->resolution = "320x200";
_levels["<quit>"] = quit;
resetStatistics();
}
HypnoEngine::~HypnoEngine() {
// Deallocate actions
// for (Levels::iterator it = _levels.begin(); it != _levels.end(); ++it) {
// Level level = (*it)._value;
// for (Hotspots::iterator itt = level.scene.hots.begin(); itt != level.scene.hots.end(); ++itt) {
// Hotspot hot = *itt;
// for (Actions::iterator ittt = hot.actions.begin(); ittt != hot.actions.end(); ++ittt)
// delete (*ittt);
// }
// }
delete _rnd;
delete _cursorCache;
_compositeSurface->free();
delete _compositeSurface;
delete g_parsedArc;
}
void HypnoEngine::initializePath(const Common::FSNode &gamePath) {
SearchMan.addDirectory(gamePath, 0, 10);
}
LibFile *HypnoEngine::loadLib(const Common::Path &prefix, const Common::Path &filename, bool encrypted) {
LibFile *lib = new LibFile();
SearchMan.add(filename.toString(), (Common::Archive *)lib, 0, true);
if (!lib->open(prefix, filename, encrypted)) {
return nullptr;
}
_archive.push_back(lib);
return lib;
}
void HypnoEngine::loadAssets() { error("Function \"%s\" not implemented", __FUNCTION__); }
Common::String HypnoEngine::findNextLevel(const Common::String &level) { error("Function \"%s\" not implemented", __FUNCTION__); }
Common::Error HypnoEngine::run() {
Graphics::ModeList modes;
modes.push_back(Graphics::Mode(640, 480));
modes.push_back(Graphics::Mode(320, 200));
initGraphicsModes(modes);
// Initialize graphics
_pixelFormat = Graphics::PixelFormat::createFormatCLUT8();
initGraphics(_screenW, _screenH, &_pixelFormat);
_compositeSurface = new Graphics::Surface();
_compositeSurface->create(_screenW, _screenH, _pixelFormat);
// Main event loop
loadAssets();
int saveSlot = ConfMan.getInt("save_slot");
if (saveSlot >= 0) { // load the savegame
loadGameState(saveSlot);
}
assert(!_nextLevel.empty());
while (!shouldQuit()) {
debug("nextLevel: %s", _nextLevel.c_str());
_prefixDir = "";
_videosPlaying.clear();
_videosLooping.clear();
if (!_nextLevel.empty()) {
_currentLevel = findNextLevel(_nextLevel);
_nextLevel = "";
_arcadeMode = "";
runLevel(_currentLevel);
} else
g_system->delayMillis(300);
}
// Only enable if subtitles are available
bool useSubtitles = false;
if (!Common::parseBool(ConfMan.get("subtitles"), useSubtitles))
warning("Failed to parse bool from subtitles options");
if (useSubtitles) {
g_system->showOverlay(false);
}
return Common::kNoError;
}
void HypnoEngine::runLevel(Common::String &name) {
if (!_levels.contains(name))
error("Level %s cannot be found", name.c_str());
_prefixDir = _levels[name]->prefix;
stopSound();
stopMusic();
// Play intros
disableCursor();
if (_levels[name]->playMusicDuringIntro && !_levels[name]->music.empty()) {
playMusic(_levels[name]->music, _levels[name]->musicRate);
}
debug("Number of videos to play: %d", _levels[name]->intros.size());
for (Filenames::iterator it = _levels[name]->intros.begin(); it != _levels[name]->intros.end(); ++it) {
MVideo v(*it, Common::Point(0, 0), false, true, false);
runIntro(v);
}
if (_levels[name]->type == TransitionLevel) {
debugC(1, kHypnoDebugScene, "Executing transition level %s", name.c_str());
runTransition((Transition *)_levels[name]);
} else if (_levels[name]->type == ArcadeLevel) {
debugC(1, kHypnoDebugArcade, "Executing arcade level %s", name.c_str());
changeScreenMode("320x200");
ArcadeShooting *arc = (ArcadeShooting *)_levels[name];
runBeforeArcade(arc);
runArcade(arc);
runAfterArcade(arc);
} else if (_levels[name]->type == CodeLevel) {
debugC(1, kHypnoDebugScene, "Executing hardcoded level %s", name.c_str());
// Resolution depends on the game
runCode((Code *)_levels[name]);
} else if (_levels[name]->type == SceneLevel) {
debugC(1, kHypnoDebugScene, "Executing scene level %s with next level: %s", name.c_str(), _levels[name]->levelIfWin.c_str());
runScene((Scene *)_levels[name]);
} else {
error("Invalid level %s", name.c_str());
}
}
void HypnoEngine::runIntros(Videos &videos) {
debugC(1, kHypnoDebugScene, "Starting run intros with %d videos!", videos.size());
Common::Event event;
bool skip = false;
int clicked[3] = {-1, -1, -1};
int clicks = 0;
for (Videos::iterator it = videos.begin(); it != videos.end(); ++it) {
playVideo(*it);
}
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
disableGameKeymaps();
keymapper->getKeymap("intro")->setEnabled(true);
while (!shouldQuit()) {
while (g_system->getEventManager()->pollEvent(event)) {
// Events
switch (event.type) {
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
if (event.customType == kActionSkipIntro)
skip = true;
break;
case Common::EVENT_LBUTTONDOWN:
if (videos.size() == 1) {
int first = (clicks - 2) % 3;
int last = clicks % 3;
clicked[last] = videos[0].decoder->getCurFrame();
if (clicks >= 2 && clicked[last] - clicked[first] <= 10)
skip = true;
clicks++;
}
break;
case Common::EVENT_RBUTTONUP:
setRButtonUp(true);
break;
default:
break;
}
}
if (skip) {
for (Videos::iterator it = videos.begin(); it != videos.end(); ++it) {
if (it->decoder)
skipVideo(*it);
}
videos.clear();
}
bool playing = false;
for (Videos::iterator it = videos.begin(); it != videos.end(); ++it) {
assert(!it->loop);
if (it->decoder) {
if (it->decoder->endOfVideo()) {
it->decoder->close();
delete it->decoder;
it->decoder = nullptr;
} else {
playing = true;
if (it->decoder->needsUpdate()) {
updateScreen(*it);
if (_subtitles && it->decoder && !it->decoder->isPaused())
_subtitles->drawSubtitle(it->decoder->getTime(), false, false);
drawScreen();
}
}
}
}
if (!playing) {
debugC(1, kHypnoDebugScene, "Not playing anymore!");
break;
}
g_system->updateScreen();
g_system->delayMillis(10);
}
for (Videos::iterator it = videos.begin(); it != videos.end(); ++it) {
delete it->decoder;
it->decoder = nullptr;
}
keymapper->getKeymap("intro")->setEnabled(false);
enableGameKeymaps();
}
void HypnoEngine::runIntro(MVideo &video) {
Common::Path path(video.path);
loadSubtitles(path);
Videos tmp;
tmp.push_back(video);
runIntros(tmp);
delete _subtitles;
_subtitles = nullptr;
g_system->hideOverlay();
}
void HypnoEngine::runCode(Code *code) { error("Function \"%s\" not implemented", __FUNCTION__); }
void HypnoEngine::showCredits() { error("Function \"%s\" not implemented", __FUNCTION__); }
void HypnoEngine::loadGame(const Common::String &nextLevel, int score, int puzzleDifficulty, int combatDifficulty) {
error("Function \"%s\" not implemented", __FUNCTION__);
}
void HypnoEngine::loadFonts(const Common::String &prefix) {
Common::File file;
Common::Path path = Common::Path(prefix).append("block05.fgx");
if (!file.open(path))
error("Cannot open font %s", path.toString().c_str());
byte *font = (byte *)malloc(file.size());
file.read(font, file.size());
_font05.set_size(file.size()*8);
_font05.set_bits((byte *)font);
file.close();
free(font);
path = Common::Path(prefix).append("scifi08.fgx");
if (!file.open(path))
error("Cannot open font %s", path.toString().c_str());
font = (byte *)malloc(file.size());
file.read(font, file.size());
_font08.set_size(file.size()*8);
_font08.set_bits((byte *)font);
file.close();
free(font);
}
void HypnoEngine::drawString(const Filename &name, const Common::String &str, int x, int y, int w, uint32 c) {
error("Function \"%s\" not implemented", __FUNCTION__);
}
void HypnoEngine::loadImage(const Common::String &name, int x, int y, bool transparent, bool palette, int frameNumber) {
debugC(1, kHypnoDebugMedia, "%s(%s, %d, %d, %d)", __FUNCTION__, name.c_str(), x, y, transparent);
Graphics::Surface *surf;
if (palette) {
byte *array;
surf = decodeFrame(name, frameNumber, &array);
loadPalette(array, 0, 256);
free(array);
} else
surf = decodeFrame(name, frameNumber);
drawImage(*surf, x, y, transparent);
surf->free();
delete surf;
}
void HypnoEngine::drawImage(Graphics::Surface &surf, int x, int y, bool transparent) {
Common::Rect srcRect(surf.w, surf.h);
Common::Rect dstRect = srcRect;
dstRect.moveTo(x, y);
_compositeSurface->clip(srcRect, dstRect);
if (transparent) {
_compositeSurface->copyRectToSurfaceWithKey(surf, dstRect.left, dstRect.top, srcRect, _transparentColor);
} else
_compositeSurface->copyRectToSurface(surf, dstRect.left, dstRect.top, srcRect);
}
Graphics::Surface *HypnoEngine::decodeFrame(const Common::String &name, int n, byte **palette) {
Common::File *file = new Common::File();
Common::Path path = convertPath(name);
if (!_prefixDir.empty())
path = _prefixDir.join(path);
if (!file->open(path))
error("unable to find video file %s", path.toString().c_str());
HypnoSmackerDecoder vd;
if (!vd.loadStream(file))
error("unable to load video %s", path.toString().c_str());
for (int f = 0; f < n; f++)
vd.decodeNextFrame();
const Graphics::Surface *frame = vd.decodeNextFrame();
Graphics::Surface *rframe = frame->convertTo(frame->format, vd.getPalette());
if (palette != nullptr) {
byte *newPalette = (byte *)malloc(3 * 256);
memcpy(newPalette, vd.getPalette(), 3 * 256);
*palette = newPalette;
}
return rframe;
}
Frames HypnoEngine::decodeFrames(const Common::String &name) {
Frames frames;
Common::File *file = new Common::File();
Common::Path path = convertPath(name);
if (!_prefixDir.empty())
path = _prefixDir.join(path);
if (!file->open(path))
error("unable to find video file %s", path.toString().c_str());
HypnoSmackerDecoder vd;
if (!vd.loadStream(file))
error("unable to load video %s", path.toString().c_str());
const Graphics::Surface *frame = nullptr;
Graphics::Surface *rframe = nullptr;
while (!vd.endOfVideo()) {
frame = vd.decodeNextFrame();
rframe = frame->convertTo(_pixelFormat, vd.getPalette());
frames.push_back(rframe);
}
return frames;
}
void HypnoEngine::changeScreenMode(const Common::String &mode) {
debugC(1, kHypnoDebugMedia, "%s(%s)", __FUNCTION__, mode.c_str());
if (mode == "640x480") {
if (_screenW == 640 && _screenH == 480)
return;
_screenW = 640;
_screenH = 480;
initGraphics(_screenW, _screenH, &_pixelFormat);
_compositeSurface->free();
delete _compositeSurface;
_compositeSurface = new Graphics::Surface();
_compositeSurface->create(_screenW, _screenH, _pixelFormat);
} else if (mode == "320x200") {
if (_screenW == 320 && _screenH == 200)
return;
_screenW = 320;
_screenH = 200;
initGraphics(_screenW, _screenH, &_pixelFormat);
_compositeSurface->free();
delete _compositeSurface;
_compositeSurface = new Graphics::Surface();
_compositeSurface->create(_screenW, _screenH, _pixelFormat);
} else
error("Unknown screen mode %s", mode.c_str());
}
void HypnoEngine::loadPalette(const Common::String &fname) {
Common::File file;
Common::Path path = convertPath(fname);
if (!_prefixDir.empty())
path = _prefixDir.join(path);
if (!file.open(path))
error("unable to find palette file %s", path.toString().c_str());
debugC(1, kHypnoDebugMedia, "Loading palette from %s", path.toString().c_str());
byte *videoPalette = (byte *)malloc(file.size());
file.read(videoPalette, file.size());
g_system->getPaletteManager()->setPalette(videoPalette + 8, 0, 256);
}
void HypnoEngine::loadPalette(const byte *palette, uint32 offset, uint32 size) {
debugC(1, kHypnoDebugMedia, "Loading palette from byte array with offset %d and size %d", offset, size);
g_system->getPaletteManager()->setPalette(palette, offset, size);
}
byte *HypnoEngine::getPalette(uint32 idx) {
byte *videoPalette = (byte *)malloc(3);
g_system->getPaletteManager()->grabPalette(videoPalette, idx, 1);
return videoPalette;
}
void HypnoEngine::updateVideo(MVideo &video) {
video.decoder->decodeNextFrame();
}
void HypnoEngine::updateScreen(MVideo &video) {
const Graphics::Surface *frame = video.decoder->decodeNextFrame();
bool dirtyPalette = video.decoder->hasDirtyPalette();
bool isFullscreen = (frame->w == _screenW && frame->h == _screenH);
if (frame->h == 0 || frame->w == 0 || video.decoder->getPalette() == nullptr)
return;
const byte *videoPalette = nullptr;
if (video.scaled && dirtyPalette) {
debugC(1, kHypnoDebugMedia, "Updating palette at frame %d", video.decoder->getCurFrame());
videoPalette = video.decoder->getPalette();
g_system->getPaletteManager()->setPalette(videoPalette, 0, 256);
}
if (video.scaled && !isFullscreen) {
Graphics::Surface *sframe = frame->scale(_screenW, _screenH);
Common::Rect srcRect(sframe->w, sframe->h);
Common::Rect dstRect = srcRect;
dstRect.moveTo(video.position);
_compositeSurface->clip(srcRect, dstRect);
if (video.transparent) {
_compositeSurface->copyRectToSurfaceWithKey(*sframe, dstRect.left, dstRect.top, srcRect, _transparentColor);
} else {
_compositeSurface->copyRectToSurface(*sframe, dstRect.left, dstRect.top, srcRect);
}
sframe->free();
delete sframe;
} else {
Common::Rect srcRect(frame->w, frame->h);
Common::Rect dstRect = srcRect;
dstRect.moveTo(video.position);
_compositeSurface->clip(srcRect, dstRect);
if (video.transparent) {
_compositeSurface->copyRectToSurfaceWithKey(*frame, dstRect.left, dstRect.top, srcRect, _transparentColor);
} else {
_compositeSurface->copyRectToSurface(*frame, dstRect.left, dstRect.top, srcRect);
}
}
}
void HypnoEngine::drawScreen() {
g_system->copyRectToScreen(_compositeSurface->getPixels(), _compositeSurface->pitch, 0, 0, _screenW, _screenH);
g_system->updateScreen();
g_system->delayMillis(10);
}
// Video handling
void HypnoEngine::adjustSubtitleSize() {
debugC(1, kHypnoDebugMedia, "%s()", __FUNCTION__);
if (_subtitles) {
// Subtitle positioning constants (as percentages of screen height)
const int HORIZONTAL_MARGIN = 20;
const float BOTTOM_MARGIN_PERCENT = 0.009f; // ~20px at 2160p
const float SUBTITLE_HEIGHT_PERCENT = 0.102f; // ~220px at 2160p
// Font sizing constants (as percentage of screen height)
const int MIN_FONT_SIZE = 8;
const float BASE_FONT_SIZE_PERCENT = 0.023f; // ~50px at 2160p
int16 h = g_system->getOverlayHeight();
int16 w = g_system->getOverlayWidth();
int bottomMargin = int(h * BOTTOM_MARGIN_PERCENT);
int topOffset = int(h * SUBTITLE_HEIGHT_PERCENT);
_subtitles->setBBox(Common::Rect(HORIZONTAL_MARGIN, h - topOffset, w - HORIZONTAL_MARGIN, h - bottomMargin));
int fontSize = MAX(MIN_FONT_SIZE, int(h * BASE_FONT_SIZE_PERCENT));
_subtitles->setColor(0xff, 0xff, 0x80);
_subtitles->setFont("NotoSerif-Regular.ttf", fontSize, Video::Subtitles::kFontStyleRegular);
_subtitles->setFont("NotoSerif-Italic.ttf", fontSize, Video::Subtitles::kFontStyleItalic);
}
}
void HypnoEngine::loadSubtitles(const Common::Path &path) {
debugC(1, kHypnoDebugMedia, "%s(%s)", __FUNCTION__, path.toString().c_str());
if (!_useSubtitles)
return;
debug("Subtitle path: %s", path.toString().c_str());
Common::String subPathStr = path.toString() + ".srt";
subPathStr.toLowercase();
subPathStr.replace('/', '_');
subPathStr.replace('\\', '_');
Common::String language(Common::getLanguageCode(_language));
if (language == "us")
language = "en";
Common::Path subPath = "subtitles";
subPath = subPath.appendComponent(language);
subPath = subPath.appendComponent(subPathStr);
debugC(1, kHypnoDebugMedia, "Loading subtitles from %s", subPath.toString().c_str());
if (_subtitles != nullptr) {
delete _subtitles;
_subtitles = nullptr;
g_system->hideOverlay();
}
_subtitles = new Video::Subtitles();
_subtitles->loadSRTFile(subPath);
if (!_subtitles->isLoaded()) {
delete _subtitles;
_subtitles = nullptr;
return;
}
g_system->showOverlay(false);
g_system->clearOverlay();
adjustSubtitleSize();
}
void HypnoEngine::playVideo(MVideo &video) {
debugC(1, kHypnoDebugMedia, "%s(%s)", __FUNCTION__, video.path.c_str());
Common::File *file = new Common::File();
Common::Path path = convertPath(video.path);
if (!_prefixDir.empty())
path = _prefixDir.join(path);
if (!file->open(path))
error("unable to find video file %s", path.toString().c_str());
if (video.decoder != nullptr) {
debugC(1, kHypnoDebugMedia, "Restarting %s!!!!", video.path.c_str());
delete video.decoder;
}
// error("Video %s was not previously closed and deallocated", video.path.c_str());
video.decoder = new HypnoSmackerDecoder();
if (!video.decoder->loadStream(file))
error("unable to load video %s", path.toString().c_str());
debugC(1, kHypnoDebugMedia, "audio track count: %d", video.decoder->getAudioTrackCount());
video.decoder->start();
}
void HypnoEngine::skipVideo(MVideo &video) {
if (!video.decoder)
return;
debugC(1, kHypnoDebugMedia, "%s()", __FUNCTION__);
video.decoder->close();
delete video.decoder;
video.decoder = nullptr;
}
// Sound handling
Audio::SeekableAudioStream *HypnoEngine::loadAudioStream(const Common::String &filename, uint32 sampleRate, bool stereo) {
debugC(1, kHypnoDebugMedia, "%s(%s, %d)", __FUNCTION__, filename.c_str(), sampleRate);
Common::Path name = convertPath(filename);
Common::File *file = new Common::File();
if (file->open(name)) {
uint32 flags = Audio::FLAG_UNSIGNED;
Common::SeekableSubReadStream *sub;
if (stereo) {
sub = new Common::SeekableSubReadStream(file, 0, file->size() - (file->size() % 2), DisposeAfterUse::YES);
flags = flags | Audio::FLAG_STEREO;
} else {
sub = new Common::SeekableSubReadStream(file, 0, file->size(), DisposeAfterUse::YES);
}
return Audio::makeRawStream(sub, sampleRate, flags, DisposeAfterUse::YES);
} else {
if (!_prefixDir.empty())
name = _prefixDir.join(name);
if (file->open(name)) {
return Audio::makeRawStream(file, sampleRate, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
} else {
debugC(1, kHypnoDebugMedia, "%s not found!", name.toString().c_str());
delete file;
return nullptr;
}
}
}
void HypnoEngine::playSound(const Common::String &filename, uint32 loops, uint32 sampleRate, bool stereo) {
Audio::SeekableAudioStream *stream = loadAudioStream(filename, sampleRate, stereo);
if (!stream)
return;
_mixer->stopHandle(_soundHandle);
Audio::LoopingAudioStream *loopstream = new Audio::LoopingAudioStream(stream, loops);
_mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundHandle, loopstream, -1, Audio::Mixer::kMaxChannelVolume);
}
void HypnoEngine::playMusic(const Common::String &filename, uint32 sampleRate, bool stereo) {
Audio::SeekableAudioStream *stream = loadAudioStream(filename, sampleRate, stereo);
if (!stream)
return;
_mixer->stopHandle(_musicHandle);
Audio::LoopingAudioStream *loopstream = new Audio::LoopingAudioStream(stream, 0);
_mixer->playStream(Audio::Mixer::kMusicSoundType, &_musicHandle, loopstream, -1, Audio::Mixer::kMaxChannelVolume);
}
void HypnoEngine::stopSound() {
debugC(1, kHypnoDebugMedia, "%s()", __FUNCTION__);
_mixer->stopHandle(_soundHandle);
}
void HypnoEngine::stopMusic() {
debugC(1, kHypnoDebugMedia, "%s()", __FUNCTION__);
_mixer->stopHandle(_musicHandle);
}
bool HypnoEngine::isMusicActive() {
return _mixer->isSoundHandleActive(_musicHandle);
}
// Path handling
Common::Path HypnoEngine::convertPath(const Common::String &name) {
Common::String path(name);
Common::String s1("\\");
Common::String s2("/");
while (path.contains(s1))
Common::replace(path, s1, s2);
s1 = Common::String("\"");
s2 = Common::String("");
Common::replace(path, s1, s2);
Common::replace(path, s1, s2);
path.toLowercase();
return Common::Path(path);
}
// Timers
static void alarmCallback(void *refCon) {
g_system->getTimerManager()->removeTimerProc(&alarmCallback);
Common::String *level = (Common::String *)refCon;
g_hypno->_nextLevel = *level;
delete level;
}
static void countdownCallback(void *refCon) {
g_hypno->_countdown = g_hypno->_countdown - 1;
}
bool HypnoEngine::startAlarm(uint32 delay, Common::String *ns) {
return g_system->getTimerManager()->installTimerProc(&alarmCallback, delay, (void *)ns, "alarm");
}
bool HypnoEngine::startCountdown(uint32 delay) {
_countdown = delay;
_timerStarted = true;
uint32 oneSecond = 1000000;
return g_system->getTimerManager()->installTimerProc(&countdownCallback, oneSecond, 0x0, "countdown");
}
void HypnoEngine::removeTimers() {
_timerStarted = false;
_keepTimerDuringScenes = false;
g_system->getTimerManager()->removeTimerProc(&alarmCallback);
g_system->getTimerManager()->removeTimerProc(&countdownCallback);
}
} // End of namespace Hypno

734
engines/hypno/hypno.h Normal file
View File

@@ -0,0 +1,734 @@
/* 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 HYPNO_H
#define HYPNO_H
#include "common/array.h"
#include "common/compression/installshieldv3_archive.h"
#include "common/random.h"
#include "common/serializer.h"
#include "common/str-array.h"
#include "common/stream.h"
#include "engines/engine.h"
#include "graphics/font.h"
#include "graphics/fontman.h"
#include "graphics/surface.h"
#include "video/subtitles.h"
#include "hypno/grammar.h"
#include "hypno/libfile.h"
namespace Audio {
class SeekableAudioStream;
}
namespace Image {
class ImageDecoder;
}
struct ADGameDescription;
namespace Hypno {
// debug channels
enum {
kHypnoDebugMedia = 1,
kHypnoDebugParser,
kHypnoDebugArcade,
kHypnoDebugScene,
};
// Player positions
enum PlayerPosition {
kPlayerTop = 'T',
kPlayerBottom = 'B',
kPlayerLeft = 'L',
kPlayerRight = 'R'
};
// Common colors
enum HypnoColors {
kHypnoNoColor = -1,
kHypnoColorRed = 250,
kHypnoColorGreen = 251,
kHypnoColorWhiteOrBlue = 252,
kHypnoColorYellow = 253,
kHypnoColorBlack = 254,
kHypnoColorCyan = 255
};
// Spider colors
enum SpiderColors {
kSpiderColorWhite = 248,
kSpiderColorBlue = 252,
};
enum HYPNOActions {
kActionNone,
kActionSkipIntro,
kActionSkipCutscene,
kActionPrimaryShoot,
kActionSkipLevel,
kActionKillPlayer,
kActionPause,
kActionLeft,
kActionDown,
kActionRight,
kActionUp,
kActionYes,
kActionNo,
kActionDifficultyChump,
kActionDifficultyPunk,
kActionDifficultyBadass,
kActionDifficultExit,
kActionRetry,
kActionRestart,
kActionNewMission,
kActionQuit,
kActionCredits,
kActionSelect,
};
class HypnoEngine;
class CursorCache {
private:
HypnoEngine *_vm;
Common::String _filename;
uint32 _frame;
byte *_palette;
Graphics::Surface *_surface;
public:
CursorCache(HypnoEngine *vm) : _vm(vm), _filename(""), _frame(0), _palette(nullptr), _surface(nullptr) {}
~CursorCache() {
if (_surface) {
_surface->free();
delete _surface;
}
free(_palette);
}
Graphics::Surface *getCursor(const Common::String &cursor, uint32 n, byte **palette);
};
class HypnoEngine : public Engine {
private:
Image::ImageDecoder *_image;
public:
HypnoEngine(OSystem *syst, const ADGameDescription *gd);
~HypnoEngine();
const ADGameDescription *_gameDescription;
bool isDemo() const;
Common::Language _language;
Common::Platform _platform;
Common::String _variant;
bool _cheatsEnabled;
bool _infiniteHealthCheat;
bool _infiniteAmmoCheat;
bool _unlockAllLevels;
bool _restoredContentEnabled;
Audio::SoundHandle _soundHandle, _musicHandle;
Common::InstallShieldV3 _installerArchive;
Common::List<LibFile*> _archive;
Common::Error run() override;
Levels _levels;
Common::HashMap<Common::String, int> _sceneState;
virtual void resetSceneState();
bool checkSceneCompleted();
bool checkLevelWon();
void runLevel(Common::String &name);
void runScene(Scene *scene);
virtual void runBeforeArcade(ArcadeShooting *arc);
virtual void runAfterArcade(ArcadeShooting *arc);
void runArcade(ArcadeShooting *arc);
// For some menus and hardcoded puzzles
virtual void runCode(Code *code);
// Level transitions
void runTransition(Transition *trans);
void restartGame();
void clearAreas();
void initializePath(const Common::FSNode &gamePath) override;
virtual void loadAssets();
// Parsing
void splitArcadeFile(const Common::String &filename, Common::String &arc, Common::String &list);
void parseArcadeShooting(const Common::String &prefix, const Common::String &name, const Common::String &data);
SegmentShootsSequence parseShootList(const Common::String &name, const Common::String &data);
void loadArcadeLevel(const Common::String &current, const Common::String &nextWin, const Common::String &nextLose, const Common::String &prefix);
void loadSceneLevel(const Common::String &current, const Common::String &next, const Common::String &prefix);
void loadSceneLevel(const char *buf, const Common::String &name, const Common::String &next, const Common::String &prefix);
LibFile *loadLib(const Common::Path &prefix, const Common::Path &filename, bool encrypted);
// User input
void clickedHotspot(Common::Point);
virtual bool hoverHotspot(Common::Point);
// Cursors
bool cursorPauseMovie(Common::Point);
bool cursorExit(Common::Point);
bool cursorMask(Common::Point);
virtual void loadGame(const Common::String &nextLevel, int score, int puzzleDifficulty, int combatDifficulty);
bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override { return (isDemo() ? false : true); }
bool canSaveAutosaveCurrently() override { return false; }
bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override { return (isDemo() ? false : true); }
Common::String _checkpoint;
bool _useSubtitles;
Video::Subtitles *_subtitles;
void adjustSubtitleSize();
void loadSubtitles(const Common::Path &path);
Common::Path _prefixDir;
Common::Path convertPath(const Common::String &);
void playVideo(MVideo &video);
void skipVideo(MVideo &video);
Graphics::Surface *decodeFrame(const Common::String &name, int frame, byte **palette = nullptr);
Frames decodeFrames(const Common::String &name);
void loadImage(const Common::String &file, int x, int y, bool transparent, bool palette = false, int frameNumber = 0);
void drawImage(Graphics::Surface &image, int x, int y, bool transparent);
void loadPalette(const Common::String &fname);
void loadPalette(const byte *palette, uint32 offset, uint32 size);
byte *getPalette(uint32 idx);
// Cursors
Common::String _defaultCursor;
uint32 _defaultCursorIdx;
CursorCache *_cursorCache;
void disableCursor();
void defaultCursor();
virtual void changeCursor(const Common::String &cursor, uint32 n, bool centerCursor = false);
virtual void changeCursor(const Common::String &cursor);
virtual void changeCursor(const Graphics::Surface &entry, byte *palette, bool centerCursor = false);
// Actions
virtual void runMenu(Hotspots *hs, bool only_menu = false);
void runBackground(Background *a);
void runOverlay(Overlay *a);
void runMice(Mice *a);
void runEscape();
void runSave(Save *a);
void runLoad(Load *a);
void runLoadCheckpoint(LoadCheckpoint *a);
void runTimer(Timer *a);
void runQuit(Quit *a);
void runCutscene(Cutscene *a);
void runIntro(Intro *a);
void runPlay(Play *a);
void runSound(Sound *a);
void runPalette(Palette *a);
void runAmbient(Ambient *a);
void runWalN(WalN *a);
bool runGlobal(Global *a);
void runTalk(Talk *a);
void runSwapPointer(SwapPointer *a);
void runChangeLevel(ChangeLevel *a);
virtual void drawBackToMenu(Hotspot *h);
// Screen
int _screenW, _screenH;
Graphics::PixelFormat _pixelFormat;
void changeScreenMode(const Common::String &mode);
Graphics::Surface *_compositeSurface;
uint32 _transparentColor;
Common::Rect screenRect;
void updateScreen(MVideo &video);
void updateVideo(MVideo &video);
void drawScreen();
// intros
void runIntro(MVideo &video);
void runIntros(Videos &videos);
Common::HashMap<Filename, bool> _intros;
// levels
Common::String _nextLevel;
Common::String _currentLevel;
virtual Common::String findNextLevel(const Common::String &level);
virtual Common::String findNextLevel(const Transition *trans);
uint32 _levelId;
// hotspots
Hotspots *_nextHotsToAdd;
Hotspots *_nextHotsToRemove;
HotspotsStack stack;
// Movies
Videos _nextSequentialVideoToPlay;
Videos _nextParallelVideoToPlay;
Videos _escapeSequentialVideoToPlay;
Videos _videosPlaying;
Videos _videosLooping;
MVideo *_masks;
MVideo *_additionalVideo;
const Graphics::Surface *_mask;
// Sounds
Filename _soundPath;
Audio::SeekableAudioStream *loadAudioStream(const Filename &filename, uint32 sampleRate = 22050, bool stereo = false);
void playSound(const Filename &filename, uint32 loops, uint32 sampleRate = 22050, bool stereo = false);
void playMusic(const Filename &filename, uint32 sampleRate = 22050, bool stereo = false);
void stopSound();
void stopMusic();
bool isMusicActive();
// Arcade
Common::String _arcadeMode;
MVideo *_background;
Filename _currentPalette;
virtual bool availableObjectives();
virtual bool checkArcadeObjectives();
ArcadeTransitions _transitions;
virtual bool checkTransition(ArcadeTransitions &transitions, ArcadeShooting *arc);
virtual Common::Point getPlayerPosition(bool needsUpdate);
virtual Common::Point computeTargetPosition(const Common::Point &mousePos);
virtual int detectTarget(const Common::Point &mousePos);
virtual void pressedKey(const int keycode);
virtual bool clickedPrimaryShoot(const Common::Point &mousePos);
virtual bool clickedSecondaryShoot(const Common::Point &mousePos);
virtual void drawShoot(const Common::Point &mousePos);
virtual bool shoot(const Common::Point &mousePos, ArcadeShooting *arc, bool secondary);
virtual void hitPlayer();
virtual void missedTarget(Shoot *s, ArcadeShooting *arc);
virtual void missNoTarget(ArcadeShooting *arc);
virtual byte *getTargetColor(Common::String name, int levelId);
virtual bool checkRButtonUp();
virtual void setRButtonUp(const bool val);
virtual void disableGameKeymaps();
virtual void enableGameKeymaps();
// Segments
Segments _segments;
uint32 _segmentIdx;
uint32 _segmentOffset;
uint32 _segmentRepetition;
uint32 _segmentRepetitionMax;
uint32 _segmentShootSequenceOffset;
uint32 _segmentShootSequenceMax;
ShootSequence _shootSequence;
virtual void findNextSegment(ArcadeShooting *arc);
virtual void initSegment(ArcadeShooting *arc);
ArcadeStats _stats;
void resetStatistics();
void incLivesUsed();
void incShotsFired();
void incEnemyHits();
void incEnemyTargets();
void incTargetsDestroyed();
void incTargetsMissed();
void incFriendliesEncountered();
void incInfoReceived();
void incScore(int inc);
void incBonus(int inc);
uint32 killRatio();
uint32 accuracyRatio();
Common::String _difficulty;
bool _skipLevel;
bool _loseLevel;
bool _skipDefeatVideo;
bool _skipNextVideo;
virtual void drawCursorArcade(const Common::Point &mousePos);
virtual void drawPlayer();
virtual void drawHealth();
virtual void drawAmmo();
int _health;
int _maxHealth;
int _ammo;
int _maxAmmo;
int _score;
int _bonus;
int _lives;
Common::String _healthString;
Common::String _scoreString;
Common::String _objString;
Common::String _targetString;
Common::String _directionString;
Common::String _enterNameString;
Filename _shootSound;
Filename _hitSound;
Filename _additionalSound;
Shoots _shoots;
Frames _playerFrames;
int _playerFrameIdx;
Common::List<int> _playerFrameSeps;
int _playerFrameStart;
int _playerFrameSep;
int _playerFrameEnd;
// Objectives
uint32 _objIdx;
uint32 _objKillsCount[2];
uint32 _objMissesCount[2];
uint32 _objKillsRequired[2];
uint32 _objMissesAllowed[2];
// Fonts
Common::BitArray _font05;
Common::BitArray _font08;
virtual void loadFonts(const Common::String &prefix = "");
virtual void drawString(const Filename &name, const Common::String &str, int x, int y, int w, uint32 c);
// Conversation
Actions _conversation;
bool _refreshConversation;
virtual void showConversation();
virtual void endConversation();
virtual void rightClickedConversation(const Common::Point &mousePos);
virtual void leftClickedConversation(const Common::Point &mousePos);
virtual bool hoverConversation(const Common::Point &mousePos);
// Credits
virtual void showCredits();
// Timers
int32 _countdown;
bool _timerStarted;
bool _keepTimerDuringScenes;
bool startAlarm(uint32, Common::String *);
bool startCountdown(uint32);
void removeTimers();
// Random
Common::RandomSource *_rnd;
};
struct chapterEntry {
int id;
int energyPos[2];
int scorePos[2];
int objectivesPos[2];
int ammoPos[2];
int ammoOffset;
int targetColor;
};
class WetEngine : public HypnoEngine {
public:
WetEngine(OSystem *syst, const ADGameDescription *gd);
~WetEngine();
Common::HashMap<int, const struct chapterEntry*> _chapterTable;
Common::Array<int> _ids;
int _lastLevel;
Common::String _name;
void loadAssets() override;
void loadAssetsDemoDisc();
void loadAssetsEarlyDemo();
void loadAssetsGen4();
void loadAssetsPCW();
void loadAssetsPCG();
void loadAssetsFullGame();
void loadAssetsNI();
void loadFonts(const Common::String &prefix = "") override;
void drawString(const Filename &name, const Common::String &str, int x, int y, int w, uint32 c) override;
void changeCursor(const Common::String &cursor) override;
void showCredits() override;
bool clickedSecondaryShoot(const Common::Point &mousePos) override;
void drawShoot(const Common::Point &target) override;
void drawPlayer() override;
void drawHealth() override;
void drawAmmo() override;
void hitPlayer() override;
void drawCursorArcade(const Common::Point &mousePos) override;
Common::Point computeTargetPosition(const Common::Point &mousePos) override;
void missedTarget(Shoot *s, ArcadeShooting *arc) override;
void missNoTarget(ArcadeShooting *arc) override;
void runCode(Code *code) override;
Common::String findNextLevel(const Common::String &level) override;
Common::String findNextLevel(const Transition *trans) override;
// Saves
Common::Error saveGameStream(Common::WriteStream *stream, bool isAutosave = false) override;
Common::Error loadGameStream(Common::SeekableReadStream *stream) override;
bool loadProfile(const Common::String &name);
void saveProfile(const Common::String &name, int levelId);
// Arcade
Common::Point getPlayerPosition(bool needsUpdate) override;
bool checkTransition(ArcadeTransitions &transitions, ArcadeShooting *arc) override;
void pressedKey(const int keycode) override;
void runBeforeArcade(ArcadeShooting *arc) override;
void runAfterArcade(ArcadeShooting *arc) override;
void findNextSegment(ArcadeShooting *arc) override;
void initSegment(ArcadeShooting *arc) override;
byte *getTargetColor(Common::String name, int levelId) override;
bool checkRButtonUp() override;
void setRButtonUp(const bool val) override;
void disableGameKeymaps() override;
void enableGameKeymaps() override;
bool hasFeature(EngineFeature f) const override {
return (f == kSupportsReturnToLauncher);
}
private:
Common::String getLocalizedString(const Common::String &name);
uint16 getNextChar(const Common::String &str, uint32 &c);
void drawGlyph(const Common::BitArray &font, int x, int y, int bitoffset, int width, int height, int pitch, uint32 color, bool invert);
void drawKoreanChar(uint16 chr, int &curx, int y, uint32 color);
void runMainMenu(Code *code);
void runLevelMenu(Code *code);
void runCheckLives(Code *code);
void endCredits(Code *code);
void showDemoScore();
uint32 findPaletteIndexZones(uint32 id);
Common::List<int> _scoreMilestones;
void restoreScoreMilestones(int score);
bool checkScoreMilestones(int score);
Frames _c33PlayerCursor;
Common::Point _c33PlayerPosition;
Common::List<PlayerPosition> _c33PlayerDirection;
bool _c33UseMouse;
void generateStaticEffect();
Common::BitArray _fontg9a;
Common::Array<uint32> _c40SegmentPath;
Common::Array<uint32> _c40SegmentNext;
int _c40SegmentIdx;
int _c40lastTurn;
int _c50LeftTurns;
int _c50RigthTurns;
bool _rButtonUp;
};
class SpiderEngine : public HypnoEngine {
public:
SpiderEngine(OSystem *syst, const ADGameDescription *gd);
~SpiderEngine();
void loadAssets() override;
void loadAssetsDemo();
void loadAssetsFullGame();
void showCredits() override;
void drawCursorArcade(const Common::Point &mousePos) override;
void drawShoot(const Common::Point &target) override;
void drawPlayer() override;
void drawHealth() override;
void missedTarget(Shoot *s, ArcadeShooting *arc) override;
void hitPlayer() override;
// Arcade
void pressedKey(const int keycode) override;
void runBeforeArcade(ArcadeShooting *arc) override;
void runAfterArcade(ArcadeShooting *arc) override;
void findNextSegment(ArcadeShooting *arc) override;
void initSegment(ArcadeShooting *arc) override;
byte *getTargetColor(Common::String name, int levelId) override;
void drawBackToMenu(Hotspot *h) override;
void runCode(Code *code) override;
Common::String findNextLevel(const Common::String &level) override;
Common::String findNextLevel(const Transition *trans) override;
void loadFonts(const Common::String &prefix = "") override;
void drawString(const Filename &name, const Common::String &str, int x, int y, int w, uint32 c) override;
void showConversation() override;
void endConversation() override;
void rightClickedConversation(const Common::Point &mousePos) override;
void leftClickedConversation(const Common::Point &mousePos) override;
bool hoverConversation(const Common::Point &mousePos) override;
void loadGame(const Common::String &nextLevel, int score, int puzzleDifficulty, int combatDifficulty) override;
Common::Error loadGameStream(Common::SeekableReadStream *stream) override;
Common::Error saveGameStream(Common::WriteStream *stream, bool isAutosave = false) override;
bool canSaveAutosaveCurrently() override {
return false; // No hypno engine should perform autosave using the default implementation
}
bool hasFeature(EngineFeature f) const override {
return (f == kSupportsSavingDuringRuntime || f == kSupportsLoadingDuringRuntime || f == kSupportsReturnToLauncher);
}
private:
void runMatrix(Code *code);
void addIngredient(Code *code);
void checkMixture(Code *code);
void runNote(Code *code);
void runFusePanel(Code *code);
void runRecept(Code *code);
void runOffice(Code *code);
void runFileCabinet(Code *code);
void runLock(Code *code);
void runFuseBox(Code *code);
void runGiveUp();
void showScore(const Common::String &prefix);
uint32 _currentPlayerPosition;
uint32 _lastPlayerPosition;
bool _fuseState[2][10] = {};
bool _isFuseRust = true;
bool _isFuseUnreadable = false;
bool ingredients[7] = {};
Common::Rect _h1Area;
Common::Rect _h2Area;
Common::Rect _h3Area;
const Graphics::Font *_font;
};
class BoyzEngine : public HypnoEngine {
public:
BoyzEngine(OSystem *syst, const ADGameDescription *gd);
~BoyzEngine();
Common::String _name;
Common::Array<int> _ids;
int _lastLevel;
bool _flashbackMode;
void loadAssets() override;
void runCode(Code *code) override;
Common::String findNextLevel(const Common::String &level) override;
Common::String findNextLevel(const Transition *trans) override;
// Scenes
void resetSceneState() override;
void runMenu(Hotspots *hs, bool only_menu = false) override;
bool hoverHotspot(Common::Point) override;
// Arcade
void runBeforeArcade(ArcadeShooting *arc) override;
void runAfterArcade(ArcadeShooting *arc) override;
void pressedKey(const int keycode) override;
int detectTarget(const Common::Point &mousePos) override;
void drawCursorArcade(const Common::Point &mousePos) override;
bool shoot(const Common::Point &mousePos, ArcadeShooting *arc, bool secondary) override;
bool clickedSecondaryShoot(const Common::Point &mousePos) override;
void showCredits() override;
// Stats
void showArcadeStats(int territory, const ArcadeStats &data);
ArcadeStats _lastStats;
ArcadeStats _globalStats;
void missedTarget(Shoot *s, ArcadeShooting *arc) override;
void drawHealth() override;
void drawAmmo() override;
void drawShoot(const Common::Point &target) override;
void hitPlayer() override;
void drawPlayer() override;
void findNextSegment(ArcadeShooting *arc) override;
void initSegment(ArcadeShooting *arc) override;
bool checkTransition(ArcadeTransitions &transitions, ArcadeShooting *arc) override;
void drawString(const Filename &name, const Common::String &str, int x, int y, int w, uint32 c) override;
// Saves
Common::Error saveGameStream(Common::WriteStream *stream, bool isAutosave = false) override;
Common::Error loadGameStream(Common::SeekableReadStream *stream) override;
Common::StringArray listProfiles();
bool loadProfile(const Common::String &name);
void saveProfile(const Common::String &name, int levelId);
private:
void renderHighlights(Hotspots *hs);
void waitForUserClick(uint32 timeout);
int pickABox();
int _selectedCorrectBox;
char selectDirection();
void runMainMenu(Code *code);
bool runExitMenu();
void runRetryMenu(Code *code);
void runCheckC3(Code *code);
void runCheckHo(Code *code);
void runCheckC5(Code *code);
void runAlarmC5(Code *code);
void runDifficultyMenu(Code *code);
void endCredits(Code *code);
int getTerritory(const Common::String &level);
Common::String lastLevelTerritory(const Common::String &level);
Common::String firstLevelTerritory(const Common::String &level);
void loadSceneState(Common::SeekableReadStream *stream);
void saveSceneState(Common::WriteStream *stream);
void unlockAllLevels();
int _previousHealth;
Graphics::Surface _healthBar[7];
Graphics::Surface _ammoBar[7];
Graphics::Surface _portrait[7];
Filename _deathDay[7];
Filename _deathNight[7];
Filename _weaponShootSound[8];
Filename _weaponReloadSound[8];
Filename _heySound[7];
int _weaponMaxAmmo[8];
byte *_crosshairsPalette;
Graphics::Surface _crosshairsInactive[8];
Graphics::Surface _crosshairsActive[8];
Graphics::Surface _crosshairsTarget[8];
Graphics::Surface _leftArrowPointer;
Graphics::Surface _rightArrowPointer;
Graphics::Surface _crossPointer;
void updateFromScript();
bool checkCup(const Common::String &name);
Script _currentScript;
ScriptMode _currentMode;
uint32 _currentActor;
uint32 _currentWeapon;
Common::Array<Filename> _warningVideosDay;
Common::Array<Filename> _warningVideosNight;
Common::Array<Filename> _warningAlarmVideos;
Filename _warningHostage;
Common::Array<Filename> _deathVideo;
Common::HashMap<Common::String, bool> _shootsDestroyed;
bool hasFeature(EngineFeature f) const override {
return (f == kSupportsReturnToLauncher);
}
};
} // End of namespace Hypno
#endif

2462
engines/hypno/lexer_arc.cpp Normal file

File diff suppressed because it is too large Load Diff

129
engines/hypno/lexer_arc.l Normal file
View File

@@ -0,0 +1,129 @@
%top{
/* 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/>.
*
*/
#define YY_NO_UNISTD_H
#define FORBIDDEN_SYMBOL_EXCEPTION_FILE
#define FORBIDDEN_SYMBOL_EXCEPTION_fprintf
#define FORBIDDEN_SYMBOL_EXCEPTION_fwrite
#define FORBIDDEN_SYMBOL_EXCEPTION_fread
#define FORBIDDEN_SYMBOL_EXCEPTION_stdin
#define FORBIDDEN_SYMBOL_EXCEPTION_stdout
#define FORBIDDEN_SYMBOL_EXCEPTION_stderr
#define FORBIDDEN_SYMBOL_EXCEPTION_exit
#define FORBIDDEN_SYMBOL_EXCEPTION_getc
#define YYERROR_VERBOSE
#include "hypno/hypno.h"
#include "hypno/grammar.h"
#include "hypno/tokens_arc.h"
}
%option noyywrap
%option noinput
%option nounput
%option yylineno
%option never-interactive
%option outfile="engines/hypno/lexer_arc.cpp"
%option prefix="HYPNO_ARC_"
%%
NONE return NONETOK;
C return CTOK;
D return DTOK;
HE return HETOK;
HL return HLTOK;
HU return HUTOK;
H[1-2] HYPNO_ARC_lval.s = scumm_strdup(HYPNO_ARC_text); return H12TOK;
H return HTOK;
P return PTOK;
AB return ABTOK;
AL return ALTOK;
AV return AVTOK;
A return ATOK;
V return VTOK;
O return OTOK;
O[0-1] HYPNO_ARC_lval.s = scumm_strdup(HYPNO_ARC_text); return ONTOK;
L return LTOK;
N return NTOK;
NR return NRTOK;
N\* return NSTOK;
M return MTOK;
R return RTOK;
R0 return R0TOK;
R1 return R1TOK;
I return ITOK;
I1 return I1TOK;
J0 return J0TOK;
J return JTOK;
K return KTOK;
G return GTOK;
Q return QTOK;
U return UTOK;
Z return ZTOK;
W return WTOK;
X return XTOK;
T return TTOK;
Tp return TPTOK;
Ts return TSTOK;
Ta return TATOK;
F[0-9] HYPNO_ARC_lval.s = scumm_strdup(HYPNO_ARC_text); return FNTOK;
F return FTOK;
S[0-9] HYPNO_ARC_lval.s = scumm_strdup(HYPNO_ARC_text); return SNTOK;
S[A-C] HYPNO_ARC_lval.s = scumm_strdup(HYPNO_ARC_text); return SNTOK;
A0 return A0TOK;
B[0-9A-F] HYPNO_ARC_lval.s = scumm_strdup(HYPNO_ARC_text); return BNTOK;
K[0-9] return KNTOK;
P0 return P0TOK;
Y[A-Z0-9] HYPNO_ARC_lval.s = scumm_strdup(HYPNO_ARC_text); return YXTOK;
22[k|K] HYPNO_ARC_lval.s = scumm_strdup(HYPNO_ARC_text); return ENCTOK;
11[k|K] HYPNO_ARC_lval.s = scumm_strdup(HYPNO_ARC_text); return ENCTOK;
[\-]?[0-9]+ HYPNO_ARC_lval.i = atoi(HYPNO_ARC_text); return NUM;
[A-Za-z_][A-Za-z_0-9]*[0-9\.]* HYPNO_ARC_lval.s = scumm_strdup(HYPNO_ARC_text); return NAME;
[A-Za-z][\-A-Za-z_0-9\\\.]+ HYPNO_ARC_lval.s = scumm_strdup(HYPNO_ARC_text); return FILENAME;
[0-9][\-A-Za-z_0-9\\\.]+ HYPNO_ARC_lval.s = scumm_strdup(HYPNO_ARC_text); return FILENAME;
320\,200 return RESTOK;
[\n|\r\n] return RETTOK;
\;.+\r /* ignore comment */
[ \t]+ /* ignore whitespace */;
. HYPNO_ARC_lval.i = HYPNO_ARC_text[0]; return BYTE;
%%
namespace Hypno {
extern Shoot *shoot;
int parse_arc(const char *code) {
YY_BUFFER_STATE bp;
yy_delete_buffer(YY_CURRENT_BUFFER);
bp = yy_scan_string(code);
yy_switch_to_buffer(bp);
HYPNO_ARC_parse();
yy_delete_buffer(bp);
delete shoot;
shoot = nullptr;
return 0;
}
} // End of namespace Hypno

2449
engines/hypno/lexer_mis.cpp Normal file

File diff suppressed because it is too large Load Diff

121
engines/hypno/lexer_mis.l Normal file
View File

@@ -0,0 +1,121 @@
%top{
/* 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/>.
*
*/
#define YY_NO_UNISTD_H
#define FORBIDDEN_SYMBOL_EXCEPTION_FILE
#define FORBIDDEN_SYMBOL_EXCEPTION_fprintf
#define FORBIDDEN_SYMBOL_EXCEPTION_fwrite
#define FORBIDDEN_SYMBOL_EXCEPTION_fread
#define FORBIDDEN_SYMBOL_EXCEPTION_stdin
#define FORBIDDEN_SYMBOL_EXCEPTION_stdout
#define FORBIDDEN_SYMBOL_EXCEPTION_stderr
#define FORBIDDEN_SYMBOL_EXCEPTION_exit
#define FORBIDDEN_SYMBOL_EXCEPTION_getc
#include "hypno/hypno.h"
#include "hypno/grammar.h"
#include "hypno/tokens_mis.h"
}
%option noyywrap
%option noinput
%option nounput
%option yylineno
%option never-interactive
%option outfile="engines/hypno/lexer_mis.cpp"
%option prefix="HYPNO_MIS_"
%%
\;.+ /* return COMMENT; */
\/\/.+ /* return COMMENT; */
MENU return MENUTOK;
AMBI return AMBITOK;
BACK return BACKTOK;
CUTS return CUTSTOK;
GLOB return GLOBTOK;
PALE return PALETOK;
HOTS return HOTSTOK;
MICE return MICETOK;
END return ENDTOK;
TIME return TIMETOK;
OVER return OVERTOK;
SMEN return SMENTOK;
ESCP return ESCPTOK;
PLAY return PLAYTOK;
SOND return SONDTOK;
TALK return TALKTOK;
INACTIVE return INACTOK;
4DBOX return FDTOK;
BOXX return BOXXTOK;
MPTR return MPTRTOK;
ESCAPE return ESCAPETOK;
SECOND return SECONDTOK;
INTRO return INTROTOK;
INTR return INTRTOK;
SWPT return SWPTTOK;
DEFAULT return DEFAULTTOK;
WAL[0-1] HYPNO_MIS_lval.s = scumm_strdup(HYPNO_MIS_text); return WALNTOK;
\|S[A-Za-z_0-9\\\.]+ HYPNO_MIS_lval.s = scumm_strdup(HYPNO_MIS_text); return PS;
\|G[A-Za-z_0-9\\\.]+ HYPNO_MIS_lval.s = scumm_strdup(HYPNO_MIS_text); return PG;
\|P[A-Za-z_0-9\\\.]+ HYPNO_MIS_lval.s = scumm_strdup(HYPNO_MIS_text); return PP;
\|I[A-Za-z_0-9\\\.]+ HYPNO_MIS_lval.s = scumm_strdup(HYPNO_MIS_text); return PI;
\|H[0-9]+ HYPNO_MIS_lval.s = scumm_strdup(HYPNO_MIS_text); return PH;
\|A[0-9]+ HYPNO_MIS_lval.s = scumm_strdup(HYPNO_MIS_text); return PA;
\|D[0-9]+ HYPNO_MIS_lval.s = scumm_strdup(HYPNO_MIS_text); return PD;
\|F[0-9]+ HYPNO_MIS_lval.s = scumm_strdup(HYPNO_MIS_text); return PF;
\|E return PE;
\|L return PL;
22[k|K] HYPNO_MIS_lval.s = scumm_strdup(HYPNO_MIS_text); return ENCTOK;
11[k|K] HYPNO_MIS_lval.s = scumm_strdup(HYPNO_MIS_text); return ENCTOK;
GS_[A-Z_0-9]+ HYPNO_MIS_lval.s = scumm_strdup(HYPNO_MIS_text); return GSSWITCH;
\/BBOX\= return BBOXTOK;
\/[A-Za-z_0-9]* HYPNO_MIS_lval.s = scumm_strdup(HYPNO_MIS_text); return FLAG;
[A-Za-z_][A-Za-z_0-9]* HYPNO_MIS_lval.s = scumm_strdup(HYPNO_MIS_text); return NAME;
[A-Za-z][A-Za-z_0-9\\\.]* HYPNO_MIS_lval.s = scumm_strdup(HYPNO_MIS_text); return FILENAME;
[\-]?[0-9]+ HYPNO_MIS_lval.i = atoi(HYPNO_MIS_text); return NUM;
[\n|\r\n] return RETTOK;
[ \t]+ /* ignore whitespace */;
. debugC(1, Hypno::kHypnoDebugParser, "<no match: %c>", *yytext); return *yytext;
%%
namespace Hypno {
extern Common::Array<uint32> *smenu_idx;
extern HotspotsStack *stack;
int parse_mis(const char *code) {
YY_BUFFER_STATE bp;
yy_delete_buffer(YY_CURRENT_BUFFER);
bp = yy_scan_string(code);
yy_switch_to_buffer(bp);
HYPNO_MIS_parse();
yy_delete_buffer(bp);
delete smenu_idx;
smenu_idx = nullptr;
delete stack;
stack = nullptr;
return 0;
}
} // End of namespace Hypno

145
engines/hypno/libfile.cpp Normal file
View File

@@ -0,0 +1,145 @@
/* 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 "hypno/libfile.h"
#include "hypno/hypno.h"
namespace Hypno {
LibFile::LibFile() : Common::Archive() {
_libfile = nullptr;
_encrypted = true;
}
LibFile::~LibFile() {
close();
}
bool LibFile::open(const Common::Path &prefix, const Common::Path &filename, bool encrypted) {
close();
_prefix = prefix;
_encrypted = encrypted;
_libfile = new Common::File();
if (!_libfile->open(filename)) {
warning("Failed to open %s", filename.toString(Common::Path::kNativeSeparator).c_str());
return false;
}
uint32 offset = 0;
while (offset < _libfile->size()) {
byte b;
uint32 size = 0;
uint32 start = _libfile->size();
FileEntry f;
_libfile->seek(offset);
debugC(1, kHypnoDebugParser, "parsing at offset %d with size %li", offset, long(_libfile->size()));
while (true) {
Common::String fname;
for (uint32 i = 0; i < 12; i++) {
b = _libfile->readByte();
if (b != 0x96 && b != 0x0)
fname += tolower(char(b));
}
if (!Common::isAlnum(*fname.c_str()))
break;
debugC(1, kHypnoDebugParser, "file: %s", fname.c_str());
f.name = Common::Path(fname);
f.start = start = _libfile->readUint32LE();
f.size = size = _libfile->readUint32LE();
if (size == 0)
error("Trying to load an empty file");
_libfile->readUint32LE(); // some field?
debugC(1, kHypnoDebugParser, "start: %d, size: %d", f.start, f.size);
_fileEntries.push_back(f);
};
offset = start + size;
}
return true;
}
const FileEntry *LibFile::getEntry(const Common::Path &path) const {
for (Common::Array<FileEntry>::const_iterator it = _fileEntries.begin(); it != _fileEntries.end(); ++it) {
if (((_prefix.join(it->name)).equalsIgnoreCase(path)) || it->name.equalsIgnoreCase(path))
return it;
}
return nullptr;
}
void LibFile::close() {
delete _libfile; _libfile = nullptr;
_fileEntries.clear();
}
bool LibFile::hasFile(const Common::Path &path) const {
return getEntry(path) != nullptr;
}
int LibFile::listMembers(Common::ArchiveMemberList &list) const {
list.clear();
for (Common::Array<FileEntry>::const_iterator it = _fileEntries.begin(); it != _fileEntries.end(); ++it)
list.push_back(getMember(it->name));
return list.size();
}
const Common::ArchiveMemberPtr LibFile::getMember(const Common::Path &path) const {
return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(path, *this));
}
Common::SeekableReadStream *LibFile::createReadStreamForMember(const Common::Path &path) const {
const FileEntry *entry = getEntry(path);
if (!entry)
return nullptr;
byte *data = (byte *)malloc(entry->size);
if (!data) {
warning("Not enough memory to load archive entry %s", path.toString().c_str());
return nullptr;
}
_libfile->seek(entry->start);
_libfile->read(data, entry->size);
Common::String name = path.baseName();
name.toLowercase(); // just in case
if (name.hasSuffix(".raw")) {
for (uint32 i = 0; i < entry->size; i++) {
data[i] = data[i] ^ 0xfe;
}
} else if (_encrypted) {
for (uint32 i = 0; i < entry->size; i++) {
if (data[i] != '\n')
data[i] = data[i] ^ 0xfe;
}
}
return new Common::MemoryReadStream(data, entry->size, DisposeAfterUse::YES);
}
} // namespace Hypno

64
engines/hypno/libfile.h Normal file
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 HYPNO_LIBFILE_H
#define HYPNO_LIBFILE_H
#include "common/archive.h"
#include "common/array.h"
#include "common/file.h"
#include "common/memstream.h"
#include "common/stream.h"
namespace Hypno {
typedef struct FileEntry {
Common::Path name;
uint32 start;
uint32 size;
} FileEntry;
class LibFile : public Common::Archive {
public:
LibFile();
~LibFile() override;
bool open(const Common::Path &prefix, const Common::Path &filename, bool encrypted);
void close();
// Common::Archive API implementation
bool hasFile(const Common::Path &path) const override;
int listMembers(Common::ArchiveMemberList &list) const override;
const Common::ArchiveMemberPtr getMember(const Common::Path &path) const override;
Common::SeekableReadStream *createReadStreamForMember(const Common::Path &path) const override;
private:
bool _encrypted;
Common::File *_libfile;
Common::Path _prefix;
Common::Array<FileEntry> _fileEntries;
const FileEntry *getEntry(const Common::Path &path) const;
};
} // End of namespace Hypno
#endif

View File

@@ -0,0 +1,399 @@
/* 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 "common/translation.h"
#include "hypno/hypno.h"
#include "hypno/detection.h"
#include "backends/keymapper/action.h"
#include "backends/keymapper/keymapper.h"
#include "backends/keymapper/standard-actions.h"
static const ADExtraGuiOptionsMap optionsList[] = {
{
GAMEOPTION_ORIGINAL_CHEATS,
{
_s("Enable original cheats"),
_s("Allow cheats using the C key."),
"cheats",
true,
0,
0
}
},
{
GAMEOPTION_INFINITE_HEALTH,
{
_s("Enable infinite health cheat"),
_s("Player health will never decrease (except for game over scenes)."),
"infiniteHealth",
false,
0,
0
}
},
{
GAMEOPTION_INFINITE_AMMO,
{
_s("Enable infinite ammo cheat"),
_s("Player ammo will never decrease."),
"infiniteAmmo",
false,
0,
0
}
},
{
GAMEOPTION_UNLOCK_ALL_LEVELS,
{
_s("Unlock all levels"),
_s("All levels will be available to play."),
"unlockAllLevels",
false,
0,
0
}
},
{
GAMEOPTION_RESTORED_CONTENT,
{
_s("Enable restored content"),
_s("Add additional content that is not enabled the original implementation."),
"restored",
true,
0,
0
}
},
AD_EXTRA_GUI_OPTIONS_TERMINATOR
};
class HypnoMetaEngine : public AdvancedMetaEngine<ADGameDescription> {
public:
const char *getName() const override {
return "hypno";
}
const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override {
return optionsList;
}
Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override;
Common::KeymapArray initKeymaps(const char *target) const override;
};
Common::Error HypnoMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
if (Common::String(desc->gameId) == "wetlands") {
*engine = (Engine *)new Hypno::WetEngine(syst, desc);
} else if (Common::String(desc->gameId) == "sinistersix") {
*engine = (Engine *)new Hypno::SpiderEngine(syst, desc);
} else if (Common::String(desc->gameId) == "soldierboyz") {
*engine = (Engine *)new Hypno::BoyzEngine(syst, desc);
} else
return Common::kUnsupportedGameidError;
return Common::kNoError;
}
Common::KeymapArray HypnoMetaEngine::initKeymaps(const char *target) const {
using namespace Common;
using namespace Hypno;
Common::String gameId = ConfMan.get("gameid", target);
KeymapArray keymaps;
Keymap *engineKeymap = new Keymap(Keymap::kKeymapTypeGame, "hypno-default", _("Default keymappings"));
Keymap *introKeymap = new Keymap(Keymap::kKeymapTypeGame, "intro", _("Intro keymappings"));
Keymap *cutsceneKeymap = new Keymap(Keymap::kKeymapTypeGame, "cutscene", _("Cutscene keymappings"));
Common::Action *act;
act = new Common::Action(kStandardActionLeftClick, _("Primary shoot"));
act->setLeftClickEvent();
act->addDefaultInputMapping("MOUSE_LEFT");
act->addDefaultInputMapping("JOY_A");
engineKeymap->addAction(act);
act = new Common::Action(kStandardActionRightClick, _("Secondary shoot"));
act->setRightClickEvent();
act->addDefaultInputMapping("MOUSE_RIGHT");
act->addDefaultInputMapping("JOY_B");
engineKeymap->addAction(act);
act = new Common::Action("SKIPINTRO", _("Skip intro"));
act->setCustomEngineActionEvent(kActionSkipIntro);
act->addDefaultInputMapping("ESCAPE");
act->addDefaultInputMapping("JOY_Y");
introKeymap->addAction(act);
act = new Common::Action("SKIPCUTSCENE", _("Skip cutscene"));
act->setCustomEngineActionEvent(kActionSkipCutscene);
act->addDefaultInputMapping("ESCAPE");
act->addDefaultInputMapping("JOY_Y");
cutsceneKeymap->addAction(act);
keymaps.push_back(engineKeymap);
keymaps.push_back(introKeymap);
keymaps.push_back(cutsceneKeymap);
introKeymap->setEnabled(false);
cutsceneKeymap->setEnabled(false);
if (gameId == "soldierboyz") {
Keymap *gameKeymap = new Keymap(Keymap::kKeymapTypeGame, "game-shortcuts", _("Game keymappings"));
Keymap *exitMenuKeymap = new Keymap(Keymap::kKeymapTypeGame, "exit-menu", _("Exit menu keymappings"));
Keymap *difficulyMenuKeymap = new Keymap(Keymap::kKeymapTypeGame, "difficulty-menu", _("Difficulty selection menu keymappings"));
Keymap *retryMenuKeymap = new Keymap(Keymap::kKeymapTypeGame, "retry-menu", _("Retry menu keymappings"));
if (ConfMan.getBool("cheats",target)) {
act = new Common::Action("SKIPLEVEL", _("Skip level (cheat)"));
act->setCustomEngineActionEvent(kActionSkipLevel);
act->addDefaultInputMapping("c");
act->addDefaultInputMapping("JOY_X");
gameKeymap->addAction(act);
}
// I18N: (Game name: Soldier Boyz) player refers to the users own character.
act = new Common::Action("KILLPLAYER", _("Kill player"));
act->setCustomEngineActionEvent(kActionKillPlayer);
act->addDefaultInputMapping("k");
act->addDefaultInputMapping("JOY_LEFT");
gameKeymap->addAction(act);
act = new Common::Action("PAUSE", _("Pause"));
act->setCustomEngineActionEvent(kActionPause);
act->addDefaultInputMapping("ESCAPE");
act->addDefaultInputMapping("JOY_UP");
gameKeymap->addAction(act);
act = new Common::Action("YES", _("Yes"));
act->setCustomEngineActionEvent(kActionYes);
act->addDefaultInputMapping("y");
act->addDefaultInputMapping("JOY_A");
exitMenuKeymap->addAction(act);
act = new Common::Action("NO", _("No"));
act->setCustomEngineActionEvent(kActionNo);
act->addDefaultInputMapping("n");
act->addDefaultInputMapping("JOY_B");
exitMenuKeymap->addAction(act);
// I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Chump is the easy mode.
act = new Common::Action("CHUMP", _("Chump"));
act->setCustomEngineActionEvent(kActionDifficultyChump);
act->addDefaultInputMapping("c");
act->addDefaultInputMapping("JOY_LEFT");
difficulyMenuKeymap->addAction(act);
// I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Punk is the medium mode.
act = new Common::Action("PUNK", _("Punk"));
act->setCustomEngineActionEvent(kActionDifficultyPunk);
act->addDefaultInputMapping("p");
act->addDefaultInputMapping("JOY_UP");
difficulyMenuKeymap->addAction(act);
// I18N: (Game name: Soldier Boyz) the game has 3 difficulty levels: Chump, Punk and Badass. Badass is the hard mode.
act = new Common::Action("BADASS", _("Badass"));
act->setCustomEngineActionEvent(kActionDifficultyBadass);
act->addDefaultInputMapping("b");
act->addDefaultInputMapping("JOY_RIGHT");
difficulyMenuKeymap->addAction(act);
act = new Common::Action("CANCEL", _("Cancel"));
act->setCustomEngineActionEvent(kActionDifficultExit);
act->addDefaultInputMapping("a");
act->addDefaultInputMapping("JOY_DOWN");
difficulyMenuKeymap->addAction(act);
// I18N: (Game name: Soldier Boyz) This makes the player restart from the last checkpoint.
act = new Common::Action("RETRY", _("Retry sector"));
act->setCustomEngineActionEvent(kActionRetry);
act->addDefaultInputMapping("s");
act->addDefaultInputMapping("JOY_LEFT");
retryMenuKeymap->addAction(act);
// I18N: (Game name: Soldier Boyz) This makes the player restart the current mission / level.
act = new Common::Action("RESTART", _("Restart territory"));
act->setCustomEngineActionEvent(kActionRestart);
act->addDefaultInputMapping("t");
act->addDefaultInputMapping("JOY_UP");
retryMenuKeymap->addAction(act);
act = new Common::Action("EXIT", _("Begin new mission"));
act->setCustomEngineActionEvent(kActionNewMission);
act->addDefaultInputMapping("n");
act->addDefaultInputMapping("JOY_RIGHT");
retryMenuKeymap->addAction(act);
act = new Common::Action("QUIT", _("Quit"));
act->setCustomEngineActionEvent(kActionQuit);
act->addDefaultInputMapping("q");
act->addDefaultInputMapping("JOY_DOWN");
retryMenuKeymap->addAction(act);
keymaps.push_back(gameKeymap);
keymaps.push_back(exitMenuKeymap);
keymaps.push_back(difficulyMenuKeymap);
keymaps.push_back(retryMenuKeymap);
exitMenuKeymap->setEnabled(false);
difficulyMenuKeymap->setEnabled(false);
retryMenuKeymap->setEnabled(false);
} else if (gameId == "sinistersix") {
Keymap *gameKeymap = new Keymap(Keymap::kKeymapTypeGame, "game-shortcuts", _("Game keymappings"));
if (ConfMan.getBool("cheats", target)) {
act = new Common::Action("SKIPLEVEL", _("Skip level (cheat)"));
act->setCustomEngineActionEvent(kActionSkipLevel);
act->addDefaultInputMapping("c");
act->addDefaultInputMapping("JOY_X");
gameKeymap->addAction(act);
}
// I18N: (Game name: Marvel Comics Spider-Man: The Sinister Six) player refers to the users own character.
act = new Common::Action("KILLPLAYER", _("Kill player"));
act->setCustomEngineActionEvent(kActionKillPlayer);
act->addDefaultInputMapping("k");
act->addDefaultInputMapping("JOY_Y");
gameKeymap->addAction(act);
act = new Common::Action("LEFT", _("Move left"));
act->setCustomEngineActionEvent(kActionLeft);
act->addDefaultInputMapping("LEFT");
act->addDefaultInputMapping("JOY_LEFT");
gameKeymap->addAction(act);
act = new Common::Action("DOWN", _("Move down"));
act->setCustomEngineActionEvent(kActionDown);
act->addDefaultInputMapping("DOWN");
act->addDefaultInputMapping("JOY_DOWN");
gameKeymap->addAction(act);
act = new Common::Action("RIGHT", _("Move right"));
act->setCustomEngineActionEvent(kActionRight);
act->addDefaultInputMapping("RIGHT");
act->addDefaultInputMapping("JOY_RIGHT");
gameKeymap->addAction(act);
act = new Common::Action("UP", _("Move up"));
act->setCustomEngineActionEvent(kActionUp);
act->addDefaultInputMapping("UP");
act->addDefaultInputMapping("JOY_UP");
gameKeymap->addAction(act);
keymaps.push_back(gameKeymap);
} else if (gameId == "wetlands") {
Keymap *gameKeymap = new Keymap(Keymap::kKeymapTypeGame, "game-shortcuts", _("Game keymappings"));
Keymap *menuKeymap = new Keymap(Keymap::kKeymapTypeGame, "menu", _("Menu keymappings"));
Keymap *pauseKeymap = new Keymap(Keymap::kKeymapTypeGame, "pause", _("Pause keymappings"));
Keymap *directionKeymap = new Keymap(Keymap::kKeymapTypeGame, "direction", _("Direction keymappings"));
act = new Common::Action("CREDITS", _("Show credits"));
act->setCustomEngineActionEvent(kActionCredits);
act->addDefaultInputMapping("c");
act->addDefaultInputMapping("JOY_LEFT_TRIGGER");
gameKeymap->addAction(act);
act = new Common::Action("SKIPLEVEL", _("Skip level (cheat)"));
act->setCustomEngineActionEvent(kActionSkipLevel);
act->addDefaultInputMapping("s");
act->addDefaultInputMapping("JOY_X");
gameKeymap->addAction(act);
// I18N: (Game name: Wetlands) player refers to the users own character.
act = new Common::Action("KILLPLAYER", _("Kill player"));
act->setCustomEngineActionEvent(kActionKillPlayer);
act->addDefaultInputMapping("k");
act->addDefaultInputMapping("JOY_RIGHT_TRIGGER");
gameKeymap->addAction(act);
act = new Common::Action("LEFT", _("Move left"));
act->setCustomEngineActionEvent(kActionLeft);
act->allowKbdRepeats();
act->addDefaultInputMapping("LEFT");
act->addDefaultInputMapping("JOY_LEFT");
directionKeymap->addAction(act);
act = new Common::Action("DOWN", _("Move down"));
act->setCustomEngineActionEvent(kActionDown);
act->allowKbdRepeats();
act->addDefaultInputMapping("DOWN");
act->addDefaultInputMapping("JOY_DOWN");
directionKeymap->addAction(act);
act = new Common::Action("RIGHT", _("Move right"));
act->setCustomEngineActionEvent(kActionRight);
act->allowKbdRepeats();
act->addDefaultInputMapping("RIGHT");
act->addDefaultInputMapping("JOY_RIGHT");
directionKeymap->addAction(act);
act = new Common::Action("UP", _("Move up"));
act->setCustomEngineActionEvent(kActionUp);
act->allowKbdRepeats();
act->addDefaultInputMapping("UP");
act->addDefaultInputMapping("JOY_UP");
directionKeymap->addAction(act);
act = new Common::Action("SELECT", _("Select"));
act->setCustomEngineActionEvent(kActionSelect);
act->addDefaultInputMapping("RETURN");
act->addDefaultInputMapping("JOY_X");
menuKeymap->addAction(act);
act = new Common::Action("PAUSE", _("Pause"));
act->setCustomEngineActionEvent(kActionPause);
act->addDefaultInputMapping("ESCAPE");
act->addDefaultInputMapping("JOY_Y");
pauseKeymap->addAction(act);
keymaps.push_back(gameKeymap);
keymaps.push_back(menuKeymap);
keymaps.push_back(pauseKeymap);
keymaps.push_back(directionKeymap);
menuKeymap->setEnabled(false);
}
return keymaps;
}
namespace Hypno {
bool HypnoEngine::isDemo() const {
return (bool)(_gameDescription->flags & ADGF_DEMO);
}
} // End of namespace Hypno
#if PLUGIN_ENABLED_DYNAMIC(HYPNO)
REGISTER_PLUGIN_DYNAMIC(HYPNO, PLUGIN_TYPE_ENGINE, HypnoMetaEngine);
#else
REGISTER_PLUGIN_STATIC(HYPNO, PLUGIN_TYPE_ENGINE, HypnoMetaEngine);
#endif

50
engines/hypno/module.mk Normal file
View File

@@ -0,0 +1,50 @@
MODULE := engines/hypno
MODULE_OBJS := \
actions.o \
arcade.o \
boyz/arcade.o \
boyz/boyz.o \
boyz/hard.o \
boyz/scene.o \
cursors.o \
grammar_mis.o \
grammar_arc.o \
hypno.o \
lexer_mis.o \
lexer_arc.o \
libfile.o \
metaengine.o \
scene.o \
spider/arcade.o \
spider/hard.o \
spider/spider.o \
spider/talk.o \
video.o \
wet/arcade.o \
wet/cursors.o \
wet/hard.o \
wet/wet.o
MODULE_DIRS += \
engines/hypno
# HACK: Skip this when including the file for detection objects.
ifeq "$(LOAD_RULES_MK)" "1"
hypno-grammar:
flex engines/hypno/lexer_arc.l
bison engines/hypno/grammar_arc.y
flex engines/hypno/lexer_mis.l
bison engines/hypno/grammar_mis.y
endif
# This module can be built as a plugin
ifeq ($(ENABLE_HYPNO), DYNAMIC_PLUGIN)
PLUGIN := 1
endif
# Include common rules
include $(srcdir)/rules.mk
# Detection objects
DETECT_OBJS += $(MODULE)/detection.o

580
engines/hypno/scene.cpp Normal file
View 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 &current, 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

View File

@@ -0,0 +1,347 @@
/* 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 "hypno/grammar.h"
#include "hypno/hypno.h"
#include "common/events.h"
#include "graphics/cursorman.h"
#include "gui/message.h"
namespace Hypno {
static const int oIndexYB[9] = {0, 1, 2, 7, 8, 3, 6, 5, 4};
static const int oIndexYE[9] = {4, 3, 2, 1, 0};
static const int shootOriginIndex[9][2] = {
{41, 3}, {51, 3}, {65, 6}, {40, 16}, {58, 20}, {67, 10}, {37, 14}, {37, 15}, {67, 22}};
void SpiderEngine::runBeforeArcade(ArcadeShooting *arc) {
_health = arc->health;
_maxHealth = _health;
resetStatistics();
_checkpoint = _currentLevel;
assert(!arc->player.empty());
_playerFrames = decodeFrames(arc->player);
_playerFrameSep = 0;
for (Frames::iterator it =_playerFrames.begin(); it != _playerFrames.end(); ++it) {
if ((*it)->getPixel(0, 0) == 255)
break;
if ((*it)->getPixel(0, 0) == 252)
break;
_playerFrameSep++;
}
if (_playerFrameSep == (int)_playerFrames.size()) {
debugC(1, kHypnoDebugArcade, "No player separator frame found in %s! (size: %d)", arc->player.c_str(), _playerFrames.size());
} else
debugC(1, kHypnoDebugArcade, "Separator frame found at %d", _playerFrameSep);
_playerFrameIdx = -1;
_currentPlayerPosition = kPlayerLeft;
_lastPlayerPosition = kPlayerLeft;
}
void SpiderEngine::runAfterArcade(ArcadeShooting *arc) {
if (_health <= 0) {
assert(_score >= _bonus);
_score -= _bonus;
}
for (Frames::iterator it =_playerFrames.begin(); it != _playerFrames.end(); ++it) {
(*it)->free();
delete (*it);
}
_playerFrames.clear();
if (isDemo() && _restoredContentEnabled) {
if (_health <= 0)
showScore("Spider-man was defeated!");
else
showScore("Spider-Man saved the day!");
_score = 0;
}
}
void SpiderEngine::initSegment(ArcadeShooting *arc) {
_segmentShootSequenceOffset = 0;
_segmentShootSequenceMax = 0;
uint32 randomSegmentShootSequence = _segmentShootSequenceOffset + _rnd->getRandomNumber(_segmentShootSequenceMax);
SegmentShoots segmentShoots = arc->shootSequence[randomSegmentShootSequence];
_shootSequence = segmentShoots.shootSequence;
_segmentRepetitionMax = segmentShoots.segmentRepetition; // Usually zero
_segmentRepetition = 0;
_segmentOffset = 0;
_segmentIdx = _segmentOffset;
}
void SpiderEngine::findNextSegment(ArcadeShooting *arc) {
_segmentIdx = _segmentIdx + 1;
}
void SpiderEngine::pressedKey(const int keycode) {
if (keycode == kActionSkipLevel) {
_skipLevel = true;
return;
} else if (keycode == kActionKillPlayer) { // Added for testing
_health = 0;
} else if (keycode == kActionLeft) {
_lastPlayerPosition = _currentPlayerPosition;
_currentPlayerPosition = kPlayerLeft;
} else if (keycode == kActionDown) {
_lastPlayerPosition = _currentPlayerPosition;
_currentPlayerPosition = kPlayerBottom;
} else if (keycode == kActionRight) {
_lastPlayerPosition = _currentPlayerPosition;
_currentPlayerPosition = kPlayerRight;
} else if (keycode == kActionUp) {
_lastPlayerPosition = _currentPlayerPosition;
_currentPlayerPosition = kPlayerTop;
}
}
void SpiderEngine::missedTarget(Shoot *s, ArcadeShooting *arc) {
if (_arcadeMode != "YC" && _arcadeMode != "YD")
return;
if ((uint32)(s->name[0]) == _currentPlayerPosition) {
if (!_infiniteHealthCheat)
_health = _health - s->attackWeight;
hitPlayer();
}
}
void SpiderEngine::hitPlayer() {
if (_playerFrameSep < (int)_playerFrames.size()) {
if (_playerFrameIdx < _playerFrameSep)
_playerFrameIdx = _playerFrameSep;
} else {
uint32 c = kHypnoColorRed; // red
_compositeSurface->fillRect(Common::Rect(0, 0, 640, 480), c);
drawScreen();
}
if (!_hitSound.empty())
playSound(_soundPath + _hitSound, 1, 11025);
}
void SpiderEngine::drawShoot(const Common::Point &target) {
uint32 c = kSpiderColorWhite; // white
uint32 ox = 0;
uint32 oy = 0;
if (_arcadeMode == "YC" || _arcadeMode == "YD") {
return; // Nothing to shoot
} else if (_arcadeMode == "YE" || _arcadeMode == "YF") {
ox = _screenW / 2;
oy = _screenH - _playerFrames[0]->h / 2;
} else if (_arcadeMode == "YB") {
uint32 idx = MIN(2, target.x / (_screenW / 3)) + 3 * MIN(2, target.y / (_screenH / 3));
ox = 60 + shootOriginIndex[idx][0];
oy = 129 + shootOriginIndex[idx][1];
} else
error("Invalid arcade mode %s", _arcadeMode.c_str());
_compositeSurface->drawLine(ox, oy, target.x + 2, target.y, c);
_compositeSurface->drawLine(ox, oy, target.x, target.y, c);
_compositeSurface->drawLine(ox, oy, target.x - 2, target.y, c);
playSound(_soundPath + _shootSound, 1);
}
void SpiderEngine::drawPlayer() {
uint32 ox = 0;
uint32 oy = 0;
if (_arcadeMode == "YC" || _arcadeMode == "YD") {
disableCursor(); // Not sure this a good place
ox = 0;
oy = 0;
if (_playerFrameIdx < 0)
_playerFrameIdx = 0;
else if (_lastPlayerPosition != _currentPlayerPosition && (_playerFrameIdx % 4 == 0 || _playerFrameIdx % 4 == 3)) {
switch (_lastPlayerPosition) {
case kPlayerLeft:
switch (_currentPlayerPosition) {
case kPlayerTop:
_lastPlayerPosition = _currentPlayerPosition;
_playerFrameIdx = 1;
break;
case kPlayerBottom:
_lastPlayerPosition = _currentPlayerPosition;
_playerFrameIdx = 13;
break;
case kPlayerRight:
_lastPlayerPosition = _currentPlayerPosition;
_playerFrameIdx = 45;
break;
}
break;
case kPlayerRight:
switch (_currentPlayerPosition) {
case kPlayerTop:
_lastPlayerPosition = _currentPlayerPosition;
_playerFrameIdx = 5;
break;
case kPlayerBottom:
_lastPlayerPosition = _currentPlayerPosition;
_playerFrameIdx = 17;
break;
case kPlayerLeft:
_lastPlayerPosition = _currentPlayerPosition;
_playerFrameIdx = 33;
break;
}
break;
case kPlayerBottom:
switch (_currentPlayerPosition) {
case kPlayerTop:
_lastPlayerPosition = _currentPlayerPosition;
_playerFrameIdx = 9;
break;
case kPlayerLeft:
_lastPlayerPosition = _currentPlayerPosition;
_playerFrameIdx = 29;
break;
case kPlayerRight:
_lastPlayerPosition = _currentPlayerPosition;
_playerFrameIdx = 41;
break;
}
break;
case kPlayerTop:
switch (_currentPlayerPosition) {
case kPlayerBottom:
_playerFrameIdx = 21;
break;
case kPlayerLeft:
_playerFrameIdx = 25;
break;
case kPlayerRight:
_playerFrameIdx = 37;
break;
}
break;
}
_lastPlayerPosition = _currentPlayerPosition;
} else if (_playerFrameIdx < 48 && _playerFrameIdx % 4 != 0 && _playerFrameIdx % 4 != 3) {
_playerFrameIdx++;
_lastPlayerPosition = _currentPlayerPosition;
} else {
if (_arcadeMode == "YD") {
switch (_lastPlayerPosition) {
case kPlayerTop:
if ((_playerFrameIdx <= 11 && (_playerFrameIdx % 4 == 0 || _playerFrameIdx % 4 == 3)) || _playerFrameIdx >= 54)
_playerFrameIdx = 49;
else
_playerFrameIdx++;
break;
case kPlayerBottom:
if ((_playerFrameIdx <= 23 && (_playerFrameIdx % 4 == 0 || _playerFrameIdx % 4 == 3)) || _playerFrameIdx >= 65)
_playerFrameIdx = 60;
else
_playerFrameIdx++;
break;
case kPlayerLeft:
if ((_playerFrameIdx <= 35 && (_playerFrameIdx % 4 == 0 || _playerFrameIdx % 4 == 3)) || _playerFrameIdx >= 77)
_playerFrameIdx = 72;
else
_playerFrameIdx++;
break;
case kPlayerRight:
if ((_playerFrameIdx <= 47 && (_playerFrameIdx % 4 == 0 || _playerFrameIdx % 4 == 3)) || _playerFrameIdx >= 89)
_playerFrameIdx = 84;
else
_playerFrameIdx++;
break;
}
}
}
} else if (_arcadeMode == "YE" || _arcadeMode == "YF") {
if (_arcadeMode == "YF") {
int fraction = _background->decoder->getFrameCount() / (_maxHealth / 2);
if (_background->decoder->getCurFrame() % fraction == 0)
_health = MAX(1, _health - 1);
if (checkArcadeObjectives())
_skipLevel = true;
}
Common::Point mousePos = g_system->getEventManager()->getMousePos();
uint32 idx = mousePos.x / (_screenW / 5);
_playerFrameIdx = oIndexYE[idx];
ox = _screenW / 2 - _playerFrames[0]->w / 2;
oy = _screenH - _playerFrames[0]->h;
} else if (_arcadeMode == "YB") {
ox = 60;
oy = 129;
if (_playerFrameIdx < _playerFrameSep) {
Common::Point mousePos = g_system->getEventManager()->getMousePos();
uint32 idx = MIN(2, mousePos.x / (_screenW / 3)) + 3 * MIN(2, mousePos.y / (_screenH / 3));
_playerFrameIdx = oIndexYB[idx];
} else {
_playerFrameIdx++;
if (_playerFrameIdx >= (int)_playerFrames.size())
_playerFrameIdx = 0;
}
} else
error("Invalid arcade mode %s", _arcadeMode.c_str());
drawImage(*_playerFrames[_playerFrameIdx], ox, oy, true);
}
void SpiderEngine::drawCursorArcade(const Common::Point &mousePos) {
if (_arcadeMode != "YC" && _arcadeMode != "YD") {
HypnoEngine::drawCursorArcade(mousePos);
}
}
void SpiderEngine::drawHealth() {
Common::Rect r;
uint32 c;
int d = (22 * (_maxHealth - _health) / _maxHealth);
if (d >= 22)
return;
r = Common::Rect(256, 152 + d, 272, 174);
if (d >= 11)
c = kHypnoColorRed; // red
else
c = kHypnoColorGreen; // green
_compositeSurface->fillRect(r, c);
r = Common::Rect(256, 152, 272, 174);
c = kSpiderColorBlue; // blue
_compositeSurface->frameRect(r, c);
drawString("block05.fgx", "ENERGY", 248, 180, 38, c);
}
byte *SpiderEngine::getTargetColor(Common::String name, int levelId) {
return getPalette(kHypnoColorRed);
}
} // End of namespace Hypno

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,241 @@
/* 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 "hypno/grammar.h"
#include "hypno/hypno.h"
namespace Hypno {
void SpiderEngine::endConversation() {
debugC(1, kHypnoDebugScene, "Ending and clearing conversation");
for (Actions::iterator itt = _conversation.begin(); itt != _conversation.end(); ++itt) {
Talk *a = (Talk *)*itt;
delete a;
}
_conversation.clear();
}
void SpiderEngine::showConversation() {
debugC(1, kHypnoDebugScene, "Showing conversation");
defaultCursor();
uint32 x = 0;
uint32 y = 0;
Graphics::Surface *speaker = decodeFrame("dialog/speaker3.smk", 0);
bool activeFound = false;
bool skipRepeated = false;
// First iteration on the talk commands
Videos videos;
for (Actions::iterator itt = _conversation.begin(); itt != _conversation.end(); ++itt) {
Talk *a = (Talk *)*itt;
for (TalkCommands::const_iterator it = a->commands.begin(); it != a->commands.end(); ++it) {
if (it->command == "P") {
if (_intros[it->path]) {
skipRepeated = true;
}
}
}
if (a->boxPos != Common::Point(0, 0)) {
if (!(x == 0 && x == y))
error("Multiple BOX positions found");
x = a->boxPos.x;
y = a->boxPos.y;
}
if (!a->intro.empty() && !_intros.contains(a->intro)) {
videos.push_back(MVideo(a->intro, a->introPos, false, false, false));
_intros[a->intro] = true;
}
}
if (videos.size() > 0) {
runIntros(videos);
videos.clear();
}
if (x == 0 && x == y)
error("BOX position not found");
// Second iteration on the talk commands
for (Actions::const_iterator itt = _conversation.begin(); itt != _conversation.end(); ++itt) {
Talk *a = (Talk *)*itt;
if (a->active && !skipRepeated) {
uint32 frame = 0;
Common::String path;
for (TalkCommands::const_iterator it = a->commands.begin(); it != a->commands.end(); ++it) {
if (it->command == "F") {
frame = it->num;
} else if (it->command == "G") {
path = it->path;
}
}
if (!path.empty()) {
activeFound = true;
Graphics::Surface *surf = decodeFrame("dialog/" + path, frame);
drawImage(*speaker, x, y, false);
drawImage(*surf, x + speaker->w, y, false);
a->rect = Common::Rect(x + speaker->w, y, x + surf->w, y + surf->h);
y = y + surf->h;
surf->free();
delete surf;
}
}
}
if (!activeFound) {
debugC(1, kHypnoDebugScene, "No active item was found in the current conversation");
// Final iteration on the talk commands
bool shouldEscape = false;
for (Actions::const_iterator itt = _conversation.begin(); itt != _conversation.end(); ++itt) {
Talk *a = (Talk *)*itt;
// Avoid this conversation next time
for (TalkCommands::const_iterator it = a->commands.begin(); it != a->commands.end(); ++it) {
if (it->command == "P") {
if (!it->path.empty()) {
_intros[it->path] = true;
}
}
}
if (!a->second.empty()) {
debugC(1, kHypnoDebugScene, "Adding %s to play after the conversation ends", a->second.c_str());
videos.push_back(MVideo(a->second, a->secondPos, false, false, false));
}
if (a->escape) {
shouldEscape = true;
}
}
if (videos.size() > 0) {
runIntros(videos);
videos.clear();
}
endConversation();
if (shouldEscape) {
runIntros(_escapeSequentialVideoToPlay);
_escapeSequentialVideoToPlay.clear();
// HACK
Hotspots *hots = stack.back();
if (hots->size() == 2) {
debugC(1, kHypnoDebugScene, "Level should end here, since there is nothing else to do");
_sceneState["GS_LEVELCOMPLETE"] = true;
_sceneState["GS_LEVELWON"] = true;
}
}
drawScreen();
}
speaker->free();
delete speaker;
}
void SpiderEngine::leftClickedConversation(const Common::Point &mousePos) {
defaultCursor();
Talk *t;
Videos videos;
for (Actions::const_iterator itt = _conversation.begin(); itt != _conversation.end(); ++itt) {
Talk *a = (Talk *)*itt;
if (a->active && a->rect.contains(mousePos)) {
a->active = false;
for (TalkCommands::const_iterator it = a->commands.begin(); it != a->commands.end(); ++it) {
if (it->command == "A") {
debugC(1, kHypnoDebugScene, "Adding option %d", it->num);
t = (Talk *)_conversation[it->num];
t->active = true;
_refreshConversation = true;
} else if (it->command == "D") {
debugC(1, kHypnoDebugScene, "Disabling option %d", it->num);
t = (Talk *)_conversation[it->num];
t->active = false;
_refreshConversation = true;
} else if (it->command == "P") {
debugC(1, kHypnoDebugScene, "Playing %s", it->path.c_str());
videos.push_back(MVideo(it->path, it->position, false, false, false));
_refreshConversation = true;
} else if (it->command == "S") {
debugC(1, kHypnoDebugScene, "Enabling variable %s", it->variable.c_str());
_sceneState[it->variable] = 1;
_refreshConversation = true;
} else if (it->command == "L") {
_sceneState["GS_LEVELCOMPLETE"] = true;
_refreshConversation = true;
}
}
}
if (_refreshConversation && !a->background.empty()) {
loadImage(a->background, a->backgroundPos.x, a->backgroundPos.y, false);
}
}
if (_sceneState["GS_LEVELCOMPLETE"]) {
debugC(1, kHypnoDebugScene, "Level is complete, clearing variables");
resetSceneState();
_sceneState["GS_LEVELCOMPLETE"] = true;
_sceneState["GS_LEVELWON"] = true;
}
if (videos.size() > 0)
runIntros(videos);
}
void SpiderEngine::rightClickedConversation(const Common::Point &mousePos) {
defaultCursor();
Videos videos;
for (Actions::const_iterator itt = _conversation.begin(); itt != _conversation.end(); ++itt) {
Talk *a = (Talk *)*itt;
if (a->active && a->rect.contains(mousePos)) {
for (TalkCommands::const_iterator it = a->commands.begin(); it != a->commands.end(); ++it) {
if (it->command == "I") {
debugC(1, kHypnoDebugScene, "Playing %s", it->path.c_str());
// Not sure why position is 50, 50 since there is only one pixel
videos.push_back(MVideo(it->path, Common::Point(0, 0), false, false, false));
}
}
}
}
if (videos.size() > 0)
runIntros(videos);
}
bool SpiderEngine::hoverConversation(const Common::Point &mousePos) {
Mice mice(_defaultCursor, 1);
for (Actions::const_iterator itt = _conversation.begin(); itt != _conversation.end(); ++itt) {
Talk *a = (Talk *)*itt;
if (a->active && a->rect.contains(mousePos)) {
runMice(&mice);
return true;
}
}
return false;
}
} // End of namespace Hypno

150
engines/hypno/tokens_arc.h Normal file
View File

@@ -0,0 +1,150 @@
/* A Bison parser, made by GNU Bison 3.8.2. */
/* Bison interface for Yacc-like parsers in C
Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation,
Inc.
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 <https://www.gnu.org/licenses/>. */
/* As a special exception, you may create a larger work that contains
part or all of the Bison parser skeleton and distribute that work
under terms of your choice, so long as that work isn't itself a
parser generator using the skeleton or a modified version thereof
as a parser skeleton. Alternatively, if you modify or redistribute
the parser skeleton itself, you may (at your option) remove this
special exception, which will cause the skeleton and the resulting
Bison output files to be licensed under the GNU General Public
License without this special exception.
This special exception was added by the Free Software Foundation in
version 2.2 of Bison. */
/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual,
especially those whose name start with YY_ or yy_. They are
private implementation details that can be changed or removed. */
#ifndef YY_HYPNO_ARC_ENGINES_HYPNO_TOKENS_ARC_H_INCLUDED
# define YY_HYPNO_ARC_ENGINES_HYPNO_TOKENS_ARC_H_INCLUDED
/* Debug traces. */
#ifndef HYPNO_ARC_DEBUG
# if defined YYDEBUG
#if YYDEBUG
# define HYPNO_ARC_DEBUG 1
# else
# define HYPNO_ARC_DEBUG 0
# endif
# else /* ! defined YYDEBUG */
# define HYPNO_ARC_DEBUG 0
# endif /* ! defined YYDEBUG */
#endif /* ! defined HYPNO_ARC_DEBUG */
#if HYPNO_ARC_DEBUG
extern int HYPNO_ARC_debug;
#endif
/* Token kinds. */
#ifndef HYPNO_ARC_TOKENTYPE
# define HYPNO_ARC_TOKENTYPE
enum HYPNO_ARC_tokentype
{
HYPNO_ARC_EMPTY = -2,
HYPNO_ARC_EOF = 0, /* "end of file" */
HYPNO_ARC_error = 256, /* error */
HYPNO_ARC_UNDEF = 257, /* "invalid token" */
NAME = 258, /* NAME */
FILENAME = 259, /* FILENAME */
BNTOK = 260, /* BNTOK */
SNTOK = 261, /* SNTOK */
YXTOK = 262, /* YXTOK */
FNTOK = 263, /* FNTOK */
ENCTOK = 264, /* ENCTOK */
ONTOK = 265, /* ONTOK */
H12TOK = 266, /* H12TOK */
NUM = 267, /* NUM */
BYTE = 268, /* BYTE */
COMMENT = 269, /* COMMENT */
ALTOK = 270, /* ALTOK */
AVTOK = 271, /* AVTOK */
ABTOK = 272, /* ABTOK */
CTOK = 273, /* CTOK */
DTOK = 274, /* DTOK */
HTOK = 275, /* HTOK */
HETOK = 276, /* HETOK */
HLTOK = 277, /* HLTOK */
HUTOK = 278, /* HUTOK */
KNTOK = 279, /* KNTOK */
RETTOK = 280, /* RETTOK */
QTOK = 281, /* QTOK */
RESTOK = 282, /* RESTOK */
PTOK = 283, /* PTOK */
FTOK = 284, /* FTOK */
TTOK = 285, /* TTOK */
TATOK = 286, /* TATOK */
TPTOK = 287, /* TPTOK */
TSTOK = 288, /* TSTOK */
ATOK = 289, /* ATOK */
VTOK = 290, /* VTOK */
OTOK = 291, /* OTOK */
LTOK = 292, /* LTOK */
MTOK = 293, /* MTOK */
NTOK = 294, /* NTOK */
NRTOK = 295, /* NRTOK */
NSTOK = 296, /* NSTOK */
RTOK = 297, /* RTOK */
R0TOK = 298, /* R0TOK */
R1TOK = 299, /* R1TOK */
ITOK = 300, /* ITOK */
I1TOK = 301, /* I1TOK */
GTOK = 302, /* GTOK */
JTOK = 303, /* JTOK */
J0TOK = 304, /* J0TOK */
KTOK = 305, /* KTOK */
UTOK = 306, /* UTOK */
ZTOK = 307, /* ZTOK */
NONETOK = 308, /* NONETOK */
A0TOK = 309, /* A0TOK */
P0TOK = 310, /* P0TOK */
WTOK = 311, /* WTOK */
XTOK = 312, /* XTOK */
CB3TOK = 313, /* CB3TOK */
C02TOK = 314 /* C02TOK */
};
typedef enum HYPNO_ARC_tokentype HYPNO_ARC_token_kind_t;
#endif
/* Value type. */
#if ! defined HYPNO_ARC_STYPE && ! defined HYPNO_ARC_STYPE_IS_DECLARED
union HYPNO_ARC_STYPE
{
#line 97 "engines/hypno/grammar_arc.y"
char *s; /* string value */
int i; /* integer value */
#line 136 "engines/hypno/tokens_arc.h"
};
typedef union HYPNO_ARC_STYPE HYPNO_ARC_STYPE;
# define HYPNO_ARC_STYPE_IS_TRIVIAL 1
# define HYPNO_ARC_STYPE_IS_DECLARED 1
#endif
extern HYPNO_ARC_STYPE HYPNO_ARC_lval;
int HYPNO_ARC_parse (void);
#endif /* !YY_HYPNO_ARC_ENGINES_HYPNO_TOKENS_ARC_H_INCLUDED */

142
engines/hypno/tokens_mis.h Normal file
View File

@@ -0,0 +1,142 @@
/* A Bison parser, made by GNU Bison 3.8.2. */
/* Bison interface for Yacc-like parsers in C
Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation,
Inc.
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 <https://www.gnu.org/licenses/>. */
/* As a special exception, you may create a larger work that contains
part or all of the Bison parser skeleton and distribute that work
under terms of your choice, so long as that work isn't itself a
parser generator using the skeleton or a modified version thereof
as a parser skeleton. Alternatively, if you modify or redistribute
the parser skeleton itself, you may (at your option) remove this
special exception, which will cause the skeleton and the resulting
Bison output files to be licensed under the GNU General Public
License without this special exception.
This special exception was added by the Free Software Foundation in
version 2.2 of Bison. */
/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual,
especially those whose name start with YY_ or yy_. They are
private implementation details that can be changed or removed. */
#ifndef YY_HYPNO_MIS_ENGINES_HYPNO_TOKENS_MIS_H_INCLUDED
# define YY_HYPNO_MIS_ENGINES_HYPNO_TOKENS_MIS_H_INCLUDED
/* Debug traces. */
#ifndef HYPNO_MIS_DEBUG
# if defined YYDEBUG
#if YYDEBUG
# define HYPNO_MIS_DEBUG 1
# else
# define HYPNO_MIS_DEBUG 0
# endif
# else /* ! defined YYDEBUG */
# define HYPNO_MIS_DEBUG 0
# endif /* ! defined YYDEBUG */
#endif /* ! defined HYPNO_MIS_DEBUG */
#if HYPNO_MIS_DEBUG
extern int HYPNO_MIS_debug;
#endif
/* Token kinds. */
#ifndef HYPNO_MIS_TOKENTYPE
# define HYPNO_MIS_TOKENTYPE
enum HYPNO_MIS_tokentype
{
HYPNO_MIS_EMPTY = -2,
HYPNO_MIS_EOF = 0, /* "end of file" */
HYPNO_MIS_error = 256, /* error */
HYPNO_MIS_UNDEF = 257, /* "invalid token" */
NAME = 258, /* NAME */
FILENAME = 259, /* FILENAME */
FLAG = 260, /* FLAG */
GSSWITCH = 261, /* GSSWITCH */
WALNTOK = 262, /* WALNTOK */
ENCTOK = 263, /* ENCTOK */
NUM = 264, /* NUM */
COMMENT = 265, /* COMMENT */
HOTSTOK = 266, /* HOTSTOK */
CUTSTOK = 267, /* CUTSTOK */
BACKTOK = 268, /* BACKTOK */
INTRTOK = 269, /* INTRTOK */
RETTOK = 270, /* RETTOK */
TIMETOK = 271, /* TIMETOK */
PALETOK = 272, /* PALETOK */
BBOXTOK = 273, /* BBOXTOK */
OVERTOK = 274, /* OVERTOK */
MICETOK = 275, /* MICETOK */
SONDTOK = 276, /* SONDTOK */
PLAYTOK = 277, /* PLAYTOK */
ENDTOK = 278, /* ENDTOK */
MENUTOK = 279, /* MENUTOK */
SMENTOK = 280, /* SMENTOK */
ESCPTOK = 281, /* ESCPTOK */
NRTOK = 282, /* NRTOK */
AMBITOK = 283, /* AMBITOK */
SWPTTOK = 284, /* SWPTTOK */
MPTRTOK = 285, /* MPTRTOK */
GLOBTOK = 286, /* GLOBTOK */
TONTOK = 287, /* TONTOK */
TOFFTOK = 288, /* TOFFTOK */
TALKTOK = 289, /* TALKTOK */
INACTOK = 290, /* INACTOK */
FDTOK = 291, /* FDTOK */
BOXXTOK = 292, /* BOXXTOK */
ESCAPETOK = 293, /* ESCAPETOK */
SECONDTOK = 294, /* SECONDTOK */
INTROTOK = 295, /* INTROTOK */
DEFAULTTOK = 296, /* DEFAULTTOK */
PG = 297, /* PG */
PA = 298, /* PA */
PD = 299, /* PD */
PH = 300, /* PH */
PF = 301, /* PF */
PP = 302, /* PP */
PI = 303, /* PI */
PS = 304, /* PS */
PE = 305, /* PE */
PL = 306 /* PL */
};
typedef enum HYPNO_MIS_tokentype HYPNO_MIS_token_kind_t;
#endif
/* Value type. */
#if ! defined HYPNO_MIS_STYPE && ! defined HYPNO_MIS_STYPE_IS_DECLARED
union HYPNO_MIS_STYPE
{
#line 57 "engines/hypno/grammar_mis.y"
char *s; /* string value */
int i; /* integer value */
#line 128 "engines/hypno/tokens_mis.h"
};
typedef union HYPNO_MIS_STYPE HYPNO_MIS_STYPE;
# define HYPNO_MIS_STYPE_IS_TRIVIAL 1
# define HYPNO_MIS_STYPE_IS_DECLARED 1
#endif
extern HYPNO_MIS_STYPE HYPNO_MIS_lval;
int HYPNO_MIS_parse (void);
#endif /* !YY_HYPNO_MIS_ENGINES_HYPNO_TOKENS_MIS_H_INCLUDED */

60
engines/hypno/video.cpp Normal file
View File

@@ -0,0 +1,60 @@
/* 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 "hypno/grammar.h"
#include "hypno/hypno.h"
namespace Hypno {
MVideo::MVideo(Common::String path_, Common::Point position_, bool transparent_, bool scaled_, bool loop_) {
decoder = nullptr;
path = path_;
position = position_;
scaled = scaled_;
transparent = transparent_;
loop = loop_;
}
bool HypnoSmackerDecoder::loadStream(Common::SeekableReadStream *stream) {
if (!SmackerDecoder::loadStream(stream))
return false;
// Map audio tracks to sound types
for (uint32 i = 0; i < 8; i++) {
Track *t = getTrack(i);
if (t && t->getTrackType() == Track::kTrackTypeAudio) {
AudioTrack *audio = (AudioTrack *)t;
audio->setMute(false);
audio->setSoundType(i == 7 ? Audio::Mixer::kSpeechSoundType : Audio::Mixer::kSFXSoundType);
}
}
return true;
}
uint32 HypnoSmackerDecoder::getSignatureVersion(uint32 signature) const {
if (signature == MKTAG('H', 'Y', 'P', '2')) {
return 2;
} else {
return SmackerDecoder::getSignatureVersion(signature);
}
}
}

1198
engines/hypno/wet/arcade.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,119 @@
/* 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 "graphics/cursorman.h"
#include "hypno/hypno.h"
namespace Hypno {
static const byte MOUSECURSOR_SCI[] = {
1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0,
1, 3, 3, 1, 0, 0, 0, 0, 0, 0, 0,
1, 3, 3, 3, 1, 0, 0, 0, 0, 0, 0,
1, 3, 3, 3, 3, 1, 0, 0, 0, 0, 0,
1, 3, 3, 3, 3, 3, 1, 0, 0, 0, 0,
1, 3, 3, 3, 3, 3, 3, 1, 0, 0, 0,
1, 3, 3, 3, 3, 3, 3, 3, 1, 0, 0,
1, 3, 3, 3, 3, 3, 3, 3, 3, 1, 0,
1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1,
1, 3, 3, 3, 3, 3, 1, 0, 0, 0, 0,
1, 3, 1, 0, 1, 3, 3, 1, 0, 0, 0,
1, 1, 0, 0, 1, 3, 3, 1, 0, 0, 0,
0, 0, 0, 0, 0, 1, 3, 3, 1, 0, 0,
0, 0, 0, 0, 0, 1, 3, 3, 1, 0, 0,
0, 0, 0, 0, 0, 0, 1, 3, 3, 1, 0};
static const byte circleCursor[] = {
0, 0, 0, 0, 250, 250, 250, 250, 250, 0, 0, 0, 0,
0, 0, 250, 250, 0, 0, 0, 0, 0, 250, 250, 0, 0,
0, 250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 250, 0,
0, 250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 250, 0,
250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 250,
250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 250,
250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 250,
0, 250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 250, 0,
0, 250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 250, 0,
0, 0, 250, 250, 0, 0, 0, 0, 0, 250, 250, 0, 0,
0, 0, 0, 0, 250, 250, 250, 250, 250, 0, 0, 0, 0};
static const byte targetCursor[] = {
0, 0, 0, 252, 252, 252, 252, 252, 252, 252, 252, 252, 0, 0, 0,
0, 0, 252, 0, 0, 250, 250, 250, 250, 250, 0, 0, 252, 0, 0,
0, 252, 0, 250, 250, 0, 0, 0, 0, 0, 250, 250, 0, 252, 0,
252, 0, 250, 0, 0, 252, 252, 252, 252, 252, 0, 0, 250, 0, 252,
252, 0, 250, 0, 252, 0, 0, 0, 0, 0, 252, 0, 250, 0, 252,
252, 250, 0, 252, 0, 0, 0, 0, 0, 0, 0, 252, 0, 250, 252,
252, 250, 0, 252, 0, 0, 0, 0, 0, 0, 0, 252, 0, 250, 252,
252, 250, 0, 252, 0, 0, 0, 0, 0, 0, 0, 252, 0, 250, 252,
252, 0, 250, 0, 252, 0, 0, 0, 0, 0, 252, 0, 250, 0, 252,
252, 0, 250, 0, 0, 252, 252, 252, 252, 252, 0, 0, 250, 0, 252,
0, 252, 0, 250, 250, 0, 0, 0, 0, 0, 250, 250, 0, 252, 0,
0, 0, 252, 0, 0, 250, 250, 250, 250, 250, 0, 0, 252, 0, 0,
0, 0, 0, 252, 252, 252, 252, 252, 252, 252, 252, 252, 0, 0, 0};
static const byte crosshairCursor[] = {
0, 0, 0, 0, 0, 0, 0, 250, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 250, 250, 250, 250, 250, 0, 0, 0, 0, 0,
0, 0, 0, 250, 250, 0, 0, 250, 0, 0, 250, 250, 0, 0, 0,
0, 0, 250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 250, 0, 0,
0, 0, 250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 250, 0, 0,
0, 250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 250, 0,
250, 250, 250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 250, 250, 250,
0, 250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 250, 0,
0, 0, 250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 250, 0, 0,
0, 0, 250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 250, 0, 0,
0, 0, 0, 250, 250, 0, 0, 250, 0, 0, 250, 250, 0, 0, 0,
0, 0, 0, 0, 0, 250, 250, 250, 250, 250, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 250, 0, 0, 0, 0, 0, 0, 0};
struct CursorTable {
const char *name;
const void *buf;
int w;
int h;
int hotspotX;
int hotspotY;
};
static const CursorTable cursorTable[] = {
{"default", MOUSECURSOR_SCI, 11, 16, 0, 0},
{"arcade", circleCursor, 13, 11, 7, 5},
{"target", targetCursor, 15, 13, 8, 6},
{"crosshair", crosshairCursor, 15, 13, 8, 6},
{nullptr, nullptr, 0, 0, 0, 0}};
void WetEngine::changeCursor(const Common::String &cursor) {
const CursorTable *entry = cursorTable;
while (entry->name) {
if (cursor == entry->name)
break;
entry++;
}
assert(entry->name);
CursorMan.replaceCursor(entry->buf, entry->w, entry->h, entry->hotspotX, entry->hotspotY, 0);
CursorMan.showMouse(true);
}
} // End of namespace Hypno

427
engines/hypno/wet/hard.cpp Normal file
View File

@@ -0,0 +1,427 @@
/* 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/bitarray.h"
#include "gui/message.h"
#include "common/events.h"
#include "common/config-manager.h"
#include "common/savefile.h"
#include "hypno/hypno.h"
#include "backends/keymapper/keymapper.h"
namespace Hypno {
void WetEngine::endCredits(Code *code) {
showCredits();
_nextLevel = "<main_menu>";
}
void WetEngine::runCode(Code *code) {
changeScreenMode("320x200");
if (code->name == "<main_menu>")
runMainMenu(code);
else if (code->name == "<level_menu>")
runLevelMenu(code);
else if (code->name == "<check_lives>")
runCheckLives(code);
else if (code->name == "<credits>")
endCredits(code);
else
error("invalid hardcoded level: %s", code->name.c_str());
}
void WetEngine::runCheckLives(Code *code) {
if (_lives < 0) {
_nextLevel = "<game_over>";
_score = 0;
_lives = 2;
restoreScoreMilestones(_score);
saveProfile(_name, _lastLevel);
} else
_nextLevel = _checkpoint;
}
void WetEngine::runLevelMenu(Code *code) {
if (_lastLevel == 0) {
_nextLevel = Common::String::format("c%d", _ids[0]);
return;
}
Common::Event event;
byte *palette;
Graphics::Surface *menu = decodeFrame("c_misc/menus.smk", 20, &palette);
loadPalette(palette, 0, 256);
free(palette);
byte black[3] = {0x00, 0x00, 0x00}; // Always red?
byte lime[3] = {0x00, 0xFF, 0x00}; // Always red?
byte green[3] = {0x2C, 0x82, 0x28}; // Always red?
int maxLevel = 20;
int currentLevel = 0;
for (int i = 0; i < maxLevel; i++)
if (i <= _lastLevel)
loadPalette((byte *) &green, 192+i, 1);
else
loadPalette((byte *) &black, 192+i, 1);
loadPalette((byte *) &lime, 192+currentLevel, 1);
drawImage(*menu, 0, 0, false);
bool cont = true;
// TODO: Should this be played as music instead?
playSound("sound/bub01.raw", 0, 22050);
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
keymapper->getKeymap("game-shortcuts")->setEnabled(false);
keymapper->getKeymap("menu")->setEnabled(true);
while (!shouldQuit() && cont) {
while (g_system->getEventManager()->pollEvent(event)) {
// Events
switch (event.type) {
case Common::EVENT_QUIT:
case Common::EVENT_RETURN_TO_LAUNCHER:
break;
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
if (event.customType == kActionDown && currentLevel < _lastLevel) {
playSound("sound/m_hilite.raw", 1, 11025);
currentLevel++;
} else if (event.customType == kActionUp && currentLevel > 0) {
playSound("sound/m_hilite.raw", 1, 11025);
currentLevel--;
} else if (event.customType == kActionSelect ) {
playSound("sound/m_choice.raw", 1, 11025);
_nextLevel = Common::String::format("c%d", _ids[currentLevel]);
cont = false;
} else if (event.customType == kActionPause) {
openMainMenuDialog();
}
for (int i = 0; i < maxLevel; i++)
if (i <= _lastLevel)
loadPalette((byte *) &green, 192+i, 1);
else
loadPalette((byte *) &black, 192+i, 1);
loadPalette((byte *) &lime, 192+currentLevel, 1);
drawImage(*menu, 0, 0, false);
break;
default:
break;
}
}
drawScreen();
g_system->delayMillis(10);
}
keymapper->getKeymap("menu")->setEnabled(false);
keymapper->getKeymap("game-shortcuts")->setEnabled(true);
menu->free();
delete menu;
}
void WetEngine::runMainMenu(Code *code) {
Common::Event event;
uint32 c = 252; // green
byte *palette;
Graphics::Surface *menu = decodeFrame("c_misc/menus.smk", 16, &palette);
Graphics::Surface *overlay = decodeFrame("c_misc/menus.smk", 18, nullptr);
loadPalette(palette, 0, 256);
free(palette);
Common::Rect subName(21, 10, 169, 24);
drawImage(*menu, 0, 0, false);
Graphics::Surface surName = overlay->getSubArea(subName);
drawImage(surName, subName.left, subName.top, true);
drawString("scifi08.fgx", _enterNameString, 48, 50, 100, c);
_name.clear();
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
keymapper->getKeymap("game-shortcuts")->setEnabled(false);
keymapper->getKeymap("pause")->setEnabled(false);
keymapper->getKeymap("direction")->setEnabled(false);
g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
bool cont = true;
while (!shouldQuit() && cont) {
while (g_system->getEventManager()->pollEvent(event)) {
// Events
switch (event.type) {
case Common::EVENT_QUIT:
case Common::EVENT_RETURN_TO_LAUNCHER:
break;
case Common::EVENT_KEYDOWN:
if (event.kbd.keycode == Common::KEYCODE_BACKSPACE)
_name.deleteLastChar();
else if (event.kbd.keycode == Common::KEYCODE_RETURN && !_name.empty()) {
cont = false;
} else if (Common::isAlpha(event.kbd.keycode)) {
playSound("sound/m_choice.raw", 1);
_name = _name + char(event.kbd.keycode - 32);
} if (event.kbd.keycode == Common::KEYCODE_ESCAPE) {
openMainMenuDialog();
}
drawImage(*menu, 0, 0, false);
drawImage(surName, subName.left, subName.top, true);
drawString("scifi08.fgx", _enterNameString, 48, 50, 100, c);
drawString("scifi08.fgx", _name, 140, 50, 170, c);
break;
default:
break;
}
}
drawScreen();
g_system->delayMillis(10);
}
if (_name == "COOLCOLE" || _unlockAllLevels) {
_lastLevel = 19;
playSound("sound/extra.raw", 1);
} else
_lastLevel = 0;
if (_name == "ELRAPIDO") {
_infiniteAmmoCheat = true;
playSound("sound/extra.raw", 1);
}
if (_name == "SAVANNAH") {
_infiniteHealthCheat = true;
playSound("sound/extra.raw", 1);
}
if ((_name == "FRASCAS" && _language == Common::ES_ESP) || \
(_name == "RITCHY" && _language == Common::FR_FRA)) {
_infiniteAmmoCheat = true;
_infiniteHealthCheat = true;
_lastLevel = 19;
playSound("sound/extra.raw", 1);
}
_name.toLowercase();
bool found = loadProfile(_name);
g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
keymapper->getKeymap("game-shortcuts")->setEnabled(true);
keymapper->getKeymap("pause")->setEnabled(true);
keymapper->getKeymap("direction")->setEnabled(true);
if (found || _name.empty()) {
menu->free();
delete menu;
overlay->free();
delete overlay;
return;
}
saveProfile(_name, _ids[_lastLevel]);
_name.toUppercase(); // We do this in order to show it again
Common::Rect subDifficulty(20, 104, 233, 119);
Graphics::Surface surDifficulty = overlay->getSubArea(subDifficulty);
drawImage(*menu, 0, 0, false);
drawImage(surDifficulty, subDifficulty.left, subDifficulty.top, true);
Common::Rect subWet(129, 149, 195, 159);
Graphics::Surface surWet = overlay->getSubArea(subWet);
drawImage(surWet, subWet.left, subWet.top, true);
playSound("sound/no_rapid.raw", 1, 11025);
Common::Rect subDamp(52, 149, 115, 159);
Graphics::Surface surDamp = overlay->getSubArea(subDamp);
Common::Rect subSoaked(202, 149, 272, 159);
Graphics::Surface surSoaked = overlay->getSubArea(subSoaked);
Common::Array<Common::String> difficulties;
difficulties.push_back("0");
difficulties.push_back("1");
difficulties.push_back("2");
uint32 idx = 1;
drawString("scifi08.fgx", _enterNameString, 48, 50, 100, c);
drawString("scifi08.fgx", _name, 140, 50, 170, c);
cont = true;
keymapper->getKeymap("game-shortcuts")->setEnabled(false);
keymapper->getKeymap("pause")->setEnabled(false);
keymapper->getKeymap("menu")->setEnabled(true);
while (!shouldQuit() && cont) {
while (g_system->getEventManager()->pollEvent(event)) {
// Events
switch (event.type) {
case Common::EVENT_QUIT:
case Common::EVENT_RETURN_TO_LAUNCHER:
break;
case Common::EVENT_LBUTTONDOWN:
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
if (!g_system->hasFeature(OSystem::kFeatureTouchscreen))
event.mouse = Common::Point(0, 0);
if (idx == 1 && (subDamp.contains(event.mouse) || subSoaked.contains(event.mouse))) {
if (subDamp.contains(event.mouse)) {
playSound("sound/no_rapid.raw", 1, 11025);
idx--;
} else if (subSoaked.contains(event.mouse)) {
playSound("sound/no_rapid.raw", 1, 11025);
idx++;
}
} else if (idx == 1 && subWet.contains(event.mouse)) {
// Nothing
} else if ((subWet.contains(event.mouse) || subDamp.contains(event.mouse) || event.customType == kActionLeft) && idx > 0) {
playSound("sound/no_rapid.raw", 1, 11025);
idx--;
} else if ((subWet.contains(event.mouse) || subSoaked.contains(event.mouse) || event.customType == kActionRight) && idx < 2) {
playSound("sound/no_rapid.raw", 1, 11025);
idx++;
} else if (event.customType == kActionSelect)
cont = false;
drawImage(*menu, 0, 0, false);
drawImage(surDifficulty, subDifficulty.left, subDifficulty.top, true);
if (difficulties[idx] == "0")
drawImage(surDamp, subDamp.left, subDamp.top, true);
else if (difficulties[idx] == "1")
drawImage(surWet, subWet.left, subWet.top, true);
else if (difficulties[idx] == "2")
drawImage(surSoaked, subSoaked.left, subSoaked.top, true);
else
error("Invalid difficulty: %s", difficulties[idx].c_str());
drawString("scifi08.fgx", _enterNameString, 48, 50, 100, c);
drawString("scifi08.fgx", _name, 140, 50, 170, c);
break;
default:
break;
}
}
drawScreen();
g_system->delayMillis(10);
}
keymapper->getKeymap("menu")->setEnabled(false);
keymapper->getKeymap("game-shortcuts")->setEnabled(true);
keymapper->getKeymap("pause")->setEnabled(true);
_name.toLowercase(); // make sure it is lowercase when we finish
_difficulty = difficulties[idx];
_nextLevel = code->levelIfWin;
menu->free();
delete menu;
overlay->free();
delete overlay;
}
void WetEngine::showDemoScore() {
Common::String fmessage = "You finished this demo level with an accuracy of %d%% and a score of %d points";
Common::String message = Common::String::format(fmessage.c_str(), accuracyRatio(), _score);
GUI::MessageDialog dialog(message);
dialog.runModal();
}
Common::String WetEngine::getLocalizedString(const Common::String &name) {
if (name == "name") {
switch (_language) {
case Common::FR_FRA:
return "NOM :";
case Common::ES_ESP:
return "NOMBRE :";
case Common::KO_KOR:
return "\xb7\xa1\x9f\x71\xb7\xb3\x9d\x62:";
default:
return "ENTER NAME :";
}
} else if (name == "health") {
switch (_language) {
case Common::FR_FRA:
return "ENERGIE";
case Common::ES_ESP:
return "ENERGIA";
case Common::KO_KOR:
return "\xb5\x41\x90\xe1\xbb\xa1"; // 체력 (health)
default:
return "ENERGY";
}
} else if (name == "objectives") {
switch (_language) {
case Common::FR_FRA:
return "OBJ.";
case Common::ES_ESP:
return "O. M.";
case Common::KO_KOR:
return "\xa1\xa2\xce\x61"; // 목표 (objective)
default:
return "M. O.";
}
} else if (name == "score") {
switch (_language) {
case Common::ES_ESP:
return "PUNTOS";
case Common::KO_KOR:
return "\xb8\xf1\xae\x81"; // 점수 (score)
default:
return "SCORE";
}
} else if (name == "target") {
switch (_language) {
case Common::FR_FRA:
return "VERROUILLAGE";
case Common::ES_ESP:
return "BLANCO FIJADO";
case Common::KO_KOR:
return "\xa1\xa2\xce\x61\x20\xbb\xe1\xaf\x81"; // 목표물포착 (target acquired)
default:
return "TARGET ACQUIRED";
}
} else if (name == "direction") {
switch (_language) {
case Common::FR_FRA:
return "DIRECTION ?";
case Common::ES_ESP:
return "ELIGE DIRECCION";
case Common::KO_KOR:
return "\xa4\x77\xd0\xb7\xac\xe5\x00\x00\xc8\x82"; // 전향선택 (choose direction)
default:
return "CHOOSE DIRECTION";
}
} else
error("Invalid string name to localize: %s", name.c_str());
}
} // End of namespace Hypno

861
engines/hypno/wet/wet.cpp Normal file
View File

@@ -0,0 +1,861 @@
/* 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/bitarray.h"
#include "common/events.h"
#include "common/savefile.h"
#include "hypno/hypno.h"
#include "engines/metaengine.h"
#include "engines/savestate.h"
namespace Hypno {
static const char *failedDetectionError = \
"Failed to load any files from missions.lib.\
Please review https://wiki.scummvm.org/index.php?title=Wetlands\
and re-add the game.";
static const chapterEntry rawChapterTable[] = {
{11, {44, 172}, {218, 172}, {0, 0}, {127, 172}, 0, kHypnoColorRed}, // c11
{10, {19, 3}, {246, 3}, {246, 11}, {2, 2}, 0, kHypnoNoColor}, // c10
{21, {70, 160}, {180, 160}, {220, 185}, {44, 162}, 215, kHypnoColorYellow}, // c21
{22, {70, 160}, {180, 160}, {220, 185}, {44, 162}, 215, kHypnoColorGreen}, // c22
{23, {70, 160}, {180, 160}, {220, 185}, {44, 162}, 215, kHypnoColorCyan}, // c23
{20, {128, 150}, {238, 150},{0, 0}, {209, 146}, 0, kHypnoColorCyan}, // c20
{31, {70, 160}, {180, 160}, {220, 185}, {44, 164}, 215, kHypnoColorGreen}, // c31
{32, {70, 160}, {180, 160}, {220, 185}, {44, 164}, 215, kHypnoColorRed}, // c32
{33, {70, 160}, {180, 160}, {220, 185}, {44, 162}, 215, kHypnoColorRed}, // c33
{30, {19, 3}, {246, 3}, {246, 11}, {2, 2}, 0, kHypnoColorRed}, // c30
{41, {70, 160}, {180, 160}, {220, 185}, {44, 162}, 215, kHypnoColorRed}, // c41
{42, {70, 160}, {180, 160}, {220, 185}, {44, 162}, 215, kHypnoColorRed}, // c42
{43, {70, 160}, {180, 160}, {220, 185}, {44, 162}, 215, kHypnoColorRed}, // c43
{44, {70, 160}, {180, 160}, {220, 185}, {44, 162}, 215, kHypnoColorRed}, // c44
{40, {19, 3}, {246, 3}, {246, 11}, {2, 2}, 0, kHypnoColorRed}, // c40
{51, {60, 167}, {190, 167}, {135, 187}, {136, 163}, 36, kHypnoColorRed}, // c51
{52, {60, 167}, {190, 167}, {135, 187}, {136, 165}, 36, kHypnoColorCyan}, // c52
{50, {19, 3}, {246, 3}, {246, 11}, {2, 2}, 0, kHypnoColorRed}, // c50 (fixed)
{61, {63, 167}, {187, 167}, {192, 188}, {152, 185}, 0, kHypnoColorCyan}, // c61
{60, {63, 167}, {187, 167}, {192, 188}, {152, 185}, 0, kHypnoColorCyan}, // c60
{0, {0, 0}, {0, 0}, {0, 0}, {0, 0}, 0, kHypnoColorRed} // NULL
};
static const chapterEntry rawChapterTableEarlyDemo[] = {
{31, {48, 15}, {205, 15}, {0, 0}, {0, 0}, 0, kHypnoColorRed}, // c31
{41, {48, 15}, {205, 15}, {0, 0}, {0, 0}, 0, kHypnoColorRed}, // c41
{0, {0, 0}, {0, 0}, {0, 0}, {0, 0}, 0, kHypnoColorRed} // NULL
};
WetEngine::WetEngine(OSystem *syst, const ADGameDescription *gd) : HypnoEngine(syst, gd) {
_screenW = 320;
_screenH = 200;
_lives = 2;
_lastLevel = 0;
_c33UseMouse = true;
_c40SegmentIdx = -1;
_c40lastTurn = -1;
_c50LeftTurns = 0;
_c50RigthTurns = 0;
_rButtonUp = false;
const chapterEntry *entry = rawChapterTable;
while (entry->id) {
_ids.push_back(entry->id);
_chapterTable[entry->id] = entry;
entry++;
}
_healthString = getLocalizedString("health");
_scoreString = getLocalizedString("score");
_objString = getLocalizedString("objectives");
_targetString = getLocalizedString("target");
_directionString = getLocalizedString("direction");
_enterNameString = getLocalizedString("name");
}
WetEngine::~WetEngine() {
}
void WetEngine::loadAssets() {
if (!isDemo()) {
_difficulty = "1"; // Medium difficulty by default
loadAssetsFullGame();
return;
}
_difficulty = ""; // No difficulty selection in demo
if (_variant == "Demo" || _variant == "DemoHebrew" || _variant == "M&MCD")
loadAssetsDemoDisc();
else if (_variant == "EarlyDemo")
loadAssetsEarlyDemo();
else if (_variant == "Gen4")
loadAssetsGen4();
else if (_variant == "PCWDemo")
loadAssetsPCW();
else if (_variant == "PCGDemo")
loadAssetsPCG();
else if (_variant == "NonInteractive" || _variant == "NonInteractiveJoystick")
loadAssetsNI();
else
error("Invalid demo version: \"%s\"", _variant.c_str());
}
void WetEngine::loadAssetsDemoDisc() {
bool encrypted = _variant == "Demo" || _variant == "M&MCD" ? true : false;
LibFile *missions = loadLib("", "wetlands/c_misc/missions.lib", encrypted);
Common::ArchiveMemberList files;
if (missions->listMembers(files) == 0)
error("%s", failedDetectionError);
Hotspot h(MakeMenu);
Hotspots hs;
Ambient *a = new Ambient("movie/selector.smk", Common::Point(0, 0), "/LOOP");
a->fullscreen = true;
h.actions.push_back(a);
hs.push_back(h);
h.type = MakeHotspot;
h.rect = Common::Rect(0, 177, 116, 192);
h.actions.clear();
h.smenu = nullptr;
ChangeLevel *cl = new ChangeLevel("<intro>");
h.actions.push_back(cl);
hs.push_back(h);
h.rect = Common::Rect(121, 177, 250, 200);
cl = new ChangeLevel("<movies>");
h.actions.clear();
h.actions.push_back(cl);
hs.push_back(h);
h.rect = Common::Rect(252, 177, 318, 200);
Quit *q = new Quit();
h.actions.clear();
h.actions.push_back(q);
hs.push_back(h);
Scene *start = new Scene();
start->resolution = "320x200";
start->hots = hs;
_levels["<start>"] = start;
Transition *intro;
if (_variant == "Demo" || _variant == "M&MCD")
intro = new Transition("c31");
else if (_variant == "DemoHebrew")
intro = new Transition("c31.mis");
else
error("Unsupported language");
if (_variant == "M&MCD") {
intro->intros.push_back("wetlands/c_misc/nw_logo.smk");
intro->intros.push_back("wetlands/c_misc/h.s");
intro->intros.push_back("wetlands/c_misc/w.s");
intro->frameImage = "wetlands/c_misc/c.s";
intro->frameNumber = 0;
} else {
intro->intros.push_back("movie/nw_logo.smk");
intro->intros.push_back("movie/hypnotix.smk");
intro->intros.push_back("movie/wetlogo.smk");
intro->frameImage = "wetlands/c_misc/c.s";
intro->frameNumber = 0;
}
_levels["<intro>"] = intro;
if (_variant == "M&MCD") // This variant has no selector
_levels["<start>"] = intro;
Transition *movies = new Transition("<quit>");
movies->intros.push_back("movie/nw_logo.smk");
movies->intros.push_back("movie/hypnotix.smk");
movies->intros.push_back("movie/wetlogo.smk");
movies->intros.push_back("movie/c42e1s.smk");
movies->intros.push_back("movie/c23e1s.smk");
movies->intros.push_back("movie/c20o1s.smk");
movies->intros.push_back("movie/c23d1s.smk");
movies->intros.push_back("movie/c40o1s.smk");
movies->intros.push_back("movie/c31d1s.smk");
movies->intros.push_back("movie/c42d1s.smk");
movies->intros.push_back("movie/c44d1s.smk");
movies->intros.push_back("movie/c44e1s.smk");
movies->intros.push_back("movie/c32d1s.smk");
movies->intros.push_back("movie/c22e1s.smk");
movies->intros.push_back("movie/c31e1s.smk");
movies->intros.push_back("movie/gameover.smk");
movies->frameImage = "";
movies->frameNumber = 0;
_levels["<movies>"] = movies;
ArcadeShooting *arc;
if (_variant == "Demo" || _variant == "M&MCD") {
loadArcadeLevel("c31.mi_", "c52", "c52", "wetlands");
if (_restoredContentEnabled) {
arc = (ArcadeShooting*) _levels["c31.mi_"];
arc->segments[0].size = 1354;
arc->objKillsRequired[0] = 2;
}
loadArcadeLevel("c52.mi_", "<game_over>", "<quit>", "wetlands");
if (_restoredContentEnabled) {
arc = (ArcadeShooting*) _levels["c52.mi_"];
arc->segments[0].size = 2383;
arc->objKillsRequired[0] = 2;
arc->objKillsRequired[1] = 13;
}
} else if (_variant == "DemoHebrew") {
loadArcadeLevel("c31.mis", "c52.mis", "c52.mis", "wetlands");
if (_restoredContentEnabled) {
arc = (ArcadeShooting*) _levels["c31.mis"];
arc->segments[0].size = 1354;
arc->objKillsRequired[0] = 2;
}
loadArcadeLevel("c52.mis", "<game_over>", "<quit>", "wetlands");
if (_restoredContentEnabled) {
arc = (ArcadeShooting*) _levels["c52.mis"];
arc->segments[0].size = 2383;
arc->objKillsRequired[0] = 2;
arc->objKillsRequired[1] = 13;
}
} else {
error("Unsupported variant");
}
Transition *over = new Transition("<quit>");
over->intros.push_back("movie/gameover.smk");
_levels["<game_over>"] = over;
loadLib("", "wetlands/c_misc/fonts.lib", true);
loadFonts();
loadLib("wetlands/sound/", "wetlands/c_misc/sound.lib", true);
_nextLevel = "<start>";
}
void WetEngine::loadAssetsEarlyDemo() {
Transition *intro;
intro = new Transition("c_misc/c31.mis");
intro->prefix = "c_misc/";
intro->intros.push_back("nw_logo.smk");
intro->intros.push_back("h.s");
intro->intros.push_back("w.s");
intro->frameImage = "c.s";
intro->frameNumber = 0;
_levels["<start>"] = intro;
loadArcadeLevel("c_misc/c31.mis", "c_misc/c41.mis", "c_misc/c41.mis", "");
loadArcadeLevel("c_misc/c41.mis", "c_misc/c61.mis", "c_misc/c61.mis", "");
loadArcadeLevel("c_misc/c61.mis", "<quit>", "<quit>", "");
Transition *over = new Transition("<quit>");
over->intros.push_back("g.s");
_levels["<game_over>"] = over;
loadFonts("c_misc/");
const chapterEntry *entry = rawChapterTableEarlyDemo;
while (entry->id) {
_chapterTable[entry->id] = entry;
entry++;
}
_nextLevel = "<start>";
}
void WetEngine::loadAssetsGen4() {
bool encrypted = false;
LibFile *missions = loadLib("", "c_misc/missions.lib", encrypted);
Common::ArchiveMemberList files;
if (missions->listMembers(files) == 0)
error("%s", failedDetectionError);
Transition *intro;
intro = new Transition("c31.mis");
intro->intros.push_back("c_misc/nw_logo.smk");
intro->intros.push_back("c_misc/h.s");
intro->intros.push_back("c_misc/w.s");
intro->frameImage = "c_misc/c.s";
intro->frameNumber = 0;
_levels["<start>"] = intro;
loadArcadeLevel("c31.mis", "c52.mis", "c52.mis", "");
ArcadeShooting *arc;
if (_restoredContentEnabled) {
arc = (ArcadeShooting*) _levels["c31.mis"];
arc->segments[0].size = 1354;
arc->objKillsRequired[0] = 2;
}
loadArcadeLevel("c52.mis", "<game_over>", "<quit>", "");
if (_restoredContentEnabled) {
arc = (ArcadeShooting*) _levels["c52.mis"];
arc->segments[0].size = 2383;
arc->objKillsRequired[0] = 2;
arc->objKillsRequired[1] = 13;
}
Transition *over = new Transition("<quit>");
over->intros.push_back("c_misc/g.s");
_levels["<game_over>"] = over;
loadLib("", "c_misc/fonts.lib", true);
loadFonts();
loadLib("sound/", "c_misc/sound.lib", true);
_nextLevel = "<start>";
}
void WetEngine::loadAssetsNI() {
Common::String musicFile = _variant == "NonInteractive" ? "wetmusic.81m" : "c44_22k.raw";
int musicRate = _variant == "NonInteractive" ? 11025 : 22050;
Transition *movies = new Transition("<quit>");
movies->music = musicFile;
movies->musicRate = musicRate;
movies->playMusicDuringIntro = true;
movies->intros.push_back("demo/nw_logo.smk");
movies->intros.push_back("demo/hypnotix.smk");
movies->intros.push_back("demo/wetlogo.smk");
movies->intros.push_back("demo/c31c1.smk");
movies->intros.push_back("demo/demo31.smk");
movies->intros.push_back("demo/c31c2.smk");
movies->intros.push_back("demo/c31e1.smk");
movies->intros.push_back("demo/logo_w.smk");
movies->intros.push_back("demo/bar01b.smk");
movies->intros.push_back("demo/gun_320.smk");
movies->intros.push_back("demo/logo_e.smk");
movies->intros.push_back("demo/c30peek.smk");
movies->intros.push_back("demo/demo30.smk");
movies->intros.push_back("demo/c30knife.smk");
movies->intros.push_back("demo/logo_t.smk");
movies->intros.push_back("demo/c51teez.smk");
movies->intros.push_back("demo/demo21.smk");
movies->intros.push_back("demo/c51kill.smk");
movies->intros.push_back("demo/logo_l.smk");
movies->intros.push_back("demo/run_320.smk");
movies->intros.push_back("demo/logo_a.smk");
movies->intros.push_back("demo/demo50.smk");
movies->intros.push_back("demo/c50gate.smk");
movies->intros.push_back("demo/logo_n.smk");
movies->intros.push_back("demo/c22end.smk");
movies->intros.push_back("demo/logo_d.smk");
movies->intros.push_back("demo/demo44.smk");
movies->intros.push_back("demo/c44boom.smk");
movies->intros.push_back("demo/logo_s.smk");
movies->intros.push_back("demo/xi.smk");
movies->intros.push_back("demo/wetlogo.smk");
movies->intros.push_back("demo/c30shoot.smk");
movies->frameImage = "";
movies->frameNumber = 0;
_levels["<start>"] = movies;
_nextLevel = "<start>";
}
void WetEngine::loadAssetsPCW() {
LibFile *missions = loadLib("", "c_misc/missions.lib", false);
Common::ArchiveMemberList files;
if (missions->listMembers(files) == 0)
error("%s", failedDetectionError);
Transition *intro = new Transition("c11.mis");
intro->intros.push_back("c_misc/nw_logo.smk");
intro->intros.push_back("c_misc/h.s");
intro->intros.push_back("c_misc/wet.smk");
_levels["<start>"] = intro;
loadArcadeLevel("c11.mis", "<quit>", "<quit>", "");
ArcadeShooting *arc;
if (_restoredContentEnabled) {
arc = (ArcadeShooting*) _levels["c11.mis"];
arc->segments[0].size = 2002;
arc->objKillsRequired[0] = 1;
arc->transitions.push_back(ArcadeTransition("", "c11/c11p2.col", "", 0, 1501));
// These videos were not included in the demo, so we replace them
arc->defeatMissBossVideo = "c11\\c11d1.smk";
arc->defeatNoEnergySecondVideo = "c11\\c11d1.smk";
}
Transition *over = new Transition("<quit>");
_levels["<game_over>"] = over;
loadLib("sound/", "c_misc/sound.lib", false);
loadLib("", "c_misc/fonts.lib", true);
loadFonts();
_nextLevel = "<start>";
}
void WetEngine::loadAssetsPCG() {
LibFile *missions = loadLib("", "missions.lib", false);
Common::ArchiveMemberList files;
if (missions->listMembers(files) == 0)
error("%s", failedDetectionError);
Transition *intro = new Transition("c31.mis");
intro->intros.push_back("nw_logo.smk");
intro->intros.push_back("h.s");
intro->intros.push_back("wet.smk");
intro->frameImage = "c.s";
intro->frameNumber = 0;
_levels["<start>"] = intro;
loadArcadeLevel("c31.mis", "<quit>", "<quit>", "");
ArcadeShooting *arc;
if (_restoredContentEnabled) {
arc = (ArcadeShooting*) _levels["c31.mis"];
arc->segments[0].size = 1354;
arc->objKillsRequired[0] = 2;
// These videos were not included in the demo, so we replace or remove them
arc->hitBoss1Video = "";
arc->hitBoss2Video = "";
arc->missBoss1Video = "";
arc->missBoss2Video = "";
arc->defeatMissBossVideo = "c31\\c31d1s.smk";
}
Transition *over = new Transition("<quit>");
over->intros.push_back("g.s");
_levels["<game_over>"] = over;
loadLib("sound/", "sound.lib", false);
loadLib("", "fonts.lib", true);
loadFonts();
_nextLevel = "<start>";
}
void WetEngine::loadAssetsFullGame() {
LibFile *missions = loadLib("", "c_misc/missions.lib", true);
Common::ArchiveMemberList files;
if (missions == nullptr || missions->listMembers(files) == 0)
error("%s", failedDetectionError);
Transition *logos = new Transition("<main_menu>");
logos->intros.push_back("c_misc/logo.smk");
logos->intros.push_back("c_misc/nw_logo.smk");
logos->intros.push_back("c_misc/hypnotix.smk");
logos->intros.push_back("c_misc/wetlogo.smk");
_levels["<start>"] = logos;
Code *menu = new Code("<main_menu>");
_levels["<main_menu>"] = menu;
_levels["<main_menu>"]->levelIfWin = "<intros>";
Code *level_menu = new Code("<level_menu>");
_levels["<level_menu>"] = level_menu;
_levels["<level_menu>"]->levelIfWin = "?";
Transition *over = new Transition("<main_menu>");
over->intros.push_back("c_misc/gameover.smk");
_levels["<game_over>"] = over;
Transition *intros = new Transition("<level_menu>");
intros->intros.push_back("c_misc/stardate.smk");
intros->intros.push_back("c_misc/intros.smk");
intros->intros.push_back("c_misc/confs.smk");
_levels["<intros>"] = intros;
Code *check_lives = new Code("<check_lives>");
_levels["<check_lives>"] = check_lives;
Code *end_credits = new Code("<credits>");
_levels["<credits>"] = end_credits;
ArcadeShooting *arc;
loadArcadeLevel("c110.mi_", "c10", "<check_lives>", "");
loadArcadeLevel("c111.mi_", "c10", "<check_lives>", "");
loadArcadeLevel("c112.mi_", "c10", "<check_lives>", "");
loadArcadeLevel("c100.mi_", "c21", "<check_lives>", "");
loadArcadeLevel("c101.mi_", "c21", "<check_lives>", "");
loadArcadeLevel("c102.mi_", "c21", "<check_lives>", "");
loadArcadeLevel("c210.mi_", "c22", "<check_lives>", "");
loadArcadeLevel("c211.mi_", "c22", "<check_lives>", "");
loadArcadeLevel("c212.mi_", "c22", "<check_lives>", "");
loadArcadeLevel("c220.mi_", "c23", "<check_lives>", "");
loadArcadeLevel("c221.mi_", "c23", "<check_lives>", "");
loadArcadeLevel("c222.mi_", "c23", "<check_lives>", "");
loadArcadeLevel("c230.mi_", "c20", "<check_lives>", "");
loadArcadeLevel("c231.mi_", "c20", "<check_lives>", "");
loadArcadeLevel("c232.mi_", "c20", "<check_lives>", "");
loadArcadeLevel("c200.mi_", "c31", "<check_lives>", "");
loadArcadeLevel("c201.mi_", "c31", "<check_lives>", "");
loadArcadeLevel("c202.mi_", "c31", "<check_lives>", "");
arc = (ArcadeShooting*) _levels["c200.mi_"];
arc->mouseBox.right = 320;
arc->mouseBox.bottom = 135;
arc = (ArcadeShooting*) _levels["c201.mi_"];
arc->mouseBox.right = 320;
arc->mouseBox.bottom = 135;
arc = (ArcadeShooting*) _levels["c202.mi_"];
arc->mouseBox.right = 320;
arc->mouseBox.bottom = 135;
loadArcadeLevel("c310.mi_", "c32", "<check_lives>", "");
loadArcadeLevel("c311.mi_", "c32", "<check_lives>", "");
loadArcadeLevel("c312.mi_", "c32", "<check_lives>", "");
loadArcadeLevel("c320.mi_", "c33", "<check_lives>", "");
loadArcadeLevel("c321.mi_", "c33", "<check_lives>", "");
loadArcadeLevel("c322.mi_", "c33", "<check_lives>", "");
loadArcadeLevel("c330.mi_", "c30", "<check_lives>", "");
loadArcadeLevel("c331.mi_", "c30", "<check_lives>", "");
loadArcadeLevel("c332.mi_", "c30", "<check_lives>", "");
loadArcadeLevel("c300.mi_", "c41", "<check_lives>", "");
arc = (ArcadeShooting*) _levels["c300.mi_"];
arc->id = 30; // Fixed from the original (3)
loadArcadeLevel("c301.mi_", "c41", "<check_lives>", "");
arc = (ArcadeShooting*) _levels["c301.mi_"];
arc->id = 30; // Fixed from the original (3)
loadArcadeLevel("c302.mi_", "c41", "<check_lives>", "");
arc = (ArcadeShooting*) _levels["c302.mi_"];
arc->id = 30; // Fixed from the original (3)
loadArcadeLevel("c410.mi_", "c42", "<check_lives>", "");
loadArcadeLevel("c411.mi_", "c42", "<check_lives>", "");
loadArcadeLevel("c412.mi_", "c42", "<check_lives>", "");
loadArcadeLevel("c420.mi_", "c43", "<check_lives>", "");
loadArcadeLevel("c421.mi_", "c43", "<check_lives>", "");
loadArcadeLevel("c422.mi_", "c43", "<check_lives>", "");
loadArcadeLevel("c430.mi_", "c44", "<check_lives>", "");
loadArcadeLevel("c431.mi_", "c44", "<check_lives>", "");
loadArcadeLevel("c432.mi_", "c44", "<check_lives>", "");
loadArcadeLevel("c440.mi_", "c40", "<check_lives>", "");
loadArcadeLevel("c441.mi_", "c40", "<check_lives>", "");
loadArcadeLevel("c442.mi_", "c40", "<check_lives>", "");
loadArcadeLevel("c400.mi_", "c51", "<check_lives>", "");
arc = (ArcadeShooting*) _levels["c400.mi_"];
arc->id = 40; // Fixed from the original (4)
loadArcadeLevel("c401.mi_", "c51", "<check_lives>", "");
arc = (ArcadeShooting*) _levels["c401.mi_"];
arc->id = 40; // Fixed from the original (4)
loadArcadeLevel("c402.mi_", "c51", "<check_lives>", "");
arc = (ArcadeShooting*) _levels["c402.mi_"];
arc->id = 40; // Fixed from the original (4)
loadArcadeLevel("c510.mi_", "c52", "<check_lives>", "");
loadArcadeLevel("c511.mi_", "c52", "<check_lives>", "");
loadArcadeLevel("c512.mi_", "c52", "<check_lives>", "");
loadArcadeLevel("c520.mi_", "c50", "<check_lives>", "");
loadArcadeLevel("c521.mi_", "c50", "<check_lives>", "");
loadArcadeLevel("c522.mi_", "c50", "<check_lives>", "");
loadArcadeLevel("c500.mi_", "c61", "<check_lives>", "");
arc = (ArcadeShooting*) _levels["c500.mi_"];
arc->id = 50; // Fixed from the original (5)
loadArcadeLevel("c501.mi_", "c61", "<check_lives>", "");
arc = (ArcadeShooting*) _levels["c501.mi_"];
arc->id = 50; // Fixed from the original (5)
loadArcadeLevel("c502.mi_", "c61", "<check_lives>", "");
arc = (ArcadeShooting*) _levels["c502.mi_"];
arc->id = 50; // Fixed from the original (5)
loadArcadeLevel("c610.mi_", "c60", "<check_lives>", "");
loadArcadeLevel("c611.mi_", "c60", "<check_lives>", "");
loadArcadeLevel("c612.mi_", "c60", "<check_lives>", "");
loadArcadeLevel("c600.mi_", "<credits>", "<check_lives>", "");
loadArcadeLevel("c601.mi_", "<credits>", "<check_lives>", "");
loadArcadeLevel("c602.mi_", "<credits>", "<check_lives>", "");
loadLib("", "c_misc/fonts.lib", true);
loadFonts();
loadLib("sound/", "c_misc/sound.lib", true);
restoreScoreMilestones(0);
_nextLevel = "<start>";
}
void WetEngine::showCredits() {
if (!isDemo() || ((_variant == "Demo" || _variant == "M&MCD") && _language == Common::EN_USA)) {
MVideo video("c_misc/credits.smk", Common::Point(0, 0), false, true, false);
runIntro(video);
}
}
void WetEngine::loadFonts(const Common::String &prefix) {
HypnoEngine::loadFonts(prefix);
if (_language == Common::KO_KOR) {
Common::File file;
if (!file.open("C_MISC/G9A.SYF"))
error("Cannot open Korean font");
byte *font = (byte *)malloc(file.size());
file.read(font, file.size());
_fontg9a.set_size(file.size()*8);
_fontg9a.set_bits((byte *)font);
free(font);
}
}
uint16 WetEngine::getNextChar(const Common::String &str, uint32 &c) {
if (c >= str.size())
return 0;
if (_language == Common::KO_KOR && (str[c] & 0x80) && c + 1 < str.size()) {
uint16 r = (str[c] << 8) | (str[c+1] & 0xff);
c += 2;
return r;
}
return str[c++];
}
void WetEngine::drawGlyph(const Common::BitArray &font, int x, int y, int bitoffset, int width, int height, int pitch, uint32 color, bool invert) {
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
if (font.get(bitoffset + j * pitch + i) == invert)
_compositeSurface->setPixel(x + (width - i - 1), y + j, color);
}
}
}
void WetEngine::drawKoreanChar(uint16 chr, int &curx, int y, uint32 color) {
// TODO: What do the first 13 bytes and a byte before ASCII char mean?
if (chr < 0x100) {
if (chr < 0x20 || chr >= 0x80) {
return;
}
drawGlyph(_fontg9a, curx, y, 104 + 19 * 8 * (chr - 0x20), 9, 9, 16, color, true);
curx += 9;
return;
}
int initial = (chr >> 10) & 0x1f;
int mid = (chr >> 5) & 0x1f;
int fin = chr & 0x1f;
int initidx = initial - 1;
static const int mididxlut[0x20] = {
-1, -1, 0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9,
10, 11, -1, -1, 12, 13, 14, 15, 16, 17, -1, -1,
18, 19, 20, 21, -1, -1};
int mididx = mididxlut[mid];
int finidx = fin >= 0x12 ? fin - 2 : fin - 1;
if (initidx < 0 || initidx > 19 || mididx < 0 || mididx > 21 || finidx < 0 || finidx >= 27)
return;
const int mid_to_init_lut[32] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1, 3, 3,
0, 0, 3, 1, 2, 4, 4, 4,
0, 0, 2, 1, 3, 0, 0, 0,
};
const int mid_to_fin_lut[32] = {
0, 0, 0, 0, 2, 0, 2, 1,
0, 0, 2, 1, 2, 3, 0, 2,
0, 0, 1, 3, 3, 1, 2, 1,
0, 0, 3, 3, 1, 1, 0, 0,
};
int initialvariant = 2 * mid_to_init_lut[mid] + (fin == 1 ? 0 : 1);
int midvariant = (fin == 1 ? 0 : 1) + (initial == 1 || initial == 2 || initial == 17 ? 0 : 2);
int finvariant = mid_to_fin_lut[mid];
int initialglyph = initidx == 0 ? 0 : initidx * 10 + initialvariant - 9;
int midglyph = mididx == 0 ? 0 : 4 * mididx + midvariant - 3;
int finglyph = finidx == 0 ? 0 : 4 * finidx + finvariant - 3;
drawGlyph(_fontg9a, curx, y, 1836 * 8 + 16 * 9 * initialglyph, 9, 9, 16, color, true);
drawGlyph(_fontg9a, curx, y, 1836 * 8 + 16 * 9 * 191 + 16 * 9 * midglyph, 9, 9, 16, color, true);
drawGlyph(_fontg9a, curx, y, 1836 * 8 + 16 * 9 * 276 + 16 * 9 * finglyph, 9, 9, 16, color, true);
curx += 9;
}
void WetEngine::drawString(const Common::String &font, const Common::String &str, int x, int y, int w, uint32 color) {
int offset = 0;
int curx = x;
if (font == "g9a.syf" && _language == Common::KO_KOR) {
for (uint32 c = 0; c < str.size(); ) {
uint16 chr = getNextChar(str, c);
drawKoreanChar(chr, curx, y, color);
}
} else if (font == "block05.fgx") {
for (uint32 c = 0; c < str.size(); ) {
uint16 chr = getNextChar(str, c);
if (chr >= 0x100 && _language == Common::KO_KOR) {
drawKoreanChar(chr, curx, y, color);
continue;
}
offset = 0;
if (chr == ':')
offset = 1;
else if (chr == '.')
offset = 4;
drawGlyph(_font05, curx + 1, offset + y, 275 + 40*chr, 5, 5, 8, color, _variant == "EarlyDemo");
curx += 6;
}
} else if (font == "scifi08.fgx") {
for (uint32 c = 0; c < str.size();) {
uint16 chr = getNextChar(str, c);
if (chr >= 0x100 && _language == Common::KO_KOR) {
drawKoreanChar(chr, curx, y, color);
continue;
}
if (chr == 0)
continue;
assert(chr >= 32);
offset = 0;
if (chr == 't')
offset = 0;
else if (chr == 'i' || chr == '%')
offset = 1;
else if (Common::isLower(chr) || chr == ':')
offset = 2;
drawGlyph(_font08, curx + 1, offset + y, 1554 + 72*(chr-32), 6, 8, 8, color, _variant == "EarlyDemo");
curx += 7;
}
} else
error("Invalid font: '%s'", font.c_str());
}
void WetEngine::saveProfile(const Common::String &name, int levelId) {
SaveStateList saves = getMetaEngine()->listSaves(_targetName.c_str());
// Find the correct level index to before saving
for (uint32 i = 0; i < _ids.size(); i++) {
if (levelId == _ids[i]) {
if (_lastLevel < int(i))
_lastLevel = int(i);
break;
}
}
uint32 slot = 0;
for (SaveStateList::iterator save = saves.begin(); save != saves.end(); ++save) {
if (save->getDescription() == name)
break;
slot++;
}
saveGameState(slot, name, false);
}
bool WetEngine::loadProfile(const Common::String &name) {
SaveStateList saves = getMetaEngine()->listSaves(_targetName.c_str());
uint32 slot = 0;
for (SaveStateList::iterator save = saves.begin(); save != saves.end(); ++save) {
if (save->getDescription() == name)
break;
slot++;
}
if (slot == saves.size()) {
debugC(1, kHypnoDebugMedia, "Failed to load %s", name.c_str());
return false;
}
loadGameState(slot);
return true;
}
Common::Error WetEngine::saveGameStream(Common::WriteStream *stream, bool isAutosave) {
if (isAutosave)
return Common::kNoError;
if (_lastLevel < 0 || _lastLevel >= 20)
error("Invalid last level!");
stream->writeString(_name);
stream->writeByte(0);
stream->writeString(_difficulty);
stream->writeByte(0);
stream->writeUint32LE(_lives);
stream->writeUint32LE(_score);
stream->writeUint32LE(_lastLevel);
return Common::kNoError;
}
Common::Error WetEngine::loadGameStream(Common::SeekableReadStream *stream) {
_name = stream->readString();
_difficulty = stream->readString();
_lives = stream->readUint32LE();
_score = stream->readUint32LE();
_lastLevel = stream->readUint32LE();
if (_lastLevel == 0)
_nextLevel = Common::String::format("c%d", _ids[0]);
else
_nextLevel = "<level_menu>";
restoreScoreMilestones(_score);
return Common::kNoError;
}
Common::String WetEngine::findNextLevel(const Transition *trans) {
if (trans->nextLevel.empty())
error("Invalid transition!");
return trans->nextLevel;
}
Common::String WetEngine::findNextLevel(const Common::String &level) {
Common::String nextLevel;
if (Common::matchString(level.c_str(), "c#") || Common::matchString(level.c_str(), "c##"))
nextLevel = level + _difficulty + ".mi_";
else {
nextLevel = level;
}
return nextLevel;
}
} // End of namespace Hypno