/** * @file engine.cpp * * Implementation of basic engine helper functions: * - Sprite blitting * - Drawing * - Angle calculation * - RNG * - Memory allocation * - File loading * - Video playback */ #include "all.h" #include "../3rdParty/Storm/Source/storm.h" DEVILUTION_BEGIN_NAMESPACE /** automap pixel color 8-bit (palette entry) */ char gbPixelCol; /** flip - if y < x */ BOOL gbRotateMap; /** Seed value before the most recent call to SetRndSeed() */ int orgseed; /** Width of sprite being blitted */ int sgnWidth; /** Current game seed */ int sglGameSeed; static CCritSect sgMemCrit; /** Number of times the current seed has been fetched */ int SeedCount; /** valid - if x/y are in bounds */ BOOL gbNotInView; /** * Specifies the increment used in the Borland C/C++ pseudo-random. */ const int RndInc = 1; /** * Specifies the multiplier used in the Borland C/C++ pseudo-random number generator algorithm. */ const int RndMult = 0x015A4E35; /** * @brief Blit CEL sprite to the back buffer at the given coordinates * @param sx Back buffer coordinate * @param sy Back buffer coordinate * @param pCelBuff Cel data * @param nCel CEL frame number * @param nWidth Width of sprite */ void CelDraw(int sx, int sy, BYTE *pCelBuff, int nCel, int nWidth) { CelBlitFrame(&gpBuffer[sx + BUFFER_WIDTH * sy], pCelBuff, nCel, nWidth); } /** * @brief Blit a given CEL frame to the given buffer * @param pBuff Target buffer * @param pCelBuff Cel data * @param nCel CEL frame number * @param nWidth Width of sprite */ void CelBlitFrame(BYTE *pBuff, BYTE *pCelBuff, int nCel, int nWidth) { int nDataSize; BYTE *pRLEBytes; assert(pCelBuff != NULL); assert(pBuff != NULL); pRLEBytes = CelGetFrame(pCelBuff, nCel, &nDataSize); CelBlitSafe(pBuff, pRLEBytes, nDataSize, nWidth); } /** * @brief Same as CelDraw but with the option to skip parts of the top and bottom of the sprite * @param sx Back buffer coordinate * @param sy Back buffer coordinate * @param pCelBuff Cel data * @param nCel CEL frame number * @param nWidth Width of sprite */ void CelClippedDraw(int sx, int sy, BYTE *pCelBuff, int nCel, int nWidth) { BYTE *pRLEBytes; int nDataSize; assert(gpBuffer); assert(pCelBuff != NULL); pRLEBytes = CelGetFrameClipped(pCelBuff, nCel, &nDataSize); CelBlitSafe( &gpBuffer[sx + BUFFER_WIDTH * sy], pRLEBytes, nDataSize, nWidth); } /** * @brief Blit CEL sprite, and apply lighting, to the back buffer at the given coordinates * @param sx Back buffer coordinate * @param sy Back buffer coordinate * @param pCelBuff Cel data * @param nCel CEL frame number * @param nWidth Width of sprite */ void CelDrawLight(int sx, int sy, BYTE *pCelBuff, int nCel, int nWidth, BYTE *tbl) { int nDataSize; BYTE *pDecodeTo, *pRLEBytes; assert(gpBuffer); assert(pCelBuff != NULL); 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); } /** * @brief Same as CelDrawLight but with the option to skip parts of the top and bottom of the sprite * @param sx Back buffer coordinate * @param sy Back buffer coordinate * @param pCelBuff Cel data * @param nCel CEL frame number * @param nWidth Width of sprite */ void CelClippedDrawLight(int sx, int sy, BYTE *pCelBuff, int nCel, int nWidth) { int nDataSize; BYTE *pRLEBytes, *pDecodeTo; assert(gpBuffer); assert(pCelBuff != NULL); pRLEBytes = CelGetFrameClipped(pCelBuff, nCel, &nDataSize); pDecodeTo = &gpBuffer[sx + BUFFER_WIDTH * sy]; if (light_table_index) CelBlitLightSafe(pDecodeTo, pRLEBytes, nDataSize, nWidth, NULL); else CelBlitSafe(pDecodeTo, pRLEBytes, nDataSize, nWidth); } /** * @brief Blit CEL sprite, and apply lighting, to the back buffer at the given coordinates, translated to a red hue * @param sx Back buffer coordinate * @param sy Back buffer coordinate * @param pCelBuff Cel data * @param nCel CEL frame number * @param nWidth Width of sprite * @param light Light shade to use */ void CelDrawLightRed(int sx, int sy, BYTE *pCelBuff, int nCel, int nWidth, char light) { int nDataSize, w, idx; BYTE *pRLEBytes, *dst, *tbl; assert(gpBuffer); assert(pCelBuff != NULL); pRLEBytes = CelGetFrameClipped(pCelBuff, nCel, &nDataSize); dst = &gpBuffer[sx + BUFFER_WIDTH * sy]; idx = light4flag ? 1024 : 4096; if (light == 2) idx += 256; // gray colors 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 Blit CEL sprite to the given buffer, checks for drawing outside the buffer * @param pDecodeTo The output buffer * @param pRLEBytes CEL pixel stream (run-length encoded) * @param nDataSize Size of CEL in bytes * @param nWidth Width of sprite */ void CelBlitSafe(BYTE *pDecodeTo, BYTE *pRLEBytes, int nDataSize, int nWidth) { int i, w; BYTE width; BYTE *src, *dst; assert(pDecodeTo != NULL); assert(pRLEBytes != NULL); assert(gpBuffer); 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 && dst > gpBufStart) { memcpy(dst, src, width); } src += width; dst += width; } else { width = -(char)width; dst += width; i -= width; } } } } /** * @brief Same as CelClippedDraw but checks for drawing outside the buffer * @param sx Back buffer coordinate * @param sy Back buffer coordinate * @param pCelBuff Cel data * @param nCel CEL frame number * @param nWidth Width of sprite */ void CelClippedDrawSafe(int sx, int sy, BYTE *pCelBuff, int nCel, int nWidth) { BYTE *pRLEBytes; int nDataSize; assert(gpBuffer); assert(pCelBuff != NULL); pRLEBytes = CelGetFrameClipped(pCelBuff, nCel, &nDataSize); CelBlitSafe( &gpBuffer[sx + BUFFER_WIDTH * sy], pRLEBytes, nDataSize, nWidth); } /** * @brief Blit CEL sprite, and apply lighting, to the given buffer, checks for drawing outside the buffer * @param pDecodeTo The output buffer * @param pRLEBytes CEL pixel stream (run-length encoded) * @param nDataSize Size of CEL in bytes * @param nWidth Width of sprite * @param tbl Palette translation table */ void CelBlitLightSafe(BYTE *pDecodeTo, BYTE *pRLEBytes, int nDataSize, int nWidth, BYTE *tbl) { int i, w; BYTE width; BYTE *src, *dst; assert(pDecodeTo != NULL); assert(pRLEBytes != NULL); assert(gpBuffer); 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 && dst > gpBufStart) { 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 CelBlitLightSafe, with transparancy applied * @param pDecodeTo The output buffer * @param pRLEBytes CEL pixel stream (run-length encoded) * @param nDataSize Size of CEL in bytes * @param nWidth Width of sprite */ void CelBlitLightTransSafe(BYTE *pDecodeTo, BYTE *pRLEBytes, int nDataSize, int nWidth) { int w; BOOL shift; BYTE *tbl; assert(pDecodeTo != NULL); assert(pRLEBytes != NULL); assert(gpBuffer); 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 && dst > gpBufStart) { 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; } } } } /** * @brief Same as CelBlitLightTransSafe * @param pBuff Target buffer * @param pCelBuff Cel data * @param nCel CEL frame number * @param nWidth Width of sprite */ void CelClippedBlitLightTrans(BYTE *pBuff, BYTE *pCelBuff, int nCel, int nWidth) { int nDataSize; BYTE *pRLEBytes; assert(pCelBuff != NULL); pRLEBytes = CelGetFrameClipped(pCelBuff, nCel, &nDataSize); if (cel_transparency_active) CelBlitLightTransSafe(pBuff, pRLEBytes, nDataSize, nWidth); else if (light_table_index) CelBlitLightSafe(pBuff, pRLEBytes, nDataSize, nWidth, NULL); else CelBlitSafe(pBuff, pRLEBytes, nDataSize, nWidth); } /** * @brief Same as CelDrawLightRed but checks for drawing outside the buffer * @param sx Back buffer coordinate * @param sy Back buffer coordinate * @param pCelBuff Cel data * @param nCel CEL frame number * @param nWidth Width of cel * @param light Light shade to use */ void CelDrawLightRedSafe(int sx, int sy, BYTE *pCelBuff, int nCel, int nWidth, char light) { int nDataSize, w, idx; BYTE *pRLEBytes, *dst, *tbl; assert(gpBuffer); assert(pCelBuff != NULL); pRLEBytes = CelGetFrameClipped(pCelBuff, nCel, &nDataSize); dst = &gpBuffer[sx + BUFFER_WIDTH * sy]; idx = light4flag ? 1024 : 4096; if (light == 2) idx += 256; // gray colors 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 && dst > gpBufStart) { while (width) { *dst = tbl[*pRLEBytes]; pRLEBytes++; dst++; width--; } } else { pRLEBytes += width; dst += width; } } else { width = -(char)width; dst += width; w -= width; } } } } /** * @brief Blit to a buffer at given coordinates * @param pBuff Target buffer * @param x Cordinate in pBuff buffer * @param y Cordinate in pBuff buffer * @param wdt Width of pBuff * @param pCelBuff Cel data * @param nCel CEL frame number * @param nWidth Width of cel */ void CelBlitWidth(BYTE *pBuff, int x, int y, int wdt, BYTE *pCelBuff, int nCel, int nWidth) { BYTE *pRLEBytes, *dst, *end; assert(pCelBuff != NULL); assert(pBuff != NULL); int i, nDataSize; BYTE width; pRLEBytes = CelGetFrame(pCelBuff, nCel, &nDataSize); end = &pRLEBytes[nDataSize]; dst = &pBuff[y * wdt + x]; for (; pRLEBytes != end; dst -= wdt + nWidth) { for (i = nWidth; i;) { width = *pRLEBytes++; if (!(width & 0x80)) { i -= width; memcpy(dst, pRLEBytes, width); dst += width; pRLEBytes += width; } else { width = -(char)width; dst += width; i -= width; } } } } /** * @brief Blit a solid colder shape one pixel larger then the given sprite shape, to the back buffer at the given coordianates * @param col Color index from current palette * @param sx Back buffer coordinate * @param sy Back buffer coordinate * @param pCelBuff CEL buffer * @param nCel CEL frame number * @param nWidth Width of sprite */ void CelBlitOutline(char col, int sx, int sy, BYTE *pCelBuff, int nCel, int nWidth) { int nDataSize, w; BYTE *src, *dst, *end; BYTE width; assert(pCelBuff != NULL); assert(gpBuffer); src = CelGetFrameClipped(pCelBuff, nCel, &nDataSize); end = &src[nDataSize]; dst = &gpBuffer[sx + BUFFER_WIDTH * sy]; for (; src != end; dst -= BUFFER_WIDTH + nWidth) { for (w = nWidth; w;) { width = *src++; if (!(width & 0x80)) { w -= width; if (dst < gpBufEnd && dst > gpBufStart) { 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; } } } } /** * @brief Set the value of a single pixel in the back buffer, checks bounds * @param sx Back buffer coordinate * @param sy Back buffer coordinate * @param col Color index from current palette */ void ENG_set_pixel(int sx, int sy, BYTE col) { BYTE *dst; 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 > gpBufStart) *dst = col; } /** * @brief Set the value of a single pixel in the back buffer to that of gbPixelCol, checks bounds * @param sx Back buffer coordinate * @param sy Back buffer coordinate */ void engine_draw_pixel(int sx, int sy) { BYTE *dst; 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 > gpBufStart) *dst = gbPixelCol; } /** * @brief Draw a line on the back buffer * @param x0 Back buffer coordinate * @param y0 Back buffer coordinate * @param x1 Back buffer coordinate * @param y1 Back buffer coordinate * @param col Color index from current palette */ void DrawLine(int x0, int y0, int x1, int y1, BYTE col) { int i, dx, dy, steps; float ix, iy, sx, sy; dx = x1 - x0; dy = y1 - y0; steps = abs(dx) > abs(dy) ? abs(dx) : abs(dy); ix = dx / (float)steps; iy = dy / (float)steps; sx = x0; sy = y0; for (i = 0; i <= steps; i++, sx += ix, sy += iy) { ENG_set_pixel(sx, sy, col); } } /** * @brief Calculate the best fit direction between two points * @param x1 Tile coordinate * @param y1 Tile coordinate * @param x2 Tile coordinate * @param y2 Tile coordinate * @return A value from the direction enum */ 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; } /** * @brief Set the RNG seed * @param s RNG seed */ void SetRndSeed(int s) { SeedCount = 0; sglGameSeed = s; orgseed = s; } /** * @brief Advance the internal RNG seed and return the new value * @return RNG seed */ int AdvanceRndSeed() { SeedCount++; sglGameSeed = static_cast(RndMult) * sglGameSeed + RndInc; return abs(sglGameSeed); } /** * @brief Get the current RNG seed * @return RNG seed */ int GetRndSeed() { return abs(sglGameSeed); } /** * @brief Main RNG function * @param idx Unused * @param v The upper limit for the return value * @return A random number from 0 to (v-1) */ int random_(BYTE idx, int v) { if (v <= 0) return 0; if (v < 0xFFFF) return (AdvanceRndSeed() >> 16) % v; return AdvanceRndSeed() % v; } /** * @brief Multithreaded safe malloc * @param dwBytes Byte size to allocate */ 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; } /** * @brief Multithreaded safe memfree * @param p Memory pointer to free */ void mem_free_dbg(void *p) { if (p) { sgMemCrit.Enter(); SMemFree(p, __FILE__, __LINE__, 0); sgMemCrit.Leave(); } } /** * @brief Load a file in to a buffer * @param pszName Path of file * @param pdwFileLen Will be set to file size if non-NULL * @return Buffer with content of file */ BYTE *LoadFileInMem(const char *pszName, DWORD *pdwFileLen) { HANDLE file; BYTE *buf; int fileLen; SFileOpenFile(pszName, &file); fileLen = SFileGetFileSize(file, NULL); if (pdwFileLen) *pdwFileLen = fileLen; if (!fileLen) app_fatal("Zero length SFILE:\n%s", pszName); buf = (BYTE *)DiabloAllocPtr(fileLen); SFileReadFile(file, buf, fileLen, NULL, NULL); SFileCloseFile(file); return buf; } /** * @brief Load a file in to the given buffer * @param pszName Path of file * @param p Target buffer * @return Size of file */ DWORD LoadFileWithMem(const char *pszName, BYTE *p) { DWORD dwFileLen; HANDLE hsFile; assert(pszName); if (p == NULL) { app_fatal("LoadFileWithMem(NULL):\n%s", pszName); } SFileOpenFile(pszName, &hsFile); dwFileLen = SFileGetFileSize(hsFile, NULL); if (dwFileLen == 0) { app_fatal("Zero length SFILE:\n%s", pszName); } SFileReadFile(hsFile, p, dwFileLen, NULL, NULL); SFileCloseFile(hsFile); return dwFileLen; } /** * @brief Apply the color swaps to a CL2 sprite * @param p CL2 buffer * @param ttbl Palette translation table * @param nCel Frame number in CL2 file */ void Cl2ApplyTrans(BYTE *p, BYTE *ttbl, int nCel) { int i, nDataSize; char width; BYTE *dst; assert(p != NULL); assert(ttbl != NULL); for (i = 1; i <= nCel; i++) { dst = CelGetFrame(p, i, &nDataSize) + 10; nDataSize -= 10; while (nDataSize) { width = *dst++; nDataSize--; assert(nDataSize >= 0); if (width < 0) { width = -width; if (width > 65) { nDataSize--; assert(nDataSize >= 0); *dst = ttbl[*dst]; dst++; } else { nDataSize -= width; assert(nDataSize >= 0); while (width--) { *dst = ttbl[*dst]; dst++; } } } } } } /** * @brief Blit CL2 sprite to the given buffer * @param pDecodeTo The output buffer * @param pRLEBytes CL2 pixel stream (run-length encoded) * @param nDataSize Size of CL2 in bytes * @param nWidth Width of sprite */ static 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 && dst > gpBufStart) { w -= width; while (width) { *dst = fill; dst++; width--; } if (!w) { w = nWidth; dst -= BUFFER_WIDTH + w; } continue; } } else { nDataSize -= width; if (dst < gpBufEnd && dst > gpBufStart) { 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; } } } } /** * @brief Blit a solid colder shape one pixel larger then the given sprite shape, to the given buffer * @param pDecodeTo The output buffer * @param pRLEBytes CL2 pixel stream (run-length encoded) * @param nDataSize Size of CL2 in bytes * @param nWidth Width of sprite * @param col Color index from current palette */ static 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 && dst > gpBufStart) { 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 && dst > gpBufStart) { w -= width; while (width) { if (*src++) { dst[-1] = col; dst[1] = col; dst[-BUFFER_WIDTH] = col; // BUGFIX: only set `if (dst+BUFFER_WIDTH < gpBufEnd)` 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; } } } } /** * @brief Blit CL2 sprite, and apply lighting, to the given buffer * @param pDecodeTo The output buffer * @param pRLEBytes CL2 pixel stream (run-length encoded) * @param nDataSize Size of CL2 in bytes * @param nWidth With of CL2 sprite * @param pTable Light color table */ static 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 && dst > gpBufStart) { w -= width; while (width) { *dst = fill; dst++; width--; } if (!w) { w = sgnWidth; dst -= BUFFER_WIDTH + w; } continue; } } else { nDataSize -= width; if (dst < gpBufEnd && dst > gpBufStart) { 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; } } } } /** * @brief Blit CL2 sprite, to the back buffer at the given coordianates * @param sx Back buffer coordinate * @param sy Back buffer coordinate * @param pCelBuff CL2 buffer * @param nCel CL2 frame number * @param nWidth Width of sprite */ void Cl2Draw(int sx, int sy, BYTE *pCelBuff, int nCel, int nWidth) { BYTE *pRLEBytes; int nDataSize; assert(gpBuffer != NULL); assert(pCelBuff != NULL); assert(nCel > 0); pRLEBytes = CelGetFrameClipped(pCelBuff, nCel, &nDataSize); Cl2BlitSafe( &gpBuffer[sx + BUFFER_WIDTH * sy], pRLEBytes, nDataSize, nWidth); } /** * @brief Blit a solid colder shape one pixel larger then the given sprite shape, to the back buffer at the given coordianates * @param col Color index from current palette * @param sx Back buffer coordinate * @param sy Back buffer coordinate * @param pCelBuff CL2 buffer * @param nCel CL2 frame number * @param nWidth Width of sprite */ void Cl2DrawOutline(char col, int sx, int sy, BYTE *pCelBuff, int nCel, int nWidth) { int nDataSize; BYTE *pRLEBytes; assert(gpBuffer != NULL); assert(pCelBuff != NULL); assert(nCel > 0); pRLEBytes = CelGetFrameClipped(pCelBuff, nCel, &nDataSize); gpBufEnd -= BUFFER_WIDTH; Cl2BlitOutlineSafe( &gpBuffer[sx + BUFFER_WIDTH * sy], pRLEBytes, nDataSize, nWidth, col); gpBufEnd += BUFFER_WIDTH; } /** * @brief Blit CL2 sprite, and apply a given lighting, to the back buffer at the given coordianates * @param sx Back buffer coordinate * @param sy Back buffer coordinate * @param pCelBuff CL2 buffer * @param nCel CL2 frame number * @param nWidth Width of sprite * @param light Light shade to use */ void Cl2DrawLightTbl(int sx, int sy, BYTE *pCelBuff, int nCel, int nWidth, char light) { int nDataSize, idx; BYTE *pRLEBytes, *pDecodeTo; assert(gpBuffer != NULL); assert(pCelBuff != NULL); assert(nCel > 0); pRLEBytes = CelGetFrameClipped(pCelBuff, nCel, &nDataSize); pDecodeTo = &gpBuffer[sx + BUFFER_WIDTH * sy]; idx = light4flag ? 1024 : 4096; if (light == 2) idx += 256; // gray colors if (light >= 4) idx += (light - 1) << 8; Cl2BlitLightSafe( pDecodeTo, pRLEBytes, nDataSize, nWidth, &pLightTbl[idx]); } /** * @brief Blit CL2 sprite, and apply lighting, to the back buffer at the given coordinates * @param sx Back buffer coordinate * @param sy Back buffer coordinate * @param pCelBuff CL2 buffer * @param nCel CL2 frame number * @param nWidth Width of sprite */ void Cl2DrawLight(int sx, int sy, BYTE *pCelBuff, int nCel, int nWidth) { int nDataSize; BYTE *pRLEBytes, *pDecodeTo; assert(gpBuffer != NULL); assert(pCelBuff != NULL); assert(nCel > 0); pRLEBytes = CelGetFrameClipped(pCelBuff, nCel, &nDataSize); pDecodeTo = &gpBuffer[sx + BUFFER_WIDTH * sy]; if (light_table_index) Cl2BlitLightSafe(pDecodeTo, pRLEBytes, nDataSize, nWidth, &pLightTbl[light_table_index * 256]); else Cl2BlitSafe(pDecodeTo, pRLEBytes, nDataSize, nWidth); } /** * @brief Fade to black and play a video * @param pszMovie file path of movie */ void PlayInGameMovie(const char *pszMovie) { PaletteFadeOut(8); play_movie(pszMovie, FALSE); ClearScreenBuffer(); force_redraw = 255; scrollrt_draw_game_screen(TRUE); PaletteFadeIn(8); force_redraw = 255; } DEVILUTION_END_NAMESPACE