mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-15 06:08:35 +00:00
791 lines
24 KiB
C++
791 lines
24 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "common/tokenizer.h"
|
|
#include "common/events.h"
|
|
#include "graphics/cursorman.h"
|
|
|
|
#include "hypno/grammar.h"
|
|
#include "hypno/hypno.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;
|
|
_background = new MVideo(arc->backgroundVideo, offset, 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];
|
|
|
|
debugC(1, kHypnoDebugArcade, "Using frame delay: %d", arc->frameDelay);
|
|
|
|
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_KEYDOWN:
|
|
pressedKey(event.kbd.keycode);
|
|
if (event.kbd.keycode == Common::KEYCODE_LCTRL)
|
|
if (clickedPrimaryShoot(mousePos))
|
|
shootingPrimary = true;
|
|
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.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 = offset;
|
|
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 (_music.empty() && !arc->music.empty()) {
|
|
_music = _soundPath + arc->music;
|
|
_musicRate = arc->musicRate;
|
|
_musicStereo = arc->musicStereo;
|
|
playSound(_music, 0, _musicRate, _musicStereo); // music loop forever
|
|
}
|
|
|
|
if (needsUpdate) {
|
|
if (shootingPrimary) {
|
|
shootingPrimary = shoot(mousePos, arc, false);
|
|
} else if (shootingSecondary) {
|
|
shootingSecondary = shoot(mousePos, arc, true);
|
|
}
|
|
|
|
drawPlayer();
|
|
drawHealth();
|
|
drawAmmo();
|
|
}
|
|
|
|
g_system->delayMillis(arc->frameDelay);
|
|
}
|
|
|
|
// 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();
|
|
_music.clear();
|
|
}
|
|
|
|
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");
|
|
|
|
g_system->copyRectToScreen(_compositeSurface->getPixels(), _compositeSurface->pitch, 0, 0, _screenW, _screenH);
|
|
}
|
|
|
|
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();
|
|
if (!_music.empty())
|
|
playSound(_music, 0, _musicRate, _musicStereo); // restore music
|
|
} 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);
|
|
if (!_music.empty())
|
|
playSound(_music, 0, _musicRate, _musicStereo); // restore music
|
|
}
|
|
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);
|
|
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;
|
|
}
|
|
|
|
} // End of namespace Hypno
|
|
|