diff --git a/src/jasper/modules/world/include/jasper/world/objectcache.hpp b/src/jasper/modules/world/include/jasper/world/objectcache.hpp index 05a1ce8..5cd73ef 100644 --- a/src/jasper/modules/world/include/jasper/world/objectcache.hpp +++ b/src/jasper/modules/world/include/jasper/world/objectcache.hpp @@ -18,7 +18,7 @@ class ObjectCache { struct Obj { ObjectId id{}; uint16_t palBank{}; - size_t tileIdx{}; + size_t tilesheetIdx{}; }; private: struct ObjSet { @@ -40,13 +40,15 @@ class ObjectCache { [[nodiscard]] ox::Optional obj(uint64_t setId, ObjectId objId) const noexcept; [[nodiscard]] - ncore::TileSheetSet const&tilesheets() const noexcept; + ncore::TileSheetSet const&tilesheetSet() const noexcept; + [[nodiscard]] + ox::Vector const&tilesheets() const noexcept; [[nodiscard]] ox::Vector const&palettes() const noexcept; private: - void addTileSheet(ox::FileAddress path, int32_t tiles) noexcept; + void addTileSheetSetEntry(ox::FileAddress path, int32_t tiles) noexcept; }; ox::Result buildObjCache(keel::Context &kctx, WorldDoc const&doc) noexcept; -} \ No newline at end of file +} diff --git a/src/jasper/modules/world/include/jasper/world/world.hpp b/src/jasper/modules/world/include/jasper/world/world.hpp index 09f879d..166314e 100644 --- a/src/jasper/modules/world/include/jasper/world/world.hpp +++ b/src/jasper/modules/world/include/jasper/world/world.hpp @@ -6,8 +6,6 @@ #include -#include "consts.hpp" -#include "worldobject.hpp" #include "worldstatic.hpp" namespace jasper::world { @@ -18,6 +16,7 @@ class World { private: ncore::Context &m_nctx; WorldStatic const&m_worldStatic; + ox::Vector, 32> m_tilesheets; #ifndef OX_OS_BareMetal //turbine::TimeMs m_prevUpdateTime{}; turbine::TimeMs m_nextUpdateTime{}; diff --git a/src/jasper/modules/world/include/jasper/world/worlddoc.hpp b/src/jasper/modules/world/include/jasper/world/worlddoc.hpp index 7e82a89..cde902e 100644 --- a/src/jasper/modules/world/include/jasper/world/worlddoc.hpp +++ b/src/jasper/modules/world/include/jasper/world/worlddoc.hpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -31,11 +32,19 @@ struct DocObjRef { constexpr static auto TypeVersion = 1; uint64_t worldObjectSetId{}; ObjectId worldObjectId{}; + bool unique{}; }; +constexpr bool operator==(DocObjRef const&a, DocObjRef const&b) noexcept { + return a.worldObjectSetId == b.worldObjectSetId + && a.worldObjectId == b.worldObjectId + && a.unique == b.unique; +} + OX_MODEL_BEGIN(DocObjRef) OX_MODEL_FIELD_RENAME(worldObjectSetId, world_object_set_id) OX_MODEL_FIELD_RENAME(worldObjectId, world_object_id) + OX_MODEL_FIELD(unique) OX_MODEL_END() struct TileDoc { @@ -82,25 +91,29 @@ struct WorldDoc { constexpr static auto TypeVersion = 1; ox::Vector objSets; // paths uint64_t objSetIdIdx{}; - int columns{}; - int rows{}; + int32_t columns{}; + int32_t rows{}; TileMap tiles; }; [[nodiscard]] -constexpr ox::Result objectSetPath(WorldDoc const&wd, uint64_t setId) noexcept { - auto const obj = ox::find_if(wd.objSets.begin(), wd.objSets.end(), [setId](ObjectSetEntry const&e) { - return e.id == setId; - }); +constexpr ox::Result objectSetPath( + WorldDoc const&wd, uint64_t setId) noexcept { + auto const obj = ox::find_if(wd.objSets.begin(), wd.objSets.end(), + [setId](ObjectSetEntry const&e) { + return e.id == setId; + }); OX_RETURN_ERROR(ox::Error(obj == wd.objSets.end(), "Obj set not found")); return obj->path; } [[nodiscard]] -constexpr ox::Result objectSetId(WorldDoc const&wd, ox::StringView setPath) noexcept { - auto const obj = ox::find_if(wd.objSets.begin(), wd.objSets.end(), [setPath](ObjectSetEntry const&e) { - return e.path == setPath; - }); +constexpr ox::Result objectSetId( + WorldDoc const&wd, ox::StringViewCR setPath) noexcept { + auto const obj = ox::find_if(wd.objSets.begin(), wd.objSets.end(), + [setPath](ObjectSetEntry const&e) { + return e.path == setPath; + }); OX_RETURN_ERROR(ox::Error(obj == wd.objSets.end(), "Obj set not found")); return obj->id; } @@ -151,3 +164,13 @@ auto &tile( void resize(WorldDoc &doc, ox::Size const&sz) noexcept; } + +template<> +struct std::hash { + [[nodiscard]] + constexpr size_t operator()(jasper::world::DocObjRef const&v) const noexcept { + return static_cast(v.worldObjectSetId) + | (static_cast(v.worldObjectId) << 15) + | (static_cast(v.unique) << 31); + } +}; diff --git a/src/jasper/modules/world/include/jasper/world/worldstatic.hpp b/src/jasper/modules/world/include/jasper/world/worldstatic.hpp index e983c70..87e69d7 100644 --- a/src/jasper/modules/world/include/jasper/world/worldstatic.hpp +++ b/src/jasper/modules/world/include/jasper/world/worldstatic.hpp @@ -10,31 +10,28 @@ #include #include -#include - #include #include "worlddoc.hpp" -#include "worldobject.hpp" namespace jasper::world { namespace ncore = nostalgia::core; -constexpr void setTopEdge(uint8_t &layerAttachments, unsigned val) noexcept { - const auto val8 = static_cast(val); +constexpr void setTopEdge(uint8_t &layerAttachments, unsigned const val) noexcept { + auto const val8 = static_cast(val); layerAttachments = (layerAttachments & 0b11111100) | val8; } -constexpr void setBottomEdge(uint8_t &layerAttachments, unsigned val) noexcept { - const auto val8 = static_cast(val); +constexpr void setBottomEdge(uint8_t &layerAttachments, unsigned const val) noexcept { + auto const val8 = static_cast(val); layerAttachments = (layerAttachments & 0b11110011) | static_cast(val8 << 2); } -constexpr void setLeftEdge(uint8_t &layerAttachments, unsigned val) noexcept { - const auto val8 = static_cast(val); +constexpr void setLeftEdge(uint8_t &layerAttachments, unsigned const val) noexcept { + auto const val8 = static_cast(val); layerAttachments = (layerAttachments & 0b11001111) | static_cast(val8 << 4); } -constexpr void setRightEdge(uint8_t &layerAttachments, unsigned val) noexcept { - const auto val8 = static_cast(val); +constexpr void setRightEdge(uint8_t &layerAttachments, unsigned const val) noexcept { + auto const val8 = static_cast(val); layerAttachments = (layerAttachments & 0b00111111) | static_cast(val8 << 6); } @@ -57,25 +54,23 @@ constexpr unsigned rightEdge(uint8_t layerAttachments) noexcept { struct TileStatic { - constexpr static auto TypeName = "net.drinkingtea.jasper.world.TileStatic"; - constexpr static auto TypeVersion = 1; - uint16_t tileIdx{}; - uint8_t palBank{}; + static constexpr auto TypeName = "net.drinkingtea.jasper.world.TileStatic"; + static constexpr auto TypeVersion = 1; + uint8_t objIdxRefSet{}; uint8_t tileType{}; uint8_t layerAttachments{}; }; OX_MODEL_BEGIN(TileStatic) - OX_MODEL_FIELD(tileIdx) - OX_MODEL_FIELD(palBank) + OX_MODEL_FIELD(objIdxRefSet) OX_MODEL_FIELD(tileType) OX_MODEL_FIELD(layerAttachments) OX_MODEL_END() struct BgLayer { - constexpr static auto TypeName = "net.drinkingtea.jasper.world.BgLayer"; - constexpr static auto TypeVersion = 1; + static constexpr auto TypeName = "net.drinkingtea.jasper.world.BgLayer"; + static constexpr auto TypeVersion = 1; uint8_t cbb{}; ox::Vector tiles; }; @@ -86,11 +81,47 @@ OX_MODEL_BEGIN(BgLayer) OX_MODEL_END() +struct ObjTileRefSet { + static constexpr auto TypeName = "net.drinkingtea.jasper.world.ObjTileRefSet"; + static constexpr auto TypeVersion = 1; + uint16_t palBank{}; + uint16_t tilesheetIdx{}; + uint16_t cbbIdx{}; + uint8_t cbb{}; + // which tilesheet to use + uint8_t tilesheetId{}; + uint8_t tileCnt{}; + // each successive frame will use tileIdx[i] + tileCnt * frameNo for the tileIdx + uint8_t frames{}; +}; + +OX_MODEL_BEGIN(ObjTileRefSet) + OX_MODEL_FIELD(palBank) + OX_MODEL_FIELD(tilesheetIdx) + OX_MODEL_FIELD(cbbIdx) + OX_MODEL_FIELD(cbb) + OX_MODEL_FIELD(tilesheetId) + OX_MODEL_FIELD(tileCnt) + OX_MODEL_FIELD(frames) +OX_MODEL_END() + +[[nodiscard]] +constexpr bool operator==(ObjTileRefSet const&a, ObjTileRefSet const&b) noexcept { + return + a.tilesheetId == b.tilesheetId && + a.tileCnt == b.tileCnt && + a.frames == b.frames && + a.tilesheetIdx == b.tilesheetIdx; +} + + struct WorldStatic { - constexpr static auto TypeName = "net.drinkingtea.jasper.world.WorldStatic"; - constexpr static auto TypeVersion = 1; - constexpr static auto Preloadable = true; - ncore::TileSheetSet tilesheets; + static constexpr auto TypeName = "net.drinkingtea.jasper.world.WorldStatic"; + static constexpr auto TypeVersion = 1; + static constexpr auto Preloadable = true; + ncore::TileSheetSet tilesheetSet; + ox::Vector objTileRefSets; + ox::Vector tilesheets; ox::Vector palettes; int16_t columns{}; int16_t rows{}; @@ -98,6 +129,8 @@ struct WorldStatic { }; OX_MODEL_BEGIN(WorldStatic) + OX_MODEL_FIELD(tilesheetSet) + OX_MODEL_FIELD(objTileRefSets) OX_MODEL_FIELD(tilesheets) OX_MODEL_FIELD(palettes) OX_MODEL_FIELD(columns) @@ -105,9 +138,22 @@ OX_MODEL_BEGIN(WorldStatic) OX_MODEL_FIELD(map) OX_MODEL_END() -void loadTile(ObjectCache const&objCache, TileStatic &dst, TileDoc const&src) noexcept; +[[nodiscard]] +constexpr bool valid(WorldStatic const&ws) noexcept { + auto const tileCnt = static_cast(ws.columns * ws.rows); + return ox::all_of(ws.map.begin(), ws.map.end(), [tileCnt](BgLayer const&v) { + return v.tiles.size() == tileCnt; + }); +} -ox::Result loadWorldStatic(ObjectCache const&objCache, WorldDoc const&doc) noexcept; +void loadTile(TileStatic &dst, TileDoc const&src) noexcept; + +ox::Result loadWorldStatic(keel::Context &kctx, ObjectCache const&objCache, WorldDoc const&doc) noexcept; + +[[nodiscard]] +constexpr size_t layers(WorldStatic const&ws) noexcept { + return ws.map.size(); +} [[nodiscard]] auto &tile( @@ -124,7 +170,4 @@ auto &cbb(ox::CommonRefWith auto&ws, size_t lyr) noexcept { return ws.map[lyr].cbb; } -[[nodiscard]] -bool isValid(WorldStatic const&ws) noexcept; - } diff --git a/src/jasper/modules/world/src/keel/typeconv.cpp b/src/jasper/modules/world/src/keel/typeconv.cpp index fc7016d..d084db3 100644 --- a/src/jasper/modules/world/src/keel/typeconv.cpp +++ b/src/jasper/modules/world/src/keel/typeconv.cpp @@ -16,7 +16,7 @@ ox::Error WorldDocToWorldStaticConverter::convert( WorldDoc &src, WorldStatic &dst) const noexcept { OX_REQUIRE(oc, buildObjCache(kctx, src)); - return loadWorldStatic(oc, src).moveTo(dst); + return loadWorldStatic(kctx, oc, src).moveTo(dst); } } diff --git a/src/jasper/modules/world/src/objectcache.cpp b/src/jasper/modules/world/src/objectcache.cpp index 9651242..4d9279f 100644 --- a/src/jasper/modules/world/src/objectcache.cpp +++ b/src/jasper/modules/world/src/objectcache.cpp @@ -20,25 +20,28 @@ void ObjectCache::clear() noexcept { ox::Error ObjectCache::indexSet( keel::Context &kctx, - uint64_t setId, + uint64_t const setId, WorldObjectSet const&objSet) noexcept { auto &set = m_objSets.emplace_back(ObjSet{ .setId = setId, .objects = {}, }); OX_REQUIRE(ts, readObj(kctx, objSet.tilesheet)); + auto tsId = ox::find(m_tilesheets.begin(), m_tilesheets.end(), objSet.tilesheet); + if (tsId == m_tilesheets.end()) { + m_tilesheets.emplace_back(objSet.tilesheet); + } for (auto const&obj : objSet.objects) { set.objects.emplace_back(Obj{ .id = obj.id, .palBank = static_cast(obj.palBank + m_palBank), - .tileIdx = getTileIdx(*ts, obj.subsheetId) + m_tileIdx, + .tilesheetIdx = getTileIdx(*ts, obj.subsheetId).or_value(0) + m_tileIdx, }); } auto const tileCnt = getTileCnt(*ts); m_tileIdx += tileCnt; - m_tilesheets.emplace_back(objSet.tilesheet); m_tilesheetSet.bpp = 4; - addTileSheet(objSet.tilesheet, static_cast(tileCnt)); + addTileSheetSetEntry(objSet.tilesheet, static_cast(tileCnt)); for (auto const&pal : objSet.palettes) { m_palettes.emplace_back(pal); OX_REQUIRE(p, readObj(kctx, pal)); @@ -61,15 +64,19 @@ ox::Optional ObjectCache::obj(uint64_t setId, ObjectId objId) return {}; } -ncore::TileSheetSet const&ObjectCache::tilesheets() const noexcept { +ncore::TileSheetSet const&ObjectCache::tilesheetSet() const noexcept { return m_tilesheetSet; } +ox::Vector const&ObjectCache::tilesheets() const noexcept { + return m_tilesheets; +} + ox::Vector const&ObjectCache::palettes() const noexcept { return m_palettes; } -void ObjectCache::addTileSheet(ox::FileAddress path, int32_t tiles) noexcept { +void ObjectCache::addTileSheetSetEntry(ox::FileAddress path, int32_t tiles) noexcept { ncore::TileSheetSetEntry entry; entry.tilesheet = std::move(path); entry.sections.push_back({ diff --git a/src/jasper/modules/world/src/studio/worldeditor/commands/editsize.cpp b/src/jasper/modules/world/src/studio/worldeditor/commands/editsize.cpp index d9db3a0..6b69f70 100644 --- a/src/jasper/modules/world/src/studio/worldeditor/commands/editsize.cpp +++ b/src/jasper/modules/world/src/studio/worldeditor/commands/editsize.cpp @@ -8,27 +8,21 @@ namespace jasper::world { EditWorldSizeCommand::EditWorldSizeCommand( - ObjectCache const&objCache, WorldDoc &doc, - WorldStatic &worldStatic, ox::Size const&size): - m_objCache(objCache), m_doc(doc), - m_worldStatic(worldStatic), m_oldSize(m_doc.columns, m_doc.rows), m_newSize(size) {} ox::Error EditWorldSizeCommand::redo() noexcept { m_oldMap = m_doc.tiles; resize(m_doc, m_newSize); - OX_RETURN_ERROR(loadWorldStatic(m_objCache, m_doc).moveTo(m_worldStatic)); return {}; } ox::Error EditWorldSizeCommand::undo() noexcept { resize(m_doc, m_oldSize); m_doc.tiles = std::move(m_oldMap); - OX_RETURN_ERROR(loadWorldStatic(m_objCache, m_doc).moveTo(m_worldStatic)); return {}; } diff --git a/src/jasper/modules/world/src/studio/worldeditor/commands/editsize.hpp b/src/jasper/modules/world/src/studio/worldeditor/commands/editsize.hpp index 757b083..ca82ca2 100644 --- a/src/jasper/modules/world/src/studio/worldeditor/commands/editsize.hpp +++ b/src/jasper/modules/world/src/studio/worldeditor/commands/editsize.hpp @@ -14,18 +14,14 @@ namespace jasper::world { class EditWorldSizeCommand: public studio::UndoCommand { private: - ObjectCache const&m_objCache; WorldDoc &m_doc; - WorldStatic &m_worldStatic; size_t m_insertIdx{}; ox::Size const m_oldSize; WorldDoc::TileMap m_oldMap; ox::Size m_newSize; public: EditWorldSizeCommand( - ObjectCache const&objCache, WorldDoc &doc, - WorldStatic &worldStatic, ox::Size const&size); ox::Error redo() noexcept override; ox::Error undo() noexcept override; diff --git a/src/jasper/modules/world/src/studio/worldeditor/commands/modifytiles.cpp b/src/jasper/modules/world/src/studio/worldeditor/commands/modifytiles.cpp index 91e5489..6beef5f 100644 --- a/src/jasper/modules/world/src/studio/worldeditor/commands/modifytiles.cpp +++ b/src/jasper/modules/world/src/studio/worldeditor/commands/modifytiles.cpp @@ -47,7 +47,7 @@ void ModifyTilesCommand::swap() noexcept { auto &activeTile = tile(m_worldStatic, mod.layer, mod.tileAddr.x, mod.tileAddr.y); std::swap(docTile.obj.worldObjectId, mod.objId); std::swap(docTile.obj.worldObjectSetId, mod.setId); - loadTile(m_objCache, activeTile, docTile); + loadTile(activeTile, docTile); } } diff --git a/src/jasper/modules/world/src/studio/worldeditor/worldeditor-imgui.cpp b/src/jasper/modules/world/src/studio/worldeditor/worldeditor-imgui.cpp index a125cc6..a459309 100644 --- a/src/jasper/modules/world/src/studio/worldeditor/worldeditor-imgui.cpp +++ b/src/jasper/modules/world/src/studio/worldeditor/worldeditor-imgui.cpp @@ -77,12 +77,12 @@ constexpr ox::Point fbPtToTileAddr( }; } -WorldEditorImGui::WorldEditorImGui(studio::StudioContext &sctx, ox::StringView path): - Editor(path), +WorldEditorImGui::WorldEditorImGui(studio::StudioContext &sctx, ox::StringParam path): + Editor(std::move(path)), m_sctx(sctx), - m_doc(makeValid(*readObj(keelCtx(m_sctx), path).unwrapThrow())), + m_doc(makeValid(*readObj(keelCtx(m_sctx), itemPath()).unwrapThrow())), m_objCache(buildObjCache(keelCtx(m_sctx), m_doc).unwrapThrow()), - m_worldStatic(loadWorldStatic(m_objCache, m_doc)), + m_worldStatic(loadWorldStatic(keelCtx(m_sctx), m_objCache, m_doc)), m_view(m_sctx, m_worldStatic) { OX_THROW_ERROR(loadObjectSets()); m_objSetPicker.filePicked.connect(this, &WorldEditorImGui::addObjSet); @@ -249,7 +249,7 @@ void WorldEditorImGui::drawPropEditor() noexcept { if (ig::PopupControlsOkCancel(popupSz.x, m_sizeEditor.show) == ig::PopupResponse::OK) { if (changed) { std::ignore = pushCommand( - m_objCache, m_doc, m_worldStatic, ox::Size{m_sizeEditor.columns, m_sizeEditor.rows}); + m_doc, ox::Size{m_sizeEditor.columns, m_sizeEditor.rows}); } } ImGui::EndPopup(); @@ -389,7 +389,7 @@ ox::Error WorldEditorImGui::handleDrop(float fbPaneScale) noexcept { m_objCache, std::move(mods)); } - OX_RETURN_ERROR(loadWorldStatic(m_objCache, m_doc).moveTo(m_worldStatic)); + OX_RETURN_ERROR(loadWorldStatic(keelCtx(m_sctx), m_objCache, m_doc).moveTo(m_worldStatic)); return {}; } @@ -416,7 +416,7 @@ ox::Error WorldEditorImGui::handleDepUpdate(ox::StringView, ox::UUID const&uuid) || ox::any_of(m_dependencies.pairs().begin(), m_dependencies.pairs().end(), depMatches); if (depUpdated) { OX_RETURN_ERROR(buildObjCache(kctx, m_doc).moveTo(m_objCache)); - OX_RETURN_ERROR(loadWorldStatic(m_objCache, m_doc).moveTo(m_worldStatic)); + OX_RETURN_ERROR(loadWorldStatic(keelCtx(m_sctx), m_objCache, m_doc).moveTo(m_worldStatic)); OX_RETURN_ERROR(loadObjectSets()); } return {}; @@ -437,8 +437,11 @@ ox::Error WorldEditorImGui::loadObjectSets() noexcept { return {}; } -ox::Error WorldEditorImGui::undoStackChanged(studio::UndoCommand const*) { +ox::Error WorldEditorImGui::undoStackChanged(studio::UndoCommand const*cmd) { OX_RETURN_ERROR(m_view.setupWorld()); + if (dynamic_cast(cmd)) { + OX_RETURN_ERROR(loadWorldStatic(keelCtx(m_sctx), m_objCache, m_doc).moveTo(m_worldStatic)); + } return {}; } diff --git a/src/jasper/modules/world/src/studio/worldeditor/worldeditor-imgui.hpp b/src/jasper/modules/world/src/studio/worldeditor/worldeditor-imgui.hpp index 20cd8d3..1e70a41 100644 --- a/src/jasper/modules/world/src/studio/worldeditor/worldeditor-imgui.hpp +++ b/src/jasper/modules/world/src/studio/worldeditor/worldeditor-imgui.hpp @@ -8,8 +8,7 @@ #include -#include - +#include #include #include "worldeditorview.hpp" @@ -23,8 +22,7 @@ class WorldEditorImGui: public studio::Editor { ox::Optional m_selection; uint8_t m_activeLayer{}; studio::StudioContext &m_sctx; - studio::ig::FilePicker m_objSetPicker{ - m_sctx, ox::String("Choose Object Set"), ox::String(FileExt_jwob)}; + studio::ig::FilePicker m_objSetPicker{m_sctx, "Choose Object Set", FileExt_jwob}; WorldDoc m_doc; ObjectCache m_objCache; struct ObjSetRef { @@ -51,7 +49,7 @@ class WorldEditorImGui: public studio::Editor { } m_sizeEditor; public: - WorldEditorImGui(studio::StudioContext &ctx, ox::StringView path); + WorldEditorImGui(studio::StudioContext &ctx, ox::StringParam path); void draw(studio::StudioContext&) noexcept final; diff --git a/src/jasper/modules/world/src/studio/worldobjectseteditor/worldobjectseteditor-imgui.cpp b/src/jasper/modules/world/src/studio/worldobjectseteditor/worldobjectseteditor-imgui.cpp index b85af1d..c4a5e2e 100644 --- a/src/jasper/modules/world/src/studio/worldobjectseteditor/worldobjectseteditor-imgui.cpp +++ b/src/jasper/modules/world/src/studio/worldobjectseteditor/worldobjectseteditor-imgui.cpp @@ -140,7 +140,7 @@ void WorldObjectSetEditorImGui::loadObj() noexcept { w = m_subsheet->columns; h = m_subsheet->rows; } - auto const idx = getTileIdx(*m_tileSheet, obj.subsheetId); + auto const idx = getTileIdx(*m_tileSheet, obj.subsheetId).or_value(0); oxLogError(m_colView.setup( m_doc.tilesheet, m_doc.palettes[obj.palBank], diff --git a/src/jasper/modules/world/src/world.cpp b/src/jasper/modules/world/src/world.cpp index e750929..c8ff88d 100644 --- a/src/jasper/modules/world/src/world.cpp +++ b/src/jasper/modules/world/src/world.cpp @@ -15,6 +15,13 @@ World::World(ncore::Context &nctx, WorldStatic const&worldStatic) noexcept: m_worldStatic(worldStatic) {} ox::Error World::setupDisplay() noexcept { + auto &kctx = keelCtx(m_nctx); + if (m_tilesheets.empty()) { + for (auto const&tsAddr : m_worldStatic.tilesheets) { + OX_REQUIRE_M(ts, keel::readObj(kctx, tsAddr)); + m_tilesheets.emplace_back(std::move(ts)); + } + } if (m_worldStatic.palettes.empty()) { return ox::Error(1, "World has no palettes"); } @@ -23,7 +30,16 @@ ox::Error World::setupDisplay() noexcept { OX_RETURN_ERROR(ncore::loadBgPalette(m_nctx, i, *pal)); ++i; } - OX_RETURN_ERROR(ncore::loadBgTileSheet(m_nctx, 0, m_worldStatic.tilesheets)); + OX_RETURN_ERROR(ncore::loadBgTileSheet(m_nctx, 0, m_worldStatic.tilesheetSet)); + for (auto const&rs : m_worldStatic.objTileRefSets) { + OX_RETURN_ERROR(ncore::loadBgTileSheet( + m_nctx, + rs.cbb, + *m_tilesheets[rs.tilesheetId], + rs.cbbIdx, + rs.tilesheetIdx, + rs.tileCnt)); + } ncore::setBgStatus(m_nctx, 0); // disable all backgrounds for (auto layerNo = 0u; auto const&layer : m_worldStatic.map) { setupLayer(layerNo, layer.cbb); @@ -33,8 +49,8 @@ ox::Error World::setupDisplay() noexcept { } void World::setupLayer( - uint_t lyr, - uint_t cbb) const noexcept { + uint_t const lyr, + uint_t const cbb) const noexcept { ncore::clearBg(m_nctx, lyr); ncore::setBgStatus(m_nctx, lyr, true); ncore::setBgCbb(m_nctx, lyr, cbb); @@ -45,21 +61,22 @@ void World::setupLayer( auto &t = tile(m_worldStatic, lyr, x, y); auto const tx = x * 2; auto const ty = y * 2; + auto const &obj = m_worldStatic.objTileRefSets[t.objIdxRefSet];; ncore::setBgTile(m_nctx, lyr, tx + 0, ty + 0, { - .tileIdx = static_cast(t.tileIdx + 0), - .palBank = t.palBank, + .tileIdx = static_cast(obj.cbbIdx + 0), + .palBank = obj.palBank, }); ncore::setBgTile(m_nctx, lyr, tx + 1, ty + 0, { - .tileIdx = static_cast(t.tileIdx + 1), - .palBank = t.palBank, + .tileIdx = static_cast(obj.cbbIdx + 1), + .palBank = obj.palBank, }); ncore::setBgTile(m_nctx, lyr, tx + 0, ty + 1, { - .tileIdx = static_cast(t.tileIdx + 2), - .palBank = t.palBank, + .tileIdx = static_cast(obj.cbbIdx + 2), + .palBank = obj.palBank, }); ncore::setBgTile(m_nctx, lyr, tx + 1, ty + 1, { - .tileIdx = static_cast(t.tileIdx + 3), - .palBank = t.palBank, + .tileIdx = static_cast(obj.cbbIdx + 3), + .palBank = obj.palBank, }); } } diff --git a/src/jasper/modules/world/src/worldstatic.cpp b/src/jasper/modules/world/src/worldstatic.cpp index 13ce27a..6e486dd 100644 --- a/src/jasper/modules/world/src/worldstatic.cpp +++ b/src/jasper/modules/world/src/worldstatic.cpp @@ -2,17 +2,18 @@ * Copyright 2023 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved. */ +#include + #include +#include #include namespace jasper::world { -void loadTile(ObjectCache const&objCache, TileStatic &dst, TileDoc const&src) noexcept { - auto const obj = objCache.obj(src.obj.worldObjectSetId, src.obj.worldObjectId).or_value({}); - dst.palBank = src.palBank; - dst.tileIdx = static_cast(obj.tileIdx); - dst.palBank = static_cast(obj.palBank); +void loadTile( + TileStatic &dst, + TileDoc const&src) noexcept { dst.tileType = src.type; setTopEdge(dst.layerAttachments, src.topLayerAttachment); setBottomEdge(dst.layerAttachments, src.bottomLayerAttachment); @@ -20,41 +21,110 @@ void loadTile(ObjectCache const&objCache, TileStatic &dst, TileDoc const&src) no setRightEdge(dst.layerAttachments, src.rightLayerAttachment); } -ox::Result loadWorldStatic(ObjectCache const&objCache, WorldDoc const&doc) noexcept { - auto const tileCnt = - static_cast(doc.columns) * static_cast(doc.rows); - WorldStatic out { - .tilesheets = {}, - .palettes = {}, - .columns = static_cast(doc.columns), - .rows = static_cast(doc.rows), - .map = { - {.tiles = ox::Vector(tileCnt),}, - {.tiles = ox::Vector(tileCnt),}, - {.tiles = ox::Vector(tileCnt),}, - }, - }; - // resources - out.tilesheets = objCache.tilesheets(); - out.palettes = objCache.palettes(); - // tiles - for (auto lyr = 0u; lyr < 3; ++lyr) { - for (auto x = 0u; x < static_cast(out.columns); ++x) { - for (auto y = 0u; y < static_cast(out.rows); ++y) { - auto &dst = tile(out, lyr, x, y); - auto &src = tile(doc, lyr, x, y);; - loadTile(objCache, dst, src); - } +/** + * Returns the index in rsrcs for the given docObjRef. + */ +static ox::Result setupTileResrc( + ox::SmallMap &cache, + keel::Context &kctx, + ox::Vector const&tilesheets, + ox::SmallMap const&objSets, + ox::Vector &rsrcs, + uint16_t &cbbIt, + DocObjRef const&docObjRef) noexcept { + if (!docObjRef.unique) { + auto const [out, err] = cache.at(docObjRef); + if (!err) { + return *out; } } + OX_REQUIRE(objSet, objSets.at(docObjRef.worldObjectSetId)); + auto const obj = ox::find_if( + objSet->objects.begin(), objSet->objects.end(), + [&docObjRef](WorldObject const&o) { + return o.id == docObjRef.worldObjectId; + }); + if (obj == objSet->objects.end()) { + return ox::Error{obj == objSet->objects.end(), "could not find WorldObject in WorldObjectSet"}; + } + OX_REQUIRE(ts, keel::readObj(kctx, objSet->tilesheet)); + OX_REQUIRE(tsIdx, ox::findIdx( + tilesheets.begin(), tilesheets.end(), objSet->tilesheet).to()); + auto const subsheetOffset = ncore::getTileIdx(*ts, obj->subsheetId); + if (!subsheetOffset) { + return ox::Error{1, "invalid subsheet idx"}; + } + auto const subsheet = ncore::getSubsheet(*ts, obj->subsheetId); + if (!subsheet) { + return ox::Error{1, "could not find subsheet"}; + } + auto const out = static_cast(rsrcs.size()); + auto const&refSet = rsrcs.emplace_back(ObjTileRefSet{ + .palBank = obj->palBank, + .tilesheetIdx = static_cast(*subsheetOffset), + .cbbIdx = cbbIt, + .tilesheetId = tsIdx, + .tileCnt = static_cast(subsheet->size()), + }); + cbbIt += refSet.tileCnt; + if (!docObjRef.unique) { + cache[docObjRef] = out; + } return out; } -bool isValid(WorldStatic const&ws) noexcept { - auto const tileCnt = static_cast(ws.columns * ws.rows); - return ox::all_of(ws.map.begin(), ws.map.end(), [tileCnt](auto &v) { - return v.tiles.size() == tileCnt; - }); +ox::Result loadWorldStatic( + keel::Context &kctx, + ObjectCache const&objCache, + WorldDoc const&doc) noexcept { + auto const tilesPerLayer = + static_cast(doc.columns) * static_cast(doc.rows); + ox::SmallMap objSets; + for (auto const&setRef : doc.objSets) { + auto [set, err] = + keel::readObj(kctx, setRef.path); + if (!err) { + objSets[setRef.id] = *set; + } + } + ox::Result result = WorldStatic{ + .tilesheetSet = objCache.tilesheetSet(), + .objTileRefSets = {}, + .tilesheets = objCache.tilesheets(), + .palettes = objCache.palettes(), + .columns = static_cast(doc.columns), + .rows = static_cast(doc.rows), + .map = { + {.tiles = ox::Vector{tilesPerLayer},}, + {.tiles = ox::Vector{tilesPerLayer},}, + {.tiles = ox::Vector{tilesPerLayer},}, + }, + }; + auto &out = result.value; + ox::SmallMap refSetCache; + uint16_t cbbIt = 4; + // tiles + for (auto lyr = 0u; lyr < layers(out); ++lyr) { + for (auto x = 0u; x < static_cast(out.columns); ++x) { + for (auto y = 0u; y < static_cast(out.rows); ++y) { + auto &dst = tile(out, lyr, x, y); + auto &src = tile(doc, lyr, x, y);; + loadTile(dst, src); + auto const refSetIdx = setupTileResrc( + refSetCache, + kctx, + out.tilesheets, + objSets, + out.objTileRefSets, + cbbIt, + src.obj); + if (refSetIdx.ok()) { + dst.objIdxRefSet = refSetIdx.value; + } + } + } + } + return result; } }