scummvm/engines/toltecs/segmap.cpp

500 lines
13 KiB
C++
Raw Normal View History

2008-08-04 11:28:57 +00:00
/* 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*
*/
#include "common/events.h"
#include "common/keyboard.h"
#include "common/file.h"
#include "common/savefile.h"
#include "common/config-manager.h"
#include "common/stream.h"
#include "graphics/primitives.h"
#include "base/plugins.h"
#include "base/version.h"
#include "sound/mixer.h"
#include "toltecs/toltecs.h"
#include "toltecs/resource.h"
#include "toltecs/screen.h"
#include "toltecs/segmap.h"
namespace Toltecs {
SegmentMap::SegmentMap(ToltecsEngine *vm) : _vm(vm) {
}
SegmentMap::~SegmentMap() {
freeSegmapMaskRectSurfaces();
2008-08-04 11:28:57 +00:00
}
void SegmentMap::load(byte *source) {
// TODO: Use MemoryReadStream
freeSegmapMaskRectSurfaces();
2008-08-04 12:18:15 +00:00
_maskRects.clear();
_pathRects.clear();
_infoRects.clear();
2008-08-04 11:28:57 +00:00
// Load mask rects
byte *maskData = source + 2;
2008-08-04 11:28:57 +00:00
uint16 maskSize = READ_LE_UINT16(source);
source += 2;
uint16 maskRectCount = READ_LE_UINT16(source);
source += 2;
uint16 maskRectDataSize = maskRectCount * 12 + 2;
debug(0, "SegmentMap::load() maskRectCount = %d", maskRectCount);
for (uint16 i = 0; i < maskRectCount; i++) {
SegmapMaskRect maskRect;
int16 maskOffset;
2008-08-04 11:28:57 +00:00
maskRect.y = READ_LE_UINT16(source);
maskRect.x = READ_LE_UINT16(source + 2);
maskRect.height = READ_LE_UINT16(source + 4);
maskRect.width = READ_LE_UINT16(source + 6);
maskOffset = READ_LE_UINT16(source + 8);
2008-09-17 11:01:43 +00:00
maskRect.priority = READ_LE_UINT16(source + 10);
loadSegmapMaskRectSurface(maskData + maskOffset, maskRect);
2008-08-04 11:28:57 +00:00
debug(0, "SegmentMap::load() (%d, %d, %d, %d, %04X, %d)",
maskRect.x, maskRect.y, maskRect.width, maskRect.height, maskOffset, maskRect.priority);
2008-08-04 11:28:57 +00:00
source += 12;
_maskRects.push_back(maskRect);
}
2008-08-04 12:18:15 +00:00
source += maskSize - maskRectDataSize;
2008-08-04 11:28:57 +00:00
// Load path rects
source += 2; // skip rects array size
uint16 pathRectCount = READ_LE_UINT16(source);
source += 2;
debug(0, "SegmentMap::load() pathRectCount = %d", pathRectCount);
for (uint16 i = 0; i < pathRectCount; i++) {
SegmapPathRect pathRect;
pathRect.y1 = READ_LE_UINT16(source);
pathRect.x1 = READ_LE_UINT16(source + 2);
pathRect.y2 = pathRect.y1 + READ_LE_UINT16(source + 4);
pathRect.x2 = pathRect.x1 + READ_LE_UINT16(source + 6);
2008-08-04 11:28:57 +00:00
debug(0, "SegmentMap::load() (%d, %d, %d, %d)", pathRect.x1, pathRect.y1, pathRect.x2, pathRect.y2);
2008-08-04 11:28:57 +00:00
source += 8;
_pathRects.push_back(pathRect);
}
// Load info rects
source += 2; // skip rects array size
uint16 infoRectCount = READ_LE_UINT16(source);
source += 2;
debug(0, "SegmentMap::load() infoRectCount = %d", infoRectCount);
for (uint16 i = 0; i < infoRectCount; i++) {
SegmapInfoRect infoRect;
infoRect.y = READ_LE_UINT16(source);
infoRect.x = READ_LE_UINT16(source + 2);
infoRect.height = READ_LE_UINT16(source + 4);
infoRect.width = READ_LE_UINT16(source + 6);
infoRect.id = source[8];
infoRect.a = source[9];
infoRect.b = source[10];
infoRect.c = source[11];
debug(0, "SegmentMap::load() (%d, %d, %d, %d) (%d, %d, %d, %d)",
infoRect.x, infoRect.y, infoRect.width, infoRect.height,
infoRect.id, (int8)infoRect.a, (int8)infoRect.b, (int8)infoRect.c);
source += 12;
_infoRects.push_back(infoRect);
}
// TODO Other stuff
}
int16 SegmentMap::findPathRectAtPoint(int16 x, int16 y) {
for (int16 rectIndex = 0; rectIndex < (int16)_pathRects.size(); rectIndex++) {
if (y >= _pathRects[rectIndex].y1 && y <= _pathRects[rectIndex].y2 &&
x >= _pathRects[rectIndex].x1 && x <= _pathRects[rectIndex].x2) {
2008-08-04 11:28:57 +00:00
return rectIndex;
}
}
return -1;
}
void SegmentMap::adjustPathPoint(int16 &x, int16 &y) {
2008-08-04 11:28:57 +00:00
if (findPathRectAtPoint(x, y) != -1)
return;
uint32 minDistance = 0xFFFFFFFF, distance;
int16 adjustedX, adjustedY, x2, y2;
2008-08-04 11:28:57 +00:00
for (int16 rectIndex = 0; rectIndex < (int16)_pathRects.size(); rectIndex++) {
2008-08-04 11:28:57 +00:00
if (x >= _pathRects[rectIndex].x1 && x < _pathRects[rectIndex].x2) {
x2 = x;
} else if (ABS(x - _pathRects[rectIndex].x1) >= ABS(x - _pathRects[rectIndex].x2)) {
x2 = _pathRects[rectIndex].x2;
2008-08-04 11:28:57 +00:00
} else {
x2 = _pathRects[rectIndex].x1;
2008-08-04 11:28:57 +00:00
}
if (ABS(y - _pathRects[rectIndex].y1) >= ABS(y - _pathRects[rectIndex].y2)) {
y2 = _pathRects[rectIndex].y2;
2008-08-04 11:28:57 +00:00
} else {
y2 = _pathRects[rectIndex].y1;
2008-08-04 11:28:57 +00:00
}
distance = ABS(y - y2) + ABS(x - x2);
if (distance < minDistance) {
if (x >= _pathRects[rectIndex].x1 && x <= _pathRects[rectIndex].x2) {
adjustedX = x;
} else {
adjustedX = x2;
}
if (y >= _pathRects[rectIndex].y1 && y <= _pathRects[rectIndex].y2) {
adjustedY = y;
} else {
adjustedY = y2;
}
2008-08-04 11:28:57 +00:00
minDistance = distance;
}
}
x = adjustedX;
y = adjustedY;
2008-08-04 11:28:57 +00:00
}
int16 SegmentMap::findNextPathRect(int16 srcRectIndex, int16 destX, int16 destY) {
2008-08-04 11:28:57 +00:00
int16 result;
uint16 minDistance, distance;
int16 x1, y1, x2, y2;
int16 xmin, xmax, ymax, ymin;
2008-08-04 11:28:57 +00:00
result = -1;
minDistance = 0xFFFF;
x1 = _pathRects[srcRectIndex].x1;
y1 = _pathRects[srcRectIndex].y1;
x2 = _pathRects[srcRectIndex].x2;
y2 = _pathRects[srcRectIndex].y2;
for (int16 rectIndex = 0; rectIndex < (int16)_pathRects.size(); rectIndex++) {
int16 nodeX = -1, nodeY = -1;
// Check if the current rectangle is connected to the source rectangle
if (x1 == _pathRects[rectIndex].x2 && y1 < _pathRects[rectIndex].y2 && y2 > _pathRects[rectIndex].y1) {
nodeX = x1;
} else if (x2 == _pathRects[rectIndex].x1 && y1 < _pathRects[rectIndex].y2 && y2 > _pathRects[rectIndex].y1) {
nodeX = x2 - 1;
} else if (y1 == _pathRects[rectIndex].y2 && x1 < _pathRects[rectIndex].x2 && x2 > _pathRects[rectIndex].x1) {
nodeY = y1;
} else if (y2 == _pathRects[rectIndex].y1 && x1 < _pathRects[rectIndex].x2 && x2 > _pathRects[rectIndex].x1) {
nodeY = y2 - 1;
} else
continue;
if (nodeX == -1) {
xmin = MAX<int16>(x1, _pathRects[rectIndex].x1);
xmax = MIN<int16>(x2, _pathRects[rectIndex].x2) - 1;
if (destX > xmin && destX < xmax) {
nodeX = destX;
} else if (ABS(destX - xmin) >= ABS(destX - xmax)) {
nodeX = xmax - 1;
2008-08-04 11:28:57 +00:00
} else {
nodeX = xmin;
2008-08-04 11:28:57 +00:00
}
}
if (nodeY == -1) {
ymin = MAX<int16>(y1, _pathRects[rectIndex].y1);
ymax = MIN<int16>(y2, _pathRects[rectIndex].y2) - 1;
if (destY > ymin && destY < ymax) {
nodeY = destY;
} else if (ABS(destY - ymin) >= ABS(destY - ymax)) {
nodeY = ymax - 1;
2008-08-04 11:28:57 +00:00
} else {
nodeY = ymin;
2008-08-04 11:28:57 +00:00
}
}
distance = ABS(destX - nodeX) + ABS(destY - nodeY);
for (uint i = 0; i < _closedPathRectsCount; i++) {
if (rectIndex == _closedPathRects[i]) {
2008-08-04 11:28:57 +00:00
distance = minDistance;
break;
}
}
for (uint i = 0; i < _deadEndPathRectsCount; i++) {
if (rectIndex == _deadEndPathRects[i]) {
2008-08-04 11:28:57 +00:00
distance = minDistance;
break;
}
}
2008-08-04 12:18:15 +00:00
if (distance < minDistance) {
2008-08-04 11:28:57 +00:00
result = rectIndex;
minDistance = distance;
_pathNodes[_pathNodesCount].x = nodeX;
_pathNodes[_pathNodesCount].y = nodeY;
2008-08-04 11:28:57 +00:00
}
}
return result;
}
struct LineData {
int pitch;
byte *surf;
};
void plotProc(int x, int y, int color, void *data) {
LineData *ld = (LineData*)data;
ld->surf[x + y * ld->pitch] = color;
}
void SegmentMap::findPath(int16 *pointsArray, int16 destX, int16 destY, int16 sourceX, int16 sourceY) {
2008-08-04 11:28:57 +00:00
// TODO: Writes to pointsArray aren't endian-safe yet
2008-08-04 11:28:57 +00:00
int16 currentRectIndex, destRectIndex;
int16 pointsCount;
2008-08-04 11:28:57 +00:00
debug(0, "SegmentMap::findPath(fromX: %d; fromY: %d; toX: %d; toY: %d)", sourceX, sourceY, destX, destY);
2008-08-04 11:28:57 +00:00
_deadEndPathRectsCount = 0;
_closedPathRectsCount = 0;
_pathNodesCount = 0;
2008-08-04 11:28:57 +00:00
pointsCount = 2;
adjustPathPoint(sourceX, sourceY);
currentRectIndex = findPathRectAtPoint(sourceX, sourceY);
2008-08-04 11:28:57 +00:00
adjustPathPoint(destX, destY);
destRectIndex = findPathRectAtPoint(destX, destY);
2008-08-04 11:28:57 +00:00
if (currentRectIndex != -1) {
if (destRectIndex != currentRectIndex) {
2008-08-04 12:18:15 +00:00
while (1) {
2008-08-04 11:28:57 +00:00
do {
_closedPathRects[_closedPathRectsCount++] = currentRectIndex;
currentRectIndex = findNextPathRect(currentRectIndex, destX, destY);
_pathNodesCount++;
} while (currentRectIndex != -1 && currentRectIndex != destRectIndex);
if (currentRectIndex != -1 && currentRectIndex == destRectIndex)
2008-08-04 11:28:57 +00:00
break;
_deadEndPathRects[_deadEndPathRectsCount++] = _closedPathRects[--_closedPathRectsCount];
_pathNodesCount -= 2;
currentRectIndex = _closedPathRects[--_closedPathRectsCount];
2008-08-04 11:28:57 +00:00
}
for (int16 i = 0; i < _pathNodesCount; i++) {
pointsArray[pointsCount++] = _pathNodes[i].y;
pointsArray[pointsCount++] = _pathNodes[i].x;
2008-08-04 11:28:57 +00:00
}
}
pointsArray[pointsCount++] = destY;
pointsArray[pointsCount++] = destX;
2008-08-04 11:28:57 +00:00
pointsArray[0] = 0;
pointsArray[1] = _pathNodesCount + 1;
2008-08-04 11:28:57 +00:00
}
debug(0, "SegmentMap::findPath() count = %d", pointsArray[1]);
#if 0 // DEBUG: Draw the path we found
int sx = sourceX, sy = sourceY;
2008-08-04 11:28:57 +00:00
LineData ld;
ld.pitch = _vm->_sceneWidth;
ld.surf = _vm->_screen->_backScreen;
for (int16 i = 0; i < pointsArray[1] * 2; i+=2) {
2008-08-04 12:18:15 +00:00
debug(0, "x = %d; y = %d", pointsArray[3+i], pointsArray[2+i]);
Graphics::drawLine(sx, sy, pointsArray[3+i], pointsArray[2+i], 0xFF, plotProc, &ld);
sx = pointsArray[3+i];
2008-08-04 11:28:57 +00:00
sy = pointsArray[2+i];
}
#endif
2008-08-04 11:28:57 +00:00
}
int8 SegmentMap::getScalingAtPoint(int16 x, int16 y) {
int8 scaling = 0;
for (uint i = 0; i < _infoRects.size(); i++) {
if (_infoRects[i].id == 0 &&
y >= _infoRects[i].y && y <= _infoRects[i].y + _infoRects[i].height &&
x >= _infoRects[i].x && x <= _infoRects[i].x + _infoRects[i].width) {
int8 topScaling = (int8)_infoRects[i].b;
int8 bottomScaling = (int8)_infoRects[i].c;
2008-08-04 11:28:57 +00:00
if (y - _infoRects[i].y > 0) {
2008-08-04 12:18:15 +00:00
scaling = (ABS(y - _infoRects[i].y) * (bottomScaling - topScaling) / _infoRects[i].height) + topScaling;
2008-08-04 11:28:57 +00:00
}
}
}
return scaling;
}
void SegmentMap::getRgbModifiertAtPoint(int16 x, int16 y, int16 id, byte &r, byte &g, byte &b) {
r = 0;
g = 0;
b = 0;
for (uint i = 0; i < _infoRects.size(); i++) {
if (_infoRects[i].id == id &&
y >= _infoRects[i].y && y <= _infoRects[i].y + _infoRects[i].height &&
x >= _infoRects[i].x && x <= _infoRects[i].x + _infoRects[i].width) {
r = _infoRects[i].a;
g = _infoRects[i].b;
b = _infoRects[i].c;
}
}
debug(0, "SegmentMap::getRgbModifiertAtPoint() r: %d; g: %d; b: %d", r, g, b);
}
void SegmentMap::loadSegmapMaskRectSurface(byte *maskData, SegmapMaskRect &maskRect) {
maskRect.surface = new Graphics::Surface();
maskRect.surface->create(maskRect.width, maskRect.height, 1);
byte *backScreen = _vm->_screen->_backScreen + maskRect.x + (maskRect.y * _vm->_sceneWidth);
byte *dest = (byte*)maskRect.surface->getBasePtr(0, 0);
for (int16 h = 0; h < maskRect.height; h++) {
int16 w = maskRect.width;
while (w > 0) {
byte mask = *maskData++;
byte count = mask & 0x7F;
if (mask & 0x80)
memcpy(dest, backScreen, count);
else
memset(dest, 0xFF, count);
w -= count;
dest += count;
backScreen += count;
}
backScreen += _vm->_sceneWidth - maskRect.width;
}
}
void SegmentMap::freeSegmapMaskRectSurfaces() {
for (uint i = 0; i < _maskRects.size(); i++) {
delete _maskRects[i].surface;
}
}
2008-08-04 11:28:57 +00:00
void SegmentMap::restoreMasksBySprite(SpriteDrawItem *sprite) {
// TODO: This needs more optimization
for (uint i = 0; i < _maskRects.size(); i++) {
2008-09-17 11:01:43 +00:00
if (sprite->priority <= _maskRects[i].priority) {
2008-08-04 12:18:15 +00:00
restoreMask(i);
2008-08-04 11:28:57 +00:00
}
}
}
void SegmentMap::restoreMask(int16 index) {
2008-08-04 11:28:57 +00:00
// TODO: This needs more optimization
SegmapMaskRect *maskRect = &_maskRects[index];
int16 skipX = 0;
int16 x = maskRect->x - _vm->_cameraX;
int16 y = maskRect->y - _vm->_cameraY;
int16 width = maskRect->width;
int16 height = maskRect->height;
byte *maskSurface = (byte*)maskRect->surface->getBasePtr(0, 0);
byte *frontScreen;
2008-08-04 11:28:57 +00:00
2008-08-04 12:18:15 +00:00
debug(0, "SegmentMap::restoreMask() screenX = %d; screenY = %d; maskX = %d; maskY = %d",
2008-08-04 11:28:57 +00:00
x, y, maskRect->x, maskRect->y);
// Not on screen, skip
if (x + width < 0 || y + height < 0 || x >= 640 || y >= _vm->_cameraHeight)
2008-08-04 12:18:15 +00:00
return;
2008-08-04 11:28:57 +00:00
if (x < 0) {
2008-08-04 12:18:15 +00:00
skipX = -x;
x = 0;
width -= skipX;
2008-08-04 11:28:57 +00:00
}
if (y < 0) {
2008-08-04 12:18:15 +00:00
int16 skipY = -y;
maskSurface += maskRect->width * skipY;
2008-08-04 12:18:15 +00:00
y = 0;
height -= skipY;
2008-08-04 11:28:57 +00:00
}
if (x + width >= 640) {
2008-08-04 12:18:15 +00:00
width -= x + width - 640;
2008-08-04 11:28:57 +00:00
}
if (y + height >= _vm->_cameraHeight) {
2008-08-04 12:18:15 +00:00
height -= y + height - _vm->_cameraHeight;
2008-08-04 11:28:57 +00:00
}
frontScreen = _vm->_screen->_frontScreen + x + (y * 640);
2008-08-04 11:28:57 +00:00
for (int16 h = 0; h < height; h++) {
maskSurface += skipX;
for (int16 w = 0; w < width; w++) {
if (*maskSurface != 0xFF)
*frontScreen = *maskSurface;
frontScreen++;
maskSurface++;
2008-08-04 11:28:57 +00:00
}
frontScreen += 640 - width;
maskSurface += maskRect->width - width - skipX;
2008-08-04 11:28:57 +00:00
}
2008-08-04 11:28:57 +00:00
}
void SegmentMap::debugDrawRects(Graphics::Surface *surf) {
for (uint16 i = 0; i < _pathRects.size(); i++) {
SegmapPathRect pathRect = _pathRects[i];
surf->frameRect(
Common::Rect(pathRect.x1, pathRect.y1, pathRect.x2, pathRect.y2),
2008-08-04 11:28:57 +00:00
255);
}
}
} // End of namespace Toltecs