354 lines
10 KiB
C++
354 lines
10 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 "dgds/minigames/dragon_arcade_ttm.h"
|
|
#include "dgds/minigames/dragon_arcade.h"
|
|
#include "dgds/ads.h"
|
|
#include "dgds/drawing.h"
|
|
#include "dgds/sound.h"
|
|
#include "dgds/includes.h"
|
|
|
|
namespace Dgds {
|
|
|
|
Common::String ArcadeFloor::dump() const {
|
|
return Common::String::format("ArcadeFloor<x:%d-%d y:%d flg:%d>",
|
|
x, x + width, yval, flag);
|
|
}
|
|
|
|
|
|
DragonArcadeTTM::DragonArcadeTTM(ArcadeNPCState *npcState) : _npcState(npcState),
|
|
_currentTTMNum(0), _currentNPCRunningTTM(0), _drawXOffset(0), _drawYOffset(0),
|
|
_startYOffset(0), _doingInit(false), _drawColBG(0), _drawColFG(0)
|
|
{
|
|
ARRAYCLEAR(_shapes3);
|
|
}
|
|
|
|
void DragonArcadeTTM::clearDataPtrs() {
|
|
for (int i = 0; i < 5; i++) {
|
|
_ttmEnvs[i] = TTMEnviro();
|
|
}
|
|
// TODO: Is this used anywhere?
|
|
// INT_39e5_3cb8 = -1;
|
|
}
|
|
|
|
int16 DragonArcadeTTM::load(const char *filename) {
|
|
TTMEnviro *env = nullptr;
|
|
int16 envNum;
|
|
for (envNum = 0; envNum < ARRAYSIZE(_ttmEnvs); envNum++) {
|
|
if (_ttmEnvs[envNum].scr == nullptr) {
|
|
env = &_ttmEnvs[envNum];
|
|
debug(1, "Arcade TTM load %s into env %d", filename, envNum);
|
|
break;
|
|
}
|
|
}
|
|
if (!env)
|
|
error("Trying to load too many TTMs in Dragon arcade");
|
|
|
|
DgdsEngine *engine = DgdsEngine::getInstance();
|
|
TTMParser dgds(engine->getResourceManager(), engine->getDecompressor());
|
|
bool parseResult = dgds.parse(env, filename);
|
|
if (!parseResult)
|
|
error("Error loading dgds arcade script %s", filename);
|
|
|
|
env->scr->seek(0);
|
|
|
|
return envNum;
|
|
}
|
|
|
|
void DragonArcadeTTM::finishTTMParse(int16 envNum) {
|
|
TTMEnviro &env = _ttmEnvs[envNum];
|
|
|
|
if (!env.scr)
|
|
error("DragonArcadeTTM::finishTTMParse: script env %d not loaded", envNum);
|
|
|
|
// Discover the frame offsets
|
|
uint16 op = 0;
|
|
for (uint frame = 0; frame < env._totalFrames; frame++) {
|
|
env._frameOffsets[frame] = env.scr->pos();
|
|
op = env.scr->readUint16LE();
|
|
while (op != 0x0ff0 && env.scr->pos() < env.scr->size()) {
|
|
switch (op & 0xf) {
|
|
case 0:
|
|
break;
|
|
case 0xf: {
|
|
TTMInterpreter::readTTMStringVal(env.scr);
|
|
break;
|
|
}
|
|
default:
|
|
env.scr->skip((op & 0xf) * 2);
|
|
break;
|
|
}
|
|
op = env.scr->readUint16LE();
|
|
}
|
|
}
|
|
env.scr->seek(0);
|
|
}
|
|
|
|
int16 DragonArcadeTTM::runNextPage(int16 pageNum) {
|
|
_shapes2[_currentTTMNum] = _shapes[_currentTTMNum];
|
|
// TODO: what is this?
|
|
//UINT_39e5_3ca2 = 0;
|
|
|
|
if (pageNum < _ttmEnvs[_currentTTMNum]._totalFrames && pageNum > -1 &&
|
|
_ttmEnvs[_currentTTMNum]._frameOffsets[pageNum] > -1) {
|
|
return runScriptPage(pageNum);
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int16 DragonArcadeTTM::handleOperation(TTMEnviro &env, int16 page, uint16 op, byte count, const int16 *ivals, const Common::String &sval) {
|
|
DgdsEngine *engine = DgdsEngine::getInstance();
|
|
Graphics::ManagedSurface &compBuffer = engine->_compositionBuffer;
|
|
switch (op) {
|
|
case 0x0020:
|
|
// This doesn't seem explicitly handled in the original, but appears in
|
|
// arcade sequence 2 - just ignore it??
|
|
break;
|
|
case 0x0070:
|
|
// Do nothing.
|
|
break;
|
|
case 0x0080: // FREE SHAPE
|
|
_allShapes[_shapes3[_currentTTMNum] * 5 + _currentTTMNum].reset();
|
|
_shapes[_currentTTMNum].reset();
|
|
break;
|
|
case 0x1021: // SET DELAY
|
|
engine->adsInterpreter()->setScriptDelay((int)(ivals[0] * MS_PER_FRAME));
|
|
break;
|
|
case 0x1031: // SET BRUSH
|
|
//debug(1, "Set brush %d for slot %d", ivals[0], _currentTTMNum);
|
|
if (!_shapes2[_currentTTMNum]) {
|
|
_brushes[_currentTTMNum].reset();
|
|
} else {
|
|
_brushes[_currentTTMNum] = Brush(_shapes2[_currentTTMNum], ivals[0]);
|
|
}
|
|
break;
|
|
case 0x1051: // SET SHAPE
|
|
_shapes3[_currentTTMNum] = ivals[0];
|
|
//debug(1, "Set img %d into slot %d", ivals[0], _currentTTMNum);
|
|
_shapes[_currentTTMNum] = _allShapes[ivals[0] * 5 + _currentTTMNum];
|
|
_shapes2[_currentTTMNum] = _allShapes[ivals[0] * 5 + _currentTTMNum];
|
|
break;
|
|
case 0x1061:
|
|
// Do nothing (ignore arg)
|
|
break;
|
|
case 0x1101:
|
|
case 0x1111:
|
|
// Do nothing (ignore arg)
|
|
break;
|
|
case 0x1201:
|
|
// This doesn't seem explicitly handled in the original, but appears in
|
|
// arcade sequence 1 - just ignore it??
|
|
break;
|
|
case 0x2002: // SET COLORS
|
|
_drawColFG = (byte)ivals[0];
|
|
_drawColBG = (byte)ivals[1];
|
|
break;
|
|
case 0x2012: { // PLAY SOUND
|
|
int16 sound;
|
|
if (ivals[0] == 0 || ivals[0] == 1) {
|
|
sound = 0x26;
|
|
} else if (ivals[0] == 2) {
|
|
sound = 0x4f;
|
|
} else {
|
|
break;
|
|
}
|
|
engine->_soundPlayer->playSFX(sound);
|
|
break;
|
|
}
|
|
case 0x4504: { // SET NPC POS 1
|
|
int16 x = _drawXOffset + ivals[0];
|
|
int16 y = _drawYOffset + ivals[1] + 2;
|
|
_npcState[_currentNPCRunningTTM].x_11 = x;
|
|
_npcState[_currentNPCRunningTTM].x_12 = x + ivals[2];
|
|
_npcState[_currentNPCRunningTTM].y_11 = y;
|
|
_npcState[_currentNPCRunningTTM].y_12 = y + ivals[3];
|
|
break;
|
|
}
|
|
case 0x4514: {// SET NPC POS 2
|
|
int16 x = _drawXOffset + ivals[0];
|
|
int16 y = _drawYOffset + ivals[1] + 2;
|
|
_npcState[_currentNPCRunningTTM].x_21 = x;
|
|
_npcState[_currentNPCRunningTTM].x_22 = x + ivals[2];
|
|
_npcState[_currentNPCRunningTTM].y_21 = y;
|
|
_npcState[_currentNPCRunningTTM].y_22 = y + ivals[3];
|
|
break;
|
|
}
|
|
case 0xA0A4: { // DRAW LINE
|
|
compBuffer.drawLine(_drawXOffset + ivals[0], _drawYOffset + ivals[1] + 2, _drawXOffset + ivals[2], _drawYOffset + ivals[3] + 2, _drawColFG);
|
|
break;
|
|
}
|
|
case 0xA104: // DRAW FILLED RECT
|
|
if (_doingInit) {
|
|
ArcadeFloor data;
|
|
data.x = (page - 1) * SCREEN_WIDTH + ivals[0];
|
|
data.width = ivals[2];
|
|
data.yval = (byte)ivals[1];
|
|
data.flag = false;
|
|
debug(1, "Floor: %s", data.dump().c_str());
|
|
_floorData.push_back(data);
|
|
} else {
|
|
const Common::Rect rect(Common::Point(ivals[0], ivals[1]), ivals[2], ivals[3]);
|
|
compBuffer.fillRect(rect, _drawColFG);
|
|
}
|
|
break;
|
|
case 0xA114: // DRAW EMPTY RECT
|
|
if (_doingInit) {
|
|
ArcadeFloor data;
|
|
data.x = (page - 1) * SCREEN_WIDTH + ivals[0];
|
|
data.width = ivals[2];
|
|
data.yval = (byte)ivals[1];
|
|
data.flag = true;
|
|
debug(1, "Floor: %s", data.dump().c_str());
|
|
_floorData.push_back(data);
|
|
} else {
|
|
const Common::Rect r(Common::Point(ivals[0], ivals[1]), ivals[2], ivals[3]);
|
|
const Common::Rect drawWin(SCREEN_WIDTH, SCREEN_HEIGHT);
|
|
Drawing::rectClipped(r, drawWin, &compBuffer, _drawColFG);
|
|
}
|
|
break;
|
|
case 0xA404: { // DRAW FILLED CIRCLE
|
|
int16 r = ivals[3] / 2;
|
|
Drawing::filledCircle(ivals[0], ivals[1], r, r, &compBuffer, _drawColFG, _drawColBG);
|
|
break;
|
|
}
|
|
case 0xA424: { // DRAW EMPTY CIRCLE
|
|
int16 r = ivals[3] / 2;
|
|
Drawing::emptyCircle(ivals[0], ivals[1], r, r, &compBuffer, _drawColFG);
|
|
break;
|
|
}
|
|
case 0xA502:
|
|
case 0xA512:
|
|
case 0xA522:
|
|
case 0xA532: { // DRAW SHAPE
|
|
if (_doingInit)
|
|
break;
|
|
|
|
ImageFlipMode flipMode = kImageFlipNone;
|
|
if (op == 0xa512)
|
|
flipMode = kImageFlipV;
|
|
else if (op == 0xa522)
|
|
flipMode = kImageFlipH;
|
|
else if (op == 0xa532)
|
|
flipMode = kImageFlipHV;
|
|
|
|
// Only draw in the scroll area
|
|
const Common::Rect drawWin(Common::Point(8, 8), SCREEN_WIDTH - 16, 117);
|
|
if (_currentNPCRunningTTM == 0) {
|
|
int16 x = ivals[0] + _npcState[0].x - 152;
|
|
int16 y = ivals[1] + _startYOffset + 2;
|
|
if (_brushes[_currentTTMNum].isValid())
|
|
_brushes[_currentTTMNum].getShape()->drawBitmap(_brushes[_currentTTMNum].getFrame(), x, y, drawWin, compBuffer, flipMode);
|
|
_npcState[0].y = y;
|
|
} else {
|
|
int16 x = ivals[0] + _drawXOffset;
|
|
int16 y = ivals[1] + _drawYOffset + 2;
|
|
if (_brushes[_currentTTMNum].isValid())
|
|
_brushes[_currentTTMNum].getShape()->drawBitmap(_brushes[_currentTTMNum].getFrame(), x, y, drawWin, compBuffer, flipMode);
|
|
_npcState[_currentNPCRunningTTM].x = x;
|
|
_npcState[_currentNPCRunningTTM].y = y;
|
|
}
|
|
break;
|
|
}
|
|
case 0xF02F: {
|
|
_shapes[_currentTTMNum].reset(new Image(engine->getResourceManager(), engine->getDecompressor()));
|
|
_shapes[_currentTTMNum]->loadBitmap(sval);
|
|
debug(1, "Load img %s into slot %d", sval.c_str(), _currentTTMNum);
|
|
_shapes2[_currentTTMNum] = _shapes[_currentTTMNum];
|
|
_allShapes[_shapes3[_currentTTMNum] * 5 + _currentTTMNum] = _shapes[_currentTTMNum];
|
|
break;
|
|
}
|
|
case 0x505F:
|
|
// Do nothing (ignore arg)
|
|
break;
|
|
default:
|
|
warning("Unsupported TTM opcode 0x%04x for Dragon arcade.", op);
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int16 DragonArcadeTTM::runScriptPage(int16 pageNum) {
|
|
Common::SeekableReadStream *scr = _ttmEnvs[_currentTTMNum].scr;
|
|
scr->seek(_ttmEnvs[_currentTTMNum]._frameOffsets[pageNum]);
|
|
|
|
uint16 opcode = scr->readUint16LE();
|
|
while (opcode != 0x0ff0 && opcode) {
|
|
int16 ivals[4] { 0, 0, 0, 0 };
|
|
Common::String sval;
|
|
byte count = (byte)(opcode & 0xf);
|
|
if (count <= 4) {
|
|
for (int i = 0; i < count; i++)
|
|
ivals[i] = scr->readUint16LE();
|
|
} else if (count == 0xf) {
|
|
sval = TTMInterpreter::readTTMStringVal(scr);
|
|
} else {
|
|
error("Unsupported TTM opcode 0x%04x with %d args for Dragon arcade.", opcode, count);
|
|
}
|
|
|
|
handleOperation(_ttmEnvs[_currentTTMNum], pageNum, opcode, count, ivals, sval);
|
|
opcode = scr->readUint16LE();
|
|
}
|
|
return 1;
|
|
|
|
}
|
|
|
|
void DragonArcadeTTM::runPagesForEachNPC(int16 xScrollOffset) {
|
|
for (_currentNPCRunningTTM = 19; _currentNPCRunningTTM > 0; _currentNPCRunningTTM--) {
|
|
ArcadeNPCState &npcState = _npcState[_currentNPCRunningTTM];
|
|
if (npcState.byte12) {
|
|
npcState.x_21 = 0;
|
|
npcState.x_11 = 0;
|
|
npcState.x_22 = 0;
|
|
npcState.x_12 = 0;
|
|
npcState.y_21 = 0;
|
|
npcState.y_11 = 0;
|
|
npcState.y_22 = 0;
|
|
npcState.y_12 = 0;
|
|
_drawXOffset = npcState.xx - xScrollOffset * 8 - 152;
|
|
_drawYOffset = npcState.yy;
|
|
_currentTTMNum = npcState.ttmNum;
|
|
|
|
// The original does this comparison, but it seems like a bug (should be &&)
|
|
// We could correct the check, but better to maintain bug compatibility.
|
|
// if (_drawXOffset > -20 || _drawXOffset < 340)
|
|
runNextPage(npcState.ttmPage);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void DragonArcadeTTM::freePages(uint16 num) {
|
|
delete _ttmEnvs[num].scr;
|
|
_ttmEnvs[num] = TTMEnviro();
|
|
}
|
|
|
|
void DragonArcadeTTM::freeShapes() {
|
|
_shapes3[_currentTTMNum] = 0;
|
|
_shapes[_currentTTMNum].reset();
|
|
_shapes2[_currentTTMNum].reset();
|
|
for (int i = 0; i < 6; i++) {
|
|
_allShapes[i * 5 + _currentTTMNum].reset();
|
|
}
|
|
}
|
|
|
|
} // end namespace Dgds
|