Files
scummvm-cursorfix/engines/dgds/minigames/dragon_arcade.cpp
2026-02-02 04:50:13 +01:00

2553 lines
66 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/util.h"
#include "common/system.h"
#include "graphics/cursorman.h"
#include "dgds/dgds.h"
#include "dgds/image.h"
#include "dgds/includes.h"
#include "dgds/minigames/dragon_arcade.h"
#include "dgds/sound.h"
#include "dgds/scene.h"
#include "dgds/drawing.h"
#include "dgds/globals.h"
#include "dgds/game_palettes.h"
#include "dgds/menu.h"
namespace Dgds {
DragonArcade::DragonArcade() : _arcadeTTM(_npcState), _lastDrawnBladeHealth(-1), _lastDrawnBossHealth(-1),
_nextRandomVal(0), _loadedArcadeStage(-1), _nextStage(0), _attemptCounter(0),
_shouldUpdateState(0), _finishCountdown(0), _bladeState1(0), _bladePageOffset(0), _mouseButtonWentDown(0),
_scrollXOffset(0), _nTickUpdates(0), _startDifficultyMaybe(0), _bossStateUpdateCounter(0), _npcStateResetCounter(0),
_scrollVelocityX(0), _uint0a17(0), _currentYOffset(0), _int0b58(0), _int0b5a(0), _int0b60(0), _ttmYAdjust(0),
_uint0be6(0), _dontMoveBladeFlag(false), _scrollXIncrement(0), _lMouseButtonState(false), _rMouseButtonState(false),
_lastLMouseButtonState(false), _lastRMouseButtonState(false), _bladeXMove(0), _bladeHorizMoveAttempt(kBladeMoveNone),
_currentArrowNum(0), _foundFloorY(0), _foundFloorFlag(false), _lastFloorY(0), _someMoveDirection(0),
_haveBigGun(true), _haveBomb(true), _enemyHasSmallGun(false), _dontRedrawBgndAndWeapons(false),
/*_arcadeNeedsBufferCopy(false), _flagInventoryOpened(false),*/ _initFinished(false), _stillLoadingScriptsMaybe(false),
_flag40ee(false), _flag40ef(false), _bladeHasFired(false), _mouseIsAvailable(false), _isMovingStage(false),
_bladeMoveFlag(kBladeMoveNone), _keyStateFlags(kBladeMoveNone), _bladeMoveFlagBeforeRButton(kBladeMoveNone)
{
}
static uint _getNextRandom() {
return DgdsEngine::getInstance()->getRandom().getRandomNumber(65535);
}
void DragonArcade::finish() {
_arcadeTTM._currentTTMNum = 0;
_arcadeTTM.freeShapes();
_arcadeTTM.freePages(0);
_arcadeTTM._currentTTMNum = 1;
_arcadeTTM.freeShapes();
_arcadeTTM.freePages(1);
_arcadeTTM._currentTTMNum = 2;
_arcadeTTM.freeShapes();
_arcadeTTM.freePages(2);
//DgdsEngine::getInstance()->getGamePals()->freePal(arcadePal);
_bulletImg.reset();
_arrowImg.reset();
_scrollImg.reset();
_loadedArcadeStage = -1;
DgdsEngine *engine = DgdsEngine::getInstance();
engine->enableKeymapper();
_initFinished = false;
//warning("TODO: DragonArcade::finish: copy/clear some vid buffers here?");
}
bool DragonArcade::doTickUpdate() {
// Note: original has a few buffer copy adjustments here,
// but we never need them I think because we clear+flip every time.
if (_finishCountdown == 0)
return false;
_nextRandomVal = _getNextRandom();
updateMouseAndJoystickStates();
updateBladeWithInputs();
int16 floorY = findFloorUnderBlade();
arcade2754(floorY);
switch (_loadedArcadeStage) {
case 0:
case 1:
case 2:
updateBlade();
arcade3e96();
break;
case 3:
updateBoss();
break;
case 4:
updateBlade();
arcade4085();
break;
case 5:
arcade4085();
break;
case 6:
updateBoss2();
break;
}
updateBullets();
drawScrollBmp();
runThenDrawBulletsInFlight();
checkBladeFireAllStages();
switch(_loadedArcadeStage) {
case 0:
case 1:
case 2:
case 4:
checkEnemyFireStage0124();
break;
case 3:
checkBossFireStage3();
break;
case 6:
checkBossFireStage6();
break;
default:
break;
}
drawHealthBars();
// Original has delay here to reduce the arcade speed to 15 FPS.
//DgdsEngine::getInstance()->setSkipNextFrame();
_nTickUpdates++;
return true;
}
void DragonArcade::updateBullets() {
for (int i = 19; i >= 0; i--) {
if (_bullets[i]._state == kBulletHittingBlade || _bullets[i]._state == kBulletHittingEnemy) {
_bullets[i]._state = kBulletInactive;
continue;
}
if (_bullets[i]._state == kBulletFlying) {
if (_bullets[i]._bulletType == 3) {
_bullets[i]._y += _bullets[i]._ySpeed;
}
if (_bullets[i]._flipMode == kImageFlipNone) {
_bullets[i]._x -= (_scrollXIncrement * 8 - 10);
if (_bullets[i]._x < 321) {
int16 collisionResult = checkBulletCollision(i);
if (collisionResult == -1) {
_bullets[i]._state = kBulletHittingEnemy;
} else if (collisionResult == 1) {
_bullets[i]._state = kBulletHittingBlade;
}
continue;
}
} else {
_bullets[i]._x -= (_scrollXIncrement * 8 + 10);
if (_bullets[i]._x > -41) {
int16 collisionResult = checkBulletCollision(i);
if (collisionResult == -1) {
_bullets[i]._state = kBulletHittingEnemy;
} else if (collisionResult == 1) {
_bullets[i]._state = kBulletHittingBlade;
}
continue;
}
}
_bullets[i]._state = kBulletInactive;
}
}
}
int16 DragonArcade::checkBulletCollision(int16 num) {
int yoff = 0;
for (int i = 19; i >= 0; i--) {
if (_npcState[i].byte12 <= 0)
continue;
if (_bullets[num]._bulletType == 3) {
yoff = 7;
}
if (_bullets[num]._bulletType != 1 || i == 0) {
if (_bullets[num]._x < _npcState[i].x_11 || _npcState[i].x_12 < _bullets[num]._x ||
_bullets[num]._y + yoff < _npcState[i].y_11 || _npcState[i].y_12 < _bullets[num]._y + yoff) {
if (_bullets[num]._x < _npcState[i].x_21 || _npcState[i].x_22 < _bullets[num]._x ||
_bullets[num]._y + yoff < _npcState[i].y_21 || _npcState[i].y_22 < _bullets[num]._y + yoff)
continue;
if (i == 0)
return -1;
if (_loadedArcadeStage == 3) {
if (_bullets[num]._bulletType == 3)
continue;
} else {
if (_loadedArcadeStage == 4) {
if (_bullets[num]._bulletType == 1 || _bullets[num]._bulletType == 3)
continue;
} else if (_loadedArcadeStage != 6) {
return -1;
}
}
if (_bullets[num]._bulletType != 2) {
return -1;
}
} else {
if (i == 0) {
bladeTakeHit();
if (_npcState[0].health != 0)
return 1;
if (_bullets[num]._bulletType != 3) {
return 1;
}
_shouldUpdateState = 3;
return 1;
}
switch (_loadedArcadeStage) {
case 0:
case 1:
case 2:
case 4:
if (_loadedArcadeStage == 4 || _npcState[i].byte12 < 30) {
if (_bullets[num]._bulletType != 1) {
playSfx(0x56);
_npcState[i].byte12 = 1;
if (_npcState[i].ttmPage < 28) {
_npcState[i].ttmPage = 21;
} else {
_npcState[i].ttmPage = 49;
}
return 1;
}
break;
}
if (_npcState[i].byte12 == 30) {
_flag40ee = false;
} else {
_flag40ef = false;
}
_npcState[i].byte12 = -8;
_npcState[i].ttmPage = 33;
break;
case 3:
if (_bullets[num]._bulletType != 3) {
if (_npcState[i].byte12 == 1) {
_npcState[i].byte12 = 7;
_npcState[i].ttmPage = 75;
}
if (_haveBigGun) {
decBossHealth();
}
decBossHealth();
if (_npcState[i].health != 0) {
return 1;
}
_npcState[i].byte12 = 8;
_npcState[i].ttmPage = 79;
setFinishCountdownIfLessThan0(0x78);
return 1;
}
break;
case 6:
if (_bullets[num]._bulletType != 2) {
if (_haveBigGun) {
decBossHealthAndCheck();
}
decBossHealthAndCheck();
return 1;
}
break;
default:
return 1;
}
}
}
}
return 0;
}
static const int16 FIRE_ALLOWABLE_PAGES[] = {
0x1A, 0x26, 0x3E, 0x74, 0x94, 0xA0, 0xB8, 0xEE
};
static const int16 FIRE_Y_OFFSETS_SMALL_GUN[] = {
0x3A, 0x5A, 0x41, 0x4F, 0x37, 0x5A, 0x47, 0x4F
};
static const int16 FIRE_Y_OFFSETS_BIG_GUN[] = {
0x38, 0x5E, 0x3E, 0x52, 0x40, 0x5E, 0x3D, 0x52
};
static const int16 FIRE_X_OFFSETS[] = {
0xC4, 0xC8, 0xC8, 0xC2, 0x7A, 0x72, 0x69, 0x78
};
void DragonArcade::checkBladeFireAllStages() {
int16 yoff;
int16 sndno;
ImageFlipMode flipMode;
_bladeHasFired = false;
if (_npcState[0].ttmNum != 0)
return;
for (int i = 0; i < 8; i++) {
if (FIRE_ALLOWABLE_PAGES[i] == _npcState[0].ttmPage) {
if (_npcState[0].ttmPage < 123) {
flipMode = kImageFlipNone;
} else {
flipMode = kImageFlipH;
}
if (_haveBigGun) {
yoff = FIRE_Y_OFFSETS_BIG_GUN[i];
} else {
yoff = FIRE_Y_OFFSETS_SMALL_GUN[i];
}
createBullet(FIRE_X_OFFSETS[i] + _npcState[0].x - 0xa0,
yoff + _arcadeTTM._startYOffset, flipMode, 0);
// is this always 0?
//if (INT_39e5_0c58 == 0) {
sndno = 47;
//} else {
//sndno = 52;
//}
playSfx(sndno);
_bladeHasFired = true;
break;
}
}
}
static const int16 ENEMY_FIRE_ALLOWABLE_PAGES[] = { 3, 12, 31, 40 };
static const int16 ENEMY_FIRE_X_OFFSETS[] = { 0xB1, 0xB3, 0x77, 0x75, };
static const int16 ENEMY_FIRE_Y_OFFSETS[] = { 0x4E, 0x56, 0x4D, 0x55,};
void DragonArcade::checkEnemyFireStage0124() {
for (int i = 9; i != 0; i--) {
if (_npcState[i].byte12 == 0)
continue;
for (int j = 0; j < 4; j++) {
if (_npcState[i].x < 340 && -20 < _npcState[i].x &&
ENEMY_FIRE_ALLOWABLE_PAGES[j] == _npcState[i].ttmPage) {
debug(1, "enemy %d @ %d firing type %d on page %d", i, _npcState[i].x, j, _npcState[i].ttmPage);
ImageFlipMode flipMode = (_npcState[i].ttmPage < 29) ? kImageFlipNone : kImageFlipH;
createBullet(ENEMY_FIRE_X_OFFSETS[j] + _npcState[i].xx - _scrollXOffset * 8 - 0xa0,
ENEMY_FIRE_Y_OFFSETS[j] + _npcState[i].yy + 3, flipMode, 1);
playSfx(0x25);
}
}
}
}
void DragonArcade::checkBossFireStage3() {
if (_npcState[1].x < 0x154 && -20 < _npcState[1].x && _npcState[1].ttmPage == 22) {
createBullet(_npcState[1].xx - _scrollXOffset * 8 - 44,
_npcState[1].yy + 70, kImageFlipH, 3);
playSfx(0x2a);
}
}
void DragonArcade::checkBossFireStage6() {
ImageFlipMode flipMode = (_npcState[1].ttmPage < 40) ? kImageFlipNone: kImageFlipH;
if (_npcState[1].x < 0x154 && -20 < _npcState[1].x &&
(_npcState[1].ttmPage == 9 || _npcState[1].ttmPage == 40)) {
createBullet(_npcState[1].xx - _scrollXOffset * 8 - 19,
_npcState[1].yy + 86, flipMode, 2);
playSfx(0x24);
}
}
void DragonArcade::limitToCenterOfScreenAndUpdateCursor() {
Common::Point lastMouse = DgdsEngine::getInstance()->getLastMouse();
/* limit mouse coords to (x = 144-190, y = 135-180) */
lastMouse.x = CLIP((int)lastMouse.x, 144, 190);
lastMouse.y = CLIP((int)lastMouse.y, 135, 180);
g_system->warpMouse(lastMouse.x, lastMouse.y);
int16 arrowNum = (lastMouse.x - 144) / 16 + ((lastMouse.y - 136) / 16) * 3;
if (_currentArrowNum != arrowNum && arrowNum < 9) {
_currentArrowNum = arrowNum;
CursorMan.replaceCursor(*(_arrowImg->getSurface(arrowNum)->surfacePtr()), 0, 0, 0, 0);
}
//if (g_arcadeNeedsBufferCopy == 0) {
// Arcade_MouseCursorDraw();
//}
}
void DragonArcade::onKeyDown(Common::KeyState kbd) {
switch (kbd.keycode) {
case Common::KEYCODE_KP7:
_keyStateFlags = static_cast<DragonBladeMoveFlag>(_keyStateFlags | kBladeMoveUp | kBladeMoveLeft);
break;
case Common::KEYCODE_KP8:
case Common::KEYCODE_UP:
case Common::KEYCODE_w:
_keyStateFlags = static_cast<DragonBladeMoveFlag>(_keyStateFlags | kBladeMoveUp);
break;
case Common::KEYCODE_KP9:
_keyStateFlags = static_cast<DragonBladeMoveFlag>(_keyStateFlags | kBladeMoveUp | kBladeMoveRight);
break;
case Common::KEYCODE_KP4:
case Common::KEYCODE_LEFT:
case Common::KEYCODE_a:
_keyStateFlags = static_cast<DragonBladeMoveFlag>(_keyStateFlags | kBladeMoveLeft);
break;
case Common::KEYCODE_KP6:
case Common::KEYCODE_RIGHT:
case Common::KEYCODE_d:
_keyStateFlags = static_cast<DragonBladeMoveFlag>(_keyStateFlags | kBladeMoveRight);
break;
case Common::KEYCODE_KP1:
_keyStateFlags = static_cast<DragonBladeMoveFlag>(_keyStateFlags | kBladeMoveDown | kBladeMoveLeft);
break;
case Common::KEYCODE_KP2:
case Common::KEYCODE_DOWN:
case Common::KEYCODE_x:
_keyStateFlags = static_cast<DragonBladeMoveFlag>(_keyStateFlags | kBladeMoveDown);
break;
case Common::KEYCODE_KP3:
_keyStateFlags = static_cast<DragonBladeMoveFlag>(_keyStateFlags | kBladeMoveDown | kBladeMoveRight);
break;
case Common::KEYCODE_SPACE:
_lMouseButtonState = true;
break;
case Common::KEYCODE_RETURN:
case Common::KEYCODE_KP_ENTER:
_rMouseButtonState = true;
break;
default:
break;
}
}
void DragonArcade::onKeyUp(Common::KeyState kbd) {
switch (kbd.keycode) {
case Common::KEYCODE_KP7:
_keyStateFlags = static_cast<DragonBladeMoveFlag>(_keyStateFlags & ~(kBladeMoveUp | kBladeMoveLeft));
break;
case Common::KEYCODE_KP8:
case Common::KEYCODE_UP:
case Common::KEYCODE_w:
_keyStateFlags = static_cast<DragonBladeMoveFlag>(_keyStateFlags & ~kBladeMoveUp);
break;
case Common::KEYCODE_KP9:
_keyStateFlags = static_cast<DragonBladeMoveFlag>(_keyStateFlags & ~(kBladeMoveUp | kBladeMoveRight));
break;
case Common::KEYCODE_KP4:
case Common::KEYCODE_LEFT:
case Common::KEYCODE_a:
_keyStateFlags = static_cast<DragonBladeMoveFlag>(_keyStateFlags & ~kBladeMoveLeft);
break;
case Common::KEYCODE_KP6:
case Common::KEYCODE_RIGHT:
case Common::KEYCODE_d:
_keyStateFlags = static_cast<DragonBladeMoveFlag>(_keyStateFlags & ~kBladeMoveRight);
break;
case Common::KEYCODE_KP1:
_keyStateFlags = static_cast<DragonBladeMoveFlag>(_keyStateFlags & ~(kBladeMoveDown | kBladeMoveLeft));
break;
case Common::KEYCODE_KP2:
case Common::KEYCODE_DOWN:
case Common::KEYCODE_x:
_keyStateFlags = static_cast<DragonBladeMoveFlag>(_keyStateFlags & ~kBladeMoveDown);
break;
case Common::KEYCODE_KP3:
_keyStateFlags = static_cast<DragonBladeMoveFlag>(_keyStateFlags & ~(kBladeMoveDown | kBladeMoveRight));
break;
case Common::KEYCODE_SPACE:
_lMouseButtonState = false;
break;
case Common::KEYCODE_RETURN:
case Common::KEYCODE_KP_ENTER:
_rMouseButtonState = false;
break;
default:
break;
}
}
void DragonArcade::keyboardUpdate() {
//warning("TODO: Keyboard update");
// TODO: Keyboard update.
}
void DragonArcade::mouseUpdate() {
limitToCenterOfScreenAndUpdateCursor();
_rMouseButtonState |= DgdsEngine::getInstance()->getScene()->isRButtonDown();
_lMouseButtonState |= DgdsEngine::getInstance()->getScene()->isLButtonDown();
int16 arrowRow = _currentArrowNum / 3;
if (arrowRow == 0) {
_keyStateFlags = kBladeMoveUp;
} else if (arrowRow == 1) {
_keyStateFlags = kBladeMoveNone;
} else if (arrowRow == 2) {
_keyStateFlags = kBladeMoveDown;
}
if (_currentArrowNum % 3 == 0) {
_keyStateFlags = static_cast<DragonBladeMoveFlag>(_keyStateFlags | kBladeMoveLeft);
} else if (_currentArrowNum % 3 == 2) {
_keyStateFlags = static_cast<DragonBladeMoveFlag>(_keyStateFlags | kBladeMoveRight);
}
}
void DragonArcade::updateMouseAndJoystickStates() {
_bladeXMove = 0;
_scrollXIncrement = 0;
//_rMouseButtonState = false;
//_lMouseButtonState = false;
if (!_mouseIsAvailable) {
//if (g_optionJoystickOnButtonState != 0) {
// Joystick_ArcadeUpdate();
//}
keyboardUpdate();
} else {
mouseUpdate();
}
if (_mouseButtonWentDown != 0x80) {
_bladeMoveFlag = kBladeMoveNone;
if ((_keyStateFlags & kBladeMoveRight) == kBladeMoveNone) {
if ((_keyStateFlags & kBladeMoveLeft) != kBladeMoveNone) {
if (_someMoveDirection) {
_someMoveDirection = 0;
}
_bladeMoveFlag = kBladeMoveLeft;
if (_bladeState1 == 0) {
_scrollVelocityX = -1;
_bladeHorizMoveAttempt = kBladeMoveLeft;
}
}
} else {
_bladeMoveFlag = kBladeMoveRight;
if (_bladeState1 == 0) {
_scrollVelocityX = 1;
_bladeHorizMoveAttempt = kBladeMoveRight;
}
}
if ((_keyStateFlags & kBladeMoveUp) == kBladeMoveNone) {
if ((_keyStateFlags & kBladeMoveDown) != kBladeMoveNone) {
_bladeMoveFlag = static_cast<DragonBladeMoveFlag>(_bladeMoveFlag | kBladeMoveDown);
}
} else {
_bladeMoveFlag = static_cast<DragonBladeMoveFlag>(_bladeMoveFlag | kBladeMoveUp);
}
if (_lMouseButtonState && !_lastLMouseButtonState) {
_mouseButtonWentDown = 1;
}
if (_rMouseButtonState && !_lastRMouseButtonState && _bladeState1 != 2 && _bladeState1 != 1) {
_mouseButtonWentDown = 2;
_bladeMoveFlagBeforeRButton = _bladeMoveFlag;
}
_lastLMouseButtonState = _lMouseButtonState;
_lastRMouseButtonState = _rMouseButtonState;
}
}
int16 DragonArcade::findFloorUnderBlade() {
updateFloorsUnderBlade();
if (_bladeState1 == 1 || _bladeState1 == 2) {
if ((_bladePageOffset + 56 == _npcState[0].ttmPage) ||
(_bladePageOffset + 22 == _npcState[0].ttmPage)) {
findFloorMatchOrMinOrMax();
} else {
findFloorMinGE();
if (isFloorNotFound()) {
_foundFloorY = -0x100;
}
}
} else if ((_bladeMoveFlag & kBladeMoveDown) == kBladeMoveNone) {
if ((_bladeMoveFlag & kBladeMoveUp) == kBladeMoveNone) {
/* Not moving up or down */
findFloorMatch();
if (isFloorNotFound()) {
findFloorMinGT();
if (isFloorNotFound()) {
findFloorMax();
if (isFloorNotFound()) {
_foundFloorY = -0x100;
}
}
}
} else {
/* Move up */
findFloorMax();
if (isFloorNotFound()) {
findFloorMatch();
if (isFloorNotFound()) {
findFloorMinGT();
if (isFloorNotFound()) {
_foundFloorY = -0x100;
}
}
}
}
} else {
/* Move down */
findFloorMinGT();
if (isFloorNotFound()) {
findFloorMatch();
if (isFloorNotFound()) {
findFloorMax();
if (isFloorNotFound()) {
_foundFloorY = -0x100;
}
}
}
}
return _foundFloorY;
}
bool DragonArcade::isNpcInsideXRange(int16 num) {
return _npcState[num].x < 321 && _npcState[num].x > 1;
}
void DragonArcade::arcade16bc() {
_bladeState1 = 5;
_npcState[0].ttmPage = _bladePageOffset + 64;
_arcadeTTM._startYOffset++;
_currentYOffset = _arcadeTTM._startYOffset;
_uint0a17++;
}
void DragonArcade::arcade1e83() {
_scrollXOffset -= _scrollXIncrement;
_npcState[0].x = _npcState[0].x - _bladeXMove;
}
void DragonArcade::arcade16de(int16 param) {
if (_bladeState1 == 2) {
_npcState[0].ttmPage = _bladePageOffset + 57;
}
else {
_npcState[0].ttmPage = _bladePageOffset + 23;
}
_npcState[0].ttmNum = 0;
_arcadeTTM._startYOffset = param;
_uint0a17 = 0;
_bladeState1 = 0;
_ttmYAdjust = 0;
_int0b5a = 15;
}
void DragonArcade::arcade4085() {
for (int i = 10; i < 12; i++) {
if (_npcStateResetCounter == 20) {
_npcState[i].ttmPage = 0;
}
_npcState[i].ttmPage++;
if (_npcState[i].ttmPage < 1 || 14 < _npcState[i].ttmPage) {
_npcState[i].byte12 = 0;
}
else {
if (_npcState[i].ttmPage == 1) {
if (isNpcInsideXRange(i))
playSfx(0x1d);
}
_npcState[i].byte12 = -5;
if (_npcState[0].health && _npcState[i].xx - 0x10 <= _npcState[0].xx &&
_npcState[0].xx <= _npcState[i].xx + 0x37) {
bladeTakeHit();
}
}
}
if ((_npcStateResetCounter & 3) == 0) {
_npcState[12].ttmPage++;
_npcState[13].ttmPage++;
if (0x1e < _npcState[12].ttmPage) {
_npcState[12].ttmPage = 15;
_npcState[13].ttmPage = 15;
}
}
for (int i = 10; i < 12; i++) {
if (_npcState[i].ttmPage == 29) {
if (isNpcInsideXRange(i + 2))
playSfx(0x59);
if (_npcState[0].health && _npcState[i].yy - 0x10 <= _npcState[0].xx && _npcState[0].xx <= _npcState[i].yy + 0x23) {
bladeTakeHit();
bladeTakeHit();
}
}
}
_npcState[14].ttmPage++;
if (0x35 < _npcState[14].ttmPage) {
if (isNpcInsideXRange(0xe))
playSfx(0x58);
_npcState[14].ttmPage = 40;
}
if (_npcState[0].health != 0 && ((_bladeMoveFlag & kBladeMoveDown) == kBladeMoveNone || _arcadeTTM._startYOffset < -6)
&& _npcState[14].xx - 0x10 <= _npcState[0].xx && _npcState[0].xx <= _npcState[14].xx + 0x23) {
bladeTakeHit();
bladeTakeHit();
if (_npcState[0].health == 0) {
/* blade dies! */
_npcState[0].ttmNum = 2;
_npcState[0].ttmPage = 34;
_npcState[0].byte12 = 14;
_bladeState1 = 14;
_shouldUpdateState = 2;
}
}
_npcStateResetCounter++;
if (_npcStateResetCounter == 60) {
_npcStateResetCounter = 0;
}
}
bool DragonArcade::isFloorNotFound() {
return abs(_foundFloorY) > 990;
}
uint16 DragonArcade::moveToNextStage() {
int xblock = _scrollXOffset + _npcState[0].x / 8;
switch (_loadedArcadeStage) {
case 0:
if (0x31 < _scrollXOffset) {
_loadedArcadeStage = 1;
}
break;
case 1:
if (!_isMovingStage && xblock == 0x80 && 0 < _scrollVelocityX && _bladeState1 == 0) {
_scrollXOffset -= _scrollVelocityX;
arcade2445();
return 1;
}
if (0x89 < xblock && xblock < 0x8d && 20 < _arcadeTTM._startYOffset &&
_arcadeTTM._startYOffset < 70 && _bladePageOffset == 0) {
_scrollXOffset = 0x89 - _npcState[0].x / 8;
_arcadeTTM._startYOffset = -13;
playSFX55AndStuff();
_loadedArcadeStage = 2;
initValuesForStage2();
return 1;
}
break;
case 2:
if ((!_isMovingStage && xblock == 0x90 && 0 < _scrollVelocityX && _bladeState1 == 0) ||
(!_isMovingStage && xblock == 0xe9 && 0 < _scrollVelocityX && _bladeState1 == 0)) {
_scrollXOffset -= _scrollVelocityX;
arcade2445();
return 1;
}
if (0x99 < xblock && xblock < 0x9c && 20 < _arcadeTTM._startYOffset &&
_arcadeTTM._startYOffset < 70 && _bladePageOffset == 0) {
_scrollXOffset = 0x9a - _npcState[0].x / 8;
_arcadeTTM._startYOffset = -13;
playSFX55AndStuff();
return 1;
}
if (_scrollXOffset < 0x100) {
if (0xf3 < xblock && xblock < 0xf6 && 30 < _arcadeTTM._startYOffset &&
_arcadeTTM._startYOffset < 60 && _bladePageOffset == 0 && _startDifficultyMaybe != 3) {
_scrollXOffset = 0xf4 - _npcState[0].x / 8;
_arcadeTTM._startYOffset = -0x1a;
playSFX55AndStuff();
return 1;
}
} else if (_bladeState1 == 0) {
//Arcade_VidPtrs_SrcFront_DstBack();
//ResetFlagAt_48a0();
//SetFlagAt_48a0_andMaybeMouseUpdate();
loadTTMScriptsForStage(3);
}
break;
case 4:
if (-2 < _arcadeTTM._startYOffset && 129 < _npcState[0].xx && _npcState[0].xx < 201 && _npcState[0].health != 0) {
playSfx(0x57);
setFinishCountdownIfLessThan0(0x14);
if (_haveBigGun) {
_npcState[0].ttmPage = 58;
} else {
_npcState[0].ttmPage = 54;
}
_bladeState1 = 13;
_mouseButtonWentDown = 0x80;
_npcState[0].byte12 = 13;
_npcState[0].health = 0;
_npcState[0].ttmNum = 2;
return 1;
}
if (_scrollXOffset < 0x100) {
if (!_isMovingStage && xblock == 0x54 && 0 < _scrollVelocityX && _bladeState1 == 0) {
_scrollXOffset -= _scrollVelocityX;
arcade2445();
return 1;
}
} else if (_bladeState1 == 0) {
//Arcade_VidPtrs_SrcFront_DstBack();
//SetFlagAt_48a0_andMaybeMouseUpdate();
//ResetFlagAt_48a0();
loadTTMScriptsForStage(6);
}
break;
case 6:
if (!_stillLoadingScriptsMaybe && _scrollXOffset < 0x100) {
_scrollXOffset = 0x100;
_npcState[0].x -= 8;
if (_npcState[0].x < 0) {
_npcState[0].x = 0;
}
} else if (0x11f < xblock && _someMoveDirection == 0) {
_someMoveDirection = 1;
}
break;
default:
break;
}
return 0;
}
void DragonArcade::playSFX55AndStuff() {
_isMovingStage = false;
playSfx(0x55);
_bladeState1 = 7;
_uint0a17 = 0;
_ttmYAdjust = 0;
_int0b5a = 15;
_scrollVelocityX = 0;
_npcState[0].ttmPage = _bladePageOffset + 67;
}
void DragonArcade::updateFloorsUnderBlade() {
_floorY.clear();
_floorFlag.clear();
const Common::Array<ArcadeFloor> &floorData = _arcadeTTM.getFloorData();
for (const auto &floor : floorData) {
if (floor.x <= _npcState[0].xx && _npcState[0].xx <= floor.x + floor.width) {
_floorY.push_back(floor.yval - 108);
_floorFlag.push_back(floor.flag);
}
}
}
void DragonArcade::findFloorMinGE() {
_foundFloorY = 999;
for (uint i = 0; i < _floorY.size(); i++) {
if (_currentYOffset <= _floorY[i] && _floorY[i] < _foundFloorY) {
_foundFloorY = _floorY[i];
_foundFloorFlag = _floorFlag[i];
}
}
}
void DragonArcade::findFloorMinGT() {
_foundFloorY = 999;
for (uint i = 0; i < _floorY.size(); i++) {
if (_currentYOffset < _floorY[i] && _floorY[i] < _foundFloorY) {
_foundFloorY = _floorY[i];
_foundFloorFlag = _floorFlag[i];
}
}
}
void DragonArcade::findFloorMatch() {
_foundFloorY = -999;
for (uint i = 0; i < _floorY.size(); i++) {
if (_floorY[i] == _currentYOffset) {
_foundFloorY = _floorY[i];
_foundFloorFlag = _floorFlag[i];
}
}
}
void DragonArcade::findFloorMax() {
_foundFloorY = -999;
for (uint i = 0; i < _floorY.size(); i++) {
if (_floorY[i] < _currentYOffset && _foundFloorY < _floorY[i]) {
_foundFloorY = _floorY[i];
_foundFloorFlag = _floorFlag[i];
}
}
}
void DragonArcade::findFloorMatchOrMinOrMax() {
findFloorMatch();
if (isFloorNotFound()) {
findFloorMinGT();
if (isFloorNotFound()) {
findFloorMax();
}
}
}
void DragonArcade::arcade2445() {
_scrollVelocityX = 0;
_bladeState1 = 6;
_npcState[0].ttmPage += 78;
_isMovingStage = true;
}
static const int16 STAGE_2_XX[] = {
0x6A3, 0x6CD, 0x7C3
};
static const int16 STAGE_2_YY[] = {
0, 0, -23
};
static const byte STAGE_2_BYTE12[] = {
4, 5, 4
};
static const int16 STAGE_2_TTMPAGE[] = {
30, 39, 30
};
static const int16 STAGE_2_VAL1_PART2[] = {
0x547, 0x557, 0x567, 0x577
};
void DragonArcade::initValuesForStage2() {
for (int i = 7; i != 0; i--) {
_npcState[i].byte12 = 0;
}
for (int i = 3; i != 0; i--) {
_npcState[i].xx = STAGE_2_XX[i - 1];
_npcState[i].yy = STAGE_2_YY[i - 1];
_npcState[i].byte12 = STAGE_2_BYTE12[i - 1];
_npcState[i].ttmPage = STAGE_2_TTMPAGE[i - 1];
_npcState[i].ttmNum = 1;
}
for (int i = 10; i < 14; i++) {
_npcState[i].xx = STAGE_2_VAL1_PART2[i - 10];
}
_npcState[18].xx = 0x52f;
_npcState[18].yy = -13;
_npcState[18].byte12 = 31;
_npcState[18].ttmPage = 32;
_npcState[18].ttmNum = 2;
}
void DragonArcade::arcade2754(int16 floorY) {
if (0 < _finishCountdown) {
_finishCountdown--;
}
if (_finishCountdown == 0) {
if (_npcState[0].health == 0) {
if (_shouldUpdateState == 0) {
_shouldUpdateState = 1;
}
} else {
_shouldUpdateState = 0;
}
} else {
_dontMoveBladeFlag = false;
if (100 < _arcadeTTM._startYOffset) {
_npcState[0].health = 0;
if (_finishCountdown < 1) {
setFinishCountdownIfLessThan0(20);
} else {
_arcadeTTM._startYOffset = 100;
}
}
if (!moveToNextStage()) {
if (floorY < _arcadeTTM._startYOffset && _currentYOffset <= floorY) {
arcade16de(floorY);
} else if (_lastFloorY < _arcadeTTM._startYOffset && _currentYOffset <= _lastFloorY) {
arcade1e83();
floorY = _lastFloorY;
arcade16de(_lastFloorY);
} else if (_bladeState1 == 0) {
if (floorY == -0x100) {
arcade16bc();
} else if (_lastFloorY != -0x100) {
// Pop to new floor level if close enough
if (abs(_lastFloorY - floorY) < 16 || _nTickUpdates == 0) {
_arcadeTTM._startYOffset = floorY;
} else if (floorY < _lastFloorY) {
findFloorMatch();
if (((_bladeMoveFlag & kBladeMoveUp) == kBladeMoveNone) ||
(_lastFloorY != _foundFloorY)) {
findFloorMinGT();
if (!isFloorNotFound()) {
_arcadeTTM._startYOffset = _foundFloorY;
floorY = _foundFloorY;
} else {
arcade16bc();
}
} else {
_arcadeTTM._startYOffset = _lastFloorY;
floorY = _lastFloorY;
}
} else if (_lastFloorY < floorY) {
if ((_bladeMoveFlag & kBladeMoveDown) == kBladeMoveNone) {
if (_arcadeTTM._startYOffset + 20 < floorY) {
_bladeState1 = 1;
_npcState[0].ttmPage = _bladePageOffset + 22;
_arcadeTTM._startYOffset += 10;
_uint0a17++;
} else {
_arcadeTTM._startYOffset = floorY;
}
} else {
findFloorMatchOrMinOrMax();
_arcadeTTM._startYOffset = _foundFloorY;
floorY = _foundFloorY;
}
}
}
} else if ((_bladeState1 == 3 || _bladeState1 == 4) && 25 < abs(_lastFloorY - floorY) && _nTickUpdates) {
floorY = _lastFloorY;
}
}
_npcState[0].xx = _scrollXOffset * 8 + _npcState[0].x;
_currentYOffset = _arcadeTTM._startYOffset;
_lastFloorY = floorY;
}
}
// Array at 0c5a in original
static const int16 NPC_RESET_COUNTS_1[4][7] {
{ 0x8, 0x19, 0x5A, 0x78, 0x8C, 0xA5, 0xBE },
{ 0x6, 0x28, 0x5A, 0x87, 0x96, 0xAA, 0xC0 },
{ 0x4, 0x37, 0x69, 0x79, 0x91, 0xA0, 0xC2 },
{ 0x2, 0x46, 0x69, 0x87, 0x9B, 0xAF, 0xC4 },
};
// Array at 0c92 in original
static const int16 NPC_RESET_COUNTS_2[4][7] {
{ 0xA, 0x37, 0x46, 0x55, 0x91, 0xAA, 0xC8 },
{ 0x19, 0x5F, 0x87, 0x9B, 0xB9, 0xD7, -0x1 },
{ 0x19, 0x23, 0x69, 0x7D, 0x9B, 0xB9, 0xD7 },
{ 0xA, 0x37, 0x46, 0x73, 0xAA, 0xC8, -0x1 },
};
void DragonArcade::arcade3e96() {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 7; j++) {
if (_loadedArcadeStage == 0 && _flag40ee && NPC_RESET_COUNTS_1[i][j] == _npcStateResetCounter) {
_npcState[i + 10].ttmPage = 1;
} else if (_loadedArcadeStage == 1) {
if (NPC_RESET_COUNTS_2[i][j] == _npcStateResetCounter) {
_npcState[i + 4 + 10].ttmPage = 1;
}
if (_flag40ee && NPC_RESET_COUNTS_1[i][j] == _npcStateResetCounter) {
_npcState[i + 10].ttmPage = 1;
}
} else if (_loadedArcadeStage == 2 && _flag40ef && NPC_RESET_COUNTS_2[i][j] == _npcStateResetCounter) {
_npcState[i + 10].ttmPage = 1;
}
}
}
for (int i = 10; i < 18; i++) {
_npcState[i].ttmPage++;
if (_npcState[i].ttmPage < 2 || _npcState[i].ttmPage > 18) {
_npcState[i].byte12 = 0;
} else {
if (_npcState[i].ttmPage == 2 && isNpcInsideXRange(i)) {
playSfx(0x5a);
}
_npcState[i].byte12 = -4;
if (_npcState[i].ttmPage < 15 &&
_npcState[i].xx - 16 <= _npcState[0].xx &&
_npcState[0].xx <= _npcState[i].xx + 12 && _npcState[0].health != 0 &&
(_loadedArcadeStage != 1 || _arcadeTTM._startYOffset < -9 ||
(_bladeMoveFlag & kBladeMoveRight) == kBladeMoveNone)) {
setFinishCountdownIfLessThan0(20);
_npcState[0].ttmNum = 2;
_npcState[0].health = 0;
if (_haveBigGun) {
_npcState[0].ttmPage = 25;
} else {
_npcState[0].ttmPage = 34;
}
_npcState[0].byte12 = 12;
_bladeState1 = 12;
_mouseButtonWentDown = 0x80;
}
}
}
_npcStateResetCounter++;
if (_npcStateResetCounter == 221) {
_npcStateResetCounter = 0;
}
}
void DragonArcade::updateBlade() {
int16 local_6 = 0;
for (int i = 9; i > 0; i--) {
if (_npcState[i].byte12 == 0)
continue;
int16 startPage = (_npcState[i].ttmPage < 30) ? 0 : 28;
if (_npcState[i].byte13 == 0 &&
((startPage != 0 && _npcState[i].xx < _npcState[0].xx) ||
(startPage == 0 && _npcState[0].xx < _npcState[i].xx))) {
if (_npcState[i].byte12 == 4 || _npcState[i].byte12 == 5) {
if (_npcState[i].byte12 == 4) {
_npcState[i].byte13 = 5;
_npcState[i].byte12 = 7;
_npcState[i].ttmPage = startPage + 26;
} else if (_npcState[i].byte12 == 5) {
_npcState[i].byte13 = 7;
_npcState[i].byte12 = 8;
_npcState[i].ttmPage = startPage + 18;
}
local_6++;
}
}
if (local_6 != 0)
continue;
switch (_npcState[i].byte12 - 1) {
case 5:
if ( _npcState[i].ttmPage < startPage + 11) {
_npcState[i].ttmPage++;
break;
}
_npcState[i].byte13 = 0;
_npcState[i].byte12 = 5;
_npcState[i].ttmPage = startPage + 11;
break;
case 6:
if (_npcState[i].ttmPage < startPage + 29) {
_npcState[i].ttmPage++;
break;
}
startPage = startPage ^ 28;
if (_npcState[i].byte13 == 7) {
_npcState[i].byte13 = 0;
_npcState[i].byte12 = 4;
_npcState[i].ttmPage = startPage + 2;
break;
}
_npcState[i].byte12 = 6;
_npcState[i].ttmPage = startPage + 9;
break;
case 7:
if (_npcState[i].ttmPage < startPage + 20) {
_npcState[i].ttmPage++;
break;
}
if (_npcState[i].byte13 == 4){
_npcState[i].byte13 = 0;
_npcState[i].byte12 = 4;
_npcState[i].ttmPage = startPage + 2;
break;
}
_npcState[i].byte12 = 7;
_npcState[i].ttmPage = startPage + 26;
break;
case 0:
if (_npcState[i].ttmPage < startPage + 23) {
_npcState[i].ttmPage++;
}
break;
case 3:
if ((15 - i == (_nextRandomVal % 16)) &&
abs(_npcState[i].y - _npcState[0].y) < 36 && _npcState[0].health != 0) {
_npcState[i].byte12 = 2;
_npcState[i].ttmPage = startPage + 3;
}
break;
case 4:
if ((15 - i == (_nextRandomVal % 16)) &&
abs(_npcState[i].y - _npcState[0].y) < 36 && _bladeState1 != 8 && _bladeState1 != 9) {
_npcState[i].byte12 = 3;
_npcState[i].ttmPage = startPage + 12;
}
break;
case 2:
if (_npcState[i].ttmPage < startPage + 17) {
_npcState[i].ttmPage++;
break;
}
_npcState[i].byte12 = 5;
_npcState[i].ttmPage = startPage + 11;
break;
case 1:
if (_npcState[i].ttmPage < startPage + 8) {
_npcState[i].ttmPage++;
}
break;
default:
break;
}
}
}
void DragonArcade::updateBoss() {
_npcState[2].ttmPage++;
if (29 < _npcState[2].ttmPage) {
_npcState[2].ttmPage = 23;
}
int16 distToBoss = _npcState[1].x - _npcState[0].x;
int16 absDistToBoss = abs(distToBoss);
bool bossIsCloseY = abs(_npcState[1].y - _npcState[0].y) < 20;
uint16 randVal = _nextRandomVal % 16;
switch(_npcState[1].byte12 - 1) {
case 0:
if (bossIsCloseY && absDistToBoss < 45) {
if (_bladeState1 != 8 && _bladeState1 != 9) {
_npcState[1].byte12 = 5;
_npcState[1].ttmPage = 30;
}
} else if (distToBoss < 0 || (_bossStateUpdateCounter < 0 && randVal == 7)) {
_npcState[1].byte12 = 3;
_npcState[1].ttmPage = 10;
_bossStateUpdateCounter++;
} else if ((bossIsCloseY && distToBoss < 70 && 0 < distToBoss && randVal == 15) || (0 < _bossStateUpdateCounter && randVal == 7)) {
_npcState[1].byte12 = 2;
_npcState[1].ttmPage = 3;
_bossStateUpdateCounter--;
} else if (_bossStateUpdateCounter == 0 && randVal == 15) {
_npcState[1].byte12 = 4;
_npcState[1].ttmPage = 17;
}
break;
case 7:
if (_npcState[1].ttmPage < 89) {
_npcState[1].ttmPage++;
} else {
_npcState[1].byte12 = 9;
_npcState[1].ttmPage = 67;
}
break;
case 9:
if (0x37 < _npcState[1].ttmPage) {
decBossHealth();
}
if ((_npcState[1].ttmPage != 67 || (_nTickUpdates % 32) == 0) && _npcState[1].ttmPage < 74) {
if (_npcState[1].ttmPage < 68) {
_npcState[1].ttmPage++;
} else if (!(_nTickUpdates & 1)) {
setFinishCountdownIfLessThan0(15);
} else {
_npcState[1].ttmPage++;
}
}
break;
case 8:
if ((_npcState[1].ttmPage != 67 || (_nTickUpdates % 32) == 0) && _npcState[1].ttmPage < 74) {
playSfx(75);
setFinishCountdownIfLessThan0(20);
if (_npcState[1].ttmPage == 67) {
_npcState[1].ttmPage = 68;
} else if (_nTickUpdates & 1) {
_npcState[1].ttmPage++;
}
}
break;
case 6:
if (_npcState[1].ttmPage < 78) {
_npcState[1].ttmPage++;
break;
}
// FALL THROUGH
case 3:
if (_npcState[1].ttmPage < 22) {
_npcState[1].ttmPage++;
} else {
_npcState[1].byte12 = 1;
_npcState[1].ttmPage = 2;
}
break;
case 2:
if (_npcState[1].ttmPage < 16) {
_npcState[1].ttmPage++;
_npcState[1].xx += 6;
} else {
_npcState[1].byte12 = 1;
_npcState[1].ttmPage = 2;
}
break;
case 1:
if (_npcState[1].ttmPage < 9) {
_npcState[1].ttmPage++;
_npcState[1].xx -= 6;
} else {
_npcState[1].byte12 = 5;
_npcState[1].ttmPage = 30;
}
break;
case 4:
if (_npcState[1].ttmPage < 37) {
_npcState[1].ttmPage++;
if (bossIsCloseY && absDistToBoss < 50 && _uint0a17 == 0 && 33 < _npcState[1].ttmPage && _npcState[1].ttmPage < 37) {
_npcState[0].byte12 = 10;
_bladeState1 = 10;
_npcState[0].ttmPage = 76;
bladeTakeHit();
if (_npcState[0].health == 0) {
_shouldUpdateState = 3;
}
}
} else {
_npcState[1].byte12 = 1;
_npcState[1].ttmPage = 2;
}
break;
default:
break;
}
// TODO: Is this supposed to be just be case 5 or apply to all cases?
if (_npcState[3].byte12 == -2) {
_npcState[3].health--;
if (_npcState[3].health == 6) {
_npcState[1].ttmPage = 49;
_npcState[1].byte12 = 10;
}
if (_npcState[3].health == 0) {
_npcState[3].byte12 = -3;
_npcState[3].ttmPage = 39;
_npcState[3].ttmNum = 1;
}
} else if (_npcState[3].byte12 == -3) {
_npcState[3].ttmPage++;
if (48 < _npcState[3].ttmPage)
_npcState[3].byte12 = 0;
}
}
static const int16 BOSS_2_PAGE_OFFSETS[] = { 2, 2, 14, 27, 33, 42, 52, 59 };
void DragonArcade::updateBoss2() {
if (_someMoveDirection > 0 && 269 < _scrollXOffset + _npcState[0].x / 8 && _scrollXOffset < 282) {
_scrollVelocityX = 1;
_scrollXOffset++;
_npcState[0].x -= 8;
}
if (_bladeState1 == 5) {
return;
}
int distToBoss = _npcState[1].x - _npcState[0].x;
int absDistToBoss = abs(distToBoss);
switch (_npcState[1].byte12 - 1) {
case 0:
if (_npcState[1].x - _npcState[0].x < 1) {
_uint0be6 = 0;
} else {
_uint0be6 = 31;
}
if ((_nextRandomVal % 16) == 15) {
if (abs(_npcState[1].y - _npcState[0].y) > 35)
return;
_npcState[1].byte12 = 4;
_npcState[1].ttmPage = _uint0be6 + 9;
}
if ((_nextRandomVal % 16) == 7 && absDistToBoss > 20 && _npcState[1].xx < 0x938) {
_npcState[1].byte12 = 2;
_npcState[1].ttmPage = _uint0be6 + 2;
} else if (!_bladeHasFired || _npcState[1].xx < 0x939) {
if (absDistToBoss < 30) {
arcade34b4();
}
} else {
_npcState[1].byte12 = 6;
_npcState[1].ttmPage = _uint0be6 + 13;
}
break;
case 4:
if (_npcState[1].ttmPage < (_uint0be6 + 31)) {
_npcState[1].ttmPage++;
}
break;
case 3:
if (_npcState[1].ttmPage < (_uint0be6 + 12)) {
_npcState[1].ttmPage++;
} else {
_npcState[1].byte12 = 1;
_npcState[1].ttmPage = _uint0be6 + 1;
}
break;
case 1:
_npcState[1].ttmPage++;
if ((_uint0be6 + 8) <= _npcState[1].ttmPage) {
_npcState[1].ttmPage = _uint0be6 + 2;
}
if (_uint0be6 == 0) {
_npcState[1].xx += 8;
} else {
_npcState[1].xx -= 8;
}
if (absDistToBoss < 30) {
arcade34b4();
}
break;
case 5:
if (_npcState[1].ttmPage < (int)(_uint0be6 + 20)) {
_npcState[1].ttmPage++;
} else {
_npcState[1].byte12 = 7;
_npcState[1].ttmPage = _uint0be6 + 21;
}
break;
case 6:
if (_npcState[1].ttmPage < (int)(_uint0be6 + 24)) {
if (_nTickUpdates & 1) {
_npcState[1].ttmPage++;
if (_uint0be6 == 0) {
_npcState[1].xx += 6;
} else {
_npcState[1].xx -= 6;
}
}
} else if (absDistToBoss < 40 || _npcState[1].xx < 0x8d4) {
_npcState[1].byte12 = 8;
_npcState[1].ttmPage = _uint0be6 + 25;
} else if (_nTickUpdates & 1) {
_npcState[1].ttmPage = _uint0be6 + 21;
if (_uint0be6 == 0) {
_npcState[1].xx += 6;
} else {
_npcState[1].xx -= 6;
}
}
break;
case 7:
if (_npcState[1].ttmPage < _uint0be6 + 27) {
_npcState[1].ttmPage++;
} else if (absDistToBoss < 40) {
arcade34b4();
} else {
_npcState[1].byte12 = 1;
_npcState[1].ttmPage = _uint0be6 + 1;
}
break;
case 2:
default:
if (_stillLoadingScriptsMaybe) {
if ((_scrollVelocityX == -1 && _npcState[1].x < 0x96) || (_scrollVelocityX == 1 && 160 < _npcState[1].x)) {
updateXScrollOffset();
}
byte bossByte12 = _npcState[1].byte12;
// Note: these never get changed? they're items in INT_TABLE_0BCE
const int16 INT_39e5_0be0 = 58;
const int16 INT_39e5_0bda = 32;
if (bossByte12 == 100) {
int16 absRand;
if (_mouseButtonWentDown == 1) {
_mouseButtonWentDown = 0;
if (_npcState[1].health == 1) {
absRand = 6;
} else {
while (true) {
absRand = abs(_nextRandomVal % 4) + 4;
if (absRand != 6)
break;
_nextRandomVal = _getNextRandom();
}
}
if (absRand == 5 || absRand == 6 || absRand == 7) {
decBossHealthAndCheck();
}
_npcState[1].ttmPage = BOSS_2_PAGE_OFFSETS[absRand] + 65;
_npcState[1].byte12 = absRand + 100;
} else {
while (absRand = abs(_nextRandomVal % 16), absRand != 0 && absRand < 4) {
bool hitBlade = false;
if (_npcState[0].health <= _startDifficultyMaybe + 2) {
absRand = 3;
hitBlade = true;
}
if (absRand != 3)
hitBlade = true;
if (hitBlade) {
_npcState[1].ttmPage = BOSS_2_PAGE_OFFSETS[absRand] + 65;
_npcState[1].byte12 = absRand + 100;
for (int16 i = _startDifficultyMaybe + 2; i != 0; i--)
bladeTakeHit();
return;
}
_nextRandomVal = _getNextRandom();
}
}
} else if ((bossByte12 != 106 || INT_39e5_0be0 + 0x41 != _npcState[1].ttmPage) &&
(bossByte12 != 103 || INT_39e5_0bda + 0x41 != _npcState[1].ttmPage)) {
// code uses 0x0b0c + byte12, but table that's actually used starts at 0xbce
static const int16 INT_TABLE_0BCE[] = {0x2a, 0x34, 0x3b, 0x2, 0xD, 0x1A, 0x20, 0x29, 0x33, 0x3A, 0x42};
assert(bossByte12 >= 97 && bossByte12 - 97 < ARRAYSIZE(INT_TABLE_0BCE));
if (INT_TABLE_0BCE[bossByte12 - 97] + 0x41 == _npcState[1].ttmPage) {
_npcState[1].byte12 = 100;
_npcState[1].ttmPage = 67;
} else if (bossByte12 != 100 && _nTickUpdates & 1) {
_npcState[1].ttmPage++;
}
}
}
}
}
void DragonArcade::updateXScrollOffset() {
int16 lastScrollOffset = _scrollXOffset;
_scrollXOffset = CLIP(_scrollXOffset + _scrollVelocityX, 0, 282);
if (lastScrollOffset != _scrollXOffset) {
_scrollXIncrement += _scrollVelocityX;
}
}
void DragonArcade::arcade34b4() {
_npcState[0].ttmPage = -1;
if (_npcState[0].x < 150) {
_scrollVelocityX = -1;
} else if (_npcState[0].x < 161) {
_scrollVelocityX = 0;
} else {
_scrollVelocityX = 1;
}
_someMoveDirection = -1;
// TODO: what is this?
// UINT_39e5_0a1f = 1;
_stillLoadingScriptsMaybe = true;
_npcState[0].byte12 = -1;
_npcState[1].byte12 = 100;
_npcState[1].ttmPage = 67;
_npcState[1].xx = _npcState[0].xx;
_npcState[1].ttmNum = 2;
}
void DragonArcade::decBossHealth() {
if (_npcState[1].health) {
_npcState[1].health--;
}
}
void DragonArcade::decBossHealthAndCheck() {
if (_npcState[1].health) {
_npcState[1].health--;
if (!_npcState[1].health) {
// boss is dead!
if (_npcState[1].ttmPage < 32) {
_npcState[1].ttmPage = 28;
} else {
_npcState[1].ttmPage = 59;
}
_npcState[1].byte12 = 5;
setFinishCountdownIfLessThan0(20);
}
}
}
void DragonArcade::bladeTakeHit() {
if (_npcState[0].health) {
_npcState[0].health--;
}
if (!_enemyHasSmallGun && _npcState[0].health) {
_npcState[0].health--;
}
if (_npcState[0].health == 0) {
/* dead! */
playSfx(0x4b);
if ((_bladeState1 == 0 && _bladePageOffset + 28 <= _npcState[0].ttmPage &&
_npcState[0].ttmPage <= _bladePageOffset + 35) || _bladeState1 == 4) {
_bladeState1 = 9;
_npcState[0].ttmPage = _bladePageOffset + 103;
} else {
_bladeState1 = 8;
_npcState[0].ttmPage = _bladePageOffset + 98;
}
setFinishCountdownIfLessThan0(15);
_npcState[0].ttmNum = 0;
_mouseButtonWentDown = 0x80;
} else {
playSfx(0x29);
}
}
void DragonArcade::initIfNeeded() {
if (_initFinished)
return;
DgdsEngine *engine = DgdsEngine::getInstance();
engine->disableKeymapper();
const char *ttmName;
const char *scrollBmpName;
const char *songName;
if (_nextStage == 4) {
ttmName = "path2.ttm";
scrollBmpName = "scroll2.bmp";
songName = "sarcade.sng";
} else {
ttmName = "path1.ttm";
scrollBmpName = "scroll.bmp";
songName = "darcade.sng";
}
engine->getGamePals()->loadPalette("arcade.pal");
_scrollImg.reset(new Image(engine->getResourceManager(), engine->getDecompressor()));
_scrollImg->loadBitmap(scrollBmpName);
_arcadeTTM.clearDataPtrs();
_arcadeTTM._currentTTMNum = 0;
int16 envNum = _arcadeTTM.load(ttmName);
_arcadeTTM.finishTTMParse(envNum);
_arcadeTTM._doingInit = true;
for (int i = 0; i < 8; i++) {
_arcadeTTM.runNextPage(i + 1);
}
_arcadeTTM._doingInit = false;
_arcadeTTM.freePages(0);
_arcadeTTM.freeShapes();
_arcadeTTM._currentTTMNum = 0;
const char *bladeTTM = _haveBigGun ? "BIGUNBLA.TTM" : "BLADE.TTM";
envNum = _arcadeTTM.load(bladeTTM);
_arcadeTTM.finishTTMParse(envNum);
_arcadeTTM.runNextPage(0);
_bulletImg.reset(new Image(engine->getResourceManager(), engine->getDecompressor()));
_bulletImg->loadBitmap("bullet.bmp");
_arrowImg.reset(new Image(engine->getResourceManager(), engine->getDecompressor()));
_arrowImg->loadBitmap("arcade.bmp");
engine->_soundPlayer->loadMusic(songName);
engine->_soundPlayer->playMusic(0);
// set font to 0?
// set text draw to 0xe?
drawBackgroundAndWeapons();
loadTTMScriptsForStage(_nextStage);
_initFinished = true;
_attemptCounter = 0;
g_system->warpMouse(166, 158);
_dontRedrawBgndAndWeapons = true;
redraw();
}
void DragonArcade::updateBladeWithInputs() {
if (_stillLoadingScriptsMaybe)
return;
if (_int0b5a != 0) {
_int0b5a--;
if (_int0b5a == 0)
_int0b58 = 0;
}
if ((_bladeHorizMoveAttempt & kBladeMoveLeft) == kBladeMoveNone) {
_bladePageOffset = 0;
} else {
_bladePageOffset = 122;
}
if (_bladeState1 == 0) {
if (!_dontMoveBladeFlag) {
_npcState[0].ttmPage++;
}
handleMouseStates();
return;
}
int16 newPage = 0;
switch (_bladeState1 - 1) {
case 0:
if (_bladePageOffset + 26 == _npcState[0].ttmPage) {
_npcState[0].ttmPage = _bladePageOffset + 22;
}
newPage = _bladePageOffset + 25;
break;
case 1:
if (_bladePageOffset + 62 == _npcState[0].ttmPage) {
_npcState[0].ttmPage = _bladePageOffset + 56;
}
newPage = _bladePageOffset + 61;
if ((_bladePageOffset + 50 < _npcState[0].ttmPage) &&
(_npcState[0].ttmPage <= _bladePageOffset + 56)) {
moveBladeX();
}
break;
case 2:
newPage = _bladePageOffset + 123;
break;
case 3:
newPage = _bladePageOffset + 44;
break;
case 4:
_npcState[0].ttmPage = _bladePageOffset + 64;
moveBladeX();
newPage = 999;
break;
case 5:
newPage = _bladePageOffset + 97;
break;
case 6:
newPage = _bladePageOffset + 77;
if (_bladePageOffset == 0) {
_scrollVelocityX = 1;
} else {
_scrollVelocityX = -1;
}
if (newPage <= _npcState[0].ttmPage + 2 && _npcState[0].ttmPage <= newPage) {
_npcState[0].x = _npcState[0].x + 4;
}
break;
case 7:
if (_npcState[0].ttmPage < _bladePageOffset + 102) {
_npcState[0].ttmPage++;
} else {
_npcState[0].ttmPage = _bladePageOffset + 102;
}
return;
case 8:
if (_npcState[0].ttmPage < _bladePageOffset + 108) {
_npcState[0].ttmPage++;
} else {
_npcState[0].ttmPage = _bladePageOffset + 108;
}
return;
case 9:
if (_npcState[0].ttmPage < 79) {
_npcState[0].ttmPage++;
_scrollVelocityX = -1;
moveBladeX();
} else {
handleMouseStates();
}
return;
case 10:
if (_npcState[0].ttmPage < (_haveBigGun ? 25 : 14)) {
_npcState[0].ttmPage++;
} else {
_npcState[3].xx = _npcState[0].xx + 22;
_npcState[3].yy = -32;
_npcState[3].byte12 = -2;
_npcState[3].ttmPage = 33;
_npcState[3].ttmNum = 2;
_npcState[3].health = 20;
_npcState[0].ttmNum = 0;
handleMouseStates();
}
return;
case 11:
if (!_haveBigGun) {
newPage = 40;
} else {
newPage = 31;
}
if (newPage < _npcState[0].ttmPage + 1) {
_npcState[0].ttmPage = newPage;
} else {
_npcState[0].ttmPage++;
}
return;
case 12:
if (!(_nTickUpdates & 1)) {
return;
}
_npcState[0].ttmPage++;
if (_haveBigGun) {
if (_npcState[0].ttmPage > 61)
_npcState[0].ttmPage = 61;
} else {
if (_npcState[0].ttmPage > 57) {
_npcState[0].ttmPage = 57;
}
}
return;
case 13:
if (!(_nTickUpdates & 1)) {
return;
}
if (_npcState[0].ttmPage + 1 < 38) {
_npcState[0].ttmPage++;
} else {
_npcState[0].ttmPage = 34;
}
return;
default:
break;
}
if (!_dontMoveBladeFlag)
_npcState[0].ttmPage++;
if (newPage < _npcState[0].ttmPage) {
handleMouseStates();
} else if (_uint0a17 == 0) {
if (_bladeState1 == 1) {
if (_bladePageOffset + 22 == _npcState[0].ttmPage) {
_ttmYAdjust = _int0b58 * -4;
_uint0a17 = 1;
}
} else if (_bladeState1 == 2 && _bladePageOffset + 56 == _npcState[0].ttmPage) {
_ttmYAdjust = _int0b58 * -4;
_uint0a17 = 1;
}
} else {
if (_mouseButtonWentDown == 1) {
_mouseButtonWentDown = 0;
if (_bladeState1 == 2) {
_npcState[0].ttmPage = _bladePageOffset + 63;
} else if (_bladeState1 == 1) {
_npcState[0].ttmPage = _bladePageOffset + 27;
}
}
if (_bladeState1 == 1 || _bladeState1 == 2 || _bladeState1 == 5) {
_ttmYAdjust += 2;
_arcadeTTM._startYOffset += _ttmYAdjust;
_npcState[0].ttmPage--;
}
}
}
void DragonArcade::moveBladeX() {
if (_dontMoveBladeFlag)
return;
if (_someMoveDirection != 0)
return;
if (_scrollVelocityX < 0) {
if (_scrollXOffset == 0) {
if (_npcState[0].x > 0)
_bladeXMove = -4;
} else if (_npcState[0].x > 260) {
_bladeXMove = -4;
_int0b60 = 0;
} else if (_npcState[0].x < 160) {
_int0b60 = 1;
_bladeXMove = 4;
updateXScrollOffset();
} else if (_int0b60 == 1) {
updateXScrollOffset();
_bladeXMove = 4;
} else {
_bladeXMove = -4;
}
} else if (_scrollVelocityX > 0) {
if (_scrollXOffset == 282) {
if (_npcState[0].x < SCREEN_WIDTH)
_bladeXMove = 4;
} else if (0xa0 < _npcState[0].x) {
_int0b60 = -1;
_bladeXMove = -4;
updateXScrollOffset();
} else if (_npcState[0].x < 60) {
_int0b60 = 0;
_bladeXMove = 4;
} else if (_int0b60 != -1) {
_bladeXMove = 4;
} else {
updateXScrollOffset();
_bladeXMove = -4;
}
}
_npcState[0].x += _bladeXMove;
}
void DragonArcade::handleMouseStates() {
if (_mouseButtonWentDown == 0) {
_bladeState1 = 0;
if ((_bladeMoveFlag & (kBladeMoveRight | kBladeMoveLeft)) == kBladeMoveNone) {
/* not moving up or down */
if ((_bladeMoveFlag & kBladeMoveDown) == kBladeMoveNone) {
_npcState[0].ttmPage = _bladePageOffset + 2;
} else {
_npcState[0].ttmPage = _bladePageOffset + 14;
}
} else {
moveBladeX();
if (!_foundFloorFlag) {
if ((_npcState[0].ttmPage < _bladePageOffset + 109) ||
(_bladePageOffset + 112 < _npcState[0].ttmPage)) {
_npcState[0].ttmPage = _bladePageOffset + 109;
}
} else if ((_bladeMoveFlag & kBladeMoveDown) == kBladeMoveNone) {
if ((_npcState[0].ttmPage < _bladePageOffset + 3) ||
(_bladePageOffset + 10 < _npcState[0].ttmPage)) {
_npcState[0].ttmPage = _bladePageOffset + 3;
}
} else if ((_npcState[0].ttmPage < _bladePageOffset + 28) ||
(_bladePageOffset + 35 < _npcState[0].ttmPage)) {
_npcState[0].ttmPage = _bladePageOffset + 28;
}
}
} else if (_mouseButtonWentDown == 1) {
if (_loadedArcadeStage == 3 && _haveBomb && _npcState[1].health != 0 &&
25 < abs(_npcState[1].y - _npcState[0].y) && abs(_npcState[1].x - _npcState[0].x) < 40) {
// use a bomb
_bladeState1 = 11;
_haveBomb = false;
_npcState[0].ttmNum = 2;
if (!_haveBigGun) {
_npcState[0].ttmPage = 4;
} else {
_npcState[0].ttmPage = 15;
}
} else if ((_bladeMoveFlag & kBladeMoveDown) == kBladeMoveNone) {
_bladeState1 = 3;
_npcState[0].ttmPage = _bladePageOffset + 113;
} else {
_bladeState1 = 4;
_npcState[0].ttmPage = _bladePageOffset + 36;
}
} else if (_mouseButtonWentDown == 2) {
if ((_bladeMoveFlagBeforeRButton & (kBladeMoveLeft | kBladeMoveRight)) == kBladeMoveNone) {
_bladeMoveFlag = static_cast<DragonBladeMoveFlag>(_bladeMoveFlagBeforeRButton | (_bladeHorizMoveAttempt & (kBladeMoveLeft | kBladeMoveRight)));
} else {
_bladeMoveFlag = _bladeMoveFlagBeforeRButton;
_bladeHorizMoveAttempt = _bladeMoveFlagBeforeRButton;
}
if ((_bladeMoveFlag & kBladeMoveUp) == kBladeMoveNone) {
if (_int0b58 < 4) {
_int0b58++;
} else {
_int0b58 = 4;
}
} else {
_int0b58 = 4;
}
_int0b5a = 0;
_scrollVelocityX = 0;
if ((_bladeMoveFlag & kBladeMoveLeft) == kBladeMoveNone) {
_bladePageOffset = 0;
} else {
_bladePageOffset = 122;
}
if ((_bladeMoveFlagBeforeRButton & (kBladeMoveLeft | kBladeMoveRight)) == kBladeMoveNone) {
// Not jumping left or right
_bladeState1 = 1;
_npcState[0].ttmPage = _bladePageOffset + 15;
debug(1, "Move: blade jump up -> ttm %d", _npcState[0].ttmPage);
} else {
// Jump to left or right
if ((_bladeMoveFlag & kBladeMoveLeft) == kBladeMoveNone) {
_scrollVelocityX = 1;
} else {
_scrollVelocityX = -1;
}
_isMovingStage = false;
_bladeState1 = 2;
_npcState[0].ttmPage = _bladePageOffset + 45;
debug(1, "Move: blade jump up -> ttm %d velocity %d", _npcState[0].ttmPage, _scrollVelocityX);
}
if ((_bladeMoveFlagBeforeRButton & kBladeMoveDown) != kBladeMoveNone) {
findFloorMinGT();
if (!isFloorNotFound()) {
if (_bladeState1 == 2) {
_npcState[0].ttmPage = _bladePageOffset + 56;
} else {
_npcState[0].ttmPage = _bladePageOffset + 22;
}
debug(1, "Move: blade jump down -> ttm %d", _npcState[0].ttmPage);
_arcadeTTM._startYOffset++;
_uint0a17++;
_currentYOffset = _arcadeTTM._startYOffset;
}
}
playSfx(0x54);
_bladeMoveFlagBeforeRButton = kBladeMoveNone;
}
_mouseButtonWentDown = 0;
}
void DragonArcade::resetStageState() {
clearAllNPCStates();
clearAllBulletStates();
_scrollXOffset = 0;
_nTickUpdates = 0;
_isMovingStage = false;
_ttmYAdjust = 0;
_uint0a17 = 0;
_shouldUpdateState = 0;
_someMoveDirection = 0;
_npcState[0].byte12 = 1;
_npcState[0].ttmNum = 0;
_npcState[0].health = (4 - _startDifficultyMaybe) * 3 + 20;
_npcState[0].ttmPage = 3;
_npcState[1].health = 0;
_lastDrawnBladeHealth = -1;
_lastDrawnBossHealth = -1;
_bladeState1 = 0;
_mouseButtonWentDown = 0;
_bladeMoveFlag = kBladeMoveNone;
}
static const int16 STAGE_0_NPC_XX[] = {
0x191, 0x1BB, 0x1F5,
0x25B, 0x2D3, 0x341,
0x535, 0x5C9, 0x623
};
static const int16 STAGE_0_NPC_YY[] = {
0, 0, 0, -40, -40, 0, 0, 0, -40
};
static const byte STAGE_0_NPC_BYTE12[] = {
5, 4, 4, 5, 4, 5, 4, 5, 5
};
static const int16 STAGE_4_NPC_XX_1[] = {
0x169, 0x19D, 0x1B9,
0x30D, 0x32D, 0x457,
0x4DB, 0x501, -1
};
static const int16 STAGE_4_NPC_YY_1[] = {
0, 0, 0, 0, 0, -40, 0, 0, -40
};
static const byte STAGE_4_NPC_BYTE12_1[] = {
5, 4, 4, 5, 4, 5, 5, 4, 5
};
void DragonArcade::initValuesForStage() {
for (int i = 9; i != 0; i--)
_npcState[i].byte12 = 0;
switch (_loadedArcadeStage) {
case 0:
for (int i = 1; i < 10; i++) {
_npcState[i].xx = STAGE_0_NPC_XX[i - 1];
_npcState[i].yy = STAGE_0_NPC_YY[i - 1];
_npcState[i].byte12 = STAGE_0_NPC_BYTE12[i - 1];
if (_npcState[i].byte12 == 5)
_npcState[i].ttmPage = 39;
else
_npcState[i].ttmPage = 30;
_npcState[i].ttmNum = 1;
}
initValuesForStage0();
break;
case 3:
initValuesForStage3();
break;
case 4:
// Note: The original also only does 8 NPCs here even though the arrays have
// 9 values in them (note the last x value is -1).
for (int i = 1; i < 9; i++) {
_npcState[i].xx = STAGE_4_NPC_XX_1[i - 1];
_npcState[i].yy = STAGE_4_NPC_YY_1[i - 1];
_npcState[i].byte12 = STAGE_4_NPC_BYTE12_1[i - 1];
if (_npcState[i].byte12 == 5)
_npcState[i].ttmPage = 39;
else
_npcState[i].ttmPage = 30;
_npcState[i].ttmNum = 1;
}
initValuesForStage4();
break;
case 6:
initValuesForStage6();
break;
default:
break;
}
}
static const int16 STAGE_0_NPC2_XX_1[] = {
0x13F, 0x150, 0x161, 0x172
};
static const int16 STAGE_0_NPC2_XX_2[] = {
0x317, 0x328, 0x339, 0x34A
};
static const int16 STAGE_0_NPC2_TTMPAGE[] = {
0, 30, 15, 0
};
void DragonArcade::initValuesForStage0() {
_npcStateResetCounter = 0;
for (int i = 10; i < 14; i++) {
_npcState[i].xx = STAGE_0_NPC2_XX_1[i - 10];
_npcState[i].yy = 2;
_npcState[i].ttmPage = STAGE_0_NPC2_TTMPAGE[i - 10];
_npcState[i].ttmNum = 2;
_npcState[i + 4].xx = STAGE_0_NPC2_XX_2[i - 10];
_npcState[i + 4].yy = -37;
_npcState[i + 4].ttmPage = STAGE_0_NPC2_TTMPAGE[i - 10];
_npcState[i + 4].ttmNum = 2;
}
_flag40ee = true;
_flag40ef = true;
_npcState[18].xx = 0x11f;
_npcState[18].yy = -13;
_npcState[18].byte12 = 30;
_npcState[18].ttmPage = 32;
_npcState[18].ttmNum = 2;
}
void DragonArcade::initValuesForStage3() {
clearAllNPCStates();
_bossStateUpdateCounter = 0;
_npcState[1].xx = 0x99c;
_npcState[1].yy = -54;
_npcState[1].byte12 = 1;
_npcState[1].ttmPage = 2;
_npcState[1].health = 20;
_npcState[1].ttmNum = 1;
_npcState[1].y = 300;
_npcState[2].xx = 0x9b2;
_npcState[2].yy = -57;
_npcState[2].byte12 = -1;
_npcState[2].ttmPage = 23;
_npcState[2].health = 0;
_npcState[2].ttmNum = 1;
}
static const int16 STAGE_4_NPC_XX[] = {
0x1F9, 0x551, 0x362, 0x592, 0x7AF
};
static const int16 STAGE_4_NPC_YY[] = {
8, 8, 6, 6, 6
};
static const int16 STAGE_4_ST_TTMPAGE[] = {
0, 0, 15, 15, 40
};
static const byte STAGE_4_ST_BYTE12[] = {
0, 0, 0xfa, 0xfa, 0xf9
};
void DragonArcade::initValuesForStage4() {
_npcStateResetCounter = 0;
for (int i = 10; i < 15; i++) {
_npcState[i].xx = STAGE_4_NPC_XX[i - 10];
_npcState[i].yy = STAGE_4_NPC_YY[i - 10];
_npcState[i].ttmPage = STAGE_4_ST_TTMPAGE[i - 10];
_npcState[i].byte12 = STAGE_4_ST_BYTE12[i - 10];
_npcState[i].health = 1;
_npcState[i].ttmNum = 2;
}
}
void DragonArcade::initValuesForStage6() {
clearAllNPCStates();
_npcState[1].xx = 0x9e2;
_npcState[1].yy = -3;
_npcState[1].ttmPage = 1;
_npcState[1].health = 10;
_npcState[1].ttmNum = 1;
_npcState[1].byte12 = 1;
_stillLoadingScriptsMaybe = false;
}
void DragonArcade::setFinishCountdownIfLessThan0(int16 val) {
if (_finishCountdown < 0)
_finishCountdown = val;
}
void DragonArcade::arcadeTick() {
DragonGlobals *globals = static_cast<DragonGlobals *>(DgdsEngine::getInstance()->getGameGlobals());
int16 arcadeState = globals->getArcadeState();
switch (arcadeState) {
case 0:
return;
case 5: {
initIfNeeded();
if (doTickUpdate())
return;
if (_shouldUpdateState == 0) {
globals->setArcadeState(6);
return;
}
_attemptCounter++;
checkToOpenMenu();
globals->setArcadeState(0);
return;
}
case 6:
case 7:
case 8:
case 9:
finish();
return;
case 10:
// Restart? No.
fadeInAndClearScreen();
finish();
globals->setArcadeState(_shouldUpdateState + 6);
return;
case 20:
// Restart? Yes.
globals->setArcadeState(30);
return;
case 30:
// Do (re)start
loadTTMScriptsForStage(_nextStage);
// These don't seem to ever be used?
// UINT_39e5_0d0e = 0;
// UINT_39e5_0d10 = 0;
globals->setArcadeState(5);
//_arcadeNeedsBufferCopy = true;
//flagInventoryOpened = false;
return;
default:
_haveBomb = arcadeState > 20;
if (_haveBomb) {
arcadeState -= 20;
globals->setArcadeState(arcadeState);
}
_enemyHasSmallGun = arcadeState > 10;
if (_enemyHasSmallGun) {
arcadeState -= 10;
globals->setArcadeState(arcadeState);
}
_haveBigGun = arcadeState > 2;
if (_haveBigGun) {
arcadeState -= 2;
globals->setArcadeState(arcadeState);
}
_nextStage = (arcadeState & 1) ? 4 : 0;
globals->setArcadeState(5);
return;
}
}
void DragonArcade::loadTTMScriptsForStage(uint16 stage) {
const char *ttm1;
const char *ttm2;
switch(stage) {
case 0:
resetStageState();
ttm1 = "STATIONA.TTM";
ttm2 = "FLAMDEAD.TTM";
_npcState[0].x = 160;
_npcState[0].xx = 160;
_arcadeTTM._startYOffset = 0;
break;
case 3:
ttm1 = "DRAGON.TTM";
ttm2 = "GRENADE.TTM";
break;
case 4:
resetStageState();
ttm1 = "STATIONA.TTM";
ttm2 = "AARC.TTM";
_npcState[0].x = 140;
_npcState[0].xx = 140;
_arcadeTTM._startYOffset = -43;
break;
case 6:
_arcadeTTM._currentNPCRunningTTM = 0;
_arcadeTTM.runNextPage(276);
ttm1 = "SNAKERUN.TTM";
if (_haveBigGun)
ttm2 = "BIGFIGHT.TTM";
else
ttm2 = "LITFIGHT.TTM";
break;
default:
return;
}
if (stage != _loadedArcadeStage) {
int16 envNum;
_arcadeTTM._currentTTMNum = 1;
_arcadeTTM.freeShapes();
_arcadeTTM.freePages(1);
// original also clears data pointers here, but we do that in freePages()
_arcadeTTM._currentTTMNum = 2;
_arcadeTTM.freeShapes();
_arcadeTTM.freePages(2);
_arcadeTTM._currentTTMNum = 1;
envNum = _arcadeTTM.load(ttm1);
_arcadeTTM.finishTTMParse(envNum);
_arcadeTTM.runNextPage(0);
_arcadeTTM._currentTTMNum = 2;
envNum = _arcadeTTM.load(ttm2);
_arcadeTTM.finishTTMParse(envNum);
_arcadeTTM.runNextPage(0);
}
_currentYOffset = _arcadeTTM._startYOffset;
_finishCountdown = -1;
_stillLoadingScriptsMaybe = 0;
_loadedArcadeStage = stage;
initValuesForStage();
}
void DragonArcade::fadeInAndClearScreen() {
DgdsEngine *engine = DgdsEngine::getInstance();
for (int fade = 63; fade > 0; fade--) {
engine->getGamePals()->setFade(0, 255, 0, fade * 4);
g_system->updateScreen();
g_system->delayMillis(5);
}
Common::Rect screenRect(SCREEN_WIDTH, SCREEN_HEIGHT);
engine->getBackgroundBuffer().fillRect(screenRect, 0);
engine->_compositionBuffer.fillRect(screenRect, 0);
}
void DragonArcade::drawBackgroundAndWeapons() {
DgdsEngine *engine = DgdsEngine::getInstance();
Image bg(engine->getResourceManager(), engine->getDecompressor());
bg.drawScreen("BGND.SCR", engine->getBackgroundBuffer());
Image weapons(engine->getResourceManager(), engine->getDecompressor());
weapons.loadBitmap("W.BMP");
if (weapons.loadedFrameCount() < 3)
error("Dragon Arcade: Expect 3 frames in w.bmp");
// Offsets are customized and hard-coded depending on weapon combination
// Frames are 0 = big gun, 1 = pistol, 2 = bomb
Graphics::ManagedSurface &dst = engine->getBackgroundBuffer();
const Common::Rect screen(SCREEN_WIDTH, SCREEN_HEIGHT);
if (!_haveBigGun && !_haveBomb) {
weapons.drawBitmap(1, 267, 160, screen, dst);
} else if (_haveBigGun && !_haveBomb) {
weapons.drawBitmap(0, 249, 159, screen, dst);
} else if (!_haveBigGun && _haveBomb) {
weapons.drawBitmap(1, 258, 155, screen, dst);
weapons.drawBitmap(2, 289, 165, screen, dst);
} else {
// have big gun and have bomb
weapons.drawBitmap(0, 246, 153, screen, dst);
weapons.drawBitmap(2, 295, 166, screen, dst);
}
}
void DragonArcade::drawBulletHitCircles(uint16 x, uint16 y, bool colorFlag) {
static const byte COLORS[2][3] = { {0, 1, 9}, {0, 4, 12} };
Graphics::ManagedSurface &dst = DgdsEngine::getInstance()->_compositionBuffer;
for (int i = 0; i < 3; i++) {
byte col = COLORS[colorFlag][i];
Drawing::filledCircle(x, y, 4 - i, 4 - i, &dst, col, col);
}
}
void DragonArcade::checkToOpenMenu() {
if (_attemptCounter < 5) {
// Open menu 45, hightlight widget 139
DgdsEngine::getInstance()->setMenuToTrigger(kMenuReplayArcade);
} else {
// Open menu 47, hightlight widget 148
DgdsEngine::getInstance()->setMenuToTrigger(kMenuArcadeFrustrated);
}
}
void DragonArcade::clearAllNPCStates() {
for (uint i = 1; i < ARRAYSIZE(_npcState); i++) {
_npcState[i].byte12 = 0;
_npcState[i].ttmPage = -1;
}
}
void DragonArcade::clearAllBulletStates() {
for (uint i = 0; i < ARRAYSIZE(_bullets); i++) {
_bullets[i]._state = kBulletInactive;
}
}
void DragonArcade::createBullet(int16 x, int16 y, ImageFlipMode flipMode, int16 bulletType) {
for (uint i = 0; i < ARRAYSIZE(_bullets); i++) {
if (_bullets[i]._state == kBulletInactive) {
_bullets[i]._state = kBulletFlying;
_bullets[i]._x = x;
_bullets[i]._y = y;
_bullets[i]._flipMode = flipMode;
_bullets[i]._bulletType = bulletType;
if (bulletType == 3)
_bullets[i]._ySpeed = _nextRandomVal & 3;
break;
}
}
}
void DragonArcade::playSfx(int16 num) const {
DgdsEngine::getInstance()->_soundPlayer->playSFX(num);
}
void DragonArcade::bladeTakeHitAndCheck() {
if (_npcState[0].health)
_npcState[0].health--;
if (!_enemyHasSmallGun && _npcState[0].health)
_npcState[0].health--;
if (_npcState[0].health <= 0) {
playSfx(75);
if ((_bladeState1 == 0 && _bladePageOffset + 28 < _npcState[0].ttmPage && _npcState[0].ttmPage <= 35)
|| _bladeState1 == 4) {
_bladeState1 = 9;
_npcState[0].ttmPage = _bladeState1 + 103;
} else {
_bladeState1 = 8;
_npcState[0].ttmPage = _bladeState1 + 98;
}
setFinishCountdownIfLessThan0(15);
_npcState[0].ttmNum = 0;
_mouseButtonWentDown = 0x80;
} else {
playSfx(41);
}
}
void DragonArcade::drawHealthBars() {
DgdsEngine *engine = DgdsEngine::getInstance();
// Note: the original here checks _npcState[0].health vs _lastDrawnBladeHealth
// and _npcState[1].health vs _lastDrawnBossHealth to avoid redrawing every time,
// but we clear the screen every time so just redraw it each time.
const Common::Rect clearRect1(Common::Point(10, 155), 64, 10);
engine->_compositionBuffer.fillRect(clearRect1, 0);
for (int i = 1; i <= _npcState[0].health; i++) {
int x = 8 + i * 2;
engine->_compositionBuffer.drawLine(x, 155, x, 162, 12);
}
_lastDrawnBladeHealth = _npcState[0].health;
if ((_loadedArcadeStage == 3 || _loadedArcadeStage == 6) || _lastDrawnBossHealth == -1) {
const Common::Rect clearRect2(Common::Point(10, 167), 60, 8);
engine->_compositionBuffer.fillRect(clearRect2, 0);
byte color = (_loadedArcadeStage == 3) ? 2 : 9;
for (int i = 1; i <= _npcState[1].health; i++) {
int x = 8 + i * 2;
engine->_compositionBuffer.drawLine(x, 167, x, 174, color);
}
_lastDrawnBossHealth = _npcState[1].health;
}
}
void DragonArcade::redraw() {
if (!_dontRedrawBgndAndWeapons)
drawBackgroundAndWeapons();
drawScrollBmp();
runThenDrawBulletsInFlight();
_lastDrawnBladeHealth = -1;
_lastDrawnBossHealth = -1;
drawHealthBars();
// TODO: What are these?
//UINT_39e5_0d10 = 0;
//UINT_39e5_0d0e = 0;
_dontRedrawBgndAndWeapons = 0;
g_system->warpMouse(166, 158);
}
void DragonArcade::drawScrollBmp() {
const Common::Rect drawWin(Common::Point(8, 8), SCREEN_WIDTH - 16, 117);
Graphics::ManagedSurface &dst = DgdsEngine::getInstance()->_compositionBuffer;
_scrollImg->drawScrollBitmap(drawWin.left, drawWin.top, drawWin.width(), drawWin.height(),
_scrollXOffset, 0, drawWin, dst);
}
void DragonArcade::runThenDrawBulletsInFlight() {
_arcadeTTM.runPagesForEachNPC(_scrollXOffset);
const Common::Rect drawWin(Common::Point(8, 8), SCREEN_WIDTH - 16, 117);
_arcadeTTM._currentTTMNum = _npcState[0].ttmNum;
_npcState[0].x_11 = 0;
_npcState[0].x_12 = 0;
_npcState[0].x_21 = 0;
_npcState[0].x_22 = 0;
_npcState[0].y_11 = 0;
_npcState[0].y_12 = 0;
_npcState[0].y_21 = 0;
_npcState[0].y_22 = 0;
_arcadeTTM._drawXOffset = _npcState[0].x - 152;
_arcadeTTM._drawYOffset = _arcadeTTM._startYOffset;
_arcadeTTM._currentNPCRunningTTM = 0;
if (-1 < _npcState[0].byte12) {
_arcadeTTM.runNextPage(_npcState[0].ttmPage);
}
for (int i = 0; i < ARRAYSIZE(_bullets); i++) {
int16 x = _bullets[i]._x;
int16 y = _bullets[i]._y;
if (_bullets[i]._state == kBulletHittingBlade) {
drawBulletHitCircles(x, y, false);
} else if (_bullets[i]._state == kBulletHittingEnemy) {
drawBulletHitCircles(x, y, true);
} else if (_bullets[i]._state == kBulletFlying) {
int16 frameno;
if (_bullets[i]._bulletType == 3) {
// TODO: check this.. it's a bit weird?
frameno = (_nextRandomVal % 3);
} else {
frameno = 0;
}
_bulletImg->drawBitmap(frameno, x, y, drawWin, DgdsEngine::getInstance()->_compositionBuffer, _bullets[i]._flipMode);
}
}
}
} // end namespace Dgds