Browse Source

🚚 Move CEL rendering to engine/render/cel_render.cpp

pull/1884/head
Gleb Mazovetskiy 5 years ago committed by Anders Jenbo
parent
commit
0edfa3de66
  1. 1
      CMakeLists.txt
  2. 3
      Source/control.cpp
  3. 1
      Source/doom.cpp
  4. 486
      Source/engine.cpp
  5. 141
      Source/engine.h
  6. 475
      Source/engine/render/cel_render.cpp
  7. 155
      Source/engine/render/cel_render.hpp
  8. 39
      Source/engine/render/common_impl.h
  9. 1
      Source/error.cpp
  10. 1
      Source/gmenu.cpp
  11. 3
      Source/interfac.cpp
  12. 1
      Source/inv.cpp
  13. 1
      Source/items.cpp
  14. 1
      Source/minitext.cpp
  15. 1
      Source/quests.cpp
  16. 1
      Source/scrollrt.cpp
  17. 1
      Source/stores.cpp

1
CMakeLists.txt

@ -351,6 +351,7 @@ set(devilutionx_SRCS
Source/controls/keymapper.cpp
Source/engine/animationinfo.cpp
Source/engine/render/automap_render.cpp
Source/engine/render/cel_render.cpp
Source/engine/render/dun_render.cpp
Source/qol/autopickup.cpp
Source/qol/common.cpp

3
Source/control.cpp

@ -10,7 +10,9 @@
#include "DiabloUI/diabloui.h"
#include "automap.h"
#include "controls/keymapper.hpp"
#include "cursor.h"
#include "engine/render/cel_render.hpp"
#include "error.h"
#include "gamemenu.h"
#include "init.h"
@ -23,7 +25,6 @@
#include "towners.h"
#include "trigs.h"
#include "utils/language.h"
#include "controls/keymapper.hpp"
namespace devilution {
namespace {

1
Source/doom.cpp

@ -7,6 +7,7 @@
#include "control.h"
#include "engine.h"
#include "engine/render/cel_render.hpp"
#include "utils/stdcompat/optional.hpp"
namespace devilution {

486
Source/engine.cpp

@ -13,14 +13,12 @@
#include <array>
#include "engine/render/common_impl.h"
#include "lighting.h"
#include "movie.h"
#include "options.h"
#include "storm/storm.h"
// TODO: temporary, remove.
#include "utils/log.hpp"
namespace devilution {
/** Seed value before the most recent call to SetRndSeed() */
@ -39,195 +37,7 @@ const uint32_t RndInc = 1;
const uint32_t RndMult = 0x015A4E35;
namespace {
constexpr bool IsCelTransparent(std::uint8_t control)
{
constexpr std::uint8_t CelTransparentMin = 0x80;
return control >= CelTransparentMin;
}
constexpr std::uint8_t GetCelTransparentWidth(std::uint8_t control)
{
return -static_cast<std::int8_t>(control);
}
constexpr std::uint8_t MaxCl2Width = 65;
uint8_t *GetLightTable(char light)
{
int idx = 4096;
if (light == 2)
idx += 256; // gray colors
if (light >= 4)
idx += (light - 1) << 8;
return &pLightTbl[idx];
}
struct ClipX {
std::int_fast16_t left;
std::int_fast16_t right;
std::int_fast16_t width;
};
ClipX CalculateClipX(std::int_fast16_t x, std::size_t w, const CelOutputBuffer &out)
{
ClipX clip;
clip.left = static_cast<std::int_fast16_t>(x < 0 ? -x : 0);
clip.right = static_cast<std::int_fast16_t>(static_cast<std::int_fast16_t>(x + w) > out.w() ? x + w - out.w() : 0);
clip.width = static_cast<std::int_fast16_t>(w - clip.left - clip.right);
return clip;
}
const byte *SkipRestOfCelLine(const byte *src, std::int_fast16_t remainingWidth)
{
while (remainingWidth > 0) {
const auto v = static_cast<std::int8_t>(*src++);
if (!IsCelTransparent(v)) {
src += v;
remainingWidth -= v;
} else {
remainingWidth += v;
}
}
return src;
}
/** Renders a CEL with only vertical clipping to the output buffer. */
template <typename RenderLine>
void RenderCelClipY(const CelOutputBuffer &out, Point position, const byte *src, std::size_t srcSize, std::size_t srcWidth, const RenderLine &renderLine)
{
const auto *srcEnd = src + srcSize;
// Skip the bottom clipped lines.
const auto dstHeight = out.h();
while (position.y >= dstHeight && src != srcEnd) {
src = SkipRestOfCelLine(src, static_cast<std::int_fast16_t>(srcWidth));
--position.y;
}
auto *dst = &out[position];
const auto *dstBegin = out.begin();
const auto dstPitch = out.pitch();
while (src != srcEnd && dst >= dstBegin) {
for (std::size_t remainingWidth = srcWidth; remainingWidth > 0;) {
auto v = static_cast<std::uint8_t>(*src++);
if (!IsCelTransparent(v)) {
renderLine(dst, reinterpret_cast<const std::uint8_t *>(src), v);
src += v;
} else {
v = GetCelTransparentWidth(v);
}
dst += v;
remainingWidth -= v;
}
dst -= dstPitch + srcWidth;
}
}
/** Renders a CEL with both horizontal and vertical clipping to the output buffer. */
template <typename RenderLine>
void RenderCelClipXY(const CelOutputBuffer &out, Point position, const byte *src, std::size_t srcSize, std::size_t srcWidth, ClipX clipX, const RenderLine &renderLine) {
const auto *srcEnd = src + srcSize;
// Skip the bottom clipped lines.
const auto dstHeight = out.h();
while (position.y >= dstHeight && src != srcEnd) {
src = SkipRestOfCelLine(src, static_cast<std::int_fast16_t>(srcWidth));
--position.y;
}
position.x += static_cast<int>(clipX.left);
auto *dst = &out[position];
const auto *dstBegin = out.begin();
const auto dstPitch = out.pitch();
while (src < srcEnd && dst >= dstBegin) {
// Skip initial src if clipping on the left.
// Handles overshoot, i.e. when the RLE segment goes into the unclipped area.
auto remainingWidth = clipX.width;
auto remainingLeftClip = clipX.left;
while (remainingLeftClip > 0) {
auto v = static_cast<std::uint8_t>(*src++);
if (!IsCelTransparent(v)) {
if (v > remainingLeftClip) {
const auto overshoot = v - remainingLeftClip;
renderLine(dst, reinterpret_cast<const std::uint8_t *>(src + remainingLeftClip), overshoot);
dst += overshoot;
remainingWidth -= overshoot;
}
src += v;
} else {
v = GetCelTransparentWidth(v);
if (v > remainingLeftClip) {
const auto overshoot = v - remainingLeftClip;
dst += overshoot;
remainingWidth -= overshoot;
}
}
remainingLeftClip -= v;
}
// Draw the non-clipped segment
while (remainingWidth > 0) {
auto v = static_cast<std::uint8_t>(*src++);
if (!IsCelTransparent(v)) {
if (v > remainingWidth) {
renderLine(dst, reinterpret_cast<const std::uint8_t *>(src), remainingWidth);
src += v;
dst += remainingWidth;
remainingWidth -= v;
break;
}
renderLine(dst, reinterpret_cast<const std::uint8_t *>(src), v);
src += v;
} else {
v = GetCelTransparentWidth(v);
if (v > remainingWidth) {
dst += remainingWidth;
remainingWidth -= v;
break;
}
}
dst += v;
remainingWidth -= v;
}
// Skip the rest of src line if clipping on the right
assert(remainingWidth <= 0);
src = SkipRestOfCelLine(src, clipX.right + remainingWidth);
dst -= dstPitch + clipX.width;
}
}
template <typename RenderLine>
void RenderCel(const CelOutputBuffer &out, Point position, const byte *src, std::size_t srcSize, std::size_t srcWidth, const RenderLine &renderLine)
{
const ClipX clipX = CalculateClipX(position.x, srcWidth, out);
if (clipX.width <= 0)
return;
if (static_cast<std::size_t>(clipX.width) == srcWidth) {
RenderCelClipY(out, position, src, srcSize, srcWidth, renderLine);
} else {
RenderCelClipXY(out, position, src, srcSize, srcWidth, clipX, renderLine);
}
}
void RenderCelWithLightTable(const CelOutputBuffer &out, Point position, const byte *src, std::size_t srcSize, std::size_t srcWidth, const std::uint8_t *tbl)
{
RenderCel(out, position, src, srcSize, srcWidth, [tbl](std::uint8_t *dst, const std::uint8_t *src, std::size_t w) {
while (w-- > 0) {
*dst++ = tbl[static_cast<std::uint8_t>(*src)];
++src;
}
});
}
constexpr auto RenderLineMemcpy = [](std::uint8_t *dst, const std::uint8_t *src, std::size_t w) {
std::memcpy(dst, src, w);
};
} // namespace
CelSprite LoadCel(const char *pszName, int width)
@ -240,300 +50,6 @@ CelSprite LoadCel(const char *pszName, const int *widths)
return CelSprite(LoadFileInMem(pszName), widths);
}
std::pair<int, int> MeasureSolidHorizontalBounds(const CelSprite &cel, int frame)
{
int nDataSize;
auto src = reinterpret_cast<const uint8_t *>(CelGetFrame(cel.Data(), frame, &nDataSize));
auto end = &src[nDataSize];
const int celWidth = cel.Width(frame);
int xBegin = celWidth;
int xEnd = 0;
int transparentRun = 0;
int xCur = 0;
bool firstTransparentRun = true;
while (src < end) {
std::int_fast16_t remainingWidth = celWidth;
while (remainingWidth > 0) {
const auto val = static_cast<std::uint8_t>(*src++);
if (IsCelTransparent(val)) {
const int width = GetCelTransparentWidth(val);
transparentRun += width;
xCur += width;
remainingWidth -= width;
if (remainingWidth == 0) {
xEnd = std::max(xEnd, celWidth - transparentRun);
xCur = 0;
firstTransparentRun = true;
transparentRun = 0;
}
} else {
if (firstTransparentRun) {
xBegin = std::min(xBegin, transparentRun);
firstTransparentRun = false;
if (xBegin == 0 && xEnd == celWidth)
break;
}
transparentRun = 0;
xCur += val;
src += val;
remainingWidth -= val;
if (remainingWidth == 0) {
xEnd = celWidth;
if (xBegin == 0)
break;
xCur = 0;
firstTransparentRun = true;
}
}
}
}
return { xBegin, xEnd };
}
void CelDrawTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame)
{
int nDataSize;
const auto *pRLEBytes = CelGetFrame(cel.Data(), frame, &nDataSize);
CelBlitSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame));
}
void CelClippedDrawTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame)
{
int nDataSize;
const auto *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize);
CelBlitSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame));
}
void CelDrawLightTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame, uint8_t *tbl)
{
int nDataSize;
const auto *pRLEBytes = CelGetFrame(cel.Data(), frame, &nDataSize);
if (light_table_index != 0 || tbl != nullptr)
CelBlitLightSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame), tbl);
else
CelBlitSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame));
}
void CelClippedDrawLightTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame)
{
int nDataSize;
const auto *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize);
if (light_table_index != 0)
CelBlitLightSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame), nullptr);
else
CelBlitSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame));
}
void CelDrawLightRedTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame, char light)
{
int nDataSize;
const auto *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize);
const std::uint8_t *tbl = GetLightTable(light);
RenderCelWithLightTable(out, { sx, sy }, pRLEBytes, nDataSize, cel.Width(frame), tbl);
}
void CelBlitSafeTo(const CelOutputBuffer &out, int sx, int sy, const byte *pRLEBytes, int nDataSize, int nWidth)
{
assert(pRLEBytes != nullptr);
RenderCel(out, { sx, sy }, pRLEBytes, nDataSize, nWidth, RenderLineMemcpy);
}
void CelClippedDrawSafeTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame)
{
int nDataSize;
const auto *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize);
CelBlitSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame));
}
void CelBlitLightSafeTo(const CelOutputBuffer &out, int sx, int sy, const byte *pRLEBytes, int nDataSize, int nWidth, uint8_t *tbl)
{
assert(pRLEBytes != nullptr);
if (tbl == nullptr)
tbl = &pLightTbl[light_table_index * 256];
RenderCelWithLightTable(out, { sx, sy }, pRLEBytes, nDataSize, nWidth, tbl);
}
void CelBlitLightTransSafeTo(const CelOutputBuffer &out, int sx, int sy, const byte *pRLEBytes, int nDataSize, int nWidth)
{
assert(pRLEBytes != nullptr);
const auto *src = pRLEBytes;
BYTE *dst = out.at(sx, sy);
const uint8_t *tbl = &pLightTbl[light_table_index * 256];
bool shift = (reinterpret_cast<uintptr_t>(dst) % 2) != 0;
for (; src != &pRLEBytes[nDataSize]; dst -= out.pitch() + nWidth, shift = !shift) {
for (int w = nWidth; w > 0;) {
auto width = static_cast<std::uint8_t>(*src++);
if (!IsCelTransparent(width)) {
w -= width;
if (dst < out.end() && dst > out.begin()) {
if (((size_t)dst % 2) == shift) {
if ((width & 1) == 0) {
goto L_ODD;
} else {
src++;
dst++;
L_EVEN:
width /= 2;
if ((width & 1) != 0) {
dst[0] = tbl[static_cast<std::uint8_t>(src[0])];
src += 2;
dst += 2;
}
width /= 2;
for (; width > 0; width--) {
dst[0] = tbl[static_cast<std::uint8_t>(src[0])];
dst[2] = tbl[static_cast<std::uint8_t>(src[2])];
src += 4;
dst += 4;
}
}
} else {
if ((width & 1) == 0) {
goto L_EVEN;
} else {
dst[0] = tbl[static_cast<std::uint8_t>(src[0])];
src++;
dst++;
L_ODD:
width /= 2;
if ((width & 1) != 0) {
dst[1] = tbl[static_cast<std::uint8_t>(src[1])];
src += 2;
dst += 2;
}
width /= 2;
for (; width > 0; width--) {
dst[1] = tbl[static_cast<std::uint8_t>(src[1])];
dst[3] = tbl[static_cast<std::uint8_t>(src[3])];
src += 4;
dst += 4;
}
}
}
} else {
src += width;
dst += width;
}
} else {
width = -static_cast<std::int8_t>(width);
dst += width;
w -= width;
}
}
}
}
/**
* @brief Same as CelBlitLightSafe, with blended transparancy applied
* @param out 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
*/
static void CelBlitLightBlendedSafeTo(const CelOutputBuffer &out, int sx, int sy, const byte *pRLEBytes, int nDataSize, int nWidth, uint8_t *tbl)
{
assert(pRLEBytes != nullptr);
if (tbl == nullptr)
tbl = &pLightTbl[light_table_index * 256];
RenderCel(out, { sx, sy }, pRLEBytes, nDataSize, nWidth, [tbl](std::uint8_t *dst, const uint8_t *src, std::size_t w) {
while (w-- > 0) {
*dst = paletteTransparencyLookup[*dst][tbl[*src++]];
++dst;
}
});
}
void CelClippedBlitLightTransTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame)
{
int nDataSize;
const byte *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize);
if (cel_transparency_active) {
if (sgOptions.Graphics.bBlendedTransparancy)
CelBlitLightBlendedSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame), nullptr);
else
CelBlitLightTransSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame));
} else if (light_table_index != 0)
CelBlitLightSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame), nullptr);
else
CelBlitSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame));
}
void CelDrawLightRedSafeTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame, char light)
{
int nDataSize;
const auto *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize);
RenderCelWithLightTable(out, { sx, sy }, pRLEBytes, nDataSize, cel.Width(frame), GetLightTable(light));
}
void CelDrawUnsafeTo(const CelOutputBuffer &out, int x, int y, const CelSprite &cel, int frame)
{
int nDataSize;
const auto *pRLEBytes = CelGetFrame(cel.Data(), frame, &nDataSize);
RenderCelClipY(out, { x, y }, pRLEBytes, nDataSize, cel.Width(frame), RenderLineMemcpy);
}
void CelBlitOutlineTo(const CelOutputBuffer &out, uint8_t col, int sx, int sy, const CelSprite &cel, int frame, bool skipColorIndexZero)
{
int nDataSize;
const byte *src = CelGetFrameClipped(cel.Data(), frame, &nDataSize);
const auto *end = &src[nDataSize];
uint8_t *dst = out.at(sx, sy);
const int celWidth = static_cast<int>(cel.Width(frame));
for (; src != end; dst -= out.pitch() + celWidth) {
for (int w = celWidth; w > 0;) {
auto width = static_cast<std::uint8_t>(*src++);
if (!IsCelTransparent(width)) {
w -= width;
if (dst < out.end() && dst > out.begin()) {
if (dst >= out.end() - out.pitch()) {
while (width > 0) {
if (!skipColorIndexZero || static_cast<std::uint8_t>(*src) > 0) {
dst[-out.pitch()] = col;
dst[-1] = col;
dst[1] = col;
}
src++;
dst++;
width--;
}
} else {
while (width > 0) {
if (!skipColorIndexZero || static_cast<std::uint8_t>(*src) > 0) {
dst[-out.pitch()] = col;
dst[-1] = col;
dst[1] = col;
dst[out.pitch()] = col;
}
src++;
dst++;
width--;
}
}
} else {
src += width;
dst += width;
}
} else {
width = GetCelTransparentWidth(width);
dst += width;
w -= width;
}
}
}
}
void DrawHorizontalLine(const CelOutputBuffer &out, Point from, int width, std::uint8_t colorIndex)
{
if (from.y < 0 || from.y >= out.h() || from.x >= out.w() || width <= 0 || from.x + width <= 0)

141
Source/engine.h

@ -396,147 +396,6 @@ private:
CelSprite LoadCel(const char *pszName, int width);
CelSprite LoadCel(const char *pszName, const int *widths);
/**
* Returns a pair of X coordinates containing the start (inclusive) and end (exclusive)
* of fully transparent columns in the sprite.
*/
std::pair<int, int> MeasureSolidHorizontalBounds(const CelSprite &cel, int frame = 1);
/**
* @brief Blit CEL sprite to the back buffer at the given coordinates
* @param out Target buffer
* @param sx Target buffer coordinate
* @param sy Target buffer coordinate
* @param cel CEL sprite
* @param frame CEL frame number
*/
void CelDrawTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame);
/**
* @briefBlit CEL sprite to the given buffer, does not perform bounds-checking.
* @param out Target buffer
* @param x Cordinate in the target buffer
* @param y Cordinate in the target buffer
* @param cel CEL sprite
* @param frame CEL frame number
*/
void CelDrawUnsafeTo(const CelOutputBuffer &out, int x, int y, const CelSprite &cel, int frame);
/**
* @brief Same as CelDrawTo but with the option to skip parts of the top and bottom of the sprite
* @param out Target buffer
* @param sx Target buffer coordinate
* @param sy Target buffer coordinate
* @param cel CEL sprite
* @param frame CEL frame number
*/
void CelClippedDrawTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame);
/**
* @brief Blit CEL sprite, and apply lighting, to the back buffer at the given coordinates
* @param out Target buffer
* @param sx Target buffer coordinate
* @param sy Target buffer coordinate
* @param cel CEL sprite
* @param frame CEL frame number
*/
void CelDrawLightTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame, uint8_t *tbl);
/**
* @brief Same as CelDrawLightTo but with the option to skip parts of the top and bottom of the sprite
* @param out Target buffer
* @param sx Target buffer coordinate
* @param sy Target buffer coordinate
* @param cel CEL sprite
* @param frame CEL frame number
*/
void CelClippedDrawLightTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame);
/**
* @brief Same as CelBlitLightTransSafeTo
* @param out Target buffer
* @param sx Target buffer coordinate
* @param sy Target buffer coordinate
* @param cel CEL sprite
* @param frame CEL frame number
*/
void CelClippedBlitLightTransTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame);
/**
* @brief Blit CEL sprite, and apply lighting, to the back buffer at the given coordinates, translated to a red hue
* @param out Target buffer
* @param sx Target buffer coordinate
* @param sy Target buffer coordinate
* @param cel CEL sprite
* @param frame CEL frame number
* @param light Light shade to use
*/
void CelDrawLightRedTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame, char light);
/**
* @brief Blit CEL sprite to the given buffer, checks for drawing outside the buffer.
* @param out Target buffer
* @param sx Target buffer coordinate
* @param sy Target buffer coordinate
* @param pRLEBytes CEL pixel stream (run-length encoded)
* @param nDataSize Size of CEL in bytes
*/
void CelBlitSafeTo(const CelOutputBuffer &out, int sx, int sy, const byte *pRLEBytes, int nDataSize, int nWidth);
/**
* @brief Same as CelClippedDrawTo but checks for drawing outside the buffer
* @param out Target buffer
* @param sx Target buffer coordinate
* @param sy Target buffer coordinate
* @param cel CEL sprite
* @param frame CEL frame number
*/
void CelClippedDrawSafeTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame);
/**
* @brief Blit CEL sprite, and apply lighting, to the given buffer, checks for drawing outside the buffer
* @param out Target buffer
* @param sx Target buffer coordinate
* @param sy Target buffer coordinate
* @param pRLEBytes CEL pixel stream (run-length encoded)
* @param nDataSize Size of CEL in bytes
* @param tbl Palette translation table
*/
void CelBlitLightSafeTo(const CelOutputBuffer &out, int sx, int sy, const byte *pRLEBytes, int nDataSize, int nWidth, uint8_t *tbl);
/**
* @brief Same as CelBlitLightSafeTo but with stippled transparancy applied
* @param out Target buffer
* @param sx Target buffer coordinate
* @param sy Target buffer coordinate
* @param pRLEBytes CEL pixel stream (run-length encoded)
* @param nDataSize Size of CEL in bytes
*/
void CelBlitLightTransSafeTo(const CelOutputBuffer &out, int sx, int sy, const byte *pRLEBytes, int nDataSize, int nWidth);
/**
* @brief Same as CelDrawLightRedTo but checks for drawing outside the buffer
* @param out Target buffer
* @param sx Target buffer coordinate
* @param sy Target buffer coordinate
* @param cel CEL sprite
* @param frame CEL frame number
* @param light Light shade to use
*/
void CelDrawLightRedSafeTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame, char light);
/**
* @brief Blit a solid colder shape one pixel larger then the given sprite shape, to the target buffer at the given coordianates
* @param out Target buffer
* @param col Color index from current palette
* @param sx Target buffer coordinate
* @param sy Target buffer coordinate
* @param pCelBuff CEL buffer
* @param frame CEL frame number
* @param skipColorIndexZero If true, color in index 0 will be treated as transparent (these are typically used for shadows in sprites)
*/
void CelBlitOutlineTo(const CelOutputBuffer &out, uint8_t col, int sx, int sy, const CelSprite &cel, int frame, bool skipColorIndexZero = true);
/**
* @brief Set the value of a single pixel in the back buffer, checks bounds
* @param out Target buffer

475
Source/engine/render/cel_render.cpp

@ -0,0 +1,475 @@
#include "engine/render/cel_render.hpp"
#include <cstddef>
#include <cstdint>
#include <cstring>
#include "engine/render/common_impl.h"
#include "options.h"
#include "palette.h"
#include "scrollrt.h"
namespace devilution {
namespace {
constexpr bool IsCelTransparent(std::uint8_t control)
{
constexpr std::uint8_t CelTransparentMin = 0x80;
return control >= CelTransparentMin;
}
constexpr std::uint8_t GetCelTransparentWidth(std::uint8_t control)
{
return -static_cast<std::int8_t>(control);
}
const byte *SkipRestOfCelLine(const byte *src, std::int_fast16_t remainingWidth)
{
while (remainingWidth > 0) {
const auto v = static_cast<std::int8_t>(*src++);
if (!IsCelTransparent(v)) {
src += v;
remainingWidth -= v;
} else {
remainingWidth += v;
}
}
return src;
}
/** Renders a CEL with only vertical clipping to the output buffer. */
template <typename RenderLine>
void RenderCelClipY(const CelOutputBuffer &out, Point position, const byte *src, std::size_t srcSize, std::size_t srcWidth, const RenderLine &renderLine)
{
const auto *srcEnd = src + srcSize;
// Skip the bottom clipped lines.
const auto dstHeight = out.h();
while (position.y >= dstHeight && src != srcEnd) {
src = SkipRestOfCelLine(src, static_cast<std::int_fast16_t>(srcWidth));
--position.y;
}
auto *dst = &out[position];
const auto *dstBegin = out.begin();
const auto dstPitch = out.pitch();
while (src != srcEnd && dst >= dstBegin) {
for (std::size_t remainingWidth = srcWidth; remainingWidth > 0;) {
auto v = static_cast<std::uint8_t>(*src++);
if (!IsCelTransparent(v)) {
renderLine(dst, reinterpret_cast<const std::uint8_t *>(src), v);
src += v;
} else {
v = GetCelTransparentWidth(v);
}
dst += v;
remainingWidth -= v;
}
dst -= dstPitch + srcWidth;
}
}
/** Renders a CEL with both horizontal and vertical clipping to the output buffer. */
template <typename RenderLine>
void RenderCelClipXY(const CelOutputBuffer &out, Point position, const byte *src, std::size_t srcSize, std::size_t srcWidth, ClipX clipX, const RenderLine &renderLine)
{
const auto *srcEnd = src + srcSize;
// Skip the bottom clipped lines.
const auto dstHeight = out.h();
while (position.y >= dstHeight && src != srcEnd) {
src = SkipRestOfCelLine(src, static_cast<std::int_fast16_t>(srcWidth));
--position.y;
}
position.x += static_cast<int>(clipX.left);
auto *dst = &out[position];
const auto *dstBegin = out.begin();
const auto dstPitch = out.pitch();
while (src < srcEnd && dst >= dstBegin) {
// Skip initial src if clipping on the left.
// Handles overshoot, i.e. when the RLE segment goes into the unclipped area.
auto remainingWidth = clipX.width;
auto remainingLeftClip = clipX.left;
while (remainingLeftClip > 0) {
auto v = static_cast<std::uint8_t>(*src++);
if (!IsCelTransparent(v)) {
if (v > remainingLeftClip) {
const auto overshoot = v - remainingLeftClip;
renderLine(dst, reinterpret_cast<const std::uint8_t *>(src + remainingLeftClip), overshoot);
dst += overshoot;
remainingWidth -= overshoot;
}
src += v;
} else {
v = GetCelTransparentWidth(v);
if (v > remainingLeftClip) {
const auto overshoot = v - remainingLeftClip;
dst += overshoot;
remainingWidth -= overshoot;
}
}
remainingLeftClip -= v;
}
// Draw the non-clipped segment
while (remainingWidth > 0) {
auto v = static_cast<std::uint8_t>(*src++);
if (!IsCelTransparent(v)) {
if (v > remainingWidth) {
renderLine(dst, reinterpret_cast<const std::uint8_t *>(src), remainingWidth);
src += v;
dst += remainingWidth;
remainingWidth -= v;
break;
}
renderLine(dst, reinterpret_cast<const std::uint8_t *>(src), v);
src += v;
} else {
v = GetCelTransparentWidth(v);
if (v > remainingWidth) {
dst += remainingWidth;
remainingWidth -= v;
break;
}
}
dst += v;
remainingWidth -= v;
}
// Skip the rest of src line if clipping on the right
assert(remainingWidth <= 0);
src = SkipRestOfCelLine(src, clipX.right + remainingWidth);
dst -= dstPitch + clipX.width;
}
}
template <typename RenderLine>
void RenderCel(const CelOutputBuffer &out, Point position, const byte *src, std::size_t srcSize, std::size_t srcWidth, const RenderLine &renderLine)
{
const ClipX clipX = CalculateClipX(position.x, srcWidth, out);
if (clipX.width <= 0)
return;
if (static_cast<std::size_t>(clipX.width) == srcWidth) {
RenderCelClipY(out, position, src, srcSize, srcWidth, renderLine);
} else {
RenderCelClipXY(out, position, src, srcSize, srcWidth, clipX, renderLine);
}
}
void RenderCelWithLightTable(const CelOutputBuffer &out, Point position, const byte *src, std::size_t srcSize, std::size_t srcWidth, const std::uint8_t *tbl)
{
RenderCel(out, position, src, srcSize, srcWidth, [tbl](std::uint8_t *dst, const std::uint8_t *src, std::size_t w) {
while (w-- > 0) {
*dst++ = tbl[static_cast<std::uint8_t>(*src)];
++src;
}
});
}
constexpr auto RenderLineMemcpy = [](std::uint8_t *dst, const std::uint8_t *src, std::size_t w) {
std::memcpy(dst, src, w);
};
} // namespace
void CelDrawTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame)
{
int nDataSize;
const auto *pRLEBytes = CelGetFrame(cel.Data(), frame, &nDataSize);
CelBlitSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame));
}
void CelClippedDrawTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame)
{
int nDataSize;
const auto *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize);
CelBlitSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame));
}
void CelDrawLightTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame, uint8_t *tbl)
{
int nDataSize;
const auto *pRLEBytes = CelGetFrame(cel.Data(), frame, &nDataSize);
if (light_table_index != 0 || tbl != nullptr)
CelBlitLightSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame), tbl);
else
CelBlitSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame));
}
void CelClippedDrawLightTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame)
{
int nDataSize;
const auto *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize);
if (light_table_index != 0)
CelBlitLightSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame), nullptr);
else
CelBlitSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame));
}
void CelDrawLightRedTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame, char light)
{
int nDataSize;
const auto *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize);
const std::uint8_t *tbl = GetLightTable(light);
RenderCelWithLightTable(out, { sx, sy }, pRLEBytes, nDataSize, cel.Width(frame), tbl);
}
void CelBlitSafeTo(const CelOutputBuffer &out, int sx, int sy, const byte *pRLEBytes, int nDataSize, int nWidth)
{
assert(pRLEBytes != nullptr);
RenderCel(out, { sx, sy }, pRLEBytes, nDataSize, nWidth, RenderLineMemcpy);
}
void CelClippedDrawSafeTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame)
{
int nDataSize;
const auto *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize);
CelBlitSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame));
}
void CelBlitLightSafeTo(const CelOutputBuffer &out, int sx, int sy, const byte *pRLEBytes, int nDataSize, int nWidth, uint8_t *tbl)
{
assert(pRLEBytes != nullptr);
if (tbl == nullptr)
tbl = &pLightTbl[light_table_index * 256];
RenderCelWithLightTable(out, { sx, sy }, pRLEBytes, nDataSize, nWidth, tbl);
}
void CelBlitLightTransSafeTo(const CelOutputBuffer &out, int sx, int sy, const byte *pRLEBytes, int nDataSize, int nWidth)
{
assert(pRLEBytes != nullptr);
const auto *src = pRLEBytes;
BYTE *dst = out.at(sx, sy);
const uint8_t *tbl = &pLightTbl[light_table_index * 256];
bool shift = (reinterpret_cast<uintptr_t>(dst) % 2) != 0;
for (; src != &pRLEBytes[nDataSize]; dst -= out.pitch() + nWidth, shift = !shift) {
for (int w = nWidth; w > 0;) {
auto width = static_cast<std::uint8_t>(*src++);
if (!IsCelTransparent(width)) {
w -= width;
if (dst < out.end() && dst > out.begin()) {
if (((size_t)dst % 2) == shift) {
if ((width & 1) == 0) {
goto L_ODD;
} else {
src++;
dst++;
L_EVEN:
width /= 2;
if ((width & 1) != 0) {
dst[0] = tbl[static_cast<std::uint8_t>(src[0])];
src += 2;
dst += 2;
}
width /= 2;
for (; width > 0; width--) {
dst[0] = tbl[static_cast<std::uint8_t>(src[0])];
dst[2] = tbl[static_cast<std::uint8_t>(src[2])];
src += 4;
dst += 4;
}
}
} else {
if ((width & 1) == 0) {
goto L_EVEN;
} else {
dst[0] = tbl[static_cast<std::uint8_t>(src[0])];
src++;
dst++;
L_ODD:
width /= 2;
if ((width & 1) != 0) {
dst[1] = tbl[static_cast<std::uint8_t>(src[1])];
src += 2;
dst += 2;
}
width /= 2;
for (; width > 0; width--) {
dst[1] = tbl[static_cast<std::uint8_t>(src[1])];
dst[3] = tbl[static_cast<std::uint8_t>(src[3])];
src += 4;
dst += 4;
}
}
}
} else {
src += width;
dst += width;
}
} else {
width = -static_cast<std::int8_t>(width);
dst += width;
w -= width;
}
}
}
}
/**
* @brief Same as CelBlitLightSafe, with blended transparancy applied
* @param out 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
*/
static void CelBlitLightBlendedSafeTo(const CelOutputBuffer &out, int sx, int sy, const byte *pRLEBytes, int nDataSize, int nWidth, uint8_t *tbl)
{
assert(pRLEBytes != nullptr);
if (tbl == nullptr)
tbl = &pLightTbl[light_table_index * 256];
RenderCel(out, { sx, sy }, pRLEBytes, nDataSize, nWidth, [tbl](std::uint8_t *dst, const uint8_t *src, std::size_t w) {
while (w-- > 0) {
*dst = paletteTransparencyLookup[*dst][tbl[*src++]];
++dst;
}
});
}
void CelClippedBlitLightTransTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame)
{
int nDataSize;
const byte *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize);
if (cel_transparency_active) {
if (sgOptions.Graphics.bBlendedTransparancy)
CelBlitLightBlendedSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame), nullptr);
else
CelBlitLightTransSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame));
} else if (light_table_index != 0)
CelBlitLightSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame), nullptr);
else
CelBlitSafeTo(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame));
}
void CelDrawLightRedSafeTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame, char light)
{
int nDataSize;
const auto *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize);
RenderCelWithLightTable(out, { sx, sy }, pRLEBytes, nDataSize, cel.Width(frame), GetLightTable(light));
}
void CelDrawUnsafeTo(const CelOutputBuffer &out, int x, int y, const CelSprite &cel, int frame)
{
int nDataSize;
const auto *pRLEBytes = CelGetFrame(cel.Data(), frame, &nDataSize);
RenderCelClipY(out, { x, y }, pRLEBytes, nDataSize, cel.Width(frame), RenderLineMemcpy);
}
void CelBlitOutlineTo(const CelOutputBuffer &out, uint8_t col, int sx, int sy, const CelSprite &cel, int frame, bool skipColorIndexZero)
{
int nDataSize;
const byte *src = CelGetFrameClipped(cel.Data(), frame, &nDataSize);
const auto *end = &src[nDataSize];
uint8_t *dst = out.at(sx, sy);
const int celWidth = static_cast<int>(cel.Width(frame));
for (; src != end; dst -= out.pitch() + celWidth) {
for (int w = celWidth; w > 0;) {
auto width = static_cast<std::uint8_t>(*src++);
if (!IsCelTransparent(width)) {
w -= width;
if (dst < out.end() && dst > out.begin()) {
if (dst >= out.end() - out.pitch()) {
while (width > 0) {
if (!skipColorIndexZero || static_cast<std::uint8_t>(*src) > 0) {
dst[-out.pitch()] = col;
dst[-1] = col;
dst[1] = col;
}
src++;
dst++;
width--;
}
} else {
while (width > 0) {
if (!skipColorIndexZero || static_cast<std::uint8_t>(*src) > 0) {
dst[-out.pitch()] = col;
dst[-1] = col;
dst[1] = col;
dst[out.pitch()] = col;
}
src++;
dst++;
width--;
}
}
} else {
src += width;
dst += width;
}
} else {
width = GetCelTransparentWidth(width);
dst += width;
w -= width;
}
}
}
}
std::pair<int, int> MeasureSolidHorizontalBounds(const CelSprite &cel, int frame)
{
int nDataSize;
auto src = reinterpret_cast<const uint8_t *>(CelGetFrame(cel.Data(), frame, &nDataSize));
auto end = &src[nDataSize];
const int celWidth = cel.Width(frame);
int xBegin = celWidth;
int xEnd = 0;
int transparentRun = 0;
int xCur = 0;
bool firstTransparentRun = true;
while (src < end) {
std::int_fast16_t remainingWidth = celWidth;
while (remainingWidth > 0) {
const auto val = static_cast<std::uint8_t>(*src++);
if (IsCelTransparent(val)) {
const int width = GetCelTransparentWidth(val);
transparentRun += width;
xCur += width;
remainingWidth -= width;
if (remainingWidth == 0) {
xEnd = std::max(xEnd, celWidth - transparentRun);
xCur = 0;
firstTransparentRun = true;
transparentRun = 0;
}
} else {
if (firstTransparentRun) {
xBegin = std::min(xBegin, transparentRun);
firstTransparentRun = false;
if (xBegin == 0 && xEnd == celWidth) {
return { xBegin, xEnd };
}
}
transparentRun = 0;
xCur += val;
src += val;
remainingWidth -= val;
if (remainingWidth == 0) {
xEnd = celWidth;
if (xBegin == 0) {
return { xBegin, xEnd };
}
xCur = 0;
firstTransparentRun = true;
}
}
}
}
return { xBegin, xEnd };
}
} // namespace devilution

155
Source/engine/render/cel_render.hpp

@ -0,0 +1,155 @@
/**
* @file cel_render.hpp
*
* CEL rendering.
*/
#pragma once
#include <utility>
#include "engine.h"
namespace devilution {
/**
* Returns a pair of X coordinates containing the start (inclusive) and end (exclusive)
* of fully transparent columns in the sprite.
*/
std::pair<int, int> MeasureSolidHorizontalBounds(const CelSprite &cel, int frame = 1);
/**
* @brief Blit CEL sprite to the back buffer at the given coordinates
* @param out Target buffer
* @param sx Target buffer coordinate
* @param sy Target buffer coordinate
* @param cel CEL sprite
* @param frame CEL frame number
*/
void CelDrawTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame);
/**
* @briefBlit CEL sprite to the given buffer, does not perform bounds-checking.
* @param out Target buffer
* @param x Cordinate in the target buffer
* @param y Cordinate in the target buffer
* @param cel CEL sprite
* @param frame CEL frame number
*/
void CelDrawUnsafeTo(const CelOutputBuffer &out, int x, int y, const CelSprite &cel, int frame);
/**
* @brief Same as CelDrawTo but with the option to skip parts of the top and bottom of the sprite
* @param out Target buffer
* @param sx Target buffer coordinate
* @param sy Target buffer coordinate
* @param cel CEL sprite
* @param frame CEL frame number
*/
void CelClippedDrawTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame);
/**
* @brief Blit CEL sprite, and apply lighting, to the back buffer at the given coordinates
* @param out Target buffer
* @param sx Target buffer coordinate
* @param sy Target buffer coordinate
* @param cel CEL sprite
* @param frame CEL frame number
*/
void CelDrawLightTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame, uint8_t *tbl);
/**
* @brief Same as CelDrawLightTo but with the option to skip parts of the top and bottom of the sprite
* @param out Target buffer
* @param sx Target buffer coordinate
* @param sy Target buffer coordinate
* @param cel CEL sprite
* @param frame CEL frame number
*/
void CelClippedDrawLightTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame);
/**
* @brief Same as CelBlitLightTransSafeTo
* @param out Target buffer
* @param sx Target buffer coordinate
* @param sy Target buffer coordinate
* @param cel CEL sprite
* @param frame CEL frame number
*/
void CelClippedBlitLightTransTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame);
/**
* @brief Blit CEL sprite, and apply lighting, to the back buffer at the given coordinates, translated to a red hue
* @param out Target buffer
* @param sx Target buffer coordinate
* @param sy Target buffer coordinate
* @param cel CEL sprite
* @param frame CEL frame number
* @param light Light shade to use
*/
void CelDrawLightRedTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame, char light);
/**
* @brief Blit CEL sprite to the given buffer, checks for drawing outside the buffer.
* @param out Target buffer
* @param sx Target buffer coordinate
* @param sy Target buffer coordinate
* @param pRLEBytes CEL pixel stream (run-length encoded)
* @param nDataSize Size of CEL in bytes
*/
void CelBlitSafeTo(const CelOutputBuffer &out, int sx, int sy, const byte *pRLEBytes, int nDataSize, int nWidth);
/**
* @brief Same as CelClippedDrawTo but checks for drawing outside the buffer
* @param out Target buffer
* @param sx Target buffer coordinate
* @param sy Target buffer coordinate
* @param cel CEL sprite
* @param frame CEL frame number
*/
void CelClippedDrawSafeTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame);
/**
* @brief Blit CEL sprite, and apply lighting, to the given buffer, checks for drawing outside the buffer
* @param out Target buffer
* @param sx Target buffer coordinate
* @param sy Target buffer coordinate
* @param pRLEBytes CEL pixel stream (run-length encoded)
* @param nDataSize Size of CEL in bytes
* @param tbl Palette translation table
*/
void CelBlitLightSafeTo(const CelOutputBuffer &out, int sx, int sy, const byte *pRLEBytes, int nDataSize, int nWidth, uint8_t *tbl);
/**
* @brief Same as CelBlitLightSafeTo but with stippled transparancy applied
* @param out Target buffer
* @param sx Target buffer coordinate
* @param sy Target buffer coordinate
* @param pRLEBytes CEL pixel stream (run-length encoded)
* @param nDataSize Size of CEL in bytes
*/
void CelBlitLightTransSafeTo(const CelOutputBuffer &out, int sx, int sy, const byte *pRLEBytes, int nDataSize, int nWidth);
/**
* @brief Same as CelDrawLightRedTo but checks for drawing outside the buffer
* @param out Target buffer
* @param sx Target buffer coordinate
* @param sy Target buffer coordinate
* @param cel CEL sprite
* @param frame CEL frame number
* @param light Light shade to use
*/
void CelDrawLightRedSafeTo(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame, char light);
/**
* @brief Blit a solid colder shape one pixel larger then the given sprite shape, to the target buffer at the given coordianates
* @param out Target buffer
* @param col Color index from current palette
* @param sx Target buffer coordinate
* @param sy Target buffer coordinate
* @param pCelBuff CEL buffer
* @param frame CEL frame number
* @param skipColorIndexZero If true, color in index 0 will be treated as transparent (these are typically used for shadows in sprites)
*/
void CelBlitOutlineTo(const CelOutputBuffer &out, uint8_t col, int sx, int sy, const CelSprite &cel, int frame, bool skipColorIndexZero = true);
} // namespace devilution

39
Source/engine/render/common_impl.h

@ -0,0 +1,39 @@
/**
* Common code for implementing various renderers.
*/
#pragma once
#include <cstdint>
#include <cstddef>
#include "engine.h"
#include "lighting.h"
namespace devilution {
inline std::uint8_t *GetLightTable(char light)
{
int idx = 4096;
if (light == 2)
idx += 256; // gray colors
if (light >= 4)
idx += (light - 1) << 8;
return &pLightTbl[idx];
}
struct ClipX {
std::int_fast16_t left;
std::int_fast16_t right;
std::int_fast16_t width;
};
inline ClipX CalculateClipX(std::int_fast16_t x, std::size_t w, const CelOutputBuffer &out)
{
ClipX clip;
clip.left = static_cast<std::int_fast16_t>(x < 0 ? -x : 0);
clip.right = static_cast<std::int_fast16_t>(static_cast<std::int_fast16_t>(x + w) > out.w() ? x + w - out.w() : 0);
clip.width = static_cast<std::int_fast16_t>(w - clip.left - clip.right);
return clip;
}
} // namespace devilution

1
Source/error.cpp

@ -6,6 +6,7 @@
#include "error.h"
#include "control.h"
#include "engine/render/cel_render.hpp"
#include "stores.h"
#include "utils/language.h"

1
Source/gmenu.cpp

@ -9,6 +9,7 @@
#include "controls/axis_direction.h"
#include "controls/controller_motion.h"
#include "engine.h"
#include "engine/render/cel_render.hpp"
#include "stores.h"
#include "utils/language.h"
#include "utils/stdcompat/optional.hpp"

3
Source/interfac.cpp

@ -6,10 +6,11 @@
#include <cstdint>
#include "control.h"
#include "DiabloUI/art_draw.h"
#include "control.h"
#include "dx.h"
#include "engine.h"
#include "engine/render/cel_render.hpp"
#include "init.h"
#include "loadsave.h"
#include "palette.h"

1
Source/inv.cpp

@ -6,6 +6,7 @@
#include <utility>
#include "cursor.h"
#include "engine/render/cel_render.hpp"
#include "minitext.h"
#include "options.h"
#include "plrmsg.h"

1
Source/items.cpp

@ -10,6 +10,7 @@
#include "cursor.h"
#include "doom.h"
#include "dx.h"
#include "engine/render/cel_render.hpp"
#include "init.h"
#include "lighting.h"
#include "missiles.h"

1
Source/minitext.cpp

@ -7,6 +7,7 @@
#include "control.h"
#include "dx.h"
#include "engine.h"
#include "engine/render/cel_render.hpp"
#include "utils/language.h"
#include "utils/stdcompat/optional.hpp"

1
Source/quests.cpp

@ -6,6 +6,7 @@
#include "control.h"
#include "cursor.h"
#include "engine/render/cel_render.hpp"
#include "gendung.h"
#include "init.h"
#include "minitext.h"

1
Source/scrollrt.cpp

@ -10,6 +10,7 @@
#include "dead.h"
#include "doom.h"
#include "dx.h"
#include "engine/render/cel_render.hpp"
#include "engine/render/dun_render.hpp"
#include "error.h"
#include "gmenu.h"

1
Source/stores.cpp

@ -8,6 +8,7 @@
#include <algorithm>
#include "cursor.h"
#include "engine/render/cel_render.hpp"
#include "init.h"
#include "minitext.h"
#include "options.h"

Loading…
Cancel
Save