devilutionX/Source/engine.cpp
Anders Jenbo f8c16a1dcb Only use safe blitting and set gpBufEnd to the correct relative values
This again allows us to only selectivly render the UI panel
This also eliminates the need for the BORDER_BOTTOM 10k memory saved!
2019-10-20 15:59:47 +02:00

1435 lines
31 KiB
C++

#include "diablo.h"
#include "../3rdParty/Storm/Source/storm.h"
DEVILUTION_BEGIN_NAMESPACE
char gbPixelCol; // automap pixel color 8-bit (palette entry)
BOOL gbRotateMap; // flip - if y < x
int orgseed;
int sgnWidth;
int sglGameSeed;
static CCritSect sgMemCrit;
int SeedCount;
BOOL gbNotInView; // valid - if x/y are in bounds
const int RndInc = 1;
const int RndMult = 0x015A4E35;
void CelDraw(int sx, int sy, BYTE *pCelBuff, int nCel, int nWidth)
{
CelBlitFrame(&gpBuffer[sx + BUFFER_WIDTH * sy], pCelBuff, nCel, nWidth);
}
void CelBlitFrame(BYTE *pBuff, BYTE *pCelBuff, int nCel, int nWidth)
{
int nDataSize;
BYTE *pRLEBytes;
/// ASSERT: assert(pCelBuff != NULL);
if (!pCelBuff)
return;
/// ASSERT: assert(pBuff != NULL);
if (!pBuff)
return;
pRLEBytes = CelGetFrame(pCelBuff, nCel, &nDataSize);
CelBlitSafe(pBuff, pRLEBytes, nDataSize, nWidth);
}
/**
* @param CelSkip Skip lower parts of sprite, must be multiple of 2, max 8
* @param CelCap Amount of sprite to render from lower to upper, must be multiple of 2, max 8
*/
void CelClippedDraw(int sx, int sy, BYTE *pCelBuff, int nCel, int nWidth, int CelSkip, int CelCap)
{
BYTE *pRLEBytes;
int nDataSize;
/// ASSERT: assert(gpBuffer);
if (!gpBuffer)
return;
/// ASSERT: assert(pCelBuff != NULL);
if (!pCelBuff)
return;
pRLEBytes = CelGetFrameClipped(pCelBuff, nCel, CelSkip, CelCap, &nDataSize);
if (pRLEBytes == NULL)
return;
CelBlitSafe(
&gpBuffer[sx + BUFFER_WIDTH * (sy - 16 * CelSkip)],
pRLEBytes,
nDataSize,
nWidth);
}
void CelDrawLight(int sx, int sy, BYTE *pCelBuff, int nCel, int nWidth, BYTE *tbl)
{
int nDataSize;
BYTE *pDecodeTo, *pRLEBytes;
/// ASSERT: assert(gpBuffer);
if (!gpBuffer)
return;
/// ASSERT: assert(pCelBuff != NULL);
if (!pCelBuff)
return;
pRLEBytes = CelGetFrame(pCelBuff, nCel, &nDataSize);
pDecodeTo = &gpBuffer[sx + BUFFER_WIDTH * sy];
if (light_table_index || tbl)
CelBlitLightSafe(pDecodeTo, pRLEBytes, nDataSize, nWidth, tbl);
else
CelBlitSafe(pDecodeTo, pRLEBytes, nDataSize, nWidth);
}
/**
* @param CelSkip Skip lower parts of sprite, must be multiple of 2, max 8
* @param CelCap Amount of sprite to render from lower to upper, must be multiple of 2, max 8
*/
void CelClippedDrawLight(int sx, int sy, BYTE *pCelBuff, int nCel, int nWidth, int CelSkip, int CelCap)
{
int nDataSize;
BYTE *pRLEBytes, *pDecodeTo;
/// ASSERT: assert(gpBuffer);
if (!gpBuffer)
return;
/// ASSERT: assert(pCelBuff != NULL);
if (!pCelBuff)
return;
pRLEBytes = CelGetFrameClipped(pCelBuff, nCel, CelSkip, CelCap, &nDataSize);
if (pRLEBytes == NULL)
return;
pDecodeTo = &gpBuffer[sx + BUFFER_WIDTH * (sy - 16 * CelSkip)];
if (light_table_index)
CelBlitLightSafe(pDecodeTo, pRLEBytes, nDataSize, nWidth);
else
CelBlitSafe(pDecodeTo, pRLEBytes, nDataSize, nWidth);
}
/**
* @param CelSkip Skip lower parts of sprite, must be multiple of 2, max 8
* @param CelCap Amount of sprite to render from lower to upper, must be multiple of 2, max 8
*/
void CelDrawLightRed(int sx, int sy, BYTE *pCelBuff, int nCel, int nWidth, int CelSkip, int CelCap, char light)
{
int nDataSize, w, idx;
BYTE *pRLEBytes, *dst, *tbl;
/// ASSERT: assert(gpBuffer);
if (!gpBuffer)
return;
/// ASSERT: assert(pCelBuff != NULL);
if (!pCelBuff)
return;
pRLEBytes = CelGetFrameClipped(pCelBuff, nCel, CelSkip, CelCap, &nDataSize);
if (pRLEBytes == NULL)
return;
dst = &gpBuffer[sx + BUFFER_WIDTH * (sy - 16 * CelSkip)];
idx = light4flag ? 1024 : 4096;
if (light == 2)
idx += 256;
if (light >= 4)
idx += (light - 1) << 8;
BYTE width;
BYTE *end;
tbl = &pLightTbl[idx];
end = &pRLEBytes[nDataSize];
for (; pRLEBytes != end; dst -= BUFFER_WIDTH + nWidth) {
for (w = nWidth; w;) {
width = *pRLEBytes++;
if (!(width & 0x80)) {
w -= width;
while (width) {
*dst = tbl[*pRLEBytes];
pRLEBytes++;
dst++;
width--;
}
} else {
width = -(char)width;
dst += width;
w -= width;
}
}
}
}
/**
* @brief Same as CelBlit but checks for drawing outside the buffer
*/
void CelBlitSafe(BYTE *pDecodeTo, BYTE *pRLEBytes, int nDataSize, int nWidth)
{
int w;
/// ASSERT: assert(pDecodeTo != NULL);
if (!pDecodeTo)
return;
/// ASSERT: assert(pRLEBytes != NULL);
if (!pRLEBytes)
return;
/// ASSERT: assert(gpBuffer);
if (!gpBuffer)
return;
int i;
BYTE width;
BYTE *src, *dst;
src = pRLEBytes;
dst = pDecodeTo;
w = nWidth;
for (; src != &pRLEBytes[nDataSize]; dst -= BUFFER_WIDTH + w) {
for (i = w; i;) {
width = *src++;
if (!(width & 0x80)) {
i -= width;
if (dst < gpBufEnd) {
if (width & 1) {
dst[0] = src[0];
src++;
dst++;
}
width >>= 1;
if (width & 1) {
dst[0] = src[0];
dst[1] = src[1];
src += 2;
dst += 2;
}
width >>= 1;
for (; width; width--) {
dst[0] = src[0];
dst[1] = src[1];
dst[2] = src[2];
dst[3] = src[3];
src += 4;
dst += 4;
}
} else {
src += width;
dst += width;
}
} else {
width = -(char)width;
dst += width;
i -= width;
}
}
}
}
/**
* @param CelSkip Skip lower parts of sprite, must be multiple of 2, max 8
* @param CelCap Amount of sprite to render from lower to upper, must be multiple of 2, max 8
*/
void CelClippedDrawSafe(int sx, int sy, BYTE *pCelBuff, int nCel, int nWidth, int CelSkip, int CelCap)
{
BYTE *pRLEBytes;
int nDataSize;
/// ASSERT: assert(gpBuffer);
if (!gpBuffer)
return;
/// ASSERT: assert(pCelBuff != NULL);
if (!pCelBuff)
return;
pRLEBytes = CelGetFrameClipped(pCelBuff, nCel, CelSkip, CelCap, &nDataSize);
if (pRLEBytes == NULL)
return;
CelBlitSafe(
&gpBuffer[sx + BUFFER_WIDTH * (sy - 16 * CelSkip)],
pRLEBytes,
nDataSize,
nWidth);
}
/**
* @param CelSkip Skip lower parts of sprite, must be multiple of 2, max 8
* @param CelCap Amount of sprite to render from lower to upper, must be multiple of 2, max 8
*/
void CelClippedBlit(BYTE *pBuff, BYTE *pCelBuff, int nCel, int nWidth, int CelSkip, int CelCap)
{
BYTE *pRLEBytes;
int nDataSize;
/// ASSERT: assert(pCelBuff != NULL);
if (!pCelBuff)
return;
/// ASSERT: assert(pBuff != NULL);
if (!pBuff)
return;
pRLEBytes = CelGetFrameClipped(pCelBuff, nCel, CelSkip, CelCap, &nDataSize);
if (pRLEBytes == NULL)
return;
CelBlitSafe(pBuff, pRLEBytes, nDataSize, nWidth);
}
/**
* @brief Same as CelBlitLight but checks for drawing outside the buffer
*/
void CelBlitLightSafe(BYTE *pDecodeTo, BYTE *pRLEBytes, int nDataSize, int nWidth, BYTE *tbl)
{
int w;
/// ASSERT: assert(pDecodeTo != NULL);
if (!pDecodeTo)
return;
/// ASSERT: assert(pRLEBytes != NULL);
if (!pRLEBytes)
return;
/// ASSERT: assert(gpBuffer);
if (!gpBuffer)
return;
int i;
BYTE width;
BYTE *src, *dst;
src = pRLEBytes;
dst = pDecodeTo;
if (tbl == NULL)
tbl = &pLightTbl[light_table_index * 256];
w = nWidth;
for (; src != &pRLEBytes[nDataSize]; dst -= BUFFER_WIDTH + w) {
for (i = w; i;) {
width = *src++;
if (!(width & 0x80)) {
i -= width;
if (dst < gpBufEnd) {
if (width & 1) {
dst[0] = tbl[src[0]];
src++;
dst++;
}
width >>= 1;
if (width & 1) {
dst[0] = tbl[src[0]];
dst[1] = tbl[src[1]];
src += 2;
dst += 2;
}
width >>= 1;
for (; width; width--) {
dst[0] = tbl[src[0]];
dst[1] = tbl[src[1]];
dst[2] = tbl[src[2]];
dst[3] = tbl[src[3]];
src += 4;
dst += 4;
}
} else {
src += width;
dst += width;
}
} else {
width = -(char)width;
dst += width;
i -= width;
}
}
}
}
/**
* @brief Same as CelBlitLightTrans but checks for drawing outside the buffer
*/
void CelBlitLightTransSafe(BYTE *pDecodeTo, BYTE *pRLEBytes, int nDataSize, int nWidth)
{
int w;
BOOL shift;
BYTE *tbl;
/// ASSERT: assert(pDecodeTo != NULL);
if (!pDecodeTo)
return;
/// ASSERT: assert(pRLEBytes != NULL);
if (!pRLEBytes)
return;
/// ASSERT: assert(gpBuffer);
if (!gpBuffer)
return;
int i;
BYTE width;
BYTE *src, *dst;
src = pRLEBytes;
dst = pDecodeTo;
tbl = &pLightTbl[light_table_index * 256];
w = nWidth;
shift = (BYTE)(size_t)dst & 1;
for (; src != &pRLEBytes[nDataSize]; dst -= BUFFER_WIDTH + w, shift = (shift + 1) & 1) {
for (i = w; i;) {
width = *src++;
if (!(width & 0x80)) {
i -= width;
if (dst < gpBufEnd) {
if (((BYTE)(size_t)dst & 1) == shift) {
if (!(width & 1)) {
goto L_ODD;
} else {
src++;
dst++;
L_EVEN:
width >>= 1;
if (width & 1) {
dst[0] = tbl[src[0]];
src += 2;
dst += 2;
}
width >>= 1;
for (; width; width--) {
dst[0] = tbl[src[0]];
dst[2] = tbl[src[2]];
src += 4;
dst += 4;
}
}
} else {
if (!(width & 1)) {
goto L_EVEN;
} else {
dst[0] = tbl[src[0]];
src++;
dst++;
L_ODD:
width >>= 1;
if (width & 1) {
dst[1] = tbl[src[1]];
src += 2;
dst += 2;
}
width >>= 1;
for (; width; width--) {
dst[1] = tbl[src[1]];
dst[3] = tbl[src[3]];
src += 4;
dst += 4;
}
}
}
} else {
src += width;
dst += width;
}
} else {
width = -(char)width;
dst += width;
i -= width;
}
}
}
}
/**
* @param CelSkip Skip lower parts of sprite, must be multiple of 2, max 8
* @param CelCap Amount of sprite to render from lower to upper, must be multiple of 2, max 8
*/
void CelClippedBlitLightTrans(BYTE *pBuff, BYTE *pCelBuff, int nCel, int nWidth, int CelSkip, int CelCap)
{
int nDataSize;
BYTE *pRLEBytes;
/// ASSERT: assert(pCelBuff != NULL);
if (!pCelBuff)
return;
pRLEBytes = CelGetFrameClipped(pCelBuff, nCel, CelSkip, CelCap, &nDataSize);
if (pRLEBytes == NULL)
return;
if (cel_transparency_active)
CelBlitLightTransSafe(pBuff, pRLEBytes, nDataSize, nWidth);
else if (light_table_index)
CelBlitLightSafe(pBuff, pRLEBytes, nDataSize, nWidth);
else
CelBlitSafe(pBuff, pRLEBytes, nDataSize, nWidth);
}
/**
* @brief Same as CelDrawLightRed but checks for drawing outside the buffer
* @param CelSkip Skip lower parts of sprite, must be multiple of 2, max 8
* @param CelCap Amount of sprite to render from lower to upper, must be multiple of 2, max 8
*/
void CelDrawLightRedSafe(int sx, int sy, BYTE *pCelBuff, int nCel, int nWidth, int CelSkip, int CelCap, char light)
{
int nDataSize, w, idx;
BYTE *pRLEBytes, *dst, *tbl;
/// ASSERT: assert(gpBuffer);
if (!gpBuffer)
return;
/// ASSERT: assert(pCelBuff != NULL);
if (!pCelBuff)
return;
pRLEBytes = CelGetFrameClipped(pCelBuff, nCel, CelSkip, CelCap, &nDataSize);
if (pRLEBytes == NULL)
return;
dst = &gpBuffer[sx + BUFFER_WIDTH * (sy - 16 * CelSkip)];
idx = light4flag ? 1024 : 4096;
if (light == 2)
idx += 256;
if (light >= 4)
idx += (light - 1) << 8;
tbl = &pLightTbl[idx];
BYTE width;
BYTE *end;
end = &pRLEBytes[nDataSize];
for (; pRLEBytes != end; dst -= BUFFER_WIDTH + nWidth) {
for (w = nWidth; w;) {
width = *pRLEBytes++;
if (!(width & 0x80)) {
w -= width;
if (dst < gpBufEnd) {
while (width) {
*dst = tbl[*pRLEBytes];
pRLEBytes++;
dst++;
width--;
}
} else {
pRLEBytes += width;
dst += width;
}
} else {
width = -(char)width;
dst += width;
w -= width;
}
}
}
}
/**
* @brief Same as CelBlit but cropped to given width
*/
void CelBlitWidth(BYTE *pBuff, int CelSkip, int hgt, int wdt, BYTE *pCelBuff, int nCel, int nWidth)
{
BYTE *pRLEBytes, *dst, *end;
/// ASSERT: assert(pCelBuff != NULL);
if (!pCelBuff)
return;
/// ASSERT: assert(pBuff != NULL);
if (!pBuff)
return;
int i, nDataSize;
BYTE width;
pRLEBytes = CelGetFrame(pCelBuff, nCel, &nDataSize);
end = &pRLEBytes[nDataSize];
dst = &pBuff[hgt * wdt + CelSkip];
for (; pRLEBytes != end; dst -= wdt + nWidth) {
for (i = nWidth; i;) {
width = *pRLEBytes++;
if (!(width & 0x80)) {
i -= width;
if (width & 1) {
dst[0] = pRLEBytes[0];
pRLEBytes++;
dst++;
}
width >>= 1;
if (width & 1) {
dst[0] = pRLEBytes[0];
dst[1] = pRLEBytes[1];
pRLEBytes += 2;
dst += 2;
}
width >>= 1;
while (width) {
dst[0] = pRLEBytes[0];
dst[1] = pRLEBytes[1];
dst[2] = pRLEBytes[2];
dst[3] = pRLEBytes[3];
pRLEBytes += 4;
dst += 4;
width--;
}
} else {
width = -(char)width;
dst += width;
i -= width;
}
}
}
}
/**
* @brief Same as CelBlitOutline but checks for drawing outside the buffer
* @param CelSkip Skip lower parts of sprite, must be multiple of 2, max 8
* @param CelCap Amount of sprite to render from lower to upper, must be multiple of 2, max 8
*/
void CelBlitOutline(char col, int sx, int sy, BYTE *pCelBuff, int nCel, int nWidth, int CelSkip, int CelCap)
{
int nDataSize, w;
BYTE *src, *dst, *end;
BYTE width;
/// ASSERT: assert(pCelBuff != NULL);
if (!pCelBuff)
return;
/// ASSERT: assert(gpBuffer);
if (!gpBuffer)
return;
src = CelGetFrameClipped(pCelBuff, nCel, CelSkip, CelCap, &nDataSize);
if (src == NULL)
return;
end = &src[nDataSize];
dst = &gpBuffer[sx + BUFFER_WIDTH * (sy - 16 * CelSkip)];
for (; src != end; dst -= BUFFER_WIDTH + nWidth) {
for (w = nWidth; w;) {
width = *src++;
if (!(width & 0x80)) {
w -= width;
if (dst < gpBufEnd) {
if (dst >= gpBufEnd - BUFFER_WIDTH) {
while (width) {
if (*src++) {
dst[-BUFFER_WIDTH] = col;
dst[-1] = col;
dst[1] = col;
}
dst++;
width--;
}
} else {
while (width) {
if (*src++) {
dst[-BUFFER_WIDTH] = col;
dst[-1] = col;
dst[1] = col;
dst[BUFFER_WIDTH] = col;
}
dst++;
width--;
}
}
} else {
src += width;
dst += width;
}
} else {
width = -(char)width;
dst += width;
w -= width;
}
}
}
}
void ENG_set_pixel(int sx, int sy, BYTE col)
{
BYTE *dst;
/// ASSERT: assert(gpBuffer);
if (sy < 0 || sy >= SCREEN_HEIGHT + SCREEN_Y || sx < SCREEN_X || sx >= SCREEN_WIDTH + SCREEN_X)
return;
dst = &gpBuffer[sx + BUFFER_WIDTH * sy];
if (dst < gpBufEnd)
*dst = col;
}
void engine_draw_pixel(int sx, int sy)
{
BYTE *dst;
/// ASSERT: assert(gpBuffer);
if (gbRotateMap) {
if (gbNotInView && (sx < 0 || sx >= SCREEN_HEIGHT + SCREEN_Y || sy < SCREEN_X || sy >= SCREEN_WIDTH + SCREEN_X))
return;
dst = &gpBuffer[sy + BUFFER_WIDTH * sx];
} else {
if (gbNotInView && (sy < 0 || sy >= SCREEN_HEIGHT + SCREEN_Y || sx < SCREEN_X || sx >= SCREEN_WIDTH + SCREEN_X))
return;
dst = &gpBuffer[sx + BUFFER_WIDTH * sy];
}
if (dst < gpBufEnd)
*dst = gbPixelCol;
}
// Exact copy from https://github.com/erich666/GraphicsGems/blob/dad26f941e12c8bf1f96ea21c1c04cd2206ae7c9/gems/DoubleLine.c
// Except:
// * not in view checks
// * global variable instead of reverse flag
// * condition for pixels_left < 0 removed
/*
Symmetric Double Step Line Algorithm
by Brian Wyvill
from "Graphics Gems", Academic Press, 1990
*/
#define GG_SWAP(A, B) \
{ \
(A) ^= (B); \
(B) ^= (A); \
(A) ^= (B); \
}
#define GG_ABSOLUTE(I, J, K) (((I) - (J)) * ((K) = (((I) - (J)) < 0 ? -1 : 1)))
void DrawLine(int x0, int y0, int x1, int y1, BYTE col)
{
int dx, dy, incr1, incr2, D, x, y, xend, c, pixels_left;
int sign_x, sign_y, step, i;
int x1_, y1_;
gbPixelCol = col;
gbNotInView = FALSE;
if (x0 < 0 + SCREEN_X || x0 >= SCREEN_WIDTH + SCREEN_X) {
gbNotInView = TRUE;
}
if (x1 < 0 + SCREEN_X || x1 >= SCREEN_WIDTH + SCREEN_X) {
gbNotInView = TRUE;
}
if (y0 < 0 + SCREEN_Y || y0 >= PANEL_Y) {
gbNotInView = TRUE;
}
if (y1 < 0 + SCREEN_Y || y1 >= PANEL_Y) {
gbNotInView = TRUE;
}
dx = GG_ABSOLUTE(x1, x0, sign_x);
dy = GG_ABSOLUTE(y1, y0, sign_y);
/* decide increment sign by the slope sign */
if (sign_x == sign_y)
step = 1;
else
step = -1;
if (dy > dx) { /* chooses axis of greatest movement (make
* dx) */
GG_SWAP(x0, y0);
GG_SWAP(x1, y1);
GG_SWAP(dx, dy);
gbRotateMap = TRUE;
} else
gbRotateMap = FALSE;
/* note error check for dx==0 should be included here */
if (x0 > x1) { /* start from the smaller coordinate */
x = x1;
y = y1;
x1_ = x0;
y1_ = y0;
} else {
x = x0;
y = y0;
x1_ = x1;
y1_ = y1;
}
/* Note dx=n implies 0 - n or (dx+1) pixels to be set */
/* Go round loop dx/4 times then plot last 0,1,2 or 3 pixels */
/* In fact (dx-1)/4 as 2 pixels are already plotted */
xend = (dx - 1) / 4;
pixels_left = (dx - 1) % 4; /* number of pixels left over at the end */
engine_draw_pixel(x, y);
engine_draw_pixel(x1_, y1_); /* plot first two points */
incr2 = 4 * dy - 2 * dx;
if (incr2 < 0) { /* slope less than 1/2 */
c = 2 * dy;
incr1 = 2 * c;
D = incr1 - dx;
for (i = 0; i < xend; i++) { /* plotting loop */
++x;
--x1_;
if (D < 0) {
/* pattern 1 forwards */
engine_draw_pixel(x, y);
engine_draw_pixel(++x, y);
/* pattern 1 backwards */
engine_draw_pixel(x1_, y1_);
engine_draw_pixel(--x1_, y1_);
D += incr1;
} else {
if (D < c) {
/* pattern 2 forwards */
engine_draw_pixel(x, y);
engine_draw_pixel(++x, y += step);
/* pattern 2 backwards */
engine_draw_pixel(x1_, y1_);
engine_draw_pixel(--x1_, y1_ -= step);
} else {
/* pattern 3 forwards */
engine_draw_pixel(x, y += step);
engine_draw_pixel(++x, y);
/* pattern 3 backwards */
engine_draw_pixel(x1_, y1_ -= step);
engine_draw_pixel(--x1_, y1_);
}
D += incr2;
}
} /* end for */
/* plot last pattern */
if (pixels_left) {
if (D < 0) {
engine_draw_pixel(++x, y); /* pattern 1 */
if (pixels_left > 1)
engine_draw_pixel(++x, y);
if (pixels_left > 2)
engine_draw_pixel(--x1_, y1_);
} else {
if (D < c) {
engine_draw_pixel(++x, y); /* pattern 2 */
if (pixels_left > 1)
engine_draw_pixel(++x, y += step);
if (pixels_left > 2)
engine_draw_pixel(--x1_, y1_);
} else {
/* pattern 3 */
engine_draw_pixel(++x, y += step);
if (pixels_left > 1)
engine_draw_pixel(++x, y);
if (pixels_left > 2)
engine_draw_pixel(--x1_, y1_ -= step);
}
}
} /* end if pixels_left */
}
/* end slope < 1/2 */
else { /* slope greater than 1/2 */
c = 2 * (dy - dx);
incr1 = 2 * c;
D = incr1 + dx;
for (i = 0; i < xend; i++) {
++x;
--x1_;
if (D > 0) {
/* pattern 4 forwards */
engine_draw_pixel(x, y += step);
engine_draw_pixel(++x, y += step);
/* pattern 4 backwards */
engine_draw_pixel(x1_, y1_ -= step);
engine_draw_pixel(--x1_, y1_ -= step);
D += incr1;
} else {
if (D < c) {
/* pattern 2 forwards */
engine_draw_pixel(x, y);
engine_draw_pixel(++x, y += step);
/* pattern 2 backwards */
engine_draw_pixel(x1_, y1_);
engine_draw_pixel(--x1_, y1_ -= step);
} else {
/* pattern 3 forwards */
engine_draw_pixel(x, y += step);
engine_draw_pixel(++x, y);
/* pattern 3 backwards */
engine_draw_pixel(x1_, y1_ -= step);
engine_draw_pixel(--x1_, y1_);
}
D += incr2;
}
} /* end for */
/* plot last pattern */
if (pixels_left) {
if (D > 0) {
engine_draw_pixel(++x, y += step); /* pattern 4 */
if (pixels_left > 1)
engine_draw_pixel(++x, y += step);
if (pixels_left > 2)
engine_draw_pixel(--x1_, y1_ -= step);
} else {
if (D < c) {
engine_draw_pixel(++x, y); /* pattern 2 */
if (pixels_left > 1)
engine_draw_pixel(++x, y += step);
if (pixels_left > 2)
engine_draw_pixel(--x1_, y1_);
} else {
/* pattern 3 */
engine_draw_pixel(++x, y += step);
if (pixels_left > 1)
engine_draw_pixel(++x, y);
if (pixels_left > 2) {
if (D > c) /* step 3 */
engine_draw_pixel(--x1_, y1_ -= step);
else /* step 2 */
engine_draw_pixel(--x1_, y1_);
}
}
}
}
}
}
int GetDirection(int x1, int y1, int x2, int y2)
{
int mx, my;
int md, ny;
mx = x2 - x1;
my = y2 - y1;
if (mx >= 0) {
if (my >= 0) {
md = DIR_S;
if (2 * mx < my)
md = DIR_SW;
} else {
my = -my;
md = DIR_E;
if (2 * mx < my)
md = DIR_NE;
}
if (2 * my < mx)
return DIR_SE;
} else {
if (my >= 0) {
ny = -mx;
md = DIR_W;
if (2 * ny < my)
md = DIR_SW;
} else {
ny = -mx;
my = -my;
md = DIR_N;
if (2 * ny < my)
md = DIR_NE;
}
if (2 * my < ny)
return DIR_NW;
}
return md;
}
void SetRndSeed(int s)
{
SeedCount = 0;
sglGameSeed = s;
orgseed = s;
}
int GetRndSeed()
{
SeedCount++;
sglGameSeed = static_cast<unsigned int>(RndMult) * sglGameSeed + RndInc;
return abs(sglGameSeed);
}
int random(BYTE idx, int v)
{
if (v <= 0)
return 0;
if (v < 0xFFFF)
return (GetRndSeed() >> 16) % v;
return GetRndSeed() % v;
}
BYTE *DiabloAllocPtr(DWORD dwBytes)
{
BYTE *buf;
sgMemCrit.Enter();
buf = (BYTE *)SMemAlloc(dwBytes, __FILE__, __LINE__, 0);
sgMemCrit.Leave();
if (buf == NULL) {
char *text = "System memory exhausted.\n"
"Make sure you have at least 64MB of free system memory before running the game";
ERR_DLG("Out of Memory Error", text);
}
return buf;
}
void mem_free_dbg(void *p)
{
if (p) {
sgMemCrit.Enter();
SMemFree(p, __FILE__, __LINE__, 0);
sgMemCrit.Leave();
}
}
BYTE *LoadFileInMem(char *pszName, DWORD *pdwFileLen)
{
HANDLE file;
BYTE *buf;
int fileLen;
WOpenFile(pszName, &file, FALSE);
fileLen = WGetFileSize(file, NULL, pszName);
if (pdwFileLen)
*pdwFileLen = fileLen;
if (!fileLen)
app_fatal("Zero length SFILE:\n%s", pszName);
buf = (BYTE *)DiabloAllocPtr(fileLen);
WReadFile(file, buf, fileLen, pszName);
WCloseFile(file);
return buf;
}
DWORD LoadFileWithMem(const char *pszName, void *p)
{
DWORD dwFileLen;
HANDLE hsFile;
/// ASSERT: assert(pszName);
if (p == NULL) {
app_fatal("LoadFileWithMem(NULL):\n%s", pszName);
}
WOpenFile(pszName, &hsFile, FALSE);
dwFileLen = WGetFileSize(hsFile, NULL, pszName);
if (dwFileLen == 0) {
app_fatal("Zero length SFILE:\n%s", pszName);
}
WReadFile(hsFile, p, dwFileLen, pszName);
WCloseFile(hsFile);
return dwFileLen;
}
/**
* @brief Apply the color swaps to a CL2 sprite
*/
void Cl2ApplyTrans(BYTE *p, BYTE *ttbl, int nCel)
{
int i, nDataSize;
char width;
BYTE *dst;
/// ASSERT: assert(p != NULL);
/// ASSERT: assert(ttbl != NULL);
for (i = 1; i <= nCel; i++) {
dst = CelGetFrame(p, i, &nDataSize) + 10;
nDataSize -= 10;
while (nDataSize) {
width = *dst++;
nDataSize--;
/// ASSERT: assert(nDataSize >= 0);
if (width < 0) {
width = -width;
if (width > 65) {
nDataSize--;
/// ASSERT: assert(nDataSize >= 0);
*dst = ttbl[*dst];
dst++;
} else {
nDataSize -= width;
/// ASSERT: assert(nDataSize >= 0);
while (width) {
*dst = ttbl[*dst];
dst++;
width--;
}
}
}
}
}
}
/**
* @param CelSkip Skip lower parts of sprite, must be multiple of 2, max 8
* @param CelCap Amount of sprite to render from lower to upper, must be multiple of 2, max 8
*/
void Cl2Draw(int sx, int sy, BYTE *pCelBuff, int nCel, int nWidth, int CelSkip, int CelCap)
{
BYTE *pRLEBytes;
int nDataSize;
/// ASSERT: assert(gpBuffer != NULL);
if (!gpBuffer)
return;
/// ASSERT: assert(pCelBuff != NULL);
if (!pCelBuff)
return;
/// ASSERT: assert(nCel > 0);
if (nCel <= 0)
return;
pRLEBytes = CelGetFrameClipped(pCelBuff, nCel, CelSkip, CelCap, &nDataSize);
if (pRLEBytes == NULL)
return;
Cl2BlitSafe(
&gpBuffer[sx + BUFFER_WIDTH * (sy - 16 * CelSkip)],
pRLEBytes,
nDataSize,
nWidth);
}
void Cl2BlitSafe(BYTE *pDecodeTo, BYTE *pRLEBytes, int nDataSize, int nWidth)
{
int w;
char width;
BYTE fill;
BYTE *src, *dst;
src = pRLEBytes;
dst = pDecodeTo;
w = nWidth;
while (nDataSize) {
width = *src++;
nDataSize--;
if (width < 0) {
width = -width;
if (width > 65) {
width -= 65;
nDataSize--;
fill = *src++;
if (dst < gpBufEnd) {
w -= width;
while (width) {
*dst = fill;
dst++;
width--;
}
if (!w) {
w = nWidth;
dst -= BUFFER_WIDTH + w;
}
continue;
}
} else {
nDataSize -= width;
if (dst < gpBufEnd) {
w -= width;
while (width) {
*dst = *src;
src++;
dst++;
width--;
}
if (!w) {
w = nWidth;
dst -= BUFFER_WIDTH + w;
}
continue;
} else {
src += width;
}
}
}
while (width) {
if (width > w) {
dst += w;
width -= w;
w = 0;
} else {
dst += width;
w -= width;
width = 0;
}
if (!w) {
w = nWidth;
dst -= BUFFER_WIDTH + w;
}
}
}
}
/**
* @param CelSkip Skip lower parts of sprite, must be multiple of 2, max 8
* @param CelCap Amount of sprite to render from lower to upper, must be multiple of 2, max 8
*/
void Cl2DrawOutline(char col, int sx, int sy, BYTE *pCelBuff, int nCel, int nWidth, int CelSkip, int CelCap)
{
int nDataSize;
BYTE *pRLEBytes;
/// ASSERT: assert(gpBuffer != NULL);
if (!gpBuffer)
return;
/// ASSERT: assert(pCelBuff != NULL);
if (!pCelBuff)
return;
/// ASSERT: assert(nCel > 0);
if (nCel <= 0)
return;
pRLEBytes = CelGetFrameClipped(pCelBuff, nCel, CelSkip, CelCap, &nDataSize);
if (pRLEBytes == NULL)
return;
gpBufEnd -= BUFFER_WIDTH;
Cl2BlitOutlineSafe(
&gpBuffer[sx + BUFFER_WIDTH * (sy - 16 * CelSkip)],
pRLEBytes,
nDataSize,
nWidth,
col);
gpBufEnd += BUFFER_WIDTH;
}
void Cl2BlitOutlineSafe(BYTE *pDecodeTo, BYTE *pRLEBytes, int nDataSize, int nWidth, char col)
{
int w;
char width;
BYTE *src, *dst;
src = pRLEBytes;
dst = pDecodeTo;
w = nWidth;
while (nDataSize) {
width = *src++;
nDataSize--;
if (width < 0) {
width = -width;
if (width > 65) {
width -= 65;
nDataSize--;
if (*src++ && dst < gpBufEnd) {
w -= width;
dst[-1] = col;
dst[width] = col;
while (width) {
dst[-BUFFER_WIDTH] = col;
dst[BUFFER_WIDTH] = col;
dst++;
width--;
}
if (!w) {
w = nWidth;
dst -= BUFFER_WIDTH + w;
}
continue;
}
} else {
nDataSize -= width;
if (dst < gpBufEnd) {
w -= width;
while (width) {
if (*src++) {
dst[-1] = col;
dst[1] = col;
dst[-BUFFER_WIDTH] = col;
dst[BUFFER_WIDTH] = col;
}
dst++;
width--;
}
if (!w) {
w = nWidth;
dst -= BUFFER_WIDTH + w;
}
continue;
} else {
src += width;
}
}
}
while (width) {
if (width > w) {
dst += w;
width -= w;
w = 0;
} else {
dst += width;
w -= width;
width = 0;
}
if (!w) {
w = nWidth;
dst -= BUFFER_WIDTH + w;
}
}
}
}
/**
* @param CelSkip Skip lower parts of sprite, must be multiple of 2, max 8
* @param CelCap Amount of sprite to render from lower to upper, must be multiple of 2, max 8
*/
void Cl2DrawLightTbl(int sx, int sy, BYTE *pCelBuff, int nCel, int nWidth, int CelSkip, int CelCap, char light)
{
int nDataSize, idx;
BYTE *pRLEBytes, *pDecodeTo;
/// ASSERT: assert(gpBuffer != NULL);
if (!gpBuffer)
return;
/// ASSERT: assert(pCelBuff != NULL);
if (!pCelBuff)
return;
/// ASSERT: assert(nCel > 0);
if (nCel <= 0)
return;
pRLEBytes = CelGetFrameClipped(pCelBuff, nCel, CelSkip, CelCap, &nDataSize);
if (pRLEBytes == NULL)
return;
pDecodeTo = &gpBuffer[sx + BUFFER_WIDTH * (sy - 16 * CelSkip)];
idx = light4flag ? 1024 : 4096;
if (light == 2)
idx += 256;
if (light >= 4)
idx += (light - 1) << 8;
Cl2BlitLightSafe(
pDecodeTo,
pRLEBytes,
nDataSize,
nWidth,
&pLightTbl[idx]);
}
void Cl2BlitLightSafe(BYTE *pDecodeTo, BYTE *pRLEBytes, int nDataSize, int nWidth, BYTE *pTable)
{
int w;
char width;
BYTE fill;
BYTE *src, *dst;
src = pRLEBytes;
dst = pDecodeTo;
w = nWidth;
sgnWidth = nWidth;
while (nDataSize) {
width = *src++;
nDataSize--;
if (width < 0) {
width = -width;
if (width > 65) {
width -= 65;
nDataSize--;
fill = pTable[*src++];
if (dst < gpBufEnd) {
w -= width;
while (width) {
*dst = fill;
dst++;
width--;
}
if (!w) {
w = sgnWidth;
dst -= BUFFER_WIDTH + w;
}
continue;
}
} else {
nDataSize -= width;
if (dst < gpBufEnd) {
w -= width;
while (width) {
*dst = pTable[*src];
src++;
dst++;
width--;
}
if (!w) {
w = sgnWidth;
dst -= BUFFER_WIDTH + w;
}
continue;
} else {
src += width;
}
}
}
while (width) {
if (width > w) {
dst += w;
width -= w;
w = 0;
} else {
dst += width;
w -= width;
width = 0;
}
if (!w) {
w = sgnWidth;
dst -= BUFFER_WIDTH + w;
}
}
}
}
/**
* @param CelSkip Skip lower parts of sprite, must be multiple of 2, max 8
* @param CelCap Amount of sprite to render from lower to upper, must be multiple of 2, max 8
*/
void Cl2DrawLight(int sx, int sy, BYTE *pCelBuff, int nCel, int nWidth, int CelSkip, int CelCap)
{
int nDataSize;
BYTE *pRLEBytes, *pDecodeTo;
/// ASSERT: assert(gpBuffer != NULL);
if (!gpBuffer)
return;
/// ASSERT: assert(pCelBuff != NULL);
if (!pCelBuff)
return;
/// ASSERT: assert(nCel > 0);
if (nCel <= 0)
return;
pRLEBytes = CelGetFrameClipped(pCelBuff, nCel, CelSkip, CelCap, &nDataSize);
if (pRLEBytes == NULL)
return;
pDecodeTo = &gpBuffer[sx + BUFFER_WIDTH * (sy - 16 * CelSkip)];
if (light_table_index)
Cl2BlitLightSafe(pDecodeTo, pRLEBytes, nDataSize, nWidth, &pLightTbl[light_table_index * 256]);
else
Cl2BlitSafe(pDecodeTo, pRLEBytes, nDataSize, nWidth);
}
void PlayInGameMovie(char *pszMovie)
{
PaletteFadeOut(8);
play_movie(pszMovie, 0);
ClearScreenBuffer();
drawpanflag = 255;
scrollrt_draw_game_screen(1);
PaletteFadeIn(8);
drawpanflag = 255;
}
DEVILUTION_END_NAMESPACE