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

View File

@@ -0,0 +1,43 @@
/* 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/system.h"
#include "dgds/dgds.h"
#include "dgds/minigames/china_tank.h"
namespace Dgds {
ChinaTank::ChinaTank() {
}
void ChinaTank::init() {
}
void ChinaTank::tick() {
g_system->displayMessageOnOSD(Common::U32String("Tank minigame not implemented yet!"));
DgdsEngine::getInstance()->setMenuToTrigger(kMenuSkipArcade);
}
void ChinaTank::end() {
}
} // end namespace Dgds

View File

@@ -0,0 +1,43 @@
/* 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 DGDS_MINIGAMES_CHINA_TANK_H
#define DGDS_MINIGAMES_CHINA_TANK_H
namespace Dgds {
/** Tank mini-game for Heart of China */
class ChinaTank {
public:
ChinaTank();
void init();
void tick();
void end();
private:
// TODO add private members
};
} // end namespace Dgds
#endif // DGDS_MINIGAMES_CHINA_TANK_H

File diff suppressed because it is too large Load Diff

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/>.
*
*/
#ifndef DGDS_MINIGAMES_CHINA_TRAIN_H
#define DGDS_MINIGAMES_CHINA_TRAIN_H
namespace Dgds {
enum PlayerType {
kPlayerLucky,
kPlayerTong,
};
enum PlayerAction {
// The list of actions is not directly referenced in the game, but is in
// the exe just after the "Heart of China Train Game" copyright
kActionStandRight = 0,
kActionWalkRight = 1,
kActionWalkLeft = 2,
kActionJumpRight = 3,
kActionJumpLeft = 4,
kActionDuckRight = 5,
kActionFallRight = 6,
kActionFallLeft = 7,
kActionStagger = 8,
kActionDeathScene = 9,
kActionClub = 10,
kActionClubHit = 11,
kActionSwing = 12,
kActionSwingHit = 13,
kActionStab = 14,
kActionStabHit = 15,
kActionBlock = 16,
kActionHeroicJump = 17,
kActionBlockUp = 18,
kActionFree6 = 19,
kActionStandLeft = 20,
kActionDuckLeft = 21,
kActionFree7 = 22,
kActionFree8 = 23,
kActionERROR = 24,
};
enum PlayerIntent {
kIntentDuck = 0,
kIntentRest = 1,
kIntentPursue = 2,
kIntentAttack = 3,
kIntentRetreat = 4,
kIntentQ = 5,
kIntentInvalid = 255,
};
class TrainPlayers;
struct PlayerData {
int16 _frame; // field 0
ImageFlipMode _flipMode; // field 1
int16 _xstep; // field 2
int16 _ystep; // field 3
int16 _xoff; // field 4
int16 _yoff; // field 5
int16 _val6; // field 6
};
struct TunnelData {
TunnelData() : _start(0), _end(0) {}
int32 _start;
int32 _end;
};
class TrainPlayer {
public:
friend class TrainPlayers;
TrainPlayer(PlayerType type);
bool isBlocking() const { return _action == kActionBlock || _action == kActionBlockUp; }
bool isDucking() const { return _action == kActionDuckRight || _action == kActionDuckLeft; }
bool isFalling() const { return _action == kActionFallRight || _action == kActionFallLeft; }
bool isJumping() const { return _action == kActionJumpRight || _action == kActionJumpLeft; }
bool isStaggering() const { return _action == kActionStagger; }
bool isStanding() const { return _action == kActionStandRight || _action == kActionStandLeft; }
bool isWalking() const { return _action == kActionWalkLeft || _action == kActionWalkRight; }
bool isTong() const { return _type == kPlayerTong; }
bool isLucky() const { return _type == kPlayerLucky; }
void checkLives();
void doAttack(TrainPlayer &other);
void doBlock();
void doClub(int16 damage);
void doPursue(const TrainPlayer &other);
void doRun();
void doJump();
void setAction(PlayerAction state, bool flag);
bool inRange(const TrainPlayer &other);
void hit(int16 damage);
void startStagger(const TrainPlayer &other);
void readStuff(const Common::String &path);
void computerDucks();
void checkDuck(const TunnelData &currentTunnel);
void doProcess();
PlayerType _type;
int16 _xpos;
int16 _ypos;
PlayerAction _action;
int16 _fatigue;
int16 _hitpoints;
PlayerIntent _intent;
int16 _ferocity;
int16 _val7;
Common::Array<Common::Array<PlayerData>> _allData;
Common::Array<PlayerData> *_currentActionData;
PlayerData *_data;
private:
TrainPlayer &chooseEnemy();
PlayerData *endOfCurrentAction();
PlayerData *startOfCurrentAction();
static int16 _blockSoundFlag;
};
class TrainPlayers {
public:
TrainPlayers();
void initPlayers();
void checkLives();
void readAnims();
void freeAnims();
void doScroll(int16 jumpOffset);
TrainPlayer _lucky;
TrainPlayer _tong;
};
/** Train fight mini-game for Heart of China */
class ChinaTrain {
public:
ChinaTrain();
void init(); // aka arcadeInit
int16 tick(); // aka arcadeLoop
void end(); // aka aracdeReset
//int16 currentCar() const { return _currentCar; }
void checkTongFall(int16 xpos, int16 car);
TrainPlayers &getPlayers() { return _players; }
const TunnelData &getCurrentTunnel() const { return _currentTunnel; }
void leaveArcade();
bool checkGap(int16 xpos, int16 offset);
void onKeyDown(Common::KeyState kbd);
void onKeyUp(Common::KeyState kbd);
void setMenuResult(bool yes);
int16 _jumpOffset;
private:
void arcadeFadeout();
void getUserInput();
void getNpcInput();
void processInput();
void handleVariables();
void drawFrame();
int16 trainArcade();
void trainRestart();
void lost();
void makeNewTunnel();
//void setupArcade(); // just sets fill=1 and _clipWin.
void initScoreWindow();
void drawBorder();
void shadeLabel(int16 x, int16 y, int16 w, int16 h, const char *label);
void shadeBox(Graphics::ManagedSurface &buf, byte backCol, byte foreCol, byte fill, int16 x, int16 y, int16 w, int16 h);
void fixBorder();
void drawCommandButtons();
void drawBmps();
void drawTunnel();
void drawMountains(int16 num);
void drawBlock(int16 x1, int16 x2, int16 param_3, int16 y1, int16 y2, int16 param_6, int16 param_7, int16 param_8);
void drawButtons(int16 *buttons);
void shadePressButton(int16 x, int16 y);
void shadeButton(int16 x, int16 y);
void drawScoreWindow();
void drawScore();
void drawSnow();
void drawTrain();
void drawActors();
void drawCar(int16 xoff, int16 frame, int16 yoff);
void fixUpTunnel();
bool calcBounce(int16 car);
void checkRegions(TrainPlayer &player);
void processOrders(TrainPlayer &player, TrainPlayer &enemy);
int16 readButtons();
void cabooseLost();
int16 _arcadeCount;
int16 _arcadeFlag;
bool _arcadeInitFlag;
int16 _arcadeDrawFlag;
int16 _failCounter;
int16 _lastMaskedArcadeFlag;
int16 _int3036;
int16 _tongAttackCounter;
int16 _tongInjuredCounter;
int16 _tongRestTarget;
int16 _lastTongHP;
int16 _lastTongFatigue;
TrainPlayers _players;
int16 _currentCar;
int32 _trackPos;
int32 _frameCnt;
int16 _xOffset;
int16 _tunnelNum;
TunnelData _currentTunnel;
int16 _cabooseTrail;
int16 _lastBtn;
const Common::Rect _clipWin;
//Common::SharedPtr<Image> _simChars;
Common::SharedPtr<Image> _rectShape;
Common::SharedPtr<Image> _luckyMaps;
Common::SharedPtr<Image> _test;
int16 _pressedCommandButton;
bool _leftButtonDown;
bool _rightButtonDown;
int16 _lastKeycode;
bool _playedTunnelSFX;
};
} // end namespace Dgds
#endif // DGDS_MINIGAMES_CHINA_TRAIN_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,228 @@
/* 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 DGDS_MINIGAMES_DRAGON_ARCADE_H
#define DGDS_MINIGAMES_DRAGON_ARCADE_H
#include "common/types.h"
#include "engines/dgds/minigames/dragon_arcade_ttm.h"
namespace Dgds {
enum DragonBulletState {
kBulletInactive = 0,
kBulletFlying = 1,
kBulletHittingBlade = 2,
kBulletHittingEnemy = 3,
};
enum DragonBladeMoveFlag {
kBladeMoveNone = 0,
kBladeMoveUp = 1,
kBladeMoveDown = 2,
kBladeMoveRight = 4,
kBladeMoveLeft = 8,
};
class ArcadeNPCState {
public:
ArcadeNPCState() : xx(0), yy(0), x(0), y(0), x_11(0), y_11(0), x_12(0), y_12(0),
ttmPage(0), byte12(0), byte13(0), health(0), ttmNum(0), x_21(0), y_21(0),
x_22(0), y_22(0) {}
int16 xx;
int16 yy;
int16 x;
int16 y;
int16 x_11;
int16 y_11;
int16 x_12;
int16 y_12;
int16 ttmPage;
int8 byte12;
int8 byte13;
int8 health;
int8 ttmNum; /* Set to 0, 1 or 2 */
int16 x_21;
int16 y_21;
int16 x_22;
int16 y_22;
};
class DragonArcadeBullet {
public:
DragonArcadeBullet() : _x(0), _y(0), _state(kBulletInactive),
_flipMode(kImageFlipNone), _bulletType(0), _ySpeed(0) {}
int16 _x;
int16 _y;
DragonBulletState _state;
ImageFlipMode _flipMode;
int16 _bulletType;
uint16 _ySpeed;
// these fields are in the original but seem to not be used.
// int16 _var2;
// int16 _var3;
};
class DragonArcade {
public:
DragonArcade();
void arcadeTick();
void onKeyDown(Common::KeyState kbd);
void onKeyUp(Common::KeyState kbd);
private:
void initIfNeeded();
void drawBackgroundAndWeapons();
void checkToOpenMenu();
void clearAllBulletStates();
void clearAllNPCStates();
void createBullet(int16 x, int16 y, ImageFlipMode flipMode, int16 bulletType);
void bladeTakeHitAndCheck();
void enemyTakeHit() { _npcState[1].health--; }
void enemyTakeHitAndCheck();
void playSfx(int16 num) const;
void drawBulletHitCircles(uint16 x, uint16 y, bool colorFlag);
void drawHealthBars();
void runThenDrawBulletsInFlight();
void redraw();
void finish();
void loadTTMScriptsForStage(uint16 stage);
void fadeInAndClearScreen();
bool doTickUpdate();
void resetStageState();
void initValuesForStage();
void initValuesForStage0();
void initValuesForStage2();
void initValuesForStage3();
void initValuesForStage4();
void initValuesForStage6();
void setFinishCountdownIfLessThan0(int16 val);
void updateBladeWithInputs();
void updateBlade();
void updateBoss();
void updateBoss2();
void decBossHealth();
void decBossHealthAndCheck();
void bladeTakeHit();
void arcade16bc();
void arcade16de(int16 param);
void arcade1e83();
void arcade2445();
void arcade2754(int16 findResult);
void arcade34b4();
void arcade3e96();
void arcade4085();
void updateXScrollOffset();
bool isNpcInsideXRange(int16 num);
void updateBullets();
void checkBladeFireAllStages();
void checkEnemyFireStage0124();
void checkBossFireStage3();
void checkBossFireStage6();
void updateMouseAndJoystickStates();
int16 findFloorUnderBlade();
int16 checkBulletCollision(int16 num);
void mouseUpdate();
void keyboardUpdate();
void limitToCenterOfScreenAndUpdateCursor();
uint16 moveToNextStage();
void findFloorMatch();
void findFloorMinGT();
void findFloorMinGE();
void findFloorMatchOrMinOrMax();
void findFloorMax();
void updateFloorsUnderBlade();
bool isFloorNotFound();
void playSFX55AndStuff();
void moveBladeX();
void handleMouseStates();
void drawScrollBmp();
int16 _lastDrawnBladeHealth;
int16 _lastDrawnBossHealth;
uint16 _nextRandomVal;
int16 _loadedArcadeStage;
int16 _nextStage;
int16 _attemptCounter;
int16 _shouldUpdateState;
int16 _finishCountdown;
int16 _bladeState1;
int16 _bladePageOffset;
uint16 _mouseButtonWentDown;
int16 _scrollXOffset;
int32 _nTickUpdates;
int16 _startDifficultyMaybe;
int16 _bossStateUpdateCounter;
int16 _npcStateResetCounter;
int16 _scrollVelocityX;
uint16 _uint0a17;
int16 _currentYOffset;
int16 _int0b58;
int16 _int0b5a;
int16 _int0b60;
int16 _ttmYAdjust;
uint16 _uint0be6;
bool _dontMoveBladeFlag;
int16 _scrollXIncrement;
bool _lMouseButtonState;
bool _rMouseButtonState;
bool _lastLMouseButtonState;
bool _lastRMouseButtonState;
int16 _bladeXMove;
int16 _currentArrowNum;
int16 _foundFloorY;
bool _foundFloorFlag;
int16 _lastFloorY;
bool _haveBigGun;
bool _haveBomb;
bool _enemyHasSmallGun;
bool _dontRedrawBgndAndWeapons;
// maybe don't need these
//bool _arcadeNeedsBufferCopy;
//bool _flagInventoryOpened;
bool _initFinished;
bool _stillLoadingScriptsMaybe;
bool _flag40ee;
bool _flag40ef;
int16 _someMoveDirection;
bool _bladeHasFired;
bool _mouseIsAvailable;
bool _isMovingStage;
DragonBladeMoveFlag _bladeMoveFlag;
DragonBladeMoveFlag _keyStateFlags;
DragonBladeMoveFlag _bladeMoveFlagBeforeRButton;
DragonBladeMoveFlag _bladeHorizMoveAttempt;
DragonArcadeBullet _bullets[20];
ArcadeNPCState _npcState[20];
Common::SharedPtr<Image> _bulletImg;
Common::SharedPtr<Image> _arrowImg;
Common::SharedPtr<Image> _scrollImg;
DragonArcadeTTM _arcadeTTM;
Common::Array<int16> _floorY;
Common::Array<bool> _floorFlag;
};
} // end namespace Dgds
#endif // DGDS_MINIGAMES_DRAGON_ARCADE_H

View File

@@ -0,0 +1,353 @@
/* 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

View File

@@ -0,0 +1,118 @@
/* 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 DGDS_MINIGAMES_DRAGON_ARCADE_TTM_H
#define DGDS_MINIGAMES_DRAGON_ARCADE_TTM_H
#include "common/ptr.h"
#include "dgds/image.h"
#include "dgds/ttm.h"
namespace Dgds {
/**
* A segment of floor in the arcade section with a start x, width, height, and flag.
*/
class ArcadeFloor {
public:
ArcadeFloor() : x(0), width(0), yval(0), flag(false) {}
int16 x;
int16 width;
byte yval;
bool flag;
Common::String dump() const;
};
/**
The regular TTM interpreter always uses the environment's shape for the brushes,
but the arcade one stores brushes as pointers to the exact frame within the shape.
In practice this may make no difference - maybe we can track use shapes2 and
store the frame number?
*/
class Brush {
public:
Brush() : _frame(0) {}
Brush(const Common::SharedPtr<Image> &shape, int16 frame) : _shape(shape), _frame(frame) {}
void reset() {
_shape.reset();
_frame = 0;
}
bool isValid() const { return _shape && _shape->loadedFrameCount() > _frame; }
const Common::SharedPtr<Image> &getShape() const { return _shape; }
int16 getFrame() const { return _frame; }
private:
Common::SharedPtr<Image> _shape;
int16 _frame;
};
class ArcadeNPCState;
/** A TTM interpreter which is simpler than the main one and
specialized to the arcade sequences. */
class DragonArcadeTTM {
public:
DragonArcadeTTM(ArcadeNPCState *npcState);
void clearDataPtrs();
int16 load(const char *filename);
void finishTTMParse(int16 envNum);
int16 runNextPage(int16 pageNum);
void freePages(uint16 num);
void freeShapes();
void runPagesForEachNPC(int16 xScrollOffset);
const Common::Array<ArcadeFloor> &getFloorData() { return _floorData; }
uint16 _currentTTMNum;
int16 _currentNPCRunningTTM;
int16 _drawXOffset;
int16 _drawYOffset;
int16 _startYOffset;
bool _doingInit;
private:
int16 runScriptPage(int16 pageNum);
int16 handleOperation(TTMEnviro &env, int16 page, uint16 op, byte count, const int16 *ivals, const Common::String &sval);
int16 _shapes3[6];
Common::SharedPtr<Image> _shapes[6];
Common::SharedPtr<Image> _shapes2[6];
Common::SharedPtr<Image> _allShapes[30];
Brush _brushes[6];
byte _drawColFG;
byte _drawColBG;
ArcadeNPCState *_npcState;
// int16 _numA1x4OpsInInit; // implicit by count of items in _floorData
Common::Array<ArcadeFloor> _floorData;
// Note: only a subset of the enviro members get used, but
// use the same structure for simplicity.
TTMEnviro _ttmEnvs[5];
};
} // end namespace Dgds
#endif // DGDS_MINIGAMES_DRAGON_ARCADE_TTM_H

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 "common/intrinsics.h"
#include "dgds/minigames/shell_game.h"
#include "dgds/dgds.h"
#include "dgds/globals.h"
#include "dgds/font.h"
#include "dgds/includes.h"
#include "dgds/sound.h"
namespace Dgds {
ShellGame::ShellGame() : _swapStatus(0), _revealPeaStep(0),
_currentPeaPosition(0), _lastPass(0), _distractStep(0),
_distractDelay(0), _state13Counter(0), _swapPea1(0), _swapPea2(0),
_lastSwapPea1(0), _swapMoveDist(0), _swapMoveStep(0), _swapCount(0),
_reverseDirection(0), _clockwise(false)
{
}
void ShellGame::init() {
DgdsEngine *engine = DgdsEngine::getInstance();
HocGlobals *globals = static_cast<HocGlobals *>(engine->getGameGlobals());
assert(globals);
_shellGameImg.reset(new Image(engine->getResourceManager(), engine->getDecompressor()));
_shellGameImg->loadBitmap("SHELLGM2.BMP");
globals->setShellPea(engine->getRandom().getRandomNumber(2));
_distractStep = 14;
_distractDelay = 0;
_state13Counter = 0;
}
static int16 _getState() {
HocGlobals *globals = static_cast<HocGlobals *>(DgdsEngine::getInstance()->getGameGlobals());
return globals->getNativeGameState();
}
static void _setState(int16 val) {
HocGlobals *globals = static_cast<HocGlobals *>(DgdsEngine::getInstance()->getGameGlobals());
globals->setNativeGameState(val);
}
static int16 _getPeaPosition() {
HocGlobals *globals = static_cast<HocGlobals *>(DgdsEngine::getInstance()->getGameGlobals());
return globals->getShellPea();
}
static void _setPeaPosition(int16 val) {
HocGlobals *globals = static_cast<HocGlobals *>(DgdsEngine::getInstance()->getGameGlobals());
globals->setShellPea(val);
}
void ShellGame::drawShellGameStr(int16 count, int16 x, int16 y) const {
const Common::String countStr = Common::String::format("%d", count);
DgdsEngine *engine = DgdsEngine::getInstance();
const DgdsFont *fnt = engine->getFontMan()->getFont(FontManager::k4x5Font);
fnt->drawString(&engine->getStoredAreaBuffer(), countStr, x, y, 50, 13);
}
void ShellGame::drawShells() const {
const Common::Rect screenRect(SCREEN_WIDTH, SCREEN_HEIGHT);
for (uint i = 0; i < 3; i++)
_shellGameImg->drawBitmap(0, 98 + i * 55, 153, screenRect, DgdsEngine::getInstance()->getStoredAreaBuffer());
}
void ShellGame::shellGameTick() {
DgdsEngine *engine = DgdsEngine::getInstance();
HocGlobals *globals = static_cast<HocGlobals *>(engine->getGameGlobals());
assert(globals);
if (!_shellGameImg)
init();
// copy the background to the saved buffer (hack.. but the original does it too)
engine->getStoredAreaBuffer().blitFrom(engine->getBackgroundBuffer());
// Draw the shekels.
drawShellGameStr(globals->getSheckels(), 131, 123);
// Draw the bet
drawShellGameStr(globals->getShellBet(), 201, 123);
update();
if (_revealPeaStep) {
revealPea(false);
} else {
if (_swapStatus)
swapShells(false);
else
drawShells();
}
}
void ShellGame::update() {
int16 state = _getState();
if (state == 1 || state == 7) {
_currentPeaPosition = _getPeaPosition();
if (state == 7)
_currentPeaPosition = _currentPeaPosition / 16;
_setPeaPosition(_getPeaPosition() & 0xf);
_revealPeaStep = 1;
state++;
} else if (state == 4 || state == 10) {
_swapStatus = 1;
_swapCount = 0;
state++;
} else if (state == 13) {
if (_state13Counter) {
_state13Counter--;
if (!_state13Counter)
state = 10;
} else {
_state13Counter = 20;
}
}
_setState(state);
}
void ShellGame::revealPea(bool flag) {
DgdsEngine *engine = DgdsEngine::getInstance();
const Common::Rect screenRect(SCREEN_WIDTH, SCREEN_HEIGHT);
if (_revealPeaStep == 1)
engine->_soundPlayer->playSFX(147);
uint16 x_offset = 55 * _currentPeaPosition;
uint16 y_offset;
if (_revealPeaStep > 40) {
y_offset = 60 - _revealPeaStep;
} else if (_revealPeaStep > 20) {
y_offset = 20;
} else {
y_offset = _revealPeaStep;
}
// Draw the pea
if (_currentPeaPosition == _getPeaPosition())
_shellGameImg->drawBitmap(1, 112 + x_offset, 166, screenRect, engine->getStoredAreaBuffer());
// Draw the shell revealing the pea
_shellGameImg->drawBitmap(0, 98 + x_offset, 153 - y_offset, screenRect, engine->getStoredAreaBuffer());
// Draw the other shells
for (int i = 0; i < 3; i++) {
if (i != _currentPeaPosition)
_shellGameImg->drawBitmap(0, 98 + i * 55, 153, screenRect, engine->getStoredAreaBuffer());
}
if (!flag) {
if (_lastPass) {
_lastPass = false;
_revealPeaStep = 0;
_setState(_getState() + 1);
} else {
if (y_offset == 0)
_lastPass = true;
else
_revealPeaStep++;
}
}
}
bool ShellGame::checkDistract() {
DgdsEngine *engine = DgdsEngine::getInstance();
HocGlobals *globals = static_cast<HocGlobals *>(engine->getGameGlobals());
int16 sheckels = globals->getSheckels();
int16 bet = globals->getShellBet();
if ((sheckels + bet >= 300)
|| (sheckels + bet >= 150 && (engine->getRandom().getRandomNumber(256) & 0xc0))
|| bet > 95
|| (bet > 45 && (engine->getRandom().getRandomNumber(3)))) {
_distractStep++;
if (_distractStep > 21)
_distractStep = 14;
return true;
}
return false;
}
void ShellGame::setupSwap() {
DgdsEngine *engine = DgdsEngine::getInstance();
do {
_swapPea1 = engine->getRandom().getRandomNumber(2);
} while (_swapPea1 == _lastSwapPea1);
_lastSwapPea1 = _swapPea1;
if (_swapPea1 == 0) {
_swapPea2 = 1;
} else if (_swapPea1 == 1) {
_swapPea2 = 2;
} else {
_swapPea1 = 0;
_swapPea2 = 2;
}
if (_getPeaPosition() == _swapPea1) {
_setPeaPosition(_swapPea2);
} else if (_getPeaPosition() == _swapPea2) {
_setPeaPosition(_swapPea1);
}
_swapMoveDist = (_swapPea2 - _swapPea1) * 55;
_lastPass = false;
_swapMoveStep = 0;
_swapStatus = 2;
}
void ShellGame::swapShells(bool flag) {
DgdsEngine *engine = DgdsEngine::getInstance();
const Common::Rect screenRect(SCREEN_WIDTH, SCREEN_HEIGHT);
if (!flag) {
if (_swapStatus == 1) {
engine->_soundPlayer->playSFX(145);
setupSwap();
_clockwise = (bool)(engine->getRandom().getRandomNumber(255) & 0x40);
_reverseDirection = 0;
}
uint16 move_speed = (2 << engine->getDifficulty()) + 2;
if (_getState() >= 10 && _getState() <= 12)
move_speed++;
if (!_lastPass) {
_swapMoveStep += move_speed;
if (abs(_swapPea1 - _swapPea2) != 1)
_swapMoveStep += move_speed;
if (_swapMoveStep > _swapMoveDist)
_swapMoveStep = _swapMoveDist;
}
}
uint16 xbase1 = 98 + _swapPea1 * 55;
uint16 xbase2 = 98 + _swapPea2 * 55;
double move = ((double)_swapMoveStep / _swapMoveDist) * M_PI;
uint16 y_offset;
if (abs(_swapPea1 - _swapPea2) == 1)
y_offset = 26;
else
y_offset = 28;
y_offset = (int16)(y_offset * sin(move));
for (int i = 0; i < 3; i++) {
if (i != _swapPea1 && i != _swapPea2)
_shellGameImg->drawBitmap(0, 98 + i * 55, 153, screenRect, engine->getStoredAreaBuffer());
}
if (!flag) {
if ((_reverseDirection == 0) && (_swapMoveStep >= (_swapMoveDist / 2))) {
if (engine->getRandom().getRandomNumber(7)) {
_reverseDirection = -1;
} else {
_reverseDirection = 1;
if (_getPeaPosition() == _swapPea1)
_setPeaPosition(_swapPea2);
else if (_getPeaPosition() == _swapPea2)
_setPeaPosition(_swapPea1);
}
}
}
uint16 x1,x2,y1,y2;
if (_reverseDirection <= 0) {
x1 = xbase1 + _swapMoveStep;
x2 = xbase2 - _swapMoveStep;
} else {
x1 = xbase2 - _swapMoveStep;
x2 = xbase1 + _swapMoveStep;
}
if (_clockwise) {
y1 = 153 - y_offset;
y2 = 153 + y_offset;
} else {
y1 = 153 + y_offset;
y2 = 153 - y_offset;
}
_shellGameImg->drawBitmap(0, x1, y1, screenRect, engine->getStoredAreaBuffer());
_shellGameImg->drawBitmap(0, x2, y2, screenRect, engine->getStoredAreaBuffer());
if (!flag) {
int16 state = _getState();
if (_lastPass) {
_lastPass = false;
if (state == 11)
_setState(0);
else
_setState(6);
_swapStatus = 0;
} else if (_distractDelay) {
_distractDelay--;
} else if (_swapMoveStep == _swapMoveDist) {
_swapCount++;
if (state == 11) {
_lastPass = true;
} else if (_swapCount == 8 && checkDistract()) {
_setState(_distractStep);
_distractDelay = 20;
} else {
if (_swapCount < 12 || engine->getRandom().getRandomNumber(7) || state >= 14)
_swapStatus = 1;
else
_lastPass = true;
}
}
}
}
void ShellGame::shellGameEnd() {
_shellGameImg.reset();
}
} // end namespace Dgds

View File

@@ -0,0 +1,67 @@
/* 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 DGDS_MINIGAMES_SHELL_GAME_H
#define DGDS_MINIGAMES_SHELL_GAME_H
#include "dgds/image.h"
namespace Dgds {
/** Native code for the shell game from Heart of China. */
class ShellGame {
public:
ShellGame();
void shellGameTick();
void shellGameEnd();
private:
void init();
void drawShellGameStr(int16 count, int16 x, int16 y) const;
void drawShells() const;
void swapShells(bool flag);
void revealPea(bool flag);
void update();
bool checkDistract();
void setupSwap();
Common::SharedPtr<Image> _shellGameImg;
uint16 _revealPeaStep;
uint16 _currentPeaPosition;
bool _lastPass;
uint16 _distractStep;
uint16 _distractDelay;
uint16 _state13Counter;
int16 _swapPea1;
int16 _swapPea2;
uint16 _lastSwapPea1;
uint16 _swapStatus;
int16 _swapMoveDist;
uint16 _swapMoveStep;
uint16 _swapCount;
int16 _reverseDirection;
bool _clockwise;
};
} // end namespace Dgds
#endif // DGDS_MINIGAMES_SHELL_GAME_H