Files
scummvm-cursorfix/engines/access/martian/martian_duct.cpp
2026-02-02 04:50:13 +01:00

1286 lines
36 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 "access/martian/martian_duct.h"
#include "access/martian/martian_game.h"
#include "access/martian/martian_resources.h"
namespace Access {
namespace Martian {
MartianDuct::MartianDuct(MartianEngine *vm) : _vm(vm), _stopMoveLoop(false), _playerX(0), _preYOffset(0), _playerY(0), _moveAngle(kMoveAngleNorth), _drawDistX(100), _drawDistY(500), _threshold1(50), _crawlFrame(0), _xOffset(160), _yOffset(160), _xScale(100), _yScale(160), _moveIntent(kMoveIntentNone), _primArrayIdx(0), _mapLoc(0), _nextPlayerX(0), _nextPlayerY(0) {
ARRAYCLEAR(_matrix[0]);
ARRAYCLEAR(_matrix[1]);
ARRAYCLEAR(_matrix[2]);
ARRAYCLEAR(_tempPoints);
}
MartianDuct::~MartianDuct() {
}
void MartianDuct::duct2() {
_playerX = 550;
_preYOffset = 10;
_playerY = 850;
doDuct();
}
void MartianDuct::duct4() {
_playerX = 2750;
_preYOffset = 10;
_playerY = 1050;
doDuct();
}
void MartianDuct::doDuct() {
_vm->_screen->forceFadeOut();
_vm->_screen->_windowXAdd = 0;
_vm->_screen->_windowYAdd = 0;
_vm->_screen->_screenYOff = 0;
_vm->_events->hideCursor();
_vm->_files->loadScreen(20, 0);
Resource *res = _vm->_files->loadFile(20, 1);
_vm->_objectsTable[20] = new SpriteResource(_vm, res);
if (_vm->_inventory->_inv[40]._value == ITEM_IN_INVENTORY) {
// Show map if we have it.
_vm->_screen->plotImage(_vm->_objectsTable[20], 8, Common::Point(140, 10));
}
_vm->_events->showCursor();
_vm->_screen->forceFadeIn();
_crawlFrame = 0;
_xOffset = 160;
_yOffset = 100;
_xScale = 160;
_yScale = 100;
// The game initialises some drawing window here but it makes no difference
_moveAngle = kMoveAngleNorth;
// game has Y and Z angles X fraction part which is always 0
_drawDistX = 100;
_drawDistY = 500;
_threshold1 = 50;
drawArrowSprites();
drawArrowSprites2();
_vm->_room->_function = FN_NONE;
// FIXME: Quick HACK: skip this part
//g_system->displayMessageOnOSD(Common::U32String("Duct section not implemented yet!"));
//_vm->_flags[0x62] = 1;
//_vm->_flags[0x55] = 1;
//_vm->_room->_function = FN_CLEAR1;
// END HACK
while (!_vm->shouldQuit()) {
clearWorkScreenArea();
updateMatrix();
applyMatrixToMapData();
// Draw duct panels
updatePrimsAndDraw();
// Draw tex over the top
_vm->_buffer2.plotImage(_vm->_objectsTable[20], _crawlFrame, Common::Point(140, 94));
copyBufBlockToScreen();
if (_vm->_room->_function != FN_NONE)
break;
do {
waitForMoveUpdate();
} while (!_stopMoveLoop && !_vm->shouldQuitOrRestart());
}
delete _vm->_objectsTable[20];
_vm->_objectsTable[20] = nullptr;
}
void MartianDuct::drawArrowSprites() {
int x;
int y;
int frame;
_vm->_events->hideCursor();
_vm->_screen->plotImage(_vm->_objectsTable[20], 7, Common::Point(4, 0x50));
if (_moveAngle == kMoveAngleNorth) { // (highlight up arrow)
x = 0x11;
y = 0x50;
frame = 9;
} else if (_moveAngle == kMoveAngleEast) { // highlight right arrow
x = 0x19;
y = 0x57;
frame = 12;
} else if (_moveAngle == kMoveAngleSouth) { // highlight down arrow
x = 0xe;
y = 0x5d;
frame = 11;
} else { // West or west2 (left arrow)
x = 4;
y = 0x57;
frame = 10;
}
_vm->_screen->plotImage(_vm->_objectsTable[20], frame, Common::Point(x, y));
_vm->_events->showCursor();
}
void MartianDuct::drawArrowSprites2() {
_vm->_events->hideCursor();
_vm->_screen->plotImage(_vm->_objectsTable[20], 14, Common::Point(16, 0x58));
if (_mapLoc > 3 && _mapLoc < 13) {
_vm->_screen->plotImage(_vm->_objectsTable[20], 13, Common::Point(17, 0x59));
}
_vm->_events->showCursor();
}
void MartianDuct::clearWorkScreenArea() {
_vm->_buffer2.fillRect(Common::Rect(100, 60, 220, 140), 0);
// For testing, clear the whole buffer:
//_vm->_buffer2.fillRect(Common::Rect(320, 200), 0);
}
void MartianDuct::copyBufBlockToScreen() {
// Start from row 60, 100 across. 100 px by 80 rows.
_vm->_screen->copyBlock(&_vm->_buffer2, Common::Rect(100, 60, 220, 140));
// For testing, copy everything:
//_vm->_screen->copyBuffer(&_vm->_buffer2);
}
void MartianDuct::updateMatrix() {
// The original does a full fixed-point matrix calculation here, but
// half the values are always 1 or 0 so it's much simpler than that.
float moveAngleRad = (float)(_moveAngle / 256.0f) * 2.0f * M_PI;
float cosVal = cos(moveAngleRad);
float sinVal = sin(moveAngleRad);
// 3D rotation through Y axis. We never rotate through the others.
_matrix[0][0] = cosVal;
_matrix[0][1] = 0.0f;
_matrix[0][2] = sinVal;
_matrix[1][0] = 0.0f;
_matrix[1][1] = 1.0f;
_matrix[1][2] = 0.0f;
_matrix[2][0] = -sinVal;
_matrix[2][1] = 0.0f;
_matrix[2][2] = cosVal;
}
void MartianDuct::applyMatrixToMapData() {
int16 shapeDataIndex;
_renderShapes.clear();
_renderPoints.clear();
for (const DuctMapPoint *pMapData = DUCT_MAP_DATA; shapeDataIndex = pMapData->shapeType, shapeDataIndex != -1; pMapData++) {
// The original sets the actual drawing x/y here but then uses them
// as inputs to the next step so we don't need to set them here.
const int16 blockX = pMapData->x;
const int16 blockY = pMapData->y;
if (abs(blockX + _threshold1 - _playerX) < _drawDistX && abs(blockY + _threshold1 - _playerY) < _drawDistY) {
const DuctShape *ptr = DUCT_SHAPE_DATA[shapeDataIndex];
const uint startPointNum = _renderPoints.size();
const Point3 *pPoint = ptr->points;
// The data is the point list followed by the other data.
for (int i = 0; i < ptr->numPts; i++) {
int16 x = pPoint->x + blockX - _playerX;
int16 y = pPoint->y - _preYOffset;
int16 z = pPoint->z + blockY - _playerY;
pPoint++;
doMatrixMulAndAddPoint(x, y, z);
}
const uint16 *dataPtr = ptr->data;
for (int j = 0; j < ptr->array2Len; j++) {
RenderShape shapeData;
// Input struct is byte,ignored,int16,[array of int16]
shapeData._col = (byte)dataPtr[0];
const int dataCount = dataPtr[1];
dataPtr += 2;
for (int i = 0; i < dataCount; i++) {
shapeData._pointIdxs.push_back(*dataPtr + startPointNum);
dataPtr++;
}
_renderShapes.push_back(shapeData);
}
}
}
}
void MartianDuct::updatePrimsAndDraw() {
int16 shapeZDepth[256];
int shapeIndexes[256];
ARRAYCLEAR(shapeIndexes, -1);
ARRAYCLEAR(shapeZDepth, (int16)INT16_MIN);
for (uint shpNum = 0; shpNum < _renderShapes.size(); shpNum++) {
const Point3 &pt0 = _renderPoints[_renderShapes[shpNum]._pointIdxs[0]];
const Point3 &pt2 = _renderPoints[_renderShapes[shpNum]._pointIdxs[2]];
int16 zDepth = ((int)pt0.z + pt2.z) / 2;
uint insertPoint = 0;
uint i = _renderShapes.size();
while (zDepth <= shapeZDepth[insertPoint] && i > 0) {
insertPoint++;
i--;
}
if (i == 0)
continue;
for (uint j = _renderShapes.size(); j > insertPoint; j--) {
shapeZDepth[j] = shapeZDepth[j - 1];
shapeIndexes[j] = shapeIndexes[j - 1];
}
shapeZDepth[insertPoint] = zDepth;
shapeIndexes[insertPoint] = shpNum;
}
//debug("**** Begin frame ****");
for (uint shapeNum = 0; shapeNum < _renderShapes.size(); shapeNum++) {
int shapeIdx = shapeIndexes[shapeNum];
_vm->_buffer2._lColor = _renderShapes[shapeIdx]._col;
int numPoints = _renderShapes[shapeIdx]._pointIdxs.size();
_primX1Array.clear();
_primY1Array.clear();
_primZ1Array.clear();
_primX2Array.clear();
_primY2Array.clear();
_primZ2Array.clear();
// Link up the points in order to make a polygon.
int ptIdx;
for (int pointNum = 0; pointNum < numPoints - 1; pointNum++) {
ptIdx = _renderShapes[shapeIdx]._pointIdxs[pointNum];
_primX1Array.push_back(_renderPoints[ptIdx].x);
_primY1Array.push_back(_renderPoints[ptIdx].y);
_primZ1Array.push_back(_renderPoints[ptIdx].z);
ptIdx = _renderShapes[shapeIdx]._pointIdxs[pointNum + 1];
_primX2Array.push_back(_renderPoints[ptIdx].x);
_primY2Array.push_back(_renderPoints[ptIdx].y);
_primZ2Array.push_back(_renderPoints[ptIdx].z);
}
ptIdx = _renderShapes[shapeIdx]._pointIdxs[numPoints - 1];
_primX1Array.push_back(_renderPoints[ptIdx].x);
_primY1Array.push_back(_renderPoints[ptIdx].y);
_primZ1Array.push_back(_renderPoints[ptIdx].z);
ptIdx = _renderShapes[shapeIdx]._pointIdxs[0];
_primX2Array.push_back(_renderPoints[ptIdx].x);
_primY2Array.push_back(_renderPoints[ptIdx].y);
_primZ2Array.push_back(_renderPoints[ptIdx].z);
assert(_primX1Array.size() == (uint)numPoints);
bool shouldDraw = !doPrimArrayUpdates(numPoints);
if (!shouldDraw)
continue;
/*
debug("** shape %d [%d] color %d ** ", shapeNum, shapeIdx, _vm->_buffer2._lColor);
debugN("XYs: ");
for (int i = 0; i < numPoints; i++) {
int16 x = _primX1Array[i];
int16 y = _primY1Array[i];
debugN("(%d %d), ", x, y);
}
debug(".");
debugN("RBs: ");
for (int i = 0; i < numPoints; i++) {
int16 x = _primX2Array[i];
int16 y = _primY2Array[i];
debugN("(%d %d), ", x, y);
}
debug(".");
*/
doDraw(numPoints);
}
//debug("**** End frame ****");
}
#define POLY_DRAW_CODE 1
void MartianDuct::doDraw(int numPoints) {
#if POLY_DRAW_CODE
//
// This code is not as efficient as the original game, but it's
// easier to understand.
//
// We have a set of line segments in the _prim**Array arrays and want
// to draw the polygons that they form, but they are not necessarily
// in order or handedness. First remove null segments, then sort the
// remaining ones by connecting up the start and end points. Since they
// are all simple shapes this works fine.
//
// First remove null lines (same start and end)
for (int i = 0; i < numPoints; i++) {
if (_primX1Array[i] == _primX2Array[i] && _primY1Array[i] == _primY2Array[i]) {
_primX1Array.remove_at(i);
_primY1Array.remove_at(i);
_primX2Array.remove_at(i);
_primY2Array.remove_at(i);
numPoints--;
i--;
continue;
}
}
// Sort the line segments
Common::Array<int> x1s;
Common::Array<int> y1s;
Common::Array<int> x2s;
Common::Array<int> y2s;
x1s.push_back(_primX1Array.remove_at(0));
y1s.push_back(_primY1Array.remove_at(0));
x2s.push_back(_primX2Array.remove_at(0));
y2s.push_back(_primY2Array.remove_at(0));
int pointsToAdd = numPoints - 1;
while (pointsToAdd > 0) {
// Find the next line segment to add, could be in either direction.
for (int i = 0; i < pointsToAdd; i++) {
if (_primX1Array[i] == x2s.back() && _primY1Array[i] == y2s.back()) {
x1s.push_back(_primX1Array.remove_at(i));
y1s.push_back(_primY1Array.remove_at(i));
x2s.push_back(_primX2Array.remove_at(i));
y2s.push_back(_primY2Array.remove_at(i));
pointsToAdd--;
break;
} else if (_primX2Array[i] == x2s.back() && _primY2Array[i] == y2s.back()) {
x1s.push_back(_primX2Array.remove_at(i));
y1s.push_back(_primY2Array.remove_at(i));
x2s.push_back(_primX1Array.remove_at(i));
y2s.push_back(_primY1Array.remove_at(i));
pointsToAdd--;
break;
}
}
}
// Complete the shape to join up to the start again.
x1s.push_back(x1s[0]);
y1s.push_back(y1s[0]);
_vm->_buffer2.drawPolygonScan(x1s.data(), y1s.data(), x1s.size(), Common::Rect(320, 200), _vm->_buffer2._lColor);
#else
int16 maxYVal = 0;
int16 minYVal = 0xff;
int i = 0;
// Count of line segments and x1/x2 pairs for each row
byte segmentCount[202];
int16 segmentCoords[200][16];
ARRAYCLEAR(segmentCount);
for (int y = 0; y < 200; y++)
ARRAYCLEAR(segmentCoords[y]);
do {
int16 primitiveX = _primXArray[i];
int16 primitiveY = _primYArray[i];
int16 primitiveB = _primY2Array[i];
int16 primitiveR = _primX2Array[i];
maxYVal = MAX(maxYVal, primitiveY);
maxYVal = MAX(maxYVal, primitiveB);
minYVal = MIN(minYVal, primitiveY);
minYVal = MIN(minYVal, primitiveB);
if (primitiveR < primitiveX) {
// Flip X and Y
SWAP(primitiveX, primitiveR);
SWAP(primitiveY, primitiveB);
}
const int16 width = primitiveR - primitiveX;
int16 x = primitiveX;
int16 _lastPrimIdx = i;
if (width == 0) {
if (primitiveY != primitiveB) {
if (primitiveB <= primitiveY)
SWAP(primitiveY, primitiveB);
int16 y = primitiveY;
do {
//byte bVar6 = (byte)array_a344[y * 0x10];
//*(int16 *)CONCAT11((char)((uint)(array_a344[y * 0x10] >> 8) + CARRY1(bVar6, byteData_bc44[y]),
// bVar6 + byteData_bc44[y]) = primitiveX;
segmentCoords[y][segmentCount[y]] = primitiveX;
segmentCoords[y][segmentCount[y] + 1] = primitiveX;
segmentCount[y]++;
y++;
} while (y != primitiveB);
}
} else {
const int height = primitiveB - primitiveY;
if (height < 0) {
if (-height < width) {
i = -width / 2;
int16 y = primitiveY;
while (true) {
do {
x++;
i -= height;
} while (i < 0);
int16 nextY = y - 1;
if (nextY == primitiveB)
break;
//byte bVar6 = (byte)array_a344[nextY * 0x10];
//*(int16 *)CONCAT11((char)((uint)(array_a344[nextY * 0x10] >> 8) +
// CARRY1(bVar6,byteData_bc44[y]),
// bVar6 + byteData_bc44[nextY]) = x;
segmentCoords[y][segmentCount[y]] = primitiveX;
segmentCoords[y][segmentCount[y] + 1] = x;
segmentCount[y]++;
i -= width;
y = nextY;
}
} else {
i = height / 2;
int16 y = primitiveY;
int16 nextY;
while (nextY = y - 1, nextY != primitiveB) {
//byte bVar6 = (byte)array_a344[nextY * 0x10];
//*(int16 *)CONCAT11((char)((uint)(array_a344[nextY * 0x10] >> 8) +
// CARRY1(bVar6,byteData_bc44[primitiveY]),
// bVar6 + byteData_bc44[primitiveY]) = x;
segmentCount[-y]++;
i += width;
y = nextY;
if (-1 < i) {
x++;
i += height;
}
}
}
SWAP(primitiveX, primitiveR);
SWAP(primitiveY, primitiveB);
//byte bVar6 = (byte)array_a344[primitiveY * 0x10];
//*(int16 *)CONCAT11((char)((uint)(array_a344[primitiveY * 0x10] >> 8) +
// CARRY1(bVar6,byteData_bc44[primitiveY]),
// bVar6 + byteData_bc44[primitiveY]) = primitiveX;
segmentCount[primitiveY]++;
} else if (height != 0 && height < width) {
i = -width / 2;
int16 y = primitiveY;
while (true) {
do {
x++;
i += height;
} while (i < 0);
//byte bVar6 = (byte)array_a344[y * 0x10];
//*(int16 *)CONCAT11((char)((uint)(array_a344[y * 0x10] >> 8) +
// CARRY1(bVar6,byteData_bc44[y]),
// bVar6 + byteData_bc44[y]) = x;
segmentCount[y]++;
y++;
if (y == primitiveB)
break;
i -= width;
}
} else if (height != 0) {
i = -height / 2;
int16 y = primitiveY;
while (true) {
//byte bVar6 = (byte)array_a344[y * 0x10];
//*(int16 *)CONCAT11((char)((uint)(array_a344[y * 0x10] >> 8) +
// CARRY1(bVar6,byteData_bc44[y]),
// bVar6 + byteData_bc44[y]) = x;
segmentCount[y]++;
y++;
if (y == primitiveB)
break;
i += width;
if (-1 < i) {
x++;
i -= height;
}
}
}
}
i = _lastPrimIdx + 1;
} while (i != numPoints);
segmentCount[200] = segmentCount[36];
for (int16 y = minYVal; y <= maxYVal; y++) {
if (segmentCount[y] == 0)
continue;
int16 *lineData = segmentCoords[y];
/*
CHECK ME: This probably is supposed to swap incorrect left/right vals, but seems
to just check values against themselves?
const int16 numSegs = segmentCount[y];
int16 right = 0;
int16 left = 0;
do {
const int16 tmpy = lineData[right];
if (tmpy < lineData[left]) {
lineData[right] = lineData[left];
lineData[left] = tmpy;
}
right++;
} while ((right != numSegs) || (right = left + 1, left = right, right != numSegs));
*/
for (int16 segNum = 0; segNum < segmentCount[y]; segNum++) {
const int16 x2 = *(lineData + 1);
const int16 x1 = *lineData;
lineData += 2;
byte *pdest = (byte *)_vm->_buffer2.getBasePtr(x1, y);
for (i = 0; i < (x2 - x1) + 1; i++) {
*pdest = _vm->_buffer2._lColor;
pdest++;
}
}
segmentCount[y] = 0;
}
#endif
}
void MartianDuct::doMatrixMulAndAddPoint(int16 x, int16 y, int16 z) {
Point3 pt;
pt.x = _matrix[0][0] * x + _matrix[1][0] * y + _matrix[2][0] * z;
pt.y = _matrix[0][1] * x + _matrix[1][1] * y + _matrix[2][1] * z;
pt.z = _matrix[0][2] * x + _matrix[1][2] * y + _matrix[2][2] * z;
_renderPoints.push_back(pt);
}
bool MartianDuct::doPrimArrayUpdates(int &numPoints) {
if (checkAndUpdatePrimArray1(numPoints))
return true;
if (checkAndUpdatePrimArray2(numPoints))
return true;
if (checkAndUpdatePrimArray3(numPoints))
return true;
if (checkAndUpdatePrimArray4(numPoints))
return true;
if (checkAndUpdatePrimArray5(numPoints))
return true;
assert(numPoints > 0);
for (int16 idx = 0; idx < numPoints; idx++) {
Point3 pt1, pt2;
getPointValuesFromArray(idx, pt1, pt2);
// The original changes the drawing primitive coordinates here
// but we only need them to store so we use a temp rect instead.
const Common::Rect r = calcFinalLineSegment(pt1, pt2);
_primX1Array[idx] = r.left;
_primY1Array[idx] = r.top;
_primX2Array[idx] = r.right;
_primY2Array[idx] = r.bottom;
}
return false;
}
void MartianDuct::getPointValuesFromArray(int idx, Point3 &pt1, Point3 &pt2) const {
pt1.x = _primX1Array[idx];
pt1.y = _primY1Array[idx];
pt1.z = _primZ1Array[idx];
pt2.x = _primX2Array[idx];
pt2.y = _primY2Array[idx];
pt2.z = _primZ2Array[idx];
}
Common::Rect MartianDuct::calcFinalLineSegment(const Point3 &pt1, const Point3 &pt2) const {
Common::Rect result;
result.left = _xOffset + ((int)pt1.x * _xScale) / pt1.z;
result.top = _yOffset - ((int)pt1.y * _yScale) / pt1.z;
result.right = _xOffset + ((int)pt2.x * _xScale) / pt2.z;
result.bottom = _yOffset - ((int)pt2.y * _yScale) / pt2.z;
return result;
}
bool MartianDuct::checkAndUpdatePrimArrayForFlag(int &numPoints, DuctFlags flag, int divmulNum) {
_primArrayIdx = 0;
int tempCount = 0;
for (int idx = 0; idx < numPoints; idx++) {
Point3 pt1, pt2;
DuctFlags xyflags;
DuctFlags rbflags;
getPointValuesFromArray(idx, pt1, pt2);
getXYandRBFlags(xyflags, rbflags, pt1, pt2);
if (xyflags == kDuctFlagNone && rbflags == kDuctFlagNone) {
storeLastValsToPrimArray(pt1, pt2); // increments _primArrayIdx
} else if (((xyflags | rbflags) & flag) == kDuctFlagNone) {
storeLastValsToPrimArray(pt1, pt2);
} else if ((xyflags & rbflags & flag) == kDuctFlagNone) {
if ((rbflags & flag) == kDuctFlagNone) {
// Implicitly xyflags & flag != none
assert((xyflags & flag) != kDuctFlagNone);
SWAP(pt1, pt2);
}
switch (divmulNum) {
case 1: pt2 = divmul1(pt1, pt2); break;
case 2: pt2 = divmul2(pt1, pt2); break;
case 3: pt2 = divmul3(pt1, pt2); break;
case 4: pt2 = divmul4(pt1, pt2); break;
case 5: pt2 = divmul5(pt1, pt2); break;
default: error("Invalid divmul num");
}
_tempPoints[tempCount] = pt2;
tempCount++;
storeLastValsToPrimArray(pt1, pt2);
}
}
numPoints = addPointsToMainPrimArray(tempCount);
return numPoints == 0;
}
bool MartianDuct::checkAndUpdatePrimArray1(int &numPoints) {
return checkAndUpdatePrimArrayForFlag(numPoints, kDuctFlagZLessThan2, 1);
}
bool MartianDuct::checkAndUpdatePrimArray2(int &numPoints) {
return checkAndUpdatePrimArrayForFlag(numPoints, kDuctFlagXLessThanNegZ, 2);
}
bool MartianDuct::checkAndUpdatePrimArray3(int &numPoints) {
return checkAndUpdatePrimArrayForFlag(numPoints, kDuctFlagZLessThanY, 3);
}
bool MartianDuct::checkAndUpdatePrimArray4(int &numPoints) {
return checkAndUpdatePrimArrayForFlag(numPoints, kDuctFlagZLessThanX, 4);
}
bool MartianDuct::checkAndUpdatePrimArray5(int &numPoints) {
return checkAndUpdatePrimArrayForFlag(numPoints, kDuctFlagYLessThanNegZ, 5);
}
//
// For these functions the original uses some fixed-point stuff, but replaced
// it with float to be much cleaner.
//
Point3 MartianDuct::divmul1(const Point3 &pt1, const Point3 &pt2) {
// called for kDuctFlagXLessThanNegZ
Point3 out;
out.z = 2;
float tmp = (2.0f - pt1.z) / (pt2.z - pt1.z);
out.x = (int)round((pt2.x - pt1.x) * tmp + pt1.x);
out.y = (int)round((pt2.y - pt1.y) * tmp + pt1.y);
return out;
}
Point3 MartianDuct::divmul2(const Point3 &pt1, const Point3 &pt2) {
// called for kDuctFlagXLessThanNegZ
Point3 out;
float tmp = (pt1.z + pt1.x * 2.0f) / ((pt1.x - pt2.x) * 2 - pt2.z + pt1.z);
out.z = (int)round((pt2.z - pt1.z) * tmp + pt1.z);
out.x = -(out.z / 2);
out.y = (int)round((pt2.y - pt1.y) * tmp + pt1.y);
return out;
}
Point3 MartianDuct::divmul3(const Point3 &pt1, const Point3 &pt2) {
// called for kDuctFlagZLessThanY
Point3 out;
float tmp = (pt1.z - pt1.y * 2.0f) / ((pt2.y - pt1.y) * 2 - pt2.z + pt1.z);
out.y = (int)round((pt2.z - pt1.z) * tmp + pt1.z);
out.z = out.y;
out.x = (int)round((pt2.x - pt1.x) * tmp + pt1.x);
return out;
}
Point3 MartianDuct::divmul4(const Point3 &pt1, const Point3 &pt2) {
// called for kDuctFlagZLessThanX
Point3 out;
float tmp = (pt1.z - pt1.x * 2.0f) / ((pt2.x - pt1.x) * 2 - pt2.z + pt1.z);
out.z = (int)round((pt2.z - pt1.z) * tmp + pt1.z);
out.x = out.z / 2;
out.y = (int)round((pt2.y - pt1.y) * tmp + pt1.y);
return out;
}
Point3 MartianDuct::divmul5(const Point3 &pt1, const Point3 &pt2) {
// called for kDuctFlagYLessThanNegZ
Point3 out;
float tmp = (pt1.z + pt1.y * 2.0f) / ((pt1.y - pt2.y) * 2 - pt2.z + pt1.z);
out.z = (int)round((pt2.z - pt1.z) * tmp + pt1.z);
out.y = -(out.z / 2);
out.x = (int)round((pt2.x - pt1.x) * tmp + pt1.x);
return out;
}
void MartianDuct::getXYandRBFlags(DuctFlags &xyflags, DuctFlags &rbflags, const Point3 &pt1, const Point3 &pt2) {
xyflags = getComparisonFlags(pt1.x * 2, pt1.y * 2, pt1.z);
rbflags = getComparisonFlags(pt2.x * 2, pt2.y * 2, pt2.z);
}
int MartianDuct::addPointsToMainPrimArray(int tempCount) {
int dstIdx = _primArrayIdx;
// Resize arrays if we need to.
if (dstIdx + tempCount > (int)_primX1Array.size()) {
_primX1Array.resize(dstIdx + tempCount);
_primY1Array.resize(dstIdx + tempCount);
_primZ1Array.resize(dstIdx + tempCount);
_primX2Array.resize(dstIdx + tempCount);
_primY2Array.resize(dstIdx + tempCount);
_primZ2Array.resize(dstIdx + tempCount);
}
for (int srcidx = 0; srcidx < tempCount; srcidx += 2) {
_primX1Array[dstIdx] = _tempPoints[srcidx].x;
_primY1Array[dstIdx] = _tempPoints[srcidx].y;
_primZ1Array[dstIdx] = _tempPoints[srcidx].z;
_primX2Array[dstIdx] = _tempPoints[srcidx + 1].x;
_primY2Array[dstIdx] = _tempPoints[srcidx + 1].y;
_primZ2Array[dstIdx] = _tempPoints[srcidx + 1].z;
dstIdx++;
}
return dstIdx;
}
DuctFlags MartianDuct::getComparisonFlags(int16 x, int16 y, int16 z) {
enum DuctFlags flags = kDuctFlagNone;
if (z < 2)
flags = kDuctFlagZLessThan2;
if (z < y)
flags = (DuctFlags)(flags | kDuctFlagZLessThanY);
if (z < x)
flags = (DuctFlags)(flags | kDuctFlagZLessThanX);
if (x < -z)
flags = (DuctFlags)(flags | kDuctFlagXLessThanNegZ);
if (y < -z)
flags = (DuctFlags)(flags | kDuctFlagYLessThanNegZ);
return flags;
}
void MartianDuct::waitForMoveUpdate() {
// Holding down Insert and O together skips the duct in the original.
// Simplify to just accept O.
if (_vm->_events->peekKeyCode() == Common::KEYCODE_o) {
_vm->_room->_function = FN_CLEAR1;
_vm->_flags[0x55] = 1;
_vm->_flags[0x62] = 1;
_stopMoveLoop = true;
return;
}
_stopMoveLoop = false;
// Wait for next frame.
// Frame rate is set as 100fps, but we really want more like 30.
_vm->_events->delayUntilNextFrame();
_vm->_events->delayUntilNextFrame();
_vm->_events->delayUntilNextFrame();
_vm->_events->pollEvents();
Common::CustomEventType action = _vm->_events->peekAction();
Common::Array<Common::Rect> btnCoords;
for (int i = 0; Martian::DUCT_ARROW_BUTTON_RANGE[i][0] != -1; i += 2) {
// DUCT_ARROW_BUTTON_RANGE is min/max X, min/max Y
btnCoords.push_back(Common::Rect(
Martian::DUCT_ARROW_BUTTON_RANGE[i][0],
Martian::DUCT_ARROW_BUTTON_RANGE[i + 1][0],
Martian::DUCT_ARROW_BUTTON_RANGE[i][1],
Martian::DUCT_ARROW_BUTTON_RANGE[i + 1][1]));
}
// Note: buttons are 0 = up, 1 = left, 2 = down, 3 = right
int hitButton = -1;
if (_vm->_events->_leftButton)
hitButton = _vm->_events->checkMouseBox1(btnCoords);
if (action == kActionMoveUp || hitButton == 0) {
_moveIntent = kMoveIntentUp;
drawArrowSprites2();
if (updateMapLocation()) {
checkFinished();
return;
}
drawArrowSprites2();
if (_moveAngle == kMoveAngleNorth) {
_playerY += 7;
} else if (_moveAngle == kMoveAngleSouth) {
_playerY -= 7;
} else if (_moveAngle == kMoveAngleEast) {
_playerX += 7;
} else if (_moveAngle == kMoveAngleWest) {
_playerX -= 7;
} else {
checkFinished();
return;
}
_stopMoveLoop = true;
_crawlFrame++;
if (_crawlFrame == 7)
_crawlFrame = 0;
checkFinished();
return;
} else if (action == kActionMoveDown || hitButton == 2) {
_moveIntent = kMoveIntentDown;
drawArrowSprites2();
if (updateMapLocation()) {
checkFinished();
return;
}
drawArrowSprites2();
if (_moveAngle == kMoveAngleNorth) {
_playerY -= 7;
} else if (_moveAngle == kMoveAngleSouth) {
_playerY += 7;
} else if (_moveAngle == kMoveAngleEast) {
_playerX -= 7;
} else if (_moveAngle == kMoveAngleWest) {
_playerX += 7;
} else {
checkFinished();
return;
}
_stopMoveLoop = true;
_crawlFrame--;
if (_crawlFrame < 0)
_crawlFrame = 6;
checkFinished();
return;
} else if (action == kActionMoveLeft || hitButton == 1) {
_moveIntent = kMoveIntentLeft;
// Action handled, clear it
_vm->_events->getAction(action);
updateMapLocation();
} else if (action == kActionMoveRight || hitButton == 3) {
_moveIntent = kMoveIntentRight;
// Action handled, clear it
_vm->_events->getAction(action);
updateMapLocation();
} else {
checkFinished();
return;
}
_moveAngle = (MoveAngle)(_moveAngle & 0xff);
drawArrowSprites();
drawArrowSprites2();
_drawDistX = 200;
_drawDistY = 500;
if (_moveAngle != kMoveAngleNorth && _moveAngle != kMoveAngleSouth) {
_drawDistX = 500;
_drawDistY = 200;
}
_stopMoveLoop = true;
}
void MartianDuct::checkFinished() {
// Check the player position against the exits
if (abs(_playerX - 2650) < 50 && abs(_playerY - 1050) < 50) {
_vm->_flags[98] = 0;
_vm->_room->_function = FN_CLEAR1;
}
else if (abs(_playerX - 550) < 50 && abs(_playerY - 750) < 50) {
// Finished!
_vm->_flags[98] = 1;
_vm->_flags[85] = 1;
_vm->_room->_function = FN_CLEAR1;
}
}
bool MartianDuct::updateMapLocation() {
uint16 blk = _threshold1 * 2;
_nextPlayerX = (_playerX / blk) * blk + _threshold1;
_nextPlayerY = (_playerY / blk) * blk + _threshold1;
const DuctMapPoint *mapPt = DUCT_MAP_DATA;
do {
const int16 pyType = mapPt->ptType;
if (pyType == -1)
return true;
if (pyType != 0xff) {
_mapLoc = pyType;
int16 thresh = _threshold1;
if (pyType == 6 || pyType == 10)
thresh = -_threshold1;
if (abs((mapPt->x + thresh) - _nextPlayerX) <= _threshold1) {
thresh = _threshold1;
if (pyType == 8)
thresh = -_threshold1;
if (abs((mapPt->y + thresh) - _nextPlayerY) <= _threshold1) {
switch (pyType) {
case 0: return checkMove0();
case 1: return checkMove1();
case 2: return checkMove2();
case 3: return checkMove3();
case 4: return checkMove4();
case 5: return checkMove5();
case 6: return checkMove6();
case 7: return checkMove7();
case 8: return checkMove8();
case 9: return checkMove9();
case 10: return checkMove10();
case 11: return checkMove11();
case 12: return checkMove12();
case 13: return checkMove13_14();
case 14: return checkMove13_14();
default: error("Unexpected point type in duct map data %d", pyType); return false;
}
}
}
}
mapPt++;
} while( true );
}
void MartianDuct::storeLastValsToPrimArray(const Point3 &pt1, const Point3 &pt2) {
_primX1Array[_primArrayIdx] = pt1.x;
_primY1Array[_primArrayIdx] = pt1.y;
_primZ1Array[_primArrayIdx] = pt1.z;
_primX2Array[_primArrayIdx] = pt2.x;
_primY2Array[_primArrayIdx] = pt2.y;
_primZ2Array[_primArrayIdx] = pt2.z;
_primArrayIdx++;
}
bool MartianDuct::checkMove0() {
if (_moveIntent == kMoveIntentRight || _moveIntent == kMoveIntentLeft) {
_moveAngle = (MoveAngle)(_moveAngle + kMoveAngleSouth);
return false;
}
if (_moveIntent == kMoveIntentUp) {
if (_moveAngle == kMoveAngleWest)
return true;
} else if (_moveIntent == kMoveIntentDown && _moveAngle == kMoveAngleEast) {
return true;
}
return false;
}
bool MartianDuct::checkMove1() {
if ((_moveIntent == kMoveIntentRight) || (_moveIntent == kMoveIntentLeft)) {
_moveAngle = (MoveAngle)(_moveAngle + kMoveAngleSouth);
return false;
}
if (_moveIntent == kMoveIntentUp) {
if (_moveAngle == kMoveAngleEast)
return true;
} else if (_moveIntent == kMoveIntentDown && _moveAngle == kMoveAngleWest) {
return true;
}
return false;
}
bool MartianDuct::checkMove2() {
if (_moveIntent == kMoveIntentRight || _moveIntent == kMoveIntentLeft) {
_moveAngle = (MoveAngle)(_moveAngle + kMoveAngleSouth);
return false;
}
if (_moveIntent == kMoveIntentUp) {
if (_moveAngle == kMoveAngleSouth)
return true;
} else if (_moveIntent == kMoveIntentDown && _moveAngle == kMoveAngleNorth) {
return true;
}
return false;
}
bool MartianDuct::checkMove3() {
if (_moveIntent == kMoveIntentRight || _moveIntent == kMoveIntentLeft) {
_moveAngle = (MoveAngle)(_moveAngle + kMoveAngleSouth);
return false;
}
if (_moveIntent == kMoveIntentUp) {
if (_moveAngle == kMoveAngleNorth)
return true;
} else if (_moveIntent == kMoveIntentDown && _moveAngle == kMoveAngleSouth) {
return true;
}
return false;
}
bool MartianDuct::checkMove4() {
if (_moveIntent == kMoveIntentRight) {
_moveAngle = (MoveAngle)(_moveAngle + kMoveAngleEast);
} else {
if (_moveIntent != kMoveIntentLeft)
return false;
_moveAngle = (MoveAngle)(_moveAngle - kMoveAngleEast);
}
_playerY = _nextPlayerY;
_playerX = _nextPlayerX;
return false;
}
bool MartianDuct::checkMove5() {
if (_moveIntent == kMoveIntentRight) {
if (_moveAngle == kMoveAngleSouth)
return true;
_moveAngle = (MoveAngle)(_moveAngle + kMoveAngleEast);
} else {
if (_moveIntent != kMoveIntentLeft) {
if (_moveIntent == kMoveIntentUp)
return _moveAngle == kMoveAngleWest;
if (_moveIntent != kMoveIntentDown)
return true;
if (_moveAngle != kMoveAngleEast)
return false;
return true;
}
if (_moveAngle == kMoveAngleNorth)
return true;
_moveAngle = (MoveAngle)(_moveAngle - kMoveAngleEast);
}
_playerY = _nextPlayerY;
_playerX = _nextPlayerX;
return false;
}
bool MartianDuct::checkMove6() {
if (_moveIntent == kMoveIntentRight) {
if (_moveAngle == kMoveAngleNorth)
return true;
_moveAngle = (MoveAngle)(_moveAngle + kMoveAngleEast);
} else {
if (_moveIntent != kMoveIntentLeft) {
if (_moveIntent == kMoveIntentUp)
return _moveAngle == kMoveAngleEast;
if (_moveIntent != kMoveIntentDown)
return true;
if (_moveAngle != kMoveAngleWest)
return false;
return true;
}
if (_moveAngle == kMoveAngleSouth)
return true;
_moveAngle = (MoveAngle)(_moveAngle - kMoveAngleEast);
}
_playerY = _nextPlayerY;
_playerX = _nextPlayerX;
return false;
}
bool MartianDuct::checkMove7() {
if (_moveIntent == kMoveIntentRight) {
if (_moveAngle == kMoveAngleEast)
return true;
_moveAngle = (MoveAngle)(_moveAngle + kMoveAngleEast);
}
else {
if (_moveIntent != kMoveIntentLeft) {
if (_moveIntent == kMoveIntentUp)
return _moveAngle == kMoveAngleSouth;
if (_moveIntent != kMoveIntentDown)
return true;
if (_moveAngle != kMoveAngleNorth)
return false;
return true;
}
if (_moveAngle == kMoveAngleWest)
return true;
_moveAngle = (MoveAngle)(_moveAngle - kMoveAngleEast);
}
_playerY = _nextPlayerY;
_playerX = _nextPlayerX;
return false;
}
bool MartianDuct::checkMove8() {
if (_moveIntent == kMoveIntentRight) {
if (_moveAngle == kMoveAngleWest)
return true;
_moveAngle = (MoveAngle)(_moveAngle + kMoveAngleEast);
} else {
if (_moveIntent != kMoveIntentLeft) {
if (_moveIntent == kMoveIntentUp)
return _moveAngle == kMoveAngleNorth;
if (_moveIntent != kMoveIntentDown)
return true;
if (_moveAngle != kMoveAngleSouth)
return false;
return true;
}
if (_moveAngle == kMoveAngleEast)
return true;
_moveAngle = (MoveAngle)(_moveAngle - kMoveAngleEast);
}
_playerY = _nextPlayerY;
_playerX = _nextPlayerX;
return false;
}
bool MartianDuct::checkMove9() {
if (_moveIntent == kMoveIntentRight) {
if ((_moveAngle != kMoveAngleNorth) && (_moveAngle != kMoveAngleEast)) {
return true;
}
_moveAngle = (MoveAngle)(_moveAngle + kMoveAngleEast);
} else {
if (_moveIntent != kMoveIntentLeft) {
if (_moveIntent == kMoveIntentUp)
return _moveAngle == kMoveAngleWest || _moveAngle == kMoveAngleNorth;
if (_moveIntent != kMoveIntentDown)
return true;
if (_moveAngle != kMoveAngleEast)
return _moveAngle == kMoveAngleSouth;
return true;
}
if (_moveAngle != kMoveAngleWest && _moveAngle != kMoveAngleSouth)
return true;
_moveAngle = (MoveAngle)(_moveAngle - kMoveAngleEast);
}
_playerY = _nextPlayerY;
_playerX = _nextPlayerX;
return false;
}
bool MartianDuct::checkMove10() {
if (_moveIntent == kMoveIntentRight) {
if (_moveAngle != kMoveAngleEast && _moveAngle != kMoveAngleSouth)
return true;
_moveAngle = (MoveAngle)(_moveAngle + kMoveAngleEast);
} else {
if (_moveIntent != kMoveIntentLeft) {
if (_moveIntent == kMoveIntentUp)
return _moveAngle == kMoveAngleEast || _moveAngle == kMoveAngleNorth;
if (_moveIntent != kMoveIntentDown)
return true;
if (_moveAngle != kMoveAngleWest)
return _moveAngle == kMoveAngleSouth;
return true;
}
if (_moveAngle != kMoveAngleNorth && _moveAngle != kMoveAngleWest)
return true;
_moveAngle = (MoveAngle)((_moveAngle - kMoveAngleEast) & 0xff);
}
_playerY = _nextPlayerY;
_playerX = _nextPlayerX;
return false;
}
bool MartianDuct::checkMove11() {
if (_moveIntent == kMoveIntentRight) {
if (_moveAngle != kMoveAngleWest && _moveAngle != kMoveAngleNorth)
return true;
_moveAngle = (MoveAngle)(_moveAngle + kMoveAngleEast);
} else {
if (_moveIntent != kMoveIntentLeft) {
if (_moveIntent == kMoveIntentUp)
return _moveAngle == kMoveAngleWest || _moveAngle == kMoveAngleSouth;
if (_moveIntent != kMoveIntentDown) {
return true;
}
if (_moveAngle != kMoveAngleNorth) {
return _moveAngle == kMoveAngleEast;
}
return true;
}
if (_moveAngle != kMoveAngleSouth && _moveAngle != kMoveAngleEast)
return true;
_moveAngle = (MoveAngle)(_moveAngle - kMoveAngleEast);
}
_playerY = _nextPlayerY;
_playerX = _nextPlayerX;
return false;
}
bool MartianDuct::checkMove12() {
if (_moveIntent == kMoveIntentRight) {
if (_moveAngle != kMoveAngleSouth && _moveAngle != kMoveAngleWest)
return true;
_moveAngle = (MoveAngle)(_moveAngle + kMoveAngleEast);
} else {
if (_moveIntent != kMoveIntentLeft) {
if (_moveIntent == kMoveIntentUp)
return _moveAngle == kMoveAngleEast || _moveAngle == kMoveAngleSouth;
if (_moveIntent != kMoveIntentDown)
return true;
if (_moveAngle != kMoveAngleNorth)
return _moveAngle == kMoveAngleWest;
return true;
}
if (_moveAngle != kMoveAngleEast && _moveAngle != kMoveAngleNorth)
return true;
_moveAngle = (MoveAngle)((_moveAngle - kMoveAngleEast) & 0xff);
}
_playerY = _nextPlayerY;
_playerX = _nextPlayerX;
return false;
}
bool MartianDuct::checkMove13_14() {
if (_moveIntent != kMoveIntentRight && _moveIntent != kMoveIntentLeft)
return false;
_moveAngle = (MoveAngle)(_moveAngle + kMoveAngleSouth);
return false;
}
}
} // end namespace Access