[Scummvm-git-logs] scummvm master -> 1dd711a2b9bf3bb6c88b55ae54945354ec3ae1fc
athrxx
noreply at scummvm.org
Wed Sep 30 18:14:33 UTC 2026
This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
1dd711a2b9 KYRA: (EOB automap feature) - improve drawing + cleanup
Commit: 1dd711a2b9bf3bb6c88b55ae54945354ec3ae1fc
https://github.com/scummvm/scummvm/commit/1dd711a2b9bf3bb6c88b55ae54945354ec3ae1fc
Author: athrxx (athrxx at scummvm.org)
Date: 2026-09-30T20:00:05+02:00
Commit Message:
KYRA: (EOB automap feature) - improve drawing + cleanup
- Improved scaling for different resolutions (tested on low res
SDL Surface modes and also with OpenGL on a UHD display)
- Use pre-generated and also reshaped icons
- Other major cleanup
- The map is still not suitable for320x200 overlays, but that's
really low priority and would require a lot of special case fine
tuning
Changed paths:
engines/kyra/engine/eobcommon.cpp
engines/kyra/engine/eobcommon.h
engines/kyra/gui/automap_eob.cpp
engines/kyra/gui/automap_eob.h
diff --git a/engines/kyra/engine/eobcommon.cpp b/engines/kyra/engine/eobcommon.cpp
index 6f5c142b4b0..60b0926988f 100644
--- a/engines/kyra/engine/eobcommon.cpp
+++ b/engines/kyra/engine/eobcommon.cpp
@@ -579,7 +579,7 @@ Common::Error EoBCoreEngine::init() {
// Always create this, regardless of whether the launcher option is enabled or not. Otherwise the map would
// be incomplete if the option is enabled later on in the game.
- _automap = new Automap_EoB(_system, &_levelBlockProperties, _wllWallFlags, _specialWallTypes, _wllShapeMap, _flags.gameID, _flags.lang, _configAutomap);
+ _automap = new Automap_EoB(_system, &_levelBlockProperties, _wllWallFlags, _specialWallTypes, _wllShapeMap, /* _wllVmpMap, */_flags.gameID, _flags.lang, _configAutomap);
assert(_automap);
return Common::kNoError;
diff --git a/engines/kyra/engine/eobcommon.h b/engines/kyra/engine/eobcommon.h
index 2c2095ce8c8..9db14cc9170 100644
--- a/engines/kyra/engine/eobcommon.h
+++ b/engines/kyra/engine/eobcommon.h
@@ -269,7 +269,6 @@ friend class EoBInfProcessor;
friend class DarkmoonSequenceHelper;
friend class CharacterGenerator;
friend class TransferPartyWiz;
-friend class Automap_EoB; // TODO: REMOVE
public:
EoBCoreEngine(OSystem *system, const GameFlags &flags);
~EoBCoreEngine() override;
diff --git a/engines/kyra/gui/automap_eob.cpp b/engines/kyra/gui/automap_eob.cpp
index 74468496e9a..b8802b85816 100644
--- a/engines/kyra/gui/automap_eob.cpp
+++ b/engines/kyra/gui/automap_eob.cpp
@@ -30,126 +30,6 @@
namespace Kyra {
-static void automapDrawBigString(Graphics::Surface &dst, const Graphics::Font *font,
- const Common::String &text, int x, int y, int w, uint32 color, int scale,
- Graphics::TextAlign align = Graphics::kTextAlignCenter) {
- if (!font || text.empty() || scale < 1)
- return;
- const int tw = font->getStringWidth(text);
- const int th = font->getFontHeight();
- if (tw <= 0 || th <= 0)
- return;
-
- if (scale == 1) {
- font->drawString(&dst, text, x, y, w, color, align);
- return;
- }
-
- const int bpp = dst.format.bytesPerPixel;
- if (bpp != 2 && bpp != 4) {
- font->drawString(&dst, text, x, y, w, color, align);
- return;
- }
-
- Graphics::Surface tmp;
- tmp.create(tw, th, dst.format);
- tmp.fillRect(Common::Rect(0, 0, tw, th), 0);
- font->drawString(&tmp, text, 0, 0, tw, color, Graphics::kTextAlignLeft);
-
- const int dw = tw * scale;
- int dx = x + (w - dw) / 2;
- if (align == Graphics::kTextAlignLeft)
- dx = x;
- else if (align == Graphics::kTextAlignRight)
- dx = x + w - dw;
- if (dx < x)
- dx = x;
- for (int sy = 0; sy < th; ++sy) {
- const byte *src = (const byte *)tmp.getBasePtr(0, sy);
- for (int sx = 0; sx < tw; ++sx) {
- const uint32 c = (bpp == 2) ? ((const uint16 *)src)[sx] : ((const uint32 *)src)[sx];
- if (c == 0)
- continue;
- const int px = dx + sx * scale;
- const int py = y + sy * scale;
- dst.fillRect(Common::Rect(px, py, px + scale, py + scale), c);
- }
- }
- tmp.free();
-}
-
-static void automapFillTri(Graphics::Surface &s, int ax, int ay, int bx, int by, int cx, int cy, uint32 color) {
- if (ay > by) {
- SWAP(ax, bx);
- SWAP(ay, by);
- }
- if (ay > cy) {
- SWAP(ax, cx);
- SWAP(ay, cy);
- }
- if (by > cy) {
- SWAP(bx, cx);
- SWAP(by, cy);
- }
- if (cy == ay)
- return;
-
- for (int y = ay; y <= cy; ++y) {
- int xLong = ax + (cx - ax) * (y - ay) / (cy - ay);
- int xShort = (y < by)
- ? (by == ay ? ax : ax + (bx - ax) * (y - ay) / (by - ay))
- : (cy == by ? bx : bx + (cx - bx) * (y - by) / (cy - by));
- int x0 = MIN(xLong, xShort);
- int x1 = MAX(xLong, xShort);
- s.hLine(x0, y, x1, color);
- }
-}
-
-// Sparse two-tone speckle to fake stone/paper grain
-static void automapNoise(Graphics::Surface &s, int x0, int y0, int w, int h, uint32 cA, uint32 cB, uint32 seed) {
- if (w <= 0 || h <= 0 || x0 < 0 || y0 < 0 || x0 + w > s.w || y0 + h > s.h)
- return;
- const int bpp = s.format.bytesPerPixel;
- if (bpp != 2 && bpp != 4)
- return;
- uint32 st = seed | 1u;
- for (int y = 0; y < h; ++y) {
- byte *row = (byte *)s.getBasePtr(x0, y0 + y);
- for (int x = 0; x < w; ++x) {
- st = st * 1664525u + 1013904223u;
- const uint32 v = st >> 24; // 0..255
- if (v < 26) {
- const uint32 c = (v & 1u) ? cA : cB;
- if (bpp == 2)
- ((uint16 *)row)[x] = (uint16)c;
- else
- ((uint32 *)row)[x] = c;
- }
- }
- }
-}
-
-static void automapBevel(Graphics::Surface &s, const Common::Rect &r, uint32 hi, uint32 lo) {
- s.hLine(r.left, r.top, r.right - 1, hi);
- s.vLine(r.left, r.top, r.bottom - 1, hi);
- s.hLine(r.left, r.bottom - 1, r.right - 1, lo);
- s.vLine(r.right - 1, r.top, r.bottom - 1, lo);
-}
-
-static void automapRivet(Graphics::Surface &s, int x, int y, int sz, uint32 dark, uint32 mid, uint32 hi) {
- s.fillRect(Common::Rect(x - sz, y - sz, x + sz + 1, y + sz + 1), dark);
- s.fillRect(Common::Rect(x - sz + 1, y - sz + 1, x + sz, y + sz), mid);
- const int q = MAX(1, sz / 2);
- s.fillRect(Common::Rect(x - sz + 1, y - sz + 1, x - sz + 1 + q, y - sz + 1 + q), hi);
-}
-
-static int automapFit(const Graphics::Font *f, const Common::String &str, int maxW, int maxSc) {
- int sc = MAX(1, maxSc);
- while (sc > 1 && f && f->getStringWidth(str) * sc > maxW)
- --sc;
- return sc;
-}
-
// Automap strings that should be translated to the game's language. Currently, only English is available.
// TODO: Probably move to KYRA.DAT eventually.
const Automap_EoB::TranslateableStrings Automap_EoB::_stringTable[] = {
@@ -241,11 +121,13 @@ const Automap_EoB::TranslateableStrings Automap_EoB::_stringTable[] = {
}
};
-Automap_EoB::Automap_EoB(OSystem *system, LevelBlockProperty **blockData, const uint8 *wllFlags, const uint8 *specialWallTypes, const int8 *wllShapeMap, int gameID, int lang, bool featureEnabled) : _system(system),
- _blockData(*blockData), _wllWallFlags(wllFlags), _specialWallTypes(specialWallTypes), _wllShapeMap(wllShapeMap), _enabled(featureEnabled), _visible(false), _automapBg(nullptr), _automapFrame(nullptr), _specialBlockIDs(nullptr), _levelNames(nullptr),
- _colors(nullptr), _legendStrings(nullptr), _controlStrings(nullptr), _numLevelNames(gameID == GI_EOB1 ? 12 : (gameID == GI_EOB2 ? 16 : 0)), _wallOfForceID(gameID == GI_EOB1 ? 0xFF : 74), _portalParamsLen(0), _portalParams(nullptr){
- _automapBg = new Graphics::Surface();
- _automapFrame = new Graphics::Surface();
+Automap_EoB::Automap_EoB(OSystem *system, LevelBlockProperty **blockData, const uint8 *wllFlags, const uint8 *specialWallTypes, const int8 *wllShapeMap, /* const uint8 *wllVmpMap,*/ int gameID, int lang, bool featureEnabled) :
+ _system(system), _blockData(*blockData), _wllWallFlags(wllFlags), _specialWallTypes(specialWallTypes), _wllShapeMap(wllShapeMap), /*_wllVmpMap(wllVmpMap),*/ _enabled(featureEnabled), _visible(false), _background(nullptr),
+ _frame(nullptr), _specialBlockIDs(nullptr), _levelNames(nullptr), _colors(nullptr), _legendStrings(nullptr), _controlStrings(nullptr), _numLevelNames(gameID == GI_EOB1 ? 12 : (gameID == GI_EOB2 ? 16 : 0)), _l(),
+ _wallOfForceID(gameID == GI_EOB1 ? 0xFF : 74), _portalParamsLen(0), _portalParams(nullptr), _drawIcon(nullptr), _legendHeadScl(1), _legendBodyScl(1), _levelStrScl(1), _coordStrScl(1), _levelStrY(0), _coordStrY(0),
+ _partyIconColor(kColorPartyFrame0), _partyIconColorStep(1) {
+ _background = new Graphics::Surface();
+ _frame = new Graphics::Surface();
uint langIndex = 0;
uint gameIndex = gameID - GI_EOB1;
@@ -268,30 +150,57 @@ Automap_EoB::Automap_EoB(OSystem *system, LevelBlockProperty **blockData, const
const uint8 illusion1 = gameID == GI_EOB1 ? 67 : 46;
const uint8 illusion2 = gameID == GI_EOB1 ? 64 : 46;
const uint8 illusion3 = gameID == GI_EOB1 ? 66 : 46;
+ const uint8 illusion4 = gameID == GI_EOB1 ? 57 : 46;
const uint8 plate1 = gameID == GI_EOB1 ? 28 : 35;
const uint8 plate2 = gameID == GI_EOB1 ? 28 : 36;
const uint8 pit = gameID == GI_EOB1 ? 27 : 38;
const uint8 stairsUp = 23;
const uint8 stairsDown = 24;
- const uint8 types[] = { teleporter, illusion1, illusion2, illusion3, stairsUp, stairsDown, pit, plate1, plate2 };
- static const uint8 eob1PortalParams[] = { 2, 4, 46, 5, 43, 6, 45, 7, 40, 7, 41, 7, 43, 7, 44, 7, 46, 8, 45, 9, 43, 10, 39, 11, 37, 11, 36, 12, 37 };
+
+ const SpecialWallType types[] = {
+ { teleporter, kIconTeleporter, 0x40 },
+ { stairsUp, kIconStairsUp, 0x10 },
+ { stairsDown, kIconStairsDown, 0x20 },
+ { pit, kIconPit, 0x80 },
+ { plate1, kIconPlate, 0x100 },
+ { plate2, kIconPlate, 0x100 },
+ { illusion1, kIconIllusionWall, 0x800 },
+ { illusion2, kIconIllusionWall, 0x800 },
+ { illusion3, kIconIllusionWall, 0x800 },
+ { illusion4, kIconIllusionWall, 0x800 }
+ };
+
+ static const uint8 eob1PortalParams[] = { 2, 4, 46, 5, 43, 6, 45, 7, 40, 7, 41, 7, 43, 7, 44, 7, 46, 8, 45, 9, 43, 10, 38, 10, 39, 11, 37, 11, 36, 12, 37 };
static const uint8 eob2PortalParams[] = { 7, 3, 54, 6, 54, 14, 69 };
- uint8 *specialBlockIDs = new uint8[ARRAYSIZE(types)]();
+ SpecialWallType *specialBlockIDs = new SpecialWallType[ARRAYSIZE(types)]();
memcpy(specialBlockIDs, types, sizeof(types));
_specialBlockIDs = specialBlockIDs;
_numSpecialBlockIDs = ARRAYSIZE(types);
_portalParams = gameID == GI_EOB1 ? eob1PortalParams : eob2PortalParams;
_portalParamsLen = (gameID == GI_EOB1 ? ARRAYSIZE(eob1PortalParams) : ARRAYSIZE(eob2PortalParams));
- createColors();
+ createColorTable();
+
+ DrawIconFunc drawIconFuncs[] = {
+ &Automap_EoB::drawIconImpl<uint8>,
+ &Automap_EoB::drawIconImpl<uint16>,
+ &Automap_EoB::drawIconImpl<uint32>
+ };
+
+ Graphics::PixelFormat fmt = _system->getOverlayFormat();
+ int f = fmt.bpp() >> 4;
+ assert(f >= 0 && f < ARRAYSIZE(drawIconFuncs));
+ _drawIcon = drawIconFuncs[f];
+ assert(_drawIcon != nullptr);
}
Automap_EoB::~Automap_EoB() {
- delete _automapBg;
- delete _automapFrame;
+ delete _background;
+ delete _frame;
delete[] _specialBlockIDs;
delete[] _colors;
+ releaseIcons();
}
void Automap_EoB::markVisited(uint16 block) {
@@ -333,7 +242,7 @@ void Automap_EoB::markSeen(uint16 block, int8 dir) {
b = nb;
if (reveal)
_blockData[b].direction |= 1;
- if ((_wllWallFlags[wn] & 9) == 8 || breakableFromHere || wn == _wallOfForceID || wn == _specialBlockIDs[4] || wn == _specialBlockIDs[5])
+ if ((_wllWallFlags[wn] & 9) == 8 || breakableFromHere || wn == _wallOfForceID || wn == _specialBlockIDs[1].wall || wn == _specialBlockIDs[2].wall)
break;
}
}
@@ -343,29 +252,82 @@ void Automap_EoB::markSeen(uint16 block, int8 dir) {
// on the map, unless the player turns around and looks at it. This is a bit annoying and we avoid it like this...
LevelBlockProperty &bp = _blockData[calcNewBlockPosition(block, dir ^ 2)];
for (int i = 0; i < 4; ++i) {
- if (bp.walls[i] == _specialBlockIDs[4] || bp.walls[i] == _specialBlockIDs[5])
+ if (bp.walls[i] == _specialBlockIDs[1].wall || bp.walls[i] == _specialBlockIDs[2].wall)
bp.direction |= 1;
}
}
+void drawString(Graphics::Surface &dst, const Graphics::Font *font, const Common::String &text, int x, int y, int w, uint32 color, int scale, Graphics::TextAlign align = Graphics::kTextAlignCenter) {
+ if (!font || text.empty() || scale < 1)
+ return;
+ const int tw = font->getStringWidth(text);
+ const int th = font->getFontHeight();
+ if (tw <= 0 || th <= 0)
+ return;
+
+ if (scale == 1) {
+ font->drawString(&dst, text, x, y, w, color, align);
+ return;
+ }
+
+ const int bpp = dst.format.bytesPerPixel;
+ if (bpp != 2 && bpp != 4) {
+ font->drawString(&dst, text, x, y, w, color, align);
+ return;
+ }
+
+ Graphics::Surface tmp;
+ tmp.create(tw, th, dst.format);
+ tmp.fillRect(Common::Rect(0, 0, tw, th), 0);
+ font->drawString(&tmp, text, 0, 0, tw, color, Graphics::kTextAlignLeft);
+
+ const int dw = tw * scale;
+ int dx = x + (w - dw) / 2;
+ if (align == Graphics::kTextAlignLeft)
+ dx = x;
+ else if (align == Graphics::kTextAlignRight)
+ dx = x + w - dw;
+ if (dx < x)
+ dx = x;
+ for (int sy = 0; sy < th; ++sy) {
+ const byte *src = (const byte *)tmp.getBasePtr(0, sy);
+ for (int sx = 0; sx < tw; ++sx) {
+ const uint32 c = (bpp == 2) ? ((const uint16 *)src)[sx] : ((const uint32 *)src)[sx];
+ if (c == 0)
+ continue;
+ const int px = dx + sx * scale;
+ const int py = y + sy * scale;
+ dst.fillRect(Common::Rect(px, py, px + scale, py + scale), c);
+ }
+ }
+ tmp.free();
+}
+
void Automap_EoB::draw(int level, uint16 partyBlock, int8 partyDirection) {
const int ow = _system->getOverlayWidth();
const int oh = _system->getOverlayHeight();
- const AutomapLayout l = createLayout();
- // Redraw the map background on an overlay resize.
- if (_automapBg->w != ow || _automapBg->h != oh)
- redrawBackground(l, ow, oh);
+ // After an overlay resize we have to recalc some parameters and recreate the background and icons.
+ if (_background->w != ow || _background->h != oh) {
+ _l = createLayout(ow, oh);
+ recalcScaling(ow, oh);
+ drawBackground(ow, oh);
+ // Normally, the icons will be made a bit smaller than the cell size, for better visuals.
+ // For low resolutions, this will just make them too tiny and ugly, so we drop that margin.
+ createIcons(_l.cell < 12);
+ }
+
+ Graphics::Surface &surf = *_frame;
+ surf.copyRectToSurface(*_background, 0, 0, Common::Rect(0, 0, ow, oh));
- Graphics::Surface &surf = *_automapFrame;
- surf.copyRectToSurface(*_automapBg, 0, 0, Common::Rect(0, 0, ow, oh));
+ const int wt = MAX(2, _l.cell / 5);
- const int cell = l.cell;
- const int offX = l.offX;
- const int offY = l.offY;
- const int wt = MAX(2, cell / 5);
+ const int doorBtnWOffs = (MAX(2, wt + 1) >> 1) + (MAX(1, _l.cell / 5) >> 1);
+ const int doorBtnLOffs = MAX(1, _l.cell / 6);
+ const int doorBtnOffsX[] = { -doorBtnLOffs, doorBtnWOffs, doorBtnLOffs, -doorBtnWOffs };
+ const int doorBtnOffsY[] = { -doorBtnWOffs, -doorBtnLOffs, doorBtnWOffs, doorBtnLOffs };
- uint legendFlags = (partyBlock != 0xFFFF) ? 1 : 0;
+ uint legendFlags = 0;
for (int by = 0; by < 32; ++by) {
for (int bx = 0; bx < 32; ++bx) {
@@ -375,106 +337,61 @@ void Automap_EoB::draw(int level, uint16 partyBlock, int8 partyDirection) {
if (!visited && !seen)
continue;
- const int sx = offX + bx * cell;
- const int sy = offY + by * cell;
+ const int sx = _l.offX + bx * _l.cell;
+ const int sy = _l.offY + by * _l.cell;
bool wall[4];
for (int d = 0; d < 4; ++d) {
const uint16 nb = calcNewBlockPosition(block, d);
uint8 wn = _blockData[nb].walls[d ^ 2];
// Check for doors or breakable block objects (like the barrels in the EOB II catacombs) which shouldn't be drawn as walls.
- wall[d] = (!(_wllWallFlags[wn] & 9) && wn != _wallOfForceID && checkForBreakableObjectType(nb) == kNoBreakableObject);
+ wall[d] = (!(_wllWallFlags[wn] & 9) && (wn != _wallOfForceID) && (checkForBreakableObjectType(nb) == kNoBreakableObject));
}
const LevelBlockProperty *bp = &_blockData[block];
- bool doorNS = ((_wllWallFlags[bp->walls[0]] | _wllWallFlags[bp->walls[2]]) & 8);
- bool doorEW = ((_wllWallFlags[bp->walls[1]] | _wllWallFlags[bp->walls[3]]) & 8);
- bool wof = (bp->walls[0] == _wallOfForceID || bp->walls[1] == _wallOfForceID || bp->walls[2] == _wallOfForceID || bp->walls[3] == _wallOfForceID);
int br = checkForBreakableObjectType(block);
- int bcolor = kColorDoor;
-
- if (br == kBreakableBarrierNS) {
- doorNS = true;
- bcolor = kColorInteractive;
- }
-
- if (br == kBreakableBarrierEW) {
- doorEW = true;
- bcolor = kColorInteractive;
- }
-
- if (doorNS)
- wall[0] = wall[2] = false;
- if (doorEW)
- wall[1] = wall[3] = false;
-
- surf.fillRect(Common::Rect(sx, sy, sx + cell, sy + cell), _colors[visited ? kColorFloor : kColorFloorSeen]);
+ bool doorNS = ((_wllWallFlags[bp->walls[0]] | _wllWallFlags[bp->walls[2]]) & 8) || (br == kBreakableBarrierNS);
+ bool doorEW = ((_wllWallFlags[bp->walls[1]] | _wllWallFlags[bp->walls[3]]) & 8) || (br == kBreakableBarrierEW);
+ bool wof = (bp->walls[0] == _wallOfForceID || bp->walls[1] == _wallOfForceID || bp->walls[2] == _wallOfForceID || bp->walls[3] == _wallOfForceID);
+
+ surf.fillRect(Common::Rect(sx, sy, sx + _l.cell, sy + _l.cell), _colors[visited ? kColorFloor : kColorFloorSeen]);
if (visited) {
- surf.hLine(sx, sy, sx + cell - 1, _colors[kColorGrid]);
- surf.vLine(sx, sy, sy + cell - 1, _colors[kColorGrid]);
+ surf.hLine(sx, sy, sx + _l.cell - 1, _colors[kColorGrid]);
+ surf.vLine(sx, sy, sy + _l.cell - 1, _colors[kColorGrid]);
}
const uint32 wc = _colors[visited ? kColorWall : kColorWallSeen];
if (wall[0])
- surf.fillRect(Common::Rect(sx, sy, sx + cell, sy + wt), wc);
+ surf.fillRect(Common::Rect(sx, sy, sx + _l.cell, sy + wt), wc);
if (wall[1])
- surf.fillRect(Common::Rect(sx + cell - wt, sy, sx + cell, sy + cell), wc);
+ surf.fillRect(Common::Rect(sx + _l.cell - wt, sy, sx + _l.cell, sy + _l.cell), wc);
if (wall[2])
- surf.fillRect(Common::Rect(sx, sy + cell - wt, sx + cell, sy + cell), wc);
+ surf.fillRect(Common::Rect(sx, sy + _l.cell - wt, sx + _l.cell, sy + _l.cell), wc);
if (wall[3])
- surf.fillRect(Common::Rect(sx, sy, sx + wt, sy + cell), wc);
-
- auto drawButton = [&surf, &legendFlags, cell](int x, int y, int alignment, int len, int wdth, uint32 color, uint addFlag) {
- switch (alignment) {
- case 0:
- x = x + cell / 2 - len / 2;
- break;
- case 1:
- x = x + cell - wdth;
- y = y + cell / 2 - len / 2;
- SWAP(len, wdth);
- break;
- case 2:
- x = x + cell / 2 - len / 2;
- y = y + cell - wdth;
- break;
- default:
- y = y + cell / 2 - len / 2;
- SWAP(len, wdth);
- break;
- }
- surf.fillRect(Common::Rect(x, y, x + len, y + wdth), color);
- legendFlags |= addFlag;
- };
-
+ surf.fillRect(Common::Rect(sx, sy, sx + wt, sy + _l.cell), wc);
+ // Draw doors and barriers.
if (doorNS || doorEW) {
- // FIXME?: Doors on the map always look "closed", regardless of whether open oder close.
- const int dt = MAX(2, wt + 1);
- const int dm = MAX(1, cell / 5);
- const int dcx = sx + cell / 2, dcy = sy + cell / 2;
-
- legendFlags |= (br != kNoBreakableObject ? 0x80 : 0x40);
-
- // Draw the door and also the door button if the door has one.
- if (doorNS) {
- surf.fillRect(Common::Rect(sx + dm, dcy - dt / 2, sx + cell - dm, dcy - dt / 2 + dt), _colors[bcolor]);
- if (_specialWallTypes[bp->walls[0]] == 1)
- drawButton(sx - dm, sy + dt, 0, dm, dm, _colors[kColorLever], 0x400);
- if (_specialWallTypes[bp->walls[2]] == 1)
- drawButton(sx + dm, sy - dt, 2, dm, dm, _colors[kColorLever], 0x400);
-
- }
- if (doorEW) {
- surf.fillRect(Common::Rect(dcx - dt / 2, sy + dm, dcx - dt / 2 + dt, sy + cell - dm), _colors[bcolor]);
- if (_specialWallTypes[bp->walls[1]] == 1)
- drawButton(sx - dt, sy - dm, 1, dm, dm, _colors[kColorLever], 0x400);
- if (_specialWallTypes[bp->walls[3]] == 1)
- drawButton(sx + dt, sy + dm, 3, dm, dm, _colors[kColorLever], 0x400);
-
+ int ovrColor = -1;
+
+ if (br == kBreakableBarrierNS || br == kBreakableBarrierEW) {
+ legendFlags |= 0x400;
+ ovrColor = kColorInteractive;
+ } else {
+ legendFlags |= 0x200;
+ // Draw the door button if the door has one.
+ for (int i = 0; i < 4; ++i) {
+ if (_specialWallTypes[bp->walls[i]] == 1) {
+ legendFlags |= 0x2000;
+ (this->*_drawIcon)(surf, 0, kIconDoorButton, sx, sy, _l.cell, kAlignCenter, doorBtnOffsX[i], doorBtnOffsY[i], kColorLever);
+ }
+ }
}
+
+ // Draw the door or barrier. FIXME?: Doors on the map always look "closed", even when they're open.
+ (this->*_drawIcon)(surf, 0, doorNS ? kIconDoorNS : kIconDoorEW, sx, sy, _l.cell, kAlignCenter, 0, 0, ovrColor);
}
- // Interactive/clickable walls (lever, niche, banner, etc.)
+ // Draw wall objects (lever, niche, banner, portal, etc.)
for (int d = 0; d < 4; ++d) {
uint16 nb = calcNewBlockPosition(block, d);
uint8 wn = _blockData[nb].walls[d ^ 2];
@@ -483,7 +400,9 @@ void Automap_EoB::draw(int level, uint16 partyBlock, int8 partyDirection) {
if (st == 0 || st == 5 || st == 6 || st == 0xFF)
continue;
- // Check if it is a stone portal.
+ // Check if it is a stone portal. This is completely hardcoded, since there is no logic there
+ // to determine whether a wall object is a portal (except analysis of the attached script, and
+ // even that wouldn't catch the "inactive"/exit-only portal in EOBI).
if (st == _portalParams[0]) {
for (int i = 1; i < _portalParamsLen && st != 20; i += 2) {
if (level == _portalParams[i] && wn == _portalParams[i + 1])
@@ -493,18 +412,19 @@ void Automap_EoB::draw(int level, uint16 partyBlock, int8 partyDirection) {
// For EOBI, there are cases where a wall has a clickable shape type, but there is no script
// function assigned to it or there isn't even a clickable shape. We don't want to draw these
- // "fake" triggers.
+ // "fake" triggers (except for one "inactive"/exit-only stone portal, that's why we check for
+ // portals first).
if (st == 2 && (_blockData[nb].assignedObjects == 0 || _wllShapeMap[wn] == 0))
continue;
- // If it is a door button on the door or on the door frame it has already been drawn
- // together with the door. Skip it here...
+ // If it is a door button on the door or on the door frame it has already been drawn together
+ // with the door. Skip it here...
if (st == 1 && (_wllWallFlags[wn] & 8))
continue;
// Breakable objects are also "interactive" (you can click them and you get a text message),
- // but it makes more sense to not to draw them here, but in the middle of the block if they're
- // block objects (like the barrels in the EOB II catacombs). But if it is a wall object (like
+ // but it makes more sense not to draw them here, but in the middle of the block if they're
+ // block objects (like the barrels in the EOB II catacombs). But if we have a wall object (like
// the breakable windows on the temple ground floor in EOBII) we should draw it here.
if (checkForBreakableObjectType(nb) != kNoBreakableObject)
continue;
@@ -513,87 +433,52 @@ void Automap_EoB::draw(int level, uint16 partyBlock, int8 partyDirection) {
if (isCenteredSwitch(nb))
continue;
- uint32 pcol = _colors[(st == 10) ? kColorNiche : (st == 1 || st == 3 || st == 4) ? kColorLever : (st == 20 ? kColorTele : kColorInteractive)];
- uint flag = (st == 10) ? 0x1000 : (st == 1 || st == 3 || st == 4) ? 0x400 : (st == 20 ? 0x2000 : 0x800);
- int plen = MAX(2, cell * 2 / (st < 10 ? 8 : st == 10 ? 6 : 3));
- int pwdth = MAX(2, cell / 4);
+ int ovrColor = (st == 2 || (st >= 7 && st <= 9)) ? kColorInteractive : -1;
+ int icon = (st < 10 ? kIconSmallObject : st == 10 ? kIconNicheNS + (d & 1): kIconPortalNS + (d & 1));
+ legendFlags |= ((st == 10) ? 0x8000 : ((st == 1 || st == 3 || st == 4) ? 0x2000 : (st == 20 ? 0x10000 : 0x4000)));
- drawButton(sx, sy, d, plen, pwdth, pcol, flag);
+ (this->*_drawIcon)(surf, 0, icon, sx, sy, _l.cell, (IconAlignment)d, 0, 0, ovrColor);
}
- // TODO: The icons could all be pregenerated instead of individually rendering them each time.
- auto drawIcon = [&surf, &legendFlags, cell](int ix, int iy, uint8 icon, const uint32 *colTable, uint addFlag) {
- const int cx = ix + cell / 2;
- const int cy = iy + cell / 2;
- const int r = MAX(2, cell / 3);
- const int margin = r * 4 / 5;
- const int step = (cell << 8) / 5;
- const int step2 = ((cell - r) << 8) / 5;
- switch (icon) {
- case 0:
- for (int i = 0; i < margin; ++i) {
- surf.drawLine(cx - margin + i, cy - i, cx + margin - i, cy - i, colTable[kColorTele]);
- surf.drawLine(cx - margin + i, cy + i, cx + margin - i , cy + i, colTable[kColorTele]);
- }
- break;
- case 1:
- case 2:
- case 3:
- for (int pos = 0, g = 0; g < 5; ++g, pos += step) {
- surf.drawLine(ix + (pos >> 8), iy, ix + (pos >> 8), iy + cell, colTable[kColorWall]);
- surf.drawLine(ix, iy + (pos >> 8), ix + cell, iy + (pos >> 8), colTable[kColorWall]);
- }
- break;
- case 4:
- automapFillTri(surf, cx - r, cy + r, cx + r, cy + r, cx, cy - r, colTable[kColorStair]);
- break;
- case 5:
- automapFillTri(surf, cx - r, cy - r, cx + r, cy - r, cx, cy + r, colTable[kColorStair]);
- break;
- case 6:
- case 7:
- case 8:
- surf.drawEllipse(cx - margin, cy - margin, cx + margin, cy + margin, colTable[icon == 6 ? kColorPit : kColorPlate], true);
- break;
- case 9:
- case 11:
- surf.fillRect(Common::Rect(cx - margin, cy - margin, cx + margin, cy + margin), colTable[icon == 9 ?kColorInteractive : kColorLever]);
- break;
- case 10:
- for (int posX = 0, g = 0; g < 3; ++g, posX += (step2 * 2)) {
- for (int posY = 0, gg = 0; gg < 3; ++gg, posY += (step2 * 2)) {
- surf.fillRect(Common::Rect(cx - r + (posX >> 8), cy - r + (posY >> 8), cx - r + ((posX + step2) >> 8), cy - r + ((posY + step2) >> 8)), colTable[kColorWoF]);
- if (g < 2 && gg < 2)
- surf.fillRect(Common::Rect(cx - r + ((posX + step2) >> 8), cy - r + ((posY + step2) >> 8), cx - r + ((posX + step2 * 2) >> 8), cy - r + ((posY + step2 * 2) >> 8)), colTable[kColorWoF]);
- }
- }
- break;
- default:
- break;
- }
- legendFlags |= addFlag;
- };
-
- if (br == kBreakableBlockObject) {
- drawIcon(sx, sy, 9, _colors, 0x800);
- } else if (wof) {
- drawIcon(sx, sy, 10, _colors, 0x200);
+ // Draw centered block icons (pits, plates, teleporters, etc.)
+ if (wof) {
+ (this->*_drawIcon)(surf, 0, kIconWallOfForce, sx, sy, _l.cell, kAlignCenter, 0, 0, -1);
+ legendFlags |= 0x1000;
+ } else if (br == kBreakableBlockObject) {
+ (this->*_drawIcon)(surf, 0, kIconBigObject, sx, sy, _l.cell, kAlignCenter, 0, 0, kColorInteractive);
+ legendFlags |= 0x4000;
} else if (isCenteredSwitch(block)) {
- drawIcon(sx, sy, 11, _colors, 0x400);
+ (this->*_drawIcon)(surf, 0, kIconBigObject, sx, sy, _l.cell, kAlignCenter, 0, 0, -1);
+ legendFlags |= 0x2000;
} else {
// Special blocks
- const uint16 flags[] = { 0x08, 0x100, 0x100, 0x100, 0x02, 0x04, 0x10, 0x20, 0x20 };
+
+
+ /* for (int ii = 0; ii < 4; ++ii) {
+ if (_wllVmpMap[bp->walls[ii]] != 1)
+ continue;
+ uint16 nbb = calcNewBlockPosition(block, ii);
+ if (_wllWallFlags[_blockData[nbb].walls[ii ^ 2]] & 1) {
+ drwIcon(sx, sy, 6, _colors, 0x100);
+ break;
+ }
+ }*/
+
for (int i = 0; i < _numSpecialBlockIDs; ++i) {
- uint8 s = _specialBlockIDs[i]; // order: teleporter, illusion1, illusion2, illusion3, stairsUp, stairsDown, pit, plate1, plate2
- // This is a bit tricky for EOBI. EOBII has the special wall type on all four walls, but not EOBI, so we can't require it.
- // However, there is at least one glitchy wall with a stairs id that can never be seen from inside the game, but would get a
- // stairs drawn on the map. To avoid that, we also check if the wall is passible from the other side.
+ uint8 s = _specialBlockIDs[i].wall;
+ // This is a bit tricky due to glitchy game data. EOBII puts the special wall type on all four walls of a block, but EOBI does not, so we can't
+ // rely on that. However, in EOBI there is at least one glitchy wall with a stairs id that can never be seen from inside the game, so the glitch
+ // is irrelevant there. But it would cause a stairs icon to be drawn on the map if we were satisfied with just checking any one of the four walls
+ // to have the special type. To avoid the issue, we also check if the wall is passable from the other side. But then there is a case of a breakable
+ // wall with a stairs behind it in EOBII which gets wrongly marked as non-passable, so the check would fail AND a wall would appear on the map.
+ // We use checkForBreakableObjectType() to detect that case and draw the stairs (we also use that above, to avoid drawing the wall).
for (int ii = 0; ii < 4; ++ii) {
if (bp->walls[ii] != s)
continue;
uint16 nbb = calcNewBlockPosition(block, ii);
- if (_wllWallFlags[_blockData[nbb].walls[ii ^ 2]] & 1) {
- drawIcon(sx, sy, i, _colors, flags[i]);
+ if ((_wllWallFlags[_blockData[nbb].walls[ii ^ 2]] & 1) || (checkForBreakableObjectType(nbb) == kBrokenBarrier)) {
+ (this->*_drawIcon)(surf, 0, _specialBlockIDs[i].icon, sx, sy, _l.cell, kAlignCenter, 0, 0, -1);
+ legendFlags |= _specialBlockIDs[i].legendFlag;
break;
}
}
@@ -602,109 +487,68 @@ void Automap_EoB::draw(int level, uint16 partyBlock, int8 partyDirection) {
}
}
- drawLegend(l, legendFlags);
-
- const int mx = offX + (partyBlock & 0x1F) * cell;
- const int my = offY + (partyBlock >> 5) * cell;
- const int cx = mx + cell / 2;
- const int cyp = my + cell / 2;
- const int r = MAX(2, cell / 2 - MAX(1, cell / 6));
- const int b = MAX(2, r * 3 / 4);
- int tipX, tipY, l1X, l1Y, l2X, l2Y;
- switch (partyDirection) {
- case 1:
- tipX = cx + r;
- tipY = cyp;
- l1X = cx - b;
- l1Y = cyp - b;
- l2X = cx - b;
- l2Y = cyp + b;
- break;
- case 2:
- tipX = cx;
- tipY = cyp + r;
- l1X = cx - b;
- l1Y = cyp - b;
- l2X = cx + b;
- l2Y = cyp - b;
- break;
- case 3:
- tipX = cx - r;
- tipY = cyp;
- l1X = cx + b;
- l1Y = cyp - b;
- l2X = cx + b;
- l2Y = cyp + b;
- break;
- default:
- tipX = cx;
- tipY = cyp - r;
- l1X = cx - b;
- l1Y = cyp + b;
- l2X = cx + b;
- l2Y = cyp + b;
- break;
+ if (partyBlock != 0xFFFF) {
+ int mx = _l.offX + (partyBlock & 0x1F) * _l.cell;
+ int my = _l.offY + (partyBlock >> 5) * _l.cell;
+ _partyIconColor = kColorPartyFrame0;
+ _partyIconColorStep = 1;
+ (this->*_drawIcon)(*_frame, 0, partyDirection, mx, my, _l.cell, kAlignCenter, 0, 0, _partyIconColor);
+ legendFlags |= (1 << partyDirection);
}
- automapFillTri(surf, tipX, tipY, l1X, l1Y, l2X, l2Y, _colors[kColorPartyEdge]);
- const int gx = (tipX + l1X + l2X) / 3, gy = (tipY + l1Y + l2Y) / 3;
- automapFillTri(surf,
- tipX + (gx - tipX) / 4, tipY + (gy - tipY) / 4,
- l1X + (gx - l1X) / 4, l1Y + (gy - l1Y) / 4,
- l2X + (gx - l2X) / 4, l2Y + (gy - l2Y) / 4, _colors[kColorParty]);
+ drawLegend(legendFlags);
const Graphics::Font *bigFont = FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont);
- const int fh = bigFont ? bigFont->getFontHeight() : 8;
- const int sc = CLIP<int>(ow / 320, 1, 3);
- const int mpad = MAX(8, l.mapW / 40);
-
if (bigFont) {
- const uint16 cb = (partyBlock != 0xFFFF) ? partyBlock : 0;
- const Common::String lvl = _levelNames[level - 1];
- const Common::String crd = Common::String::format("X %d Y %d", cb & 0x1F, cb >> 5);
- const int pm = MAX(3, sc * 2);
- const int innerW = l.plW - 2 * pm;
- const int innerH = l.plH - 2 * pm;
- const int gap = MAX(2, innerH / 12);
- const int lvlBand = (innerH - gap) * 6 / 10;
- const int crdBand = (innerH - gap) - lvlBand;
- const int lvlSc = automapFit(bigFont, lvl, innerW, MAX(1, lvlBand / fh));
- const int crdSc = automapFit(bigFont, crd, innerW, MAX(1, crdBand / fh));
- const int lvlY = l.plY + pm + (lvlBand - fh * lvlSc) / 2;
- const int crdY = l.plY + pm + lvlBand + gap + (crdBand - fh * crdSc) / 2;
- automapDrawBigString(surf, bigFont, lvl, l.plX, lvlY, l.plW, _colors[kColorGold], lvlSc);
- automapDrawBigString(surf, bigFont, crd, l.plX, crdY, l.plW, _colors[kColorPanelTxt], crdSc);
-
+ Common::String lvl = _levelNames[level - 1];
+ drawString(surf, bigFont, lvl, _l.plX, _levelStrY, _l.plW, _colors[kColorGold], _levelStrScl);
+ if (partyBlock != 0xFFFF) {
+ Common::String crd = Common::String::format("X %d Y %d", partyBlock & 0x1F, partyBlock >> 5);
+ drawString(surf, bigFont, crd, _l.plX, _coordStrY, _l.plW, _colors[kColorPanelTxt], _coordStrScl);
+ }
+
+ /*
// Footer: TODO? We could write something? Or just leave it blank?
Common::String foot;
if (!foot.empty()) {
- const int footH = (l.mapY + l.mapH) - l.footY;
+ const int fh = bigFont ? bigFont->getFontHeight() : 8;
+ const int sc = CLIP<int>(ow / 320, 1, 3);
+ const int mpad = MAX(8, _l.mapW / 40);
+ const int footH = (_l.mapY + _l.mapH) - _l.footY;
const int fpad = MAX(2, footH / 6);
const int maxFsc = MAX(1, (footH - 2 * fpad) / fh);
- const int fw = l.mapW - 2 * mpad;
- const int fsc = MIN(automapFit(bigFont, foot, fw, sc), maxFsc);
- const int fy = l.footY + (footH - fh * fsc) / 2;
- automapDrawBigString(surf, bigFont, foot, l.mapX + mpad, fy, fw, _colors[kColorInk], fsc, Graphics::kTextAlignLeft);
- }
+ const int fw = _l.mapW - 2 * mpad;
+ const int fsc = MIN(fitString(bigFont, foot, fw, sc), maxFsc);
+ const int fy = _l.footY + (footH - fh * fsc) / 2;
+ drawString(surf, bigFont, foot, _l.mapX + mpad, fy, fw, _colors[kColorInk], fsc, Graphics::kTextAlignLeft);
+ }*/
}
_system->copyRectToOverlay(surf.getPixels(), surf.pitch, 0, 0, ow, oh);
_system->updateScreen();
}
-Automap_EoB::AutomapLayout Automap_EoB::createLayout() const {
- const int ow = _system->getOverlayWidth();
- const int oh = _system->getOverlayHeight();
+void Automap_EoB::drawPartyIcon(uint16 partyBlock, int8 partyDirection) {
+ _partyIconColor += _partyIconColorStep;
+ if (_partyIconColor == kColorPartyFrame9 || _partyIconColor == kColorPartyFrame0)
+ _partyIconColorStep *= -1;
+ int mx = _l.offX + (partyBlock & 0x1F) * _l.cell;
+ int my = _l.offY + (partyBlock >> 5) * _l.cell;
+ (this->*_drawIcon)(*_frame, 0, partyDirection, mx, my, _l.cell, kAlignCenter, 0, 0, _partyIconColor);
+ _system->copyRectToOverlay(_frame->getBasePtr(mx, my), _frame->pitch, mx, my, _l.cell, _l.cell);
+ _system->updateScreen();
+}
+
+Automap_EoB::AutomapLayout Automap_EoB::createLayout(int width, int height) const {
AutomapLayout l;
- // Stone frame around the overlay, a side panel on the right,
- // and a parchment map inset filling the rest
- l.frame = CLIP<int>(oh / 36, 8, 28);
- const int gap = MAX(4, l.frame / 2);
- l.sideW = MAX(150, ow / 4);
+ // Stone frame around the overlay, a side panel on the right, and a parchment map inset filling the rest
+ l.frame = CLIP<int>(height / 36, 8, 28);
+ const int gap = MAX<int>(4, l.frame / 2);
+ l.sideW = MAX<int>(150, width / 4);
const int inX = l.frame, inY = l.frame;
- const int inW = ow - 2 * l.frame, inH = oh - 2 * l.frame;
+ const int inW = width - 2 * l.frame, inH = height - 2 * l.frame;
l.mapX = inX;
l.mapY = inY;
@@ -715,7 +559,7 @@ Automap_EoB::AutomapLayout Automap_EoB::createLayout() const {
l.sideH = inH;
const int mpad = MAX(8, l.mapW / 40);
- const int footStrip = MAX(18, oh / 22);
+ const int footStrip = MAX(18, height / 22);
const int availW = l.mapW - 2 * mpad;
const int availH = l.mapH - 2 * mpad - footStrip;
l.cell = MAX(2, MIN(availW / 32, availH / 32));
@@ -728,206 +572,542 @@ Automap_EoB::AutomapLayout Automap_EoB::createLayout() const {
l.plX = l.sideX + spad;
l.plY = l.sideY + spad;
l.plW = l.sideW - 2 * spad;
- l.plH = MAX(30, oh / 8);
+ l.plH = MAX(30, height / 8);
return l;
}
-void Automap_EoB::createColors() {
+void Automap_EoB::recalcScaling(int width, int height) {
+ const Graphics::Font *bigFont = FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont);
+ int fh = bigFont ? bigFont->getFontHeight() : 8;
+
+ int scl = CLIP<int>(MIN<int>(width / 320, height / 200), 1, 12);
+ _legendHeadScl = fitString(bigFont, _legendStrings[0], _l.plW * 4 / 5, scl);
+
+ int isz = 2 * _l.cell + MAX(2, _legendHeadScl * 2);
+ int tw = _l.plW - isz;
+ _legendBodyScl = 12;
+ for (int i = 1; i < kNumLegendStrings; ++i)
+ _legendBodyScl = MIN<int>(_legendBodyScl, fitString(bigFont, _legendStrings[i], tw, _legendBodyScl));
+ for (int i = 2; i < kNumControlStrings; i += 2) {
+ Common::String str = Common::String::format("%s %s", _controlStrings[i], _controlStrings[i + 1]);
+ _legendBodyScl = MIN<int>(_legendBodyScl, fitString(bigFont, str, _l.plW, _legendBodyScl));
+ }
+
+ int pm = MAX<int>(3, scl * 2);
+ int innerW = _l.plW - 2 * pm;
+ int innerH = _l.plH - 2 * pm;
+ int gap = MAX<int>(2, innerH / 12);
+ int lvlBand = (innerH - gap) * 6 / 10;
+ _levelStrScl = MAX<int>(1, lvlBand / fh);
+ for (int i = 0; i < _numLevelNames; ++i)
+ _levelStrScl = MIN<int>(_levelStrScl, fitString(bigFont, _levelNames[i], innerW, _levelStrScl));
+
+ int crdBand = (innerH - gap) - lvlBand;
+ Common::String crd("X 00 Y 00");
+ _coordStrScl = fitString(bigFont, crd, innerW, MAX<int>(1, crdBand / fh));
+
+ _levelStrY = _l.plY + pm + (lvlBand - fh * _levelStrScl) / 2;
+ _coordStrY = _l.plY + pm + lvlBand + gap + (crdBand - fh * _coordStrScl) / 2;
+}
+
+void Automap_EoB::createColorTable() {
static const uint8 rgbTable[kNumColors][3] = {
- { 0x54, 0x56, 0x5e }, // stone
- { 0x3c, 0x3e, 0x45 }, // stone dark
- { 0x23, 0x25, 0x2a }, // stone edge
- { 0x6b, 0x6e, 0x78 }, // stone hi
- { 0xc2, 0xc5, 0xcd }, // rivet
- { 0xd6, 0xbf, 0x94 }, // paper
- { 0xe2, 0xcd, 0xa4 }, // paper hi
- { 0xc4, 0xab, 0x78 }, // paper lo
- { 0xa9, 0x8b, 0x56 }, // paper edge
- { 0x3a, 0x2a, 0x18 }, // ink
- { 0x7a, 0x60, 0x38 }, // ink soft
- { 0xbb, 0x9c, 0x5e }, // floor
- { 0xc7, 0xb1, 0x80 }, // floor seen
- { 0x8a, 0x70, 0x38 }, // grid
- { 0x2c, 0x1e, 0x10 }, // wall
- { 0x8a, 0x73, 0x4a }, // wall seen
- { 0x3f, 0x7a, 0x3a }, // wall of force
- { 0x7a, 0x4a, 0x1c }, // door
- { 0x3f, 0x7a, 0x3a }, // stair
- { 0x5f, 0x5f, 0xca }, // tele
- { 0x9b, 0x6c, 0x2e }, // plate
- { 0x6c, 0x6e, 0x80 }, // pit
- { 0x9a, 0x2d, 0x2d }, // lever
- { 0x9a, 0x4d, 0xad }, // interactive
- { 0x95, 0x65, 0x26 }, // niche
- { 0xa8, 0x28, 0x1c }, // party
- { 0x5a, 0x14, 0x0e }, // party edge
- { 0x1c, 0x1a, 0x16 }, // plaque bg
- { 0x0d, 0x0c, 0x0a }, // plaque ed
- { 0xf0, 0xc8, 0x50 }, // gold
- { 0xb8, 0x92, 0x3a }, // gold dim
- { 0xd8, 0xc8, 0xa8 } // panel text
+ { 0x54, 0x56, 0x5e }, // stone
+ { 0x3c, 0x3e, 0x45 }, // stone dark
+ { 0x23, 0x25, 0x2a }, // stone edge
+ { 0x6b, 0x6e, 0x78 }, // stone hi
+ { 0xc2, 0xc5, 0xcd }, // rivet
+ { 0xd6, 0xbf, 0x94 }, // paper
+ { 0xe2, 0xcd, 0xa4 }, // paper hi
+ { 0xc4, 0xab, 0x78 }, // paper lo
+ { 0xa9, 0x8b, 0x56 }, // paper edge
+ { 0x3a, 0x2a, 0x18 }, // ink
+ { 0x7a, 0x60, 0x38 }, // ink soft
+ { 0xbb, 0x9c, 0x5e }, // floor
+ { 0xc7, 0xb1, 0x80 }, // floor seen
+ { 0x8a, 0x70, 0x38 }, // grid
+ { 0x2c, 0x1e, 0x10 }, // wall
+ { 0x8a, 0x73, 0x4a }, // wall seen
+ { 0x3f, 0x7a, 0x3a }, // wall of force
+ { 0x7a, 0x4a, 0x1c }, // door
+ { 0x3f, 0x7a, 0x3a }, // stair
+ { 0x5f, 0x5f, 0xca }, // tele
+ { 0x9b, 0x6c, 0x2e }, // plate
+ { 0x6c, 0x6e, 0x80 }, // pit
+ { 0x9a, 0x2d, 0x2d }, // lever
+ { 0x9a, 0x4d, 0xad }, // interactive
+ { 0x95, 0x65, 0x26 }, // niche
+ { 0x1c, 0x1a, 0x16 }, // plaque bg
+ { 0x0d, 0x0c, 0x0a }, // plaque ed
+ { 0xf0, 0xc8, 0x50 }, // gold
+ { 0xb8, 0x92, 0x3a }, // gold dim
+ { 0xd8, 0xc8, 0xa8 }, // panel text
+ { 0xfe, 0x12, 0xfe }, // transparency key
+ { 0xa8, 0x28, 0x1c }, // party frame 0
+ { 0xff, 0xff, 0xff }, // party frame 1 (placeholder, correct values will be calculated)
+ { 0xff, 0xff, 0xff }, // party frame 2 (placeholder, correct values will be calculated)
+ { 0xff, 0xff, 0xff }, // party frame 3 (placeholder, correct values will be calculated)
+ { 0xff, 0xff, 0xff }, // party frame 4 (placeholder, correct values will be calculated)
+ { 0xff, 0xff, 0xff }, // party frame 5 (placeholder, correct values will be calculated)
+ { 0xff, 0xff, 0xff }, // party frame 6 (placeholder, correct values will be calculated)
+ { 0xff, 0xff, 0xff }, // party frame 7 (placeholder, correct values will be calculated)
+ { 0xff, 0xff, 0xff }, // party frame 8 (placeholder, correct values will be calculated)
+ { 0xff, 0xaa, 0xaa } // party frame 9
};
Graphics::PixelFormat fmt = _system->getOverlayFormat();
uint32 *colors = new uint32[kNumColors]();
- for (int i = 0; i < kNumColors; ++i)
+ for (int i = 0; i < kColorPartyFrame0; ++i)
colors[i] = fmt.RGBToColor(rgbTable[i][0], rgbTable[i][1], rgbTable[i][2]);
+
+ // Create color gradient for the party icon frames (we let it blink for better visibility).
+ float algoBase = 1.25f; // It looks much better with a non-linear gradient.
+ int div = 0;
+ for (int i = 0; i < 10; ++i)
+ div += powf(algoBase, i);
+
+ int stepR = (rgbTable[kColorPartyFrame9][0] - rgbTable[kColorPartyFrame0][0]) * 0x10000 / div;
+ int stepG = (rgbTable[kColorPartyFrame9][1] - rgbTable[kColorPartyFrame0][1]) * 0x10000 / div;
+ int stepB = (rgbTable[kColorPartyFrame9][2] - rgbTable[kColorPartyFrame0][2]) * 0x10000 / div;
+
+ uint32 curR = rgbTable[kColorPartyFrame0][0] * 0x10000;
+ uint32 curG = rgbTable[kColorPartyFrame0][1] * 0x10000;
+ uint32 curB = rgbTable[kColorPartyFrame0][2] * 0x10000;
+
+ for (int i = 0; i < 10; ++i) {
+ colors[kColorPartyFrame0 + i] = fmt.RGBToColor(curR >> 16, curG >> 16, curB >> 16);
+ curR += stepR * powf(algoBase, i);
+ curG += stepG * powf(algoBase, i);
+ curB += stepB * powf(algoBase, i);
+ }
+
_colors = colors;
}
-void Automap_EoB::redrawBackground(const AutomapLayout &l, int width, int height) {
+void automapFillTri(Graphics::Surface &s, int ax, int ay, int bx, int by, int cx, int cy, uint32 color) {
+ if (ay > by) {
+ SWAP(ax, bx);
+ SWAP(ay, by);
+ }
+ if (ay > cy) {
+ SWAP(ax, cx);
+ SWAP(ay, cy);
+ }
+ if (by > cy) {
+ SWAP(bx, cx);
+ SWAP(by, cy);
+ }
+ if (cy == ay)
+ return;
+
+ for (int y = ay; y <= cy; ++y) {
+ int xLong = ax + (cx - ax) * (y - ay) / (cy - ay);
+ int xShort = (y < by)
+ ? (by == ay ? ax : ax + (bx - ax) * (y - ay) / (by - ay))
+ : (cy == by ? bx : bx + (cx - bx) * (y - by) / (cy - by));
+ int x0 = MIN(xLong, xShort);
+ int x1 = MAX(xLong, xShort);
+ s.hLine(x0, y, x1, color);
+ }
+}
+
+void Automap_EoB::createIcons(bool lowResTarget) {
+ const uint32 *colTable = _colors;
+ Graphics::PixelFormat fmt = _system->getOverlayFormat();
+ const Graphics::Font *bigFont = FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont);
+ int lineH = (bigFont ? bigFont->getFontHeight() : 8) * _legendBodyScl;
+
+ auto renderIcon = [&fmt, colTable](Graphics::Surface &surf, int iconID, int boxFitWidth, bool fitWithMargin, bool fillBackground) {
+ auto prepSurface = [&fmt, &surf, colTable](int width, int height, int bkgColor) {
+ surf.create(width, height, fmt);
+ surf.fillRect(Common::Rect(surf.w, surf.h), colTable[bkgColor == -1 ? kColorTransp : bkgColor]);
+ };
+
+ int size1half = MAX(2, boxFitWidth / 3);
+ int size1full = size1half * 2;
+
+ // For better visuals, we reduce the icon size so there is a bit of a gap between the icon and the cell border.
+ // But not if we're in a low res environment where the icon would just get too small...
+ int size2half = fitWithMargin ? size1half * 4 / 5 : size1half;
+ int size2full = size2half * 2;
+
+ int step1 = MAX(256, (boxFitWidth << 8) / 5);
+ int step2 = MAX(256, ((boxFitWidth - size1half) << 8) / 5);
+ int step2a = step2 * 2;
+
+ int sButtonSize = MAX(1, boxFitWidth / 5);
+ int mButtonSize = MAX(2, boxFitWidth / 4);
+ int nicheWd = MAX(2, boxFitWidth * 2 / 5);
+ int portalWd = MAX(2, boxFitWidth * 2 / 3);
+ int doorLen = boxFitWidth - 2 * MAX(1, boxFitWidth / 5);
+ int doorWdth = MAX(2, boxFitWidth / 5) + 1;
+
+ switch (iconID) {
+ case kIconPartyNorth:
+ prepSurface(size2full, size2full, -1);
+ automapFillTri(surf, 0, size2full - 1, size2full - 1, size2full - 1, size2half, 0, colTable[kColorPartyFrame0]);
+ break;
+ case kIconPartyEast:
+ prepSurface(size2full, size2full, -1);
+ automapFillTri(surf, 0, 0, 0, size2full - 1, size2full - 1, size2half, colTable[kColorPartyFrame0]);
+ break;
+ case kIconPartySouth:
+ prepSurface(size2full, size2full, -1);
+ automapFillTri(surf, 0, 0, size2full - 1, 0, size2half, size2full - 1, colTable[kColorPartyFrame0]);
+ break;
+ case kIconPartyWest:
+ prepSurface(size2full, size2full, -1);
+ automapFillTri(surf, size2full - 1, 0, size2full - 1, size2full - 1, 0, size2half, colTable[kColorPartyFrame0]);
+ break;
+ case kIconTeleporter:
+ prepSurface(size2full, size2full, -1);
+ for (int i = 0; i < size2half; ++i) {
+ surf.drawLine(i, size2half - i, size2full - i - 1, size2half - i, colTable[kColorTele]);
+ surf.drawLine(i, size2half + i, size2full - i - 1, size2half + i, colTable[kColorTele]);
+ }
+ break;
+ case kIconStairsUp:
+ prepSurface(size2full, size2full, -1);
+ automapFillTri(surf, 0, size2full - 1, size2full - 1, size2full - 1, size2half, 0, colTable[kColorStair]);
+ break;
+ case kIconStairsDown:
+ prepSurface(size2full, size2full, -1);
+ automapFillTri(surf, 0, 0, size2full - 1, 0, size2half, size2full - 1, colTable[kColorStair]);
+ break;
+ case kIconPit:
+ case kIconPlate:
+ prepSurface(size2full, size2full, -1);
+ surf.drawEllipse(0, 0, size2full - 1, size2full - 1, colTable[iconID == kIconPit ? kColorPit : kColorPlate], true);
+ break;
+ case kIconIllusionWall:
+ prepSurface(boxFitWidth, boxFitWidth, fillBackground ? kColorFloor : -1);
+ for (int pos = 0, g = 0; g < 5; ++g, pos += step1) {
+ surf.drawLine(pos >> 8, 0, pos >> 8, boxFitWidth - 1, colTable[kColorWall]);
+ surf.drawLine(0, pos >> 8, boxFitWidth - 1, pos >> 8, colTable[kColorWall]);
+ }
+ break;
+ case 11:
+ break;
+ case kIconWallOfForce:
+ prepSurface(size1full, size1full, -1);
+ for (int posX = 0, g = 0; g < 3; ++g, posX += step2a) {
+ for (int posY = 0, gg = 0; gg < 3; ++gg, posY += step2a) {
+ surf.fillRect(Common::Rect(posX >> 8, posY >> 8, ((posX + step2) >> 8) - 1, ((posY + step2) >> 8) - 1), colTable[kColorWoF]);
+ if (g < 2 && gg < 2)
+ surf.fillRect(Common::Rect((posX + step2) >> 8, (posY + step2) >> 8, ((posX + step2a) >> 8) - 1, ((posY + step2a) >> 8) - 1), colTable[kColorWoF]);
+ }
+ }
+ break;
+ case kIconBigObject:
+ prepSurface(size2full, size2full, -1);
+ surf.fillRect(Common::Rect(size2full, size2full), colTable[kColorLever]);
+ break;
+ case kIconDoorButton:
+ prepSurface(sButtonSize, sButtonSize, -1);
+ surf.fillRect(Common::Rect(sButtonSize, sButtonSize), colTable[kColorLever]);
+ break;
+ case kIconSmallObject:
+ prepSurface(mButtonSize, mButtonSize, -1);
+ surf.fillRect(Common::Rect(mButtonSize, mButtonSize), colTable[kColorLever]);
+ break;
+ case kIconNicheNS:
+ prepSurface(nicheWd, mButtonSize, -1);
+ surf.fillRect(Common::Rect(nicheWd, mButtonSize), colTable[kColorNiche]);
+ break;
+ case kIconNicheEW:
+ prepSurface(mButtonSize, nicheWd, -1);
+ surf.fillRect(Common::Rect(mButtonSize, nicheWd), colTable[kColorNiche]);
+ break;
+ case kIconPortalNS:
+ prepSurface(portalWd, mButtonSize, -1);
+ surf.fillRect(Common::Rect(portalWd, mButtonSize), colTable[kColorTele]);
+ break;
+ case kIconPortalEW:
+ prepSurface(mButtonSize, portalWd, -1);
+ surf.fillRect(Common::Rect(mButtonSize, portalWd), colTable[kColorTele]);
+ break;
+ case kIconDoorNS:
+ prepSurface(doorLen, doorWdth, -1);
+ surf.fillRect(Common::Rect(doorLen, doorWdth), colTable[kColorDoor]);
+ break;
+ case kIconDoorEW:
+ prepSurface(doorWdth, doorLen, -1);
+ surf.fillRect(Common::Rect(doorWdth, doorLen), colTable[kColorDoor]);
+ break;
+ case kIconSpecial:
+ break;
+ default:
+ break;
+ }
+ };
+
+ releaseIcons();
+
+ // We make two sets of icons, one for the map, and one for the legend, so that the map icon size matches the cell size and the
+ // legend icon size matches the text line height. And we also want to have a wall background color for the legend illusion wall icon
+ // since it would otherwise be hardly visible on the "stone" background.
+ for (int i = 0; i < kIconIDMax; ++i) {
+ Graphics::Surface *surf = new Graphics::Surface();
+ renderIcon(*surf, i, _l.cell, (lowResTarget == false), false);
+ if (surf->getPixels() == nullptr) {
+ delete surf;
+ surf = nullptr;
+ }
+ _mapIcons.push_back(surf);
+
+ surf = new Graphics::Surface();
+ renderIcon(*surf, i, lineH, false, true);
+ if (surf->getPixels() == nullptr) {
+ delete surf;
+ surf = nullptr;
+ }
+ _legendIcons.push_back(surf);
+ }
+}
+
+void Automap_EoB::releaseIcons() {
+ for (uint i = 0; i < _mapIcons.size(); ++i) {
+ if (_mapIcons[i] == nullptr)
+ continue;
+ _mapIcons[i]->free();
+ delete _mapIcons[i];
+ }
+ _mapIcons.clear();
+ for (uint i = 0; i < _legendIcons.size(); ++i) {
+ if (_legendIcons[i] == nullptr)
+ continue;
+ _legendIcons[i]->free();
+ delete _legendIcons[i];
+ }
+ _legendIcons.clear();
+}
+
+// Sparse two-tone speckle to fake stone/paper grain
+void automapNoise(Graphics::Surface &s, int x0, int y0, int w, int h, uint32 cA, uint32 cB, uint32 seed) {
+ if (w <= 0 || h <= 0 || x0 < 0 || y0 < 0 || x0 + w > s.w || y0 + h > s.h)
+ return;
+ const int bpp = s.format.bytesPerPixel;
+ if (bpp != 2 && bpp != 4)
+ return;
+ uint32 st = seed | 1u;
+ for (int y = 0; y < h; ++y) {
+ byte *row = (byte *)s.getBasePtr(x0, y0 + y);
+ for (int x = 0; x < w; ++x) {
+ st = st * 1664525u + 1013904223u;
+ const uint32 v = st >> 24;
+ if (v < 26) {
+ const uint32 c = (v & 1u) ? cA : cB;
+ if (bpp == 2)
+ ((uint16 *)row)[x] = (uint16)c;
+ else
+ ((uint32 *)row)[x] = c;
+ }
+ }
+ }
+}
+
+void automapBevel(Graphics::Surface &s, const Common::Rect &r, uint32 hi, uint32 lo) {
+ s.hLine(r.left, r.top, r.right - 1, hi);
+ s.vLine(r.left, r.top, r.bottom - 1, hi);
+ s.hLine(r.left, r.bottom - 1, r.right - 1, lo);
+ s.vLine(r.right - 1, r.top, r.bottom - 1, lo);
+}
+
+void automapRivet(Graphics::Surface &s, int x, int y, int sz, uint32 dark, uint32 mid, uint32 hi) {
+ s.fillRect(Common::Rect(x - sz, y - sz, x + sz + 1, y + sz + 1), dark);
+ s.fillRect(Common::Rect(x - sz + 1, y - sz + 1, x + sz, y + sz), mid);
+ const int q = MAX(1, sz / 2);
+ s.fillRect(Common::Rect(x - sz + 1, y - sz + 1, x - sz + 1 + q, y - sz + 1 + q), hi);
+}
+
+void Automap_EoB::drawBackground(int width, int height) {
const Graphics::PixelFormat fmt = _system->getOverlayFormat();
const Graphics::Font *bigFont = FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont);
- const int fh = bigFont ? bigFont->getFontHeight() : 8;
- const int sc = CLIP<int>(width / 320, 1, 3);
- const int mpad = MAX(8, l.mapW / 40);
+ int fh = bigFont ? bigFont->getFontHeight() : 8;
+ int mpad = MAX(8, _l.mapW / 40);
- _automapBg->free();
- _automapBg->create(width, height, fmt);
- _automapFrame->free();
- _automapFrame->create(width, height, fmt);
- Graphics::Surface &bg = *_automapBg;
+ _background->free();
+ _background->create(width, height, fmt);
+ _frame->free();
+ _frame->create(width, height, fmt);
+ Graphics::Surface &bg = *_background;
bg.fillRect(Common::Rect(0, 0, width, height), _colors[kColorStone]);
automapNoise(bg, 0, 0, width, height, _colors[kColorStoneHi], _colors[kColorStoneEdge], 0x9e3779b9u);
automapBevel(bg, Common::Rect(0, 0, width, height), _colors[kColorStoneHi], _colors[kColorStoneEdge]);
automapBevel(bg, Common::Rect(2, 2, width - 2, height - 2), _colors[kColorStone], _colors[kColorStoneEdge]);
- const int rv = MAX(2, l.frame / 4);
- const int ri = l.frame / 2;
+ const int rv = MAX(2, _l.frame / 4);
+ const int ri = _l.frame / 2;
automapRivet(bg, ri, ri, rv, _colors[kColorStoneEdge], _colors[kColorStoneHi], _colors[kColorRivet]);
automapRivet(bg, width - ri, ri, rv, _colors[kColorStoneEdge], _colors[kColorStoneHi], _colors[kColorRivet]);
automapRivet(bg, ri, height - ri, rv, _colors[kColorStoneEdge], _colors[kColorStoneHi], _colors[kColorRivet]);
automapRivet(bg, width - ri, height - ri, rv, _colors[kColorStoneEdge], _colors[kColorStoneHi], _colors[kColorRivet]);
- const Common::Rect mr(l.mapX, l.mapY, l.mapX + l.mapW, l.mapY + l.mapH);
+ const Common::Rect mr(_l.mapX, _l.mapY, _l.mapX + _l.mapW, _l.mapY + _l.mapH);
bg.fillRect(mr, _colors[kColorPaperEdge]);
bg.fillRect(Common::Rect(mr.left + 2, mr.top + 2, mr.right - 2, mr.bottom - 2), _colors[kColorPaper]);
- automapNoise(bg, mr.left + 2, mr.top + 2, l.mapW - 4, l.mapH - 4, _colors[kColorPaperHi], _colors[kColorPaperLo], 0x85ebca6bu);
+ automapNoise(bg, mr.left + 2, mr.top + 2, _l.mapW - 4, _l.mapH - 4, _colors[kColorPaperHi], _colors[kColorPaperLo], 0x85ebca6bu);
bg.frameRect(Common::Rect(mr.left + 2, mr.top + 2, mr.right - 2, mr.bottom - 2), _colors[kColorInkSoft]);
bg.frameRect(Common::Rect(mr.left + 5, mr.top + 5, mr.right - 5, mr.bottom - 5), _colors[kColorInk]);
- bg.hLine(l.mapX + mpad, l.footY, l.mapX + l.mapW - mpad, _colors[kColorInkSoft]);
+ bg.hLine(_l.mapX + mpad, _l.footY, _l.mapX + _l.mapW - mpad, _colors[kColorInkSoft]);
- const Common::Rect sr(l.sideX, l.sideY, l.sideX + l.sideW, l.sideY + l.sideH);
+ const Common::Rect sr(_l.sideX, _l.sideY, _l.sideX + _l.sideW, _l.sideY + _l.sideH);
bg.fillRect(sr, _colors[kColorStoneDark]);
- automapNoise(bg, sr.left, sr.top, l.sideW, l.sideH, _colors[kColorStoneHi], _colors[kColorStoneEdge], 0xc2b2ae35u);
+ automapNoise(bg, sr.left, sr.top, _l.sideW, _l.sideH, _colors[kColorStoneHi], _colors[kColorStoneEdge], 0xc2b2ae35u);
automapBevel(bg, sr, _colors[kColorStoneEdge], _colors[kColorStoneHi]);
- bg.fillRect(Common::Rect(l.plX, l.plY, l.plX + l.plW, l.plY + l.plH), _colors[kColorPlaqueEd]);
- bg.fillRect(Common::Rect(l.plX + 2, l.plY + 2, l.plX + l.plW - 2, l.plY + l.plH - 2), _colors[kColorPlaqueBg]);
+ bg.fillRect(Common::Rect(_l.plX, _l.plY, _l.plX + _l.plW, _l.plY + _l.plH), _colors[kColorPlaqueEd]);
+ bg.fillRect(Common::Rect(_l.plX + 2, _l.plY + 2, _l.plX + _l.plW - 2, _l.plY + _l.plH - 2), _colors[kColorPlaqueBg]);
- int lx = l.plX, colW = l.plW;
- int cyy = l.plY + l.plH + MAX(8, sc * 6);
+ int cyy = _l.plY + _l.plH + MAX<int>(8, _legendHeadScl * 6);
- automapDrawBigString(bg, bigFont, _legendStrings[0], lx, cyy, colW, _colors[kColorGoldDim], sc);
- cyy += fh * sc + MAX(4, sc * 3);
- bg.hLine(lx, cyy, lx + colW - 1, _colors[kColorStoneHi]);
- bg.hLine(lx, cyy + 1, lx + colW - 1, _colors[kColorStoneEdge]);
+ drawString(bg, bigFont, _legendStrings[0], _l.plX, cyy, _l.plW, _colors[kColorGoldDim], _legendHeadScl);
+ cyy += fh * _legendHeadScl + MAX<int>(4, _legendHeadScl * 3);
+ bg.hLine(_l.plX, cyy, _l.plX + _l.plW - 1, _colors[kColorStoneHi]);
+ bg.hLine(_l.plX, cyy + 1, _l.plX + _l.plW - 1, _colors[kColorStoneEdge]);
}
-void Automap_EoB::drawLegend(const AutomapLayout &l, uint flags) {
- Graphics::Surface &bg = *_automapFrame;
+void Automap_EoB::drawLegend(uint flags) {
+ Graphics::Surface &bg = *_frame;
const Graphics::Font *bigFont = FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont);
int fh = bigFont ? bigFont->getFontHeight() : 8;
- int sc = CLIP<int>(_automapBg->w / 320, 1, 3);
- int isz = MAX(6, fh * sc);
- int rowH = fh * sc + MAX(4, sc * 4);
- int lx = l.plX;
- int colW = l.plW;
- int cyy = l.plY + l.plH + MAX(8, sc * 6) + fh * sc + MAX(4, sc * 3) + MAX(6, sc * 4);
-
- for (int i = 0; i < ARRAYSIZE(_stringTable[0].legendStrings) - 1; ++i) {
+
+ const IconID legendIcons[][2] = {
+ { kIconPartyNorth, kIconNone },
+ { kIconPartyEast, kIconNone },
+ { kIconPartySouth, kIconNone },
+ { kIconPartyWest, kIconNone },
+ { kIconStairsUp, kIconNone },
+ { kIconStairsDown, kIconNone },
+ { kIconTeleporter, kIconNone },
+ { kIconPit, kIconNone },
+ { kIconPlate, kIconNone },
+ { kIconDoorNS, kIconNone },
+ { kIconDoorNS, kIconNone },
+ { kIconIllusionWall, kIconNone },
+ { kIconWallOfForce, kIconNone },
+ { kIconBigObject, kIconSmallObject },
+ { kIconBigObject, kIconSmallObject },
+ { kIconNicheNS, kIconNone },
+ { kIconPortalNS, kIconNone }
+ };
+
+ assert(ARRAYSIZE(legendIcons) == kNumLegendStrings + 2); // ignore first string (title), but there is also only one string for the party (regardless of direction)
+
+ int lh = fh * _legendBodyScl;
+ int isz = 2 * lh + MAX(2, _legendHeadScl * 2);
+ int tw = _l.plW - isz;
+ int rowH = lh + MAX(4, _legendBodyScl * 4);
+
+ int cyy = _l.plY + _l.plH + MAX(8, _legendBodyScl * 6) + fh * _legendHeadScl + MAX(4, _legendHeadScl * 3) + (isz - lh) / 2;
+ int tx = _l.plX + isz;
+ int strIdx = 1;
+
+ for (int i = 0; i < ARRAYSIZE(legendIcons); ++i) {
+ if (i > 3) // same string for the party icon, regardless of direction
+ ++strIdx;
+
if (!(flags & (1 << i)))
continue;
- int ix = lx;
- int iy = cyy;
+ int ovr = (i == 10 || i == 14) ? kColorInteractive : -1;
- switch (i) {
- case 0: // party (up)
- automapFillTri(bg, ix + isz / 2, iy, ix + isz, iy + isz, ix, iy + isz, _colors[kColorParty]);
- break;
- case 1: // stairs up
- automapFillTri(bg, ix, iy + isz, ix + isz, iy + isz, ix + isz / 2, iy, _colors[kColorStair]);
- break;
- case 2: // stairs down
- automapFillTri(bg, ix, iy, ix + isz, iy, ix + isz / 2, iy + isz, _colors[kColorStair]);
- break;
- case 3: // teleporter
- for (int ii = 0; ii < isz / 2 - sc; ++ii) {
- bg.drawLine(ix + sc + ii, iy + isz / 2 - ii, ix + isz - sc - ii, iy + isz / 2 - ii, _colors[kColorTele]);
- bg.drawLine(ix + sc + ii, iy + isz / 2 + ii, ix + isz - sc - ii, iy + isz / 2 + ii, _colors[kColorTele]);
- }
- break;
- case 4: // pit
- bg.drawEllipse(ix + sc, iy + sc, ix + isz - sc, iy + isz - sc, _colors[kColorPit], true);
- break;
- case 5: // plate
- bg.drawEllipse(ix + sc, iy + sc, ix + isz - sc, iy + isz - sc, _colors[kColorPlate], true);
- break;
- case 6: // door
- bg.fillRect(Common::Rect(ix, iy + isz / 3, ix + isz, iy + isz - isz / 3), _colors[kColorDoor]);
- break;
- case 7: // barrier
- bg.fillRect(Common::Rect(ix, iy + isz / 3, ix + isz, iy + isz - isz / 3), _colors[kColorInteractive]);
- break;
- case 8: { // illusionary wall
- bg.fillRect(Common::Rect(ix + sc, iy + sc, ix + isz - sc, iy + isz - sc), _colors[kColorFloor]);
- int step = ((isz - 2 * sc) << 8) / 5;
- for (int g = 0; g < 5; ++g) {
- bg.drawLine(ix + sc + (step * g >> 8), iy + sc, ix + sc + (step * g >> 8), iy + isz - sc, _colors[kColorWall]);
- bg.drawLine(ix + sc, iy + sc + (step * g >> 8), ix + isz - sc, iy + sc + (step * g >> 8), _colors[kColorWall]);
- }
- } break;
- case 9: // wall of force
- bg.fillRect(Common::Rect(ix + sc, iy + sc, ix + isz - sc, iy + isz - sc), _colors[kColorWoF]);
- break;
- case 10:
- bg.fillRect(Common::Rect(ix + isz / 3, iy, ix + isz - isz / 3, iy + isz), _colors[kColorLever]);
- break;
- case 11:
- bg.fillRect(Common::Rect(ix + isz / 3, iy, ix + isz - isz / 3, iy + isz), _colors[kColorInteractive]);
- break;
- case 12: // niche
- bg.fillRect(Common::Rect(ix + isz / 3, iy, ix + isz - isz / 3, iy + isz), _colors[kColorNiche]);
- break;
- case 13: // stone portal
- bg.fillRect(Common::Rect(ix + isz / 3, iy, ix + isz - isz / 3, iy + isz), _colors[kColorTele]);
- default:
- break;
- }
- const int tx = lx + isz + MAX(4, sc * 2);
- const int tw = colW - isz - MAX(4, sc * 2);
- const int lsc = automapFit(bigFont, _legendStrings[i + 1], tw, sc);
- automapDrawBigString(bg, bigFont, _legendStrings[i + 1], tx, iy + (isz - fh * lsc) / 2, tw, _colors[kColorPanelTxt], lsc, Graphics::kTextAlignLeft);
+ (this->*_drawIcon)(bg, 1, legendIcons[i][0], _l.plX, cyy, lh, kAlignCenter, 0, 0, ovr);
+ if (legendIcons[i][1] != kIconNone)
+ (this->*_drawIcon)(bg, 1, legendIcons[i][1], _l.plX + lh, cyy, lh, kAlignCenter, 0, 0, ovr);
+
+ drawString(bg, bigFont, _legendStrings[strIdx], tx, cyy, tw, _colors[kColorPanelTxt], _legendBodyScl, Graphics::kTextAlignLeft);
cyy += rowH;
}
- cyy += MAX(6, sc * 5);
- automapDrawBigString(bg, bigFont, _controlStrings[0], lx, cyy, colW, _colors[kColorGoldDim], sc);
- cyy += fh * sc + MAX(4, sc * 3);
- bg.hLine(lx, cyy, lx + colW - 1, _colors[kColorStoneHi]);
- bg.hLine(lx, cyy + 1, lx + colW - 1, _colors[kColorStoneEdge]);
- cyy += MAX(6, sc * 4);
- const int chipPad = MAX(2, sc * 2);
- const int chipH = fh * sc + chipPad;
+ cyy += MAX(4, _legendHeadScl * 4);
+ drawString(bg, bigFont, _controlStrings[0], _l.plX, cyy, _l.plW, _colors[kColorGoldDim], _legendHeadScl);
+ cyy += fh * _legendHeadScl + MAX(4, _legendHeadScl * 3);
+ bg.hLine(_l.plX, cyy, _l.plX + _l.plW - 1, _colors[kColorStoneHi]);
+ bg.hLine(_l.plX, cyy + 1, _l.plX + _l.plW - 1, _colors[kColorStoneEdge]);
+ cyy += MAX(6, _legendBodyScl * 6);
+ const int chipPad = MAX(2, _legendBodyScl * 2);
+ const int chipH = lh + chipPad;
int maxW = 0;
- for (int i = 2; i < ARRAYSIZE(_stringTable[0].controlStrings); i += 2)
- maxW = MAX<int>(maxW, (bigFont ? bigFont->getStringWidth(_controlStrings[i]) * sc : 6 * sc) + chipPad * 2);
-
- for (int i = 2; i < ARRAYSIZE(_stringTable[0].controlStrings); i += 2) {
- int chipW = (bigFont ? bigFont->getStringWidth(_controlStrings[i]) * sc : 6 * sc) + chipPad * 2;
- bg.fillRect(Common::Rect(lx, cyy, lx + chipW, cyy + chipH), _colors[kColorPlaqueEd]);
- bg.fillRect(Common::Rect(lx + 1, cyy + 1, lx + chipW - 1, cyy + chipH - 1), _colors[kColorPlaqueBg]);
- automapDrawBigString(bg, bigFont, _controlStrings[i], lx, cyy + chipPad / 2, chipW, _colors[kColorGold], sc);
- automapDrawBigString(bg, bigFont, _controlStrings[i + 1], lx + maxW + MAX(4, sc * 3), cyy + (chipH - fh * sc) / 2, colW - maxW - MAX(4, sc * 3), _colors[kColorPanelTxt], sc, Graphics::kTextAlignLeft);
- cyy += fh * sc + MAX(4, sc * 3);
+ for (int i = 2; i < kNumControlStrings; i += 2)
+ maxW = MAX<int>(maxW, (bigFont ? bigFont->getStringWidth(_controlStrings[i]) * _legendBodyScl : 6 * _legendBodyScl) + chipPad * 2);
+
+ for (int i = 2; i < kNumControlStrings; i += 2) {
+ int chipW = (bigFont ? bigFont->getStringWidth(_controlStrings[i]) * _legendBodyScl : 6 * _legendBodyScl) + chipPad * 2;
+ bg.fillRect(Common::Rect(_l.plX, cyy, _l.plX + chipW, cyy + chipH), _colors[kColorPlaqueEd]);
+ bg.fillRect(Common::Rect(_l.plX + 1, cyy + 1, _l.plX + chipW - 1, cyy + chipH - 1), _colors[kColorPlaqueBg]);
+ drawString(bg, bigFont, _controlStrings[i], _l.plX, cyy + chipPad / 2, chipW, _colors[kColorGold], _legendBodyScl);
+ drawString(bg, bigFont, _controlStrings[i + 1], _l.plX + maxW + MAX(4, _legendBodyScl * 3), cyy + (chipH - lh) / 2, _l.plW - maxW - MAX(4, _legendBodyScl * 3), _colors[kColorPanelTxt], _legendBodyScl, Graphics::kTextAlignLeft);
+ cyy += lh + MAX(4, _legendBodyScl * 3);
+ }
+}
+
+template<typename T> void Automap_EoB::drawIconImpl(Graphics::Surface &surf, int iconSet, int iconID, int cellX, int cellY, int boxFitWidth, IconAlignment alignment, int extraX, int extraY, int overrideColor) {
+ assert(iconID >= 0 && ((iconSet == 0 && iconID < (int)_mapIcons.size()) || (iconSet == 1 && iconID < (int)_legendIcons.size())));
+ if ((iconSet == 0 && _mapIcons[iconID] == nullptr) || (iconSet == 1 && _legendIcons[iconID] == nullptr) || iconSet < 0 || iconSet > 1)
+ return;
+
+ int x = cellX, y = cellY;
+ Graphics::Surface &icn = (iconSet == 0) ? *_mapIcons[iconID] : *_legendIcons[iconID];
+
+ if (alignment >= kAlignTopCenter && alignment <= kAlignCenter) {
+ const uint8 alignFlags[] = { 1, 6, 9, 2, 3 };
+ if (alignFlags[alignment] & 1)
+ x += ((boxFitWidth >> 1) - (icn.w >> 1));
+ if (alignFlags[alignment] & 2)
+ y += ((boxFitWidth >> 1) - (icn.h >> 1));
+ if (alignFlags[alignment] & 4)
+ x += (boxFitWidth - icn.w);
+ if (alignFlags[alignment] & 8)
+ y += (boxFitWidth - icn.h);
+ }
+
+ x += extraX;
+ y += extraY;
+
+ const T *src = reinterpret_cast<const T*>(icn.getPixels());
+ T *dst = reinterpret_cast<T*>(surf.getBasePtr(x, y));
+ T kc = static_cast<T>(_colors[kColorTransp]);
+ int pitch = surf.pitch / sizeof(T);
+
+ if (overrideColor >= 0) {
+ uint32 col = _colors[overrideColor];
+ for (int h = icn.h; h > 0; --h) {
+ T *d = dst;
+ for (int w = icn.w; w > 0; --w) {
+ if (*src++ != kc)
+ *d = col;
+ ++d;
+ }
+ dst += pitch;
+ }
+ } else {
+ for (int h = icn.h; h > 0; --h) {
+ T *d = dst;
+ for (int w = icn.w; w > 0; --w) {
+ T pixel = *src++;
+ if (pixel != kc)
+ *d = pixel;
+ ++d;
+ }
+ dst += pitch;
+ }
}
}
+template void Automap_EoB::drawIconImpl<uint8>(Graphics::Surface&, int, int, int, int, int, IconAlignment, int, int, int);
+template void Automap_EoB::drawIconImpl<uint16>(Graphics::Surface&, int, int, int, int, int, IconAlignment, int, int, int);
+template void Automap_EoB::drawIconImpl<uint32>(Graphics::Surface&, int, int, int, int, int, IconAlignment, int, int, int);
+
+int Automap_EoB::fitString(const Graphics::Font *f, const Common::String &str, int maxW, int maxSc) const {
+ int wdth = f ? f->getStringWidth(str) : 0;
+ return (wdth == 0) ? 1 : CLIP<int>(maxW / wdth, 1, maxSc);
+}
+
uint16 Automap_EoB::calcNewBlockPosition(uint16 block, int8 dir) const {
- static const int16 blockPosTable[] = {-32, 1, 32, -1};
+ const int16 blockPosTable[] = { -32, 1, 32, -1 };
return (block + blockPosTable[dir & 3]) & 0x3FF;
}
@@ -1028,6 +1208,8 @@ int EoBCoreEngine::clickedAutomap(Button *button) {
int currentBlock = _currentBlock;
int currentDirection = _currentDirection;
int numLevels = (_flags.gameID == GI_EOB2) ? 16 : 12;
+ const uint8 partyIconBlinkSpeed = 100;
+ uint32 partyIconBlinkTimer = _system->getMillis() + partyIconBlinkSpeed;
int ow = _system->getOverlayWidth();
int oh = _system->getOverlayHeight();
@@ -1088,6 +1270,11 @@ int EoBCoreEngine::clickedAutomap(Button *button) {
if (update) {
_automap->draw(_currentLevel, _currentBlock, _currentDirection);
update = false;
+ } else if (_currentBlock != 0xFFFF) {
+ if (_system->getMillis() > partyIconBlinkTimer) {
+ _automap->drawPartyIcon(_currentBlock, _currentDirection);
+ partyIconBlinkTimer = _system->getMillis() + partyIconBlinkSpeed;
+ }
}
delayUntil(frameEnd, false, false, false); // TODO? allow GMM loading on the map screen, seen comment above
diff --git a/engines/kyra/gui/automap_eob.h b/engines/kyra/gui/automap_eob.h
index 6025908048b..c79fe3bbf3f 100644
--- a/engines/kyra/gui/automap_eob.h
+++ b/engines/kyra/gui/automap_eob.h
@@ -24,6 +24,7 @@
#if defined(ENABLE_EOB) || defined(ENABLE_LOL)
+#include "common/array.h"
#include "common/scummsys.h"
namespace Common {
@@ -32,6 +33,7 @@ class SeekableReadStreamEndianWrapper;
} // End of namespace Common
namespace Graphics {
+class Font;
struct Surface;
} // End of namespace Graphics
@@ -44,13 +46,14 @@ class EoBCoreEngine; // TODO: REMOVE
class Automap_EoB {
public:
- Automap_EoB(OSystem *system, LevelBlockProperty **blockData, const uint8 *wllFlags, const uint8 *specialWallTypes, const int8 *wllShapeMap, int gameID, int lang, bool featureEnabled);
+ Automap_EoB(OSystem *system, LevelBlockProperty **blockData, const uint8 *wllFlags, const uint8 *specialWallTypes, const int8 *wllShapeMap, /* const uint8 *wllVmpMap,*/ int gameID, int lang, bool featureEnabled);
~Automap_EoB();
void markVisited(uint16 block);
void markSeen(uint16 block, int8 dir);
void draw(int level, uint16 partyBlock, int8 partyDirection);
+ void drawPartyIcon(uint16 partyBlock, int8 partyDirection);
private:
// Geometry shared by drawing and click hit-testing.
@@ -64,16 +67,65 @@ private:
int footY; // top of the selected-note footer strip
};
+ enum {
+ kNumLegendStrings = 15,
+ kNumControlStrings = 6
+ };
+
struct TranslateableStrings {
- const char *const legendStrings[15];
- const char *const controlStrings[6];
+ const char *const legendStrings[kNumLegendStrings];
+ const char *const controlStrings[kNumControlStrings];
const char *const levelNames[2][16];
};
- AutomapLayout createLayout() const;
- void createColors();
- void redrawBackground(const AutomapLayout &l, int width, int height);
- void drawLegend(const AutomapLayout &l, uint flags);
+ enum IconID : int {
+ kIconNone = -1,
+ kIconPartyNorth = 0,
+ kIconPartyEast = 1,
+ kIconPartySouth = 2,
+ kIconPartyWest = 3,
+ kIconTeleporter = 4,
+ kIconStairsUp = 5,
+ kIconStairsDown = 6,
+ kIconPit = 7,
+ kIconPlate = 8,
+ kIconIllusionWall = 10,
+ kIconWallOfForce = 12,
+ kIconBigObject = 15,
+ kIconDoorButton = 17,
+ kIconSmallObject = 18,
+ kIconNicheNS = 20,
+ kIconNicheEW = 21,
+ kIconPortalNS = 22,
+ kIconPortalEW = 23,
+ kIconDoorNS = 25,
+ kIconDoorEW = 26,
+ kIconSpecial = 30,
+ kIconIDMax = 35
+ };
+
+ AutomapLayout createLayout(int width, int height) const;
+ void recalcScaling(int width, int height);
+ void createColorTable();
+ void createIcons(bool lowResSurface);
+ void releaseIcons();
+ void drawBackground(int width, int height);
+ void drawLegend(uint flags);
+
+ enum IconAlignment : int {
+ kAlignTopLeft = -1,
+ kAlignTopCenter = 0,
+ kAlignRightCenter,
+ kAlignBottomCenter,
+ kAlignLeftCenter,
+ kAlignCenter
+ };
+
+ template<typename T> void drawIconImpl(Graphics::Surface &surf, int iconSet, int iconID, int cellX, int cellY, int boxFitWidth, IconAlignment alignment, int extraX, int extraY, int overrideColor);
+ typedef void (Automap_EoB::*DrawIconFunc)(Graphics::Surface &surf, int iconSet, int iconID, int cellX, int cellY, int boxFitWidth, IconAlignment alignment, int extraX, int extraY, int overrideColor);
+ DrawIconFunc _drawIcon;
+
+ int fitString(const Graphics::Font *f, const Common::String &str, int maxW, int maxSc) const;
uint16 calcNewBlockPosition(uint16 block, int8 dir) const;
bool isVisited(uint16 block) const;
bool isSeen(uint16 block) const;
@@ -95,22 +147,39 @@ private:
const uint8 *const _wllWallFlags;
const uint8 *const _specialWallTypes;
const int8 *const _wllShapeMap;
- const uint8 *_specialBlockIDs;
- int _numSpecialBlockIDs;
+ //const uint8 *const _wllVmpMap;
const uint8 _wallOfForceID;
const uint8 *_portalParams;
int _portalParamsLen;
const bool _enabled;
+ struct SpecialWallType {
+ uint8 wall;
+ uint8 icon;
+ uint16 legendFlag;
+ };
+ const SpecialWallType *_specialBlockIDs;
+ int _numSpecialBlockIDs;
+
static const TranslateableStrings _stringTable[];
const char *const *_legendStrings;
const char *const *_controlStrings;
const char *const *_levelNames;
const int _numLevelNames;
+ AutomapLayout _l;
+ int _levelStrScl;
+ int _coordStrScl;
+ int _legendHeadScl;
+ int _legendBodyScl;
+ int _levelStrY;
+ int _coordStrY;
+
OSystem *_system;
- Graphics::Surface *_automapBg;
- Graphics::Surface *_automapFrame;
+ Graphics::Surface *_background;
+ Graphics::Surface *_frame;
+ Common::Array<Graphics::Surface*> _mapIcons;
+ Common::Array<Graphics::Surface*> _legendIcons;
private:
// Colors
@@ -140,17 +209,28 @@ private:
kColorLever,
kColorInteractive,
kColorNiche,
- kColorParty,
- kColorPartyEdge,
kColorPlaqueBg,
kColorPlaqueEd,
kColorGold,
kColorGoldDim,
kColorPanelTxt,
+ kColorTransp,
+ kColorPartyFrame0,
+ kColorPartyFrame1,
+ kColorPartyFrame2,
+ kColorPartyFrame3,
+ kColorPartyFrame4,
+ kColorPartyFrame5,
+ kColorPartyFrame6,
+ kColorPartyFrame7,
+ kColorPartyFrame8,
+ kColorPartyFrame9,
kNumColors
};
const uint32 *_colors;
+ int _partyIconColor;
+ int _partyIconColorStep;
};
} // End of namespace Kyra
More information about the Scummvm-git-logs
mailing list