Skip to content

Commit 9e51f1f

Browse files
committed
Allow custom assets for towns
1 parent f9d4898 commit 9e51f1f

File tree

8 files changed

+710
-568
lines changed

8 files changed

+710
-568
lines changed

Source/diablo.cpp

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
#include "levels/setmaps.h"
7070
#include "levels/themes.h"
7171
#include "levels/town.h"
72+
#include "levels/town_data.h"
7273
#include "levels/trigs.h"
7374
#include "lighting.h"
7475
#include "loadsave.h"
@@ -109,6 +110,7 @@
109110
#include "utils/display.h"
110111
#include "utils/is_of.hpp"
111112
#include "utils/language.h"
113+
#include "utils/log.hpp"
112114
#include "utils/parse_int.hpp"
113115
#include "utils/paths.h"
114116
#include "utils/screen_reader.hpp"
@@ -1369,19 +1371,40 @@ tl::expected<void, std::string> LoadLvlGFX()
13691371

13701372
switch (leveltype) {
13711373
case DTYPE_TOWN: {
1372-
auto cel = LoadFileInMemWithStatus("nlevels\\towndata\\town.cel");
1374+
const TownVisualAssets &active = GetActiveTownConfigForTileLoad().visualAssets;
1375+
const TownVisualAssets &tristramAssets = GetTownRegistry().GetTown(TristramTownId).visualAssets;
1376+
LogVerbose("LoadLvlGFX (town): cel {} | til {} | special {} | pal {}",
1377+
active.dungeonCelPath, active.megaTilePath, active.specialCelsPath, active.palettePath);
1378+
1379+
// CEL: try mod/active primary, then Tristram chain on failure.
1380+
auto cel = LoadFileInMemWithStatus(active.dungeonCelPath.c_str());
13731381
if (!cel.has_value()) {
1374-
ASSIGN_OR_RETURN(pDungeonCels, LoadFileInMemWithStatus("levels\\towndata\\town.cel"));
1382+
if (active.dungeonCelPath != tristramAssets.dungeonCelPath)
1383+
cel = LoadFileInMemWithStatus(tristramAssets.dungeonCelPath.c_str());
1384+
if (!cel.has_value()) {
1385+
ASSIGN_OR_RETURN(pDungeonCels, LoadFileInMemWithStatus(TristramRetailTownPaths::DungeonCel));
1386+
} else {
1387+
pDungeonCels = std::move(*cel);
1388+
}
13751389
} else {
13761390
pDungeonCels = std::move(*cel);
13771391
}
1378-
auto til = LoadFileInMemWithStatus<MegaTile>("nlevels\\towndata\\town.til");
1392+
1393+
// TIL: same pattern.
1394+
auto til = LoadFileInMemWithStatus<MegaTile>(active.megaTilePath.c_str());
13791395
if (!til.has_value()) {
1380-
ASSIGN_OR_RETURN(pMegaTiles, LoadFileInMemWithStatus<MegaTile>("levels\\towndata\\town.til"));
1396+
if (active.megaTilePath != tristramAssets.megaTilePath)
1397+
til = LoadFileInMemWithStatus<MegaTile>(tristramAssets.megaTilePath.c_str());
1398+
if (!til.has_value()) {
1399+
ASSIGN_OR_RETURN(pMegaTiles, LoadFileInMemWithStatus<MegaTile>(TristramRetailTownPaths::MegaTile));
1400+
} else {
1401+
pMegaTiles = std::move(*til);
1402+
}
13811403
} else {
13821404
pMegaTiles = std::move(*til);
13831405
}
1384-
ASSIGN_OR_RETURN(pSpecialCels, LoadCelWithStatus("levels\\towndata\\towns", SpecialCelWidth));
1406+
1407+
ASSIGN_OR_RETURN(pSpecialCels, LoadCelWithStatus(active.specialCelsPath.c_str(), SpecialCelWidth));
13851408
return {};
13861409
}
13871410
case DTYPE_CATHEDRAL:

Source/engine/palette.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@
2525
#include "engine/random.hpp"
2626
#include "headless_mode.hpp"
2727
#include "hwcursor.hpp"
28+
#include "levels/town_data.h"
2829
#include "options.h"
2930
#include "utils/display.h"
31+
#include "utils/log.hpp"
3032
#include "utils/palette_blending.hpp"
3133
#include "utils/sdl_compat.h"
3234
#include "utils/str_cat.hpp"
@@ -205,7 +207,9 @@ void LoadRndLvlPal(dungeon_type l)
205207
return;
206208

207209
if (l == DTYPE_TOWN) {
208-
LoadPaletteAndInitBlending("levels\\towndata\\town.pal");
210+
const char *palPath = GetActiveTownConfigForTileLoad().visualAssets.palettePath.c_str();
211+
LogVerbose("LoadRndLvlPal (town): {}", palPath);
212+
LoadPaletteAndInitBlending(palPath);
209213
return;
210214
}
211215

Source/engine/render/scrollrt.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,7 @@ static void DrawDungeon(const Surface & /*out*/, const Lightmap & /*lightmap*/,
547547
void DrawCell(const Surface &out, const Lightmap lightmap, Point tilePosition, Point targetBufferPosition, int lightTableIndex)
548548
{
549549
const uint16_t levelPieceId = dPiece[tilePosition.x][tilePosition.y];
550+
if (levelPieceId >= MAXTILES) return;
550551
const MICROS *pMap = &DPieceMicros[levelPieceId];
551552

552553
const uint8_t *tbl = LightTables[lightTableIndex].data();
@@ -686,6 +687,7 @@ void DrawFloorTile(const Surface &out, const Lightmap &lightmap, Point tilePosit
686687
#endif
687688

688689
const uint16_t levelPieceId = dPiece[tilePosition.x][tilePosition.y];
690+
if (levelPieceId >= MAXTILES) return;
689691
{
690692
const LevelCelBlock levelCelBlock { DPieceMicros[levelPieceId].mt[0] };
691693
if (levelCelBlock.hasValue()) {

Source/levels/gendung.cpp

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include <cstddef>
44
#include <cstdint>
5+
#include <cstring>
56
#include <memory>
67
#include <stack>
78
#include <string>
@@ -78,12 +79,18 @@ std::unique_ptr<uint16_t[]> LoadMinData(size_t &tileCount)
7879
{
7980
switch (leveltype) {
8081
case DTYPE_TOWN: {
81-
auto min = LoadFileInMemWithStatus<uint16_t>("nlevels\\towndata\\town.min", &tileCount);
82+
const TownVisualAssets &active = GetActiveTownConfigForTileLoad().visualAssets;
83+
const TownVisualAssets &tristramAssets = GetTownRegistry().GetTown(TristramTownId).visualAssets;
84+
LogVerbose("LoadMinData (town): {}", active.pieceMinPath);
85+
auto min = LoadFileInMemWithStatus<uint16_t>(active.pieceMinPath.c_str(), &tileCount);
8286
if (!min.has_value()) {
83-
return LoadFileInMem<uint16_t>("levels\\towndata\\town.min", &tileCount);
84-
} else {
87+
if (active.pieceMinPath != tristramAssets.pieceMinPath)
88+
min = LoadFileInMemWithStatus<uint16_t>(tristramAssets.pieceMinPath.c_str(), &tileCount);
89+
if (!min.has_value())
90+
return LoadFileInMem<uint16_t>(TristramRetailTownPaths::PieceMin, &tileCount);
8591
return std::move(*min);
8692
}
93+
return std::move(*min);
8794
}
8895
case DTYPE_CATHEDRAL:
8996
return LoadFileInMem<uint16_t>("levels\\l1data\\l1.min", &tileCount);
@@ -443,11 +450,20 @@ void CreateDungeon(uint32_t rseed, lvl_entry entry)
443450
tl::expected<void, std::string> LoadLevelSOLData()
444451
{
445452
switch (leveltype) {
446-
case DTYPE_TOWN:
447-
if (!LoadFileInMemWithStatus("nlevels\\towndata\\town.sol", SOLData).has_value()) {
448-
RETURN_IF_ERROR(LoadFileInMemWithStatus("levels\\towndata\\town.sol", SOLData));
453+
case DTYPE_TOWN: {
454+
const TownVisualAssets &active = GetActiveTownConfigForTileLoad().visualAssets;
455+
const TownVisualAssets &tristramAssets = GetTownRegistry().GetTown(TristramTownId).visualAssets;
456+
LogVerbose("LoadLevelSOLData (town): {}", active.solPath);
457+
if (!LoadFileInMemWithStatus(active.solPath.c_str(), SOLData).has_value()) {
458+
// Try Tristram primary (skip if same path to avoid a redundant attempt).
459+
bool loaded = active.solPath != tristramAssets.solPath
460+
&& LoadFileInMemWithStatus(tristramAssets.solPath.c_str(), SOLData).has_value();
461+
if (!loaded) {
462+
RETURN_IF_ERROR(LoadFileInMemWithStatus(TristramRetailTownPaths::Sol, SOLData));
463+
}
449464
}
450465
break;
466+
}
451467
case DTYPE_CATHEDRAL:
452468
RETURN_IF_ERROR(LoadFileInMemWithStatus("levels\\l1data\\l1.sol", SOLData));
453469
// Fix incorrectly marked arched tiles
@@ -509,12 +525,15 @@ tl::expected<void, std::string> LoadLevelSOLData()
509525

510526
void SetDungeonMicros(std::unique_ptr<std::byte[]> &dungeonCels, uint_fast8_t &microTileLen)
511527
{
528+
// Clear the DPieceMicros array so if other towns have less tiles, they don't break with segfault
529+
std::memset(DPieceMicros, 0, sizeof(DPieceMicros));
530+
512531
microTileLen = 10;
513532
size_t blocks = 10;
514533

515534
if (leveltype == DTYPE_TOWN) {
516-
microTileLen = 16;
517-
blocks = 16;
535+
microTileLen = GetActiveTownConfigForTileLoad().visualAssets.microTileLen;
536+
blocks = microTileLen;
518537
} else if (leveltype == DTYPE_HELL) {
519538
microTileLen = 12;
520539
blocks = 16;
@@ -523,9 +542,14 @@ void SetDungeonMicros(std::unique_ptr<std::byte[]> &dungeonCels, uint_fast8_t &m
523542
size_t tileCount;
524543
const std::unique_ptr<uint16_t[]> levelPieces = LoadMinData(tileCount);
525544

545+
const size_t pieceCount = tileCount / blocks;
546+
if (pieceCount > MAXTILES) {
547+
LogWarn("Tileset has {} level pieces but the engine supports at most {}; extra pieces will be ignored.",
548+
pieceCount, MAXTILES);
549+
}
526550
ankerl::unordered_dense::map<uint16_t, DunFrameInfo> frameToTypeMap;
527551
frameToTypeMap.reserve(4096);
528-
for (size_t levelPieceId = 0; levelPieceId < tileCount / blocks; levelPieceId++) {
552+
for (size_t levelPieceId = 0; levelPieceId < std::min(pieceCount, static_cast<size_t>(MAXTILES)); levelPieceId++) {
529553
uint16_t *pieces = &levelPieces[blocks * levelPieceId];
530554
for (uint32_t block = 0; block < blocks; block++) {
531555
const LevelCelBlock levelCelBlock { Swap16LE(pieces[blocks - 2 + (block & 1) - (block & 0xE)]) };
@@ -546,7 +570,7 @@ void SetDungeonMicros(std::unique_ptr<std::byte[]> &dungeonCels, uint_fast8_t &m
546570

547571
std::vector<std::pair<uint16_t, uint16_t>> celBlockAdjustments = ComputeCelBlockAdjustments(frameToTypeList);
548572
if (celBlockAdjustments.size() == 0) return;
549-
for (size_t levelPieceId = 0; levelPieceId < tileCount / blocks; levelPieceId++) {
573+
for (size_t levelPieceId = 0; levelPieceId < std::min(pieceCount, static_cast<size_t>(MAXTILES)); levelPieceId++) {
550574
for (uint32_t block = 0; block < blocks; block++) {
551575
LevelCelBlock &levelCelBlock = DPieceMicros[levelPieceId].mt[block];
552576
const uint16_t frame = levelCelBlock.frame();

0 commit comments

Comments
 (0)