[jasper/world] Rework World system
All checks were successful
Build / build (push) Successful in 3m43s

This commit is contained in:
Gary Talent 2025-01-08 22:06:04 -06:00
parent 95587f664f
commit fc184258c9
14 changed files with 274 additions and 122 deletions

View File

@ -18,7 +18,7 @@ class ObjectCache {
struct Obj { struct Obj {
ObjectId id{}; ObjectId id{};
uint16_t palBank{}; uint16_t palBank{};
size_t tileIdx{}; size_t tilesheetIdx{};
}; };
private: private:
struct ObjSet { struct ObjSet {
@ -40,11 +40,13 @@ class ObjectCache {
[[nodiscard]] [[nodiscard]]
ox::Optional<Obj> obj(uint64_t setId, ObjectId objId) const noexcept; ox::Optional<Obj> obj(uint64_t setId, ObjectId objId) const noexcept;
[[nodiscard]] [[nodiscard]]
ncore::TileSheetSet const&tilesheets() const noexcept; ncore::TileSheetSet const&tilesheetSet() const noexcept;
[[nodiscard]]
ox::Vector<ox::FileAddress> const&tilesheets() const noexcept;
[[nodiscard]] [[nodiscard]]
ox::Vector<ox::FileAddress> const&palettes() const noexcept; ox::Vector<ox::FileAddress> const&palettes() const noexcept;
private: private:
void addTileSheet(ox::FileAddress path, int32_t tiles) noexcept; void addTileSheetSetEntry(ox::FileAddress path, int32_t tiles) noexcept;
}; };
ox::Result<ObjectCache> buildObjCache(keel::Context &kctx, WorldDoc const&doc) noexcept; ox::Result<ObjectCache> buildObjCache(keel::Context &kctx, WorldDoc const&doc) noexcept;

View File

@ -6,8 +6,6 @@
#include <nostalgia/core/context.hpp> #include <nostalgia/core/context.hpp>
#include "consts.hpp"
#include "worldobject.hpp"
#include "worldstatic.hpp" #include "worldstatic.hpp"
namespace jasper::world { namespace jasper::world {
@ -18,6 +16,7 @@ class World {
private: private:
ncore::Context &m_nctx; ncore::Context &m_nctx;
WorldStatic const&m_worldStatic; WorldStatic const&m_worldStatic;
ox::Vector<keel::AssetRef<ncore::CompactTileSheet>, 32> m_tilesheets;
#ifndef OX_OS_BareMetal #ifndef OX_OS_BareMetal
//turbine::TimeMs m_prevUpdateTime{}; //turbine::TimeMs m_prevUpdateTime{};
turbine::TimeMs m_nextUpdateTime{}; turbine::TimeMs m_nextUpdateTime{};

View File

@ -6,6 +6,7 @@
#include <ox/fs/fs.hpp> #include <ox/fs/fs.hpp>
#include <ox/std/error.hpp> #include <ox/std/error.hpp>
#include <ox/std/hash.hpp>
#include <ox/std/size.hpp> #include <ox/std/size.hpp>
#include <ox/std/types.hpp> #include <ox/std/types.hpp>
#include <ox/std/vector.hpp> #include <ox/std/vector.hpp>
@ -31,11 +32,19 @@ struct DocObjRef {
constexpr static auto TypeVersion = 1; constexpr static auto TypeVersion = 1;
uint64_t worldObjectSetId{}; uint64_t worldObjectSetId{};
ObjectId worldObjectId{}; 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_BEGIN(DocObjRef)
OX_MODEL_FIELD_RENAME(worldObjectSetId, world_object_set_id) OX_MODEL_FIELD_RENAME(worldObjectSetId, world_object_set_id)
OX_MODEL_FIELD_RENAME(worldObjectId, world_object_id) OX_MODEL_FIELD_RENAME(worldObjectId, world_object_id)
OX_MODEL_FIELD(unique)
OX_MODEL_END() OX_MODEL_END()
struct TileDoc { struct TileDoc {
@ -82,14 +91,16 @@ struct WorldDoc {
constexpr static auto TypeVersion = 1; constexpr static auto TypeVersion = 1;
ox::Vector<ObjectSetEntry> objSets; // paths ox::Vector<ObjectSetEntry> objSets; // paths
uint64_t objSetIdIdx{}; uint64_t objSetIdIdx{};
int columns{}; int32_t columns{};
int rows{}; int32_t rows{};
TileMap tiles; TileMap tiles;
}; };
[[nodiscard]] [[nodiscard]]
constexpr ox::Result<ox::String> objectSetPath(WorldDoc const&wd, uint64_t setId) noexcept { constexpr ox::Result<ox::String> objectSetPath(
auto const obj = ox::find_if(wd.objSets.begin(), wd.objSets.end(), [setId](ObjectSetEntry const&e) { 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; return e.id == setId;
}); });
OX_RETURN_ERROR(ox::Error(obj == wd.objSets.end(), "Obj set not found")); OX_RETURN_ERROR(ox::Error(obj == wd.objSets.end(), "Obj set not found"));
@ -97,8 +108,10 @@ constexpr ox::Result<ox::String> objectSetPath(WorldDoc const&wd, uint64_t setId
} }
[[nodiscard]] [[nodiscard]]
constexpr ox::Result<uint64_t> objectSetId(WorldDoc const&wd, ox::StringView setPath) noexcept { constexpr ox::Result<uint64_t> objectSetId(
auto const obj = ox::find_if(wd.objSets.begin(), wd.objSets.end(), [setPath](ObjectSetEntry const&e) { 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; return e.path == setPath;
}); });
OX_RETURN_ERROR(ox::Error(obj == wd.objSets.end(), "Obj set not found")); OX_RETURN_ERROR(ox::Error(obj == wd.objSets.end(), "Obj set not found"));
@ -151,3 +164,13 @@ auto &tile(
void resize(WorldDoc &doc, ox::Size const&sz) noexcept; void resize(WorldDoc &doc, ox::Size const&sz) noexcept;
} }
template<>
struct std::hash<jasper::world::DocObjRef> {
[[nodiscard]]
constexpr size_t operator()(jasper::world::DocObjRef const&v) const noexcept {
return static_cast<uint32_t>(v.worldObjectSetId)
| (static_cast<uint32_t>(v.worldObjectId) << 15)
| (static_cast<uint32_t>(v.unique) << 31);
}
};

View File

@ -10,31 +10,28 @@
#include <ox/std/types.hpp> #include <ox/std/types.hpp>
#include <ox/std/vector.hpp> #include <ox/std/vector.hpp>
#include <nostalgia/core/tilesheet.hpp>
#include <jasper/world/objectcache.hpp> #include <jasper/world/objectcache.hpp>
#include "worlddoc.hpp" #include "worlddoc.hpp"
#include "worldobject.hpp"
namespace jasper::world { namespace jasper::world {
namespace ncore = nostalgia::core; namespace ncore = nostalgia::core;
constexpr void setTopEdge(uint8_t &layerAttachments, unsigned val) noexcept { constexpr void setTopEdge(uint8_t &layerAttachments, unsigned const val) noexcept {
const auto val8 = static_cast<uint8_t>(val); auto const val8 = static_cast<uint8_t>(val);
layerAttachments = (layerAttachments & 0b11111100) | val8; layerAttachments = (layerAttachments & 0b11111100) | val8;
} }
constexpr void setBottomEdge(uint8_t &layerAttachments, unsigned val) noexcept { constexpr void setBottomEdge(uint8_t &layerAttachments, unsigned const val) noexcept {
const auto val8 = static_cast<uint8_t>(val); auto const val8 = static_cast<uint8_t>(val);
layerAttachments = (layerAttachments & 0b11110011) | static_cast<uint8_t>(val8 << 2); layerAttachments = (layerAttachments & 0b11110011) | static_cast<uint8_t>(val8 << 2);
} }
constexpr void setLeftEdge(uint8_t &layerAttachments, unsigned val) noexcept { constexpr void setLeftEdge(uint8_t &layerAttachments, unsigned const val) noexcept {
const auto val8 = static_cast<uint8_t>(val); auto const val8 = static_cast<uint8_t>(val);
layerAttachments = (layerAttachments & 0b11001111) | static_cast<uint8_t>(val8 << 4); layerAttachments = (layerAttachments & 0b11001111) | static_cast<uint8_t>(val8 << 4);
} }
constexpr void setRightEdge(uint8_t &layerAttachments, unsigned val) noexcept { constexpr void setRightEdge(uint8_t &layerAttachments, unsigned const val) noexcept {
const auto val8 = static_cast<uint8_t>(val); auto const val8 = static_cast<uint8_t>(val);
layerAttachments = (layerAttachments & 0b00111111) | static_cast<uint8_t>(val8 << 6); layerAttachments = (layerAttachments & 0b00111111) | static_cast<uint8_t>(val8 << 6);
} }
@ -57,25 +54,23 @@ constexpr unsigned rightEdge(uint8_t layerAttachments) noexcept {
struct TileStatic { struct TileStatic {
constexpr static auto TypeName = "net.drinkingtea.jasper.world.TileStatic"; static constexpr auto TypeName = "net.drinkingtea.jasper.world.TileStatic";
constexpr static auto TypeVersion = 1; static constexpr auto TypeVersion = 1;
uint16_t tileIdx{}; uint8_t objIdxRefSet{};
uint8_t palBank{};
uint8_t tileType{}; uint8_t tileType{};
uint8_t layerAttachments{}; uint8_t layerAttachments{};
}; };
OX_MODEL_BEGIN(TileStatic) OX_MODEL_BEGIN(TileStatic)
OX_MODEL_FIELD(tileIdx) OX_MODEL_FIELD(objIdxRefSet)
OX_MODEL_FIELD(palBank)
OX_MODEL_FIELD(tileType) OX_MODEL_FIELD(tileType)
OX_MODEL_FIELD(layerAttachments) OX_MODEL_FIELD(layerAttachments)
OX_MODEL_END() OX_MODEL_END()
struct BgLayer { struct BgLayer {
constexpr static auto TypeName = "net.drinkingtea.jasper.world.BgLayer"; static constexpr auto TypeName = "net.drinkingtea.jasper.world.BgLayer";
constexpr static auto TypeVersion = 1; static constexpr auto TypeVersion = 1;
uint8_t cbb{}; uint8_t cbb{};
ox::Vector<TileStatic> tiles; ox::Vector<TileStatic> tiles;
}; };
@ -86,11 +81,47 @@ OX_MODEL_BEGIN(BgLayer)
OX_MODEL_END() 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 { struct WorldStatic {
constexpr static auto TypeName = "net.drinkingtea.jasper.world.WorldStatic"; static constexpr auto TypeName = "net.drinkingtea.jasper.world.WorldStatic";
constexpr static auto TypeVersion = 1; static constexpr auto TypeVersion = 1;
constexpr static auto Preloadable = true; static constexpr auto Preloadable = true;
ncore::TileSheetSet tilesheets; ncore::TileSheetSet tilesheetSet;
ox::Vector<ObjTileRefSet> objTileRefSets;
ox::Vector<ox::FileAddress> tilesheets;
ox::Vector<ox::FileAddress> palettes; ox::Vector<ox::FileAddress> palettes;
int16_t columns{}; int16_t columns{};
int16_t rows{}; int16_t rows{};
@ -98,6 +129,8 @@ struct WorldStatic {
}; };
OX_MODEL_BEGIN(WorldStatic) OX_MODEL_BEGIN(WorldStatic)
OX_MODEL_FIELD(tilesheetSet)
OX_MODEL_FIELD(objTileRefSets)
OX_MODEL_FIELD(tilesheets) OX_MODEL_FIELD(tilesheets)
OX_MODEL_FIELD(palettes) OX_MODEL_FIELD(palettes)
OX_MODEL_FIELD(columns) OX_MODEL_FIELD(columns)
@ -105,9 +138,22 @@ OX_MODEL_BEGIN(WorldStatic)
OX_MODEL_FIELD(map) OX_MODEL_FIELD(map)
OX_MODEL_END() 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<size_t>(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<WorldStatic> loadWorldStatic(ObjectCache const&objCache, WorldDoc const&doc) noexcept; void loadTile(TileStatic &dst, TileDoc const&src) noexcept;
ox::Result<WorldStatic> 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]] [[nodiscard]]
auto &tile( auto &tile(
@ -124,7 +170,4 @@ auto &cbb(ox::CommonRefWith<WorldStatic> auto&ws, size_t lyr) noexcept {
return ws.map[lyr].cbb; return ws.map[lyr].cbb;
} }
[[nodiscard]]
bool isValid(WorldStatic const&ws) noexcept;
} }

View File

@ -16,7 +16,7 @@ ox::Error WorldDocToWorldStaticConverter::convert(
WorldDoc &src, WorldDoc &src,
WorldStatic &dst) const noexcept { WorldStatic &dst) const noexcept {
OX_REQUIRE(oc, buildObjCache(kctx, src)); OX_REQUIRE(oc, buildObjCache(kctx, src));
return loadWorldStatic(oc, src).moveTo(dst); return loadWorldStatic(kctx, oc, src).moveTo(dst);
} }
} }

View File

@ -20,25 +20,28 @@ void ObjectCache::clear() noexcept {
ox::Error ObjectCache::indexSet( ox::Error ObjectCache::indexSet(
keel::Context &kctx, keel::Context &kctx,
uint64_t setId, uint64_t const setId,
WorldObjectSet const&objSet) noexcept { WorldObjectSet const&objSet) noexcept {
auto &set = m_objSets.emplace_back(ObjSet{ auto &set = m_objSets.emplace_back(ObjSet{
.setId = setId, .setId = setId,
.objects = {}, .objects = {},
}); });
OX_REQUIRE(ts, readObj<ncore::TileSheet>(kctx, objSet.tilesheet)); OX_REQUIRE(ts, readObj<ncore::TileSheet>(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) { for (auto const&obj : objSet.objects) {
set.objects.emplace_back(Obj{ set.objects.emplace_back(Obj{
.id = obj.id, .id = obj.id,
.palBank = static_cast<uint16_t>(obj.palBank + m_palBank), .palBank = static_cast<uint16_t>(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); auto const tileCnt = getTileCnt(*ts);
m_tileIdx += tileCnt; m_tileIdx += tileCnt;
m_tilesheets.emplace_back(objSet.tilesheet);
m_tilesheetSet.bpp = 4; m_tilesheetSet.bpp = 4;
addTileSheet(objSet.tilesheet, static_cast<int32_t>(tileCnt)); addTileSheetSetEntry(objSet.tilesheet, static_cast<int32_t>(tileCnt));
for (auto const&pal : objSet.palettes) { for (auto const&pal : objSet.palettes) {
m_palettes.emplace_back(pal); m_palettes.emplace_back(pal);
OX_REQUIRE(p, readObj<ncore::Palette>(kctx, pal)); OX_REQUIRE(p, readObj<ncore::Palette>(kctx, pal));
@ -61,15 +64,19 @@ ox::Optional<ObjectCache::Obj> ObjectCache::obj(uint64_t setId, ObjectId objId)
return {}; return {};
} }
ncore::TileSheetSet const&ObjectCache::tilesheets() const noexcept { ncore::TileSheetSet const&ObjectCache::tilesheetSet() const noexcept {
return m_tilesheetSet; return m_tilesheetSet;
} }
ox::Vector<ox::FileAddress> const&ObjectCache::tilesheets() const noexcept {
return m_tilesheets;
}
ox::Vector<ox::FileAddress> const&ObjectCache::palettes() const noexcept { ox::Vector<ox::FileAddress> const&ObjectCache::palettes() const noexcept {
return m_palettes; 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; ncore::TileSheetSetEntry entry;
entry.tilesheet = std::move(path); entry.tilesheet = std::move(path);
entry.sections.push_back({ entry.sections.push_back({

View File

@ -8,27 +8,21 @@
namespace jasper::world { namespace jasper::world {
EditWorldSizeCommand::EditWorldSizeCommand( EditWorldSizeCommand::EditWorldSizeCommand(
ObjectCache const&objCache,
WorldDoc &doc, WorldDoc &doc,
WorldStatic &worldStatic,
ox::Size const&size): ox::Size const&size):
m_objCache(objCache),
m_doc(doc), m_doc(doc),
m_worldStatic(worldStatic),
m_oldSize(m_doc.columns, m_doc.rows), m_oldSize(m_doc.columns, m_doc.rows),
m_newSize(size) {} m_newSize(size) {}
ox::Error EditWorldSizeCommand::redo() noexcept { ox::Error EditWorldSizeCommand::redo() noexcept {
m_oldMap = m_doc.tiles; m_oldMap = m_doc.tiles;
resize(m_doc, m_newSize); resize(m_doc, m_newSize);
OX_RETURN_ERROR(loadWorldStatic(m_objCache, m_doc).moveTo(m_worldStatic));
return {}; return {};
} }
ox::Error EditWorldSizeCommand::undo() noexcept { ox::Error EditWorldSizeCommand::undo() noexcept {
resize(m_doc, m_oldSize); resize(m_doc, m_oldSize);
m_doc.tiles = std::move(m_oldMap); m_doc.tiles = std::move(m_oldMap);
OX_RETURN_ERROR(loadWorldStatic(m_objCache, m_doc).moveTo(m_worldStatic));
return {}; return {};
} }

View File

@ -14,18 +14,14 @@ namespace jasper::world {
class EditWorldSizeCommand: public studio::UndoCommand { class EditWorldSizeCommand: public studio::UndoCommand {
private: private:
ObjectCache const&m_objCache;
WorldDoc &m_doc; WorldDoc &m_doc;
WorldStatic &m_worldStatic;
size_t m_insertIdx{}; size_t m_insertIdx{};
ox::Size const m_oldSize; ox::Size const m_oldSize;
WorldDoc::TileMap m_oldMap; WorldDoc::TileMap m_oldMap;
ox::Size m_newSize; ox::Size m_newSize;
public: public:
EditWorldSizeCommand( EditWorldSizeCommand(
ObjectCache const&objCache,
WorldDoc &doc, WorldDoc &doc,
WorldStatic &worldStatic,
ox::Size const&size); ox::Size const&size);
ox::Error redo() noexcept override; ox::Error redo() noexcept override;
ox::Error undo() noexcept override; ox::Error undo() noexcept override;

View File

@ -47,7 +47,7 @@ void ModifyTilesCommand::swap() noexcept {
auto &activeTile = tile(m_worldStatic, mod.layer, mod.tileAddr.x, mod.tileAddr.y); auto &activeTile = tile(m_worldStatic, mod.layer, mod.tileAddr.x, mod.tileAddr.y);
std::swap(docTile.obj.worldObjectId, mod.objId); std::swap(docTile.obj.worldObjectId, mod.objId);
std::swap(docTile.obj.worldObjectSetId, mod.setId); std::swap(docTile.obj.worldObjectSetId, mod.setId);
loadTile(m_objCache, activeTile, docTile); loadTile(activeTile, docTile);
} }
} }

View File

@ -77,12 +77,12 @@ constexpr ox::Point fbPtToTileAddr(
}; };
} }
WorldEditorImGui::WorldEditorImGui(studio::StudioContext &sctx, ox::StringView path): WorldEditorImGui::WorldEditorImGui(studio::StudioContext &sctx, ox::StringParam path):
Editor(path), Editor(std::move(path)),
m_sctx(sctx), m_sctx(sctx),
m_doc(makeValid(*readObj<WorldDoc>(keelCtx(m_sctx), path).unwrapThrow())), m_doc(makeValid(*readObj<WorldDoc>(keelCtx(m_sctx), itemPath()).unwrapThrow())),
m_objCache(buildObjCache(keelCtx(m_sctx), m_doc).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) { m_view(m_sctx, m_worldStatic) {
OX_THROW_ERROR(loadObjectSets()); OX_THROW_ERROR(loadObjectSets());
m_objSetPicker.filePicked.connect(this, &WorldEditorImGui::addObjSet); 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 (ig::PopupControlsOkCancel(popupSz.x, m_sizeEditor.show) == ig::PopupResponse::OK) {
if (changed) { if (changed) {
std::ignore = pushCommand<EditWorldSizeCommand>( std::ignore = pushCommand<EditWorldSizeCommand>(
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(); ImGui::EndPopup();
@ -389,7 +389,7 @@ ox::Error WorldEditorImGui::handleDrop(float fbPaneScale) noexcept {
m_objCache, m_objCache,
std::move(mods)); 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 {}; 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); || ox::any_of(m_dependencies.pairs().begin(), m_dependencies.pairs().end(), depMatches);
if (depUpdated) { if (depUpdated) {
OX_RETURN_ERROR(buildObjCache(kctx, m_doc).moveTo(m_objCache)); 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()); OX_RETURN_ERROR(loadObjectSets());
} }
return {}; return {};
@ -437,8 +437,11 @@ ox::Error WorldEditorImGui::loadObjectSets() noexcept {
return {}; return {};
} }
ox::Error WorldEditorImGui::undoStackChanged(studio::UndoCommand const*) { ox::Error WorldEditorImGui::undoStackChanged(studio::UndoCommand const*cmd) {
OX_RETURN_ERROR(m_view.setupWorld()); OX_RETURN_ERROR(m_view.setupWorld());
if (dynamic_cast<EditWorldSizeCommand const*>(cmd)) {
OX_RETURN_ERROR(loadWorldStatic(keelCtx(m_sctx), m_objCache, m_doc).moveTo(m_worldStatic));
}
return {}; return {};
} }

View File

@ -8,8 +8,7 @@
#include <studio/studio.hpp> #include <studio/studio.hpp>
#include <turbine/context.hpp> #include <jasper/world/consts.hpp>
#include <jasper/world/objectcache.hpp> #include <jasper/world/objectcache.hpp>
#include "worldeditorview.hpp" #include "worldeditorview.hpp"
@ -23,8 +22,7 @@ class WorldEditorImGui: public studio::Editor {
ox::Optional<studio::Selection> m_selection; ox::Optional<studio::Selection> m_selection;
uint8_t m_activeLayer{}; uint8_t m_activeLayer{};
studio::StudioContext &m_sctx; studio::StudioContext &m_sctx;
studio::ig::FilePicker m_objSetPicker{ studio::ig::FilePicker m_objSetPicker{m_sctx, "Choose Object Set", FileExt_jwob};
m_sctx, ox::String("Choose Object Set"), ox::String(FileExt_jwob)};
WorldDoc m_doc; WorldDoc m_doc;
ObjectCache m_objCache; ObjectCache m_objCache;
struct ObjSetRef { struct ObjSetRef {
@ -51,7 +49,7 @@ class WorldEditorImGui: public studio::Editor {
} m_sizeEditor; } m_sizeEditor;
public: public:
WorldEditorImGui(studio::StudioContext &ctx, ox::StringView path); WorldEditorImGui(studio::StudioContext &ctx, ox::StringParam path);
void draw(studio::StudioContext&) noexcept final; void draw(studio::StudioContext&) noexcept final;

View File

@ -140,7 +140,7 @@ void WorldObjectSetEditorImGui::loadObj() noexcept {
w = m_subsheet->columns; w = m_subsheet->columns;
h = m_subsheet->rows; 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( oxLogError(m_colView.setup(
m_doc.tilesheet, m_doc.tilesheet,
m_doc.palettes[obj.palBank], m_doc.palettes[obj.palBank],

View File

@ -15,6 +15,13 @@ World::World(ncore::Context &nctx, WorldStatic const&worldStatic) noexcept:
m_worldStatic(worldStatic) {} m_worldStatic(worldStatic) {}
ox::Error World::setupDisplay() noexcept { 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<ncore::CompactTileSheet>(kctx, tsAddr));
m_tilesheets.emplace_back(std::move(ts));
}
}
if (m_worldStatic.palettes.empty()) { if (m_worldStatic.palettes.empty()) {
return ox::Error(1, "World has no palettes"); 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)); OX_RETURN_ERROR(ncore::loadBgPalette(m_nctx, i, *pal));
++i; ++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 ncore::setBgStatus(m_nctx, 0); // disable all backgrounds
for (auto layerNo = 0u; auto const&layer : m_worldStatic.map) { for (auto layerNo = 0u; auto const&layer : m_worldStatic.map) {
setupLayer(layerNo, layer.cbb); setupLayer(layerNo, layer.cbb);
@ -33,8 +49,8 @@ ox::Error World::setupDisplay() noexcept {
} }
void World::setupLayer( void World::setupLayer(
uint_t lyr, uint_t const lyr,
uint_t cbb) const noexcept { uint_t const cbb) const noexcept {
ncore::clearBg(m_nctx, lyr); ncore::clearBg(m_nctx, lyr);
ncore::setBgStatus(m_nctx, lyr, true); ncore::setBgStatus(m_nctx, lyr, true);
ncore::setBgCbb(m_nctx, lyr, cbb); ncore::setBgCbb(m_nctx, lyr, cbb);
@ -45,21 +61,22 @@ void World::setupLayer(
auto &t = tile(m_worldStatic, lyr, x, y); auto &t = tile(m_worldStatic, lyr, x, y);
auto const tx = x * 2; auto const tx = x * 2;
auto const ty = y * 2; auto const ty = y * 2;
auto const &obj = m_worldStatic.objTileRefSets[t.objIdxRefSet];;
ncore::setBgTile(m_nctx, lyr, tx + 0, ty + 0, { ncore::setBgTile(m_nctx, lyr, tx + 0, ty + 0, {
.tileIdx = static_cast<uint_t>(t.tileIdx + 0), .tileIdx = static_cast<uint_t>(obj.cbbIdx + 0),
.palBank = t.palBank, .palBank = obj.palBank,
}); });
ncore::setBgTile(m_nctx, lyr, tx + 1, ty + 0, { ncore::setBgTile(m_nctx, lyr, tx + 1, ty + 0, {
.tileIdx = static_cast<uint_t>(t.tileIdx + 1), .tileIdx = static_cast<uint_t>(obj.cbbIdx + 1),
.palBank = t.palBank, .palBank = obj.palBank,
}); });
ncore::setBgTile(m_nctx, lyr, tx + 0, ty + 1, { ncore::setBgTile(m_nctx, lyr, tx + 0, ty + 1, {
.tileIdx = static_cast<uint_t>(t.tileIdx + 2), .tileIdx = static_cast<uint_t>(obj.cbbIdx + 2),
.palBank = t.palBank, .palBank = obj.palBank,
}); });
ncore::setBgTile(m_nctx, lyr, tx + 1, ty + 1, { ncore::setBgTile(m_nctx, lyr, tx + 1, ty + 1, {
.tileIdx = static_cast<uint_t>(t.tileIdx + 3), .tileIdx = static_cast<uint_t>(obj.cbbIdx + 3),
.palBank = t.palBank, .palBank = obj.palBank,
}); });
} }
} }

View File

@ -2,17 +2,18 @@
* Copyright 2023 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved. * Copyright 2023 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/ */
#include <keel/media.hpp>
#include <nostalgia/core/gfx.hpp> #include <nostalgia/core/gfx.hpp>
#include <jasper/world/worldobject.hpp>
#include <jasper/world/worldstatic.hpp> #include <jasper/world/worldstatic.hpp>
namespace jasper::world { namespace jasper::world {
void loadTile(ObjectCache const&objCache, TileStatic &dst, TileDoc const&src) noexcept { void loadTile(
auto const obj = objCache.obj(src.obj.worldObjectSetId, src.obj.worldObjectId).or_value({}); TileStatic &dst,
dst.palBank = src.palBank; TileDoc const&src) noexcept {
dst.tileIdx = static_cast<uint16_t>(obj.tileIdx);
dst.palBank = static_cast<uint8_t>(obj.palBank);
dst.tileType = src.type; dst.tileType = src.type;
setTopEdge(dst.layerAttachments, src.topLayerAttachment); setTopEdge(dst.layerAttachments, src.topLayerAttachment);
setBottomEdge(dst.layerAttachments, src.bottomLayerAttachment); 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); setRightEdge(dst.layerAttachments, src.rightLayerAttachment);
} }
ox::Result<WorldStatic> loadWorldStatic(ObjectCache const&objCache, WorldDoc const&doc) noexcept { /**
auto const tileCnt = * Returns the index in rsrcs for the given docObjRef.
static_cast<size_t>(doc.columns) * static_cast<size_t>(doc.rows); */
WorldStatic out { static ox::Result<uint8_t> setupTileResrc(
.tilesheets = {}, ox::SmallMap<DocObjRef, uint8_t> &cache,
.palettes = {}, keel::Context &kctx,
.columns = static_cast<int16_t>(doc.columns), ox::Vector<ox::FileAddress> const&tilesheets,
.rows = static_cast<int16_t>(doc.rows), ox::SmallMap<uint64_t, WorldObjectSet> const&objSets,
.map = { ox::Vector<ObjTileRefSet> &rsrcs,
{.tiles = ox::Vector<TileStatic>(tileCnt),}, uint16_t &cbbIt,
{.tiles = ox::Vector<TileStatic>(tileCnt),}, DocObjRef const&docObjRef) noexcept {
{.tiles = ox::Vector<TileStatic>(tileCnt),}, if (!docObjRef.unique) {
}, auto const [out, err] = cache.at(docObjRef);
}; if (!err) {
// resources return *out;
out.tilesheets = objCache.tilesheets();
out.palettes = objCache.palettes();
// tiles
for (auto lyr = 0u; lyr < 3; ++lyr) {
for (auto x = 0u; x < static_cast<size_t>(out.columns); ++x) {
for (auto y = 0u; y < static_cast<size_t>(out.rows); ++y) {
auto &dst = tile(out, lyr, x, y);
auto &src = tile(doc, lyr, x, y);;
loadTile(objCache, dst, src);
} }
} }
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<ncore::TileSheet>(kctx, objSet->tilesheet));
OX_REQUIRE(tsIdx, ox::findIdx(
tilesheets.begin(), tilesheets.end(), objSet->tilesheet).to<uint8_t>());
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<uint8_t>(rsrcs.size());
auto const&refSet = rsrcs.emplace_back(ObjTileRefSet{
.palBank = obj->palBank,
.tilesheetIdx = static_cast<uint16_t>(*subsheetOffset),
.cbbIdx = cbbIt,
.tilesheetId = tsIdx,
.tileCnt = static_cast<uint8_t>(subsheet->size()),
});
cbbIt += refSet.tileCnt;
if (!docObjRef.unique) {
cache[docObjRef] = out;
} }
return out; return out;
} }
bool isValid(WorldStatic const&ws) noexcept { ox::Result<WorldStatic> loadWorldStatic(
auto const tileCnt = static_cast<size_t>(ws.columns * ws.rows); keel::Context &kctx,
return ox::all_of(ws.map.begin(), ws.map.end(), [tileCnt](auto &v) { ObjectCache const&objCache,
return v.tiles.size() == tileCnt; WorldDoc const&doc) noexcept {
}); auto const tilesPerLayer =
static_cast<size_t>(doc.columns) * static_cast<size_t>(doc.rows);
ox::SmallMap<uint64_t, WorldObjectSet> objSets;
for (auto const&setRef : doc.objSets) {
auto [set, err] =
keel::readObj<WorldObjectSet>(kctx, setRef.path);
if (!err) {
objSets[setRef.id] = *set;
}
}
ox::Result<WorldStatic> result = WorldStatic{
.tilesheetSet = objCache.tilesheetSet(),
.objTileRefSets = {},
.tilesheets = objCache.tilesheets(),
.palettes = objCache.palettes(),
.columns = static_cast<int16_t>(doc.columns),
.rows = static_cast<int16_t>(doc.rows),
.map = {
{.tiles = ox::Vector<TileStatic>{tilesPerLayer},},
{.tiles = ox::Vector<TileStatic>{tilesPerLayer},},
{.tiles = ox::Vector<TileStatic>{tilesPerLayer},},
},
};
auto &out = result.value;
ox::SmallMap<DocObjRef, uint8_t> refSetCache;
uint16_t cbbIt = 4;
// tiles
for (auto lyr = 0u; lyr < layers(out); ++lyr) {
for (auto x = 0u; x < static_cast<size_t>(out.columns); ++x) {
for (auto y = 0u; y < static_cast<size_t>(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;
} }
} }