[jasper/world] Add WorldObjectEditor
All checks were successful
Build / build (push) Successful in 2m44s
All checks were successful
Build / build (push) Successful in 2m44s
This commit is contained in:
parent
ec099aa386
commit
aa1f063a6d
@ -15,7 +15,7 @@
|
||||
namespace jasper::world {
|
||||
|
||||
struct PrefabDoc {
|
||||
constexpr static auto TypeName = "net.drinkingtea.nostalgia.world.PrefabDoc";
|
||||
constexpr static auto TypeName = "net.drinkingtea.jasper.world.PrefabDoc";
|
||||
constexpr static auto TypeVersion = 1;
|
||||
constexpr static auto Preloadable = true;
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
#include "consts.hpp"
|
||||
#include "prefab.hpp"
|
||||
#include "worldobjectset.hpp"
|
||||
#include "worldobject.hpp"
|
||||
#include "worldstatic.hpp"
|
||||
|
||||
namespace jasper::world {
|
||||
|
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2023 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <nostalgia/core/context.hpp>
|
||||
#include <nostalgia/core/tilesheet.hpp>
|
||||
|
||||
namespace jasper::world {
|
||||
|
||||
namespace ncore = nostalgia::core;
|
||||
|
||||
struct PaletteCycle {
|
||||
static constexpr auto TypeName = "net.drinkingtea.jasper.world.PaletteCycle";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
static constexpr auto Preloadable = true;
|
||||
ox::FileAddress palette;
|
||||
uint16_t intervalMs = 1000;
|
||||
};
|
||||
|
||||
oxModelBegin(PaletteCycle)
|
||||
oxModelField(palette)
|
||||
oxModelField(intervalMs)
|
||||
oxModelEnd()
|
||||
|
||||
|
||||
struct WorldObject {
|
||||
static constexpr auto TypeName = "net.drinkingtea.jasper.world.WorldObject";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
static constexpr auto Preloadable = true;
|
||||
int id{};
|
||||
ox::String name;
|
||||
uint16_t palBank{};
|
||||
ncore::SubSheetId subsheetId{};
|
||||
uint64_t collisionMap{};
|
||||
};
|
||||
|
||||
oxModelBegin(WorldObject)
|
||||
oxModelField(id)
|
||||
oxModelField(name)
|
||||
oxModelField(palBank)
|
||||
oxModelField(subsheetId)
|
||||
oxModelField(collisionMap)
|
||||
oxModelEnd()
|
||||
|
||||
|
||||
struct WorldObjectSet {
|
||||
static constexpr auto TypeName = "net.drinkingtea.jasper.world.WorldObjectSet";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
static constexpr auto Preloadable = true;
|
||||
int objIdIdx = 0;
|
||||
ox::FileAddress tilesheet;
|
||||
ox::Vector<PaletteCycle> palettes;
|
||||
ox::Vector<WorldObject> objects;
|
||||
};
|
||||
|
||||
oxModelBegin(WorldObjectSet)
|
||||
oxModelField(objIdIdx)
|
||||
oxModelField(tilesheet)
|
||||
oxModelField(palettes)
|
||||
oxModelField(objects)
|
||||
oxModelEnd()
|
||||
|
||||
|
||||
ox::Error loadObjectSet(WorldObjectSet const&os) noexcept;
|
||||
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <nostalgia/core/context.hpp>
|
||||
|
||||
#include "consts.hpp"
|
||||
#include "prefab.hpp"
|
||||
#include "worldstatic.hpp"
|
||||
|
||||
namespace jasper::world {
|
||||
|
||||
namespace ncore = nostalgia::core;
|
||||
|
||||
struct WorldObject {
|
||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.world.WorldObject";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
static constexpr auto Preloadable = true;
|
||||
ox::FileAddress tilesheet;
|
||||
ncore::SubSheetId subSheetId;
|
||||
};
|
||||
|
||||
oxModelBegin(WorldObject)
|
||||
oxModelField(tilesheet)
|
||||
oxModelField(subsheetId)
|
||||
oxModelEnd()
|
||||
|
||||
struct WorldObjectSet {
|
||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.world.WorldObjectSet";
|
||||
static constexpr auto TypeVersion = 1;
|
||||
static constexpr auto Preloadable = true;
|
||||
ox::Vector<WorldObject> objects;
|
||||
};
|
||||
|
||||
oxModelBegin(WorldObjectSet)
|
||||
oxModelField(objects)
|
||||
oxModelEnd()
|
||||
|
||||
}
|
@ -18,7 +18,7 @@ namespace ncore = nostalgia::core;
|
||||
|
||||
struct SpriteDoc {
|
||||
|
||||
constexpr static auto TypeName = "net.drinkingtea.nostalgia.world.SpriteDoc";
|
||||
constexpr static auto TypeName = "net.drinkingtea.jasper.world.SpriteDoc";
|
||||
constexpr static auto TypeVersion = 1;
|
||||
constexpr static auto Preloadable = true;
|
||||
|
||||
@ -29,7 +29,7 @@ struct SpriteDoc {
|
||||
|
||||
struct TileDoc {
|
||||
|
||||
constexpr static auto TypeName = "net.drinkingtea.nostalgia.world.TileDoc";
|
||||
constexpr static auto TypeName = "net.drinkingtea.jasper.world.TileDoc";
|
||||
constexpr static auto TypeVersion = 1;
|
||||
constexpr static auto Preloadable = true;
|
||||
|
||||
@ -72,7 +72,7 @@ struct WorldDoc {
|
||||
using TileMapLayer = ox::Vector<TileMapRow>;
|
||||
using TileMap = ox::Vector<TileMapLayer>;
|
||||
|
||||
constexpr static auto TypeName = "net.drinkingtea.nostalgia.world.WorldDoc";
|
||||
constexpr static auto TypeName = "net.drinkingtea.jasper.world.WorldDoc";
|
||||
constexpr static auto TypeVersion = 1;
|
||||
constexpr static auto Preloadable = true;
|
||||
|
||||
@ -141,7 +141,7 @@ constexpr unsigned rightEdge(uint8_t layerAttachments) noexcept {
|
||||
|
||||
struct WorldStatic {
|
||||
|
||||
constexpr static auto TypeName = "net.drinkingtea.nostalgia.world.WorldStatic";
|
||||
constexpr static auto TypeName = "net.drinkingtea.jasper.world.WorldStatic";
|
||||
constexpr static auto TypeVersion = 1;
|
||||
constexpr static auto Preloadable = true;
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include <keel/module.hpp>
|
||||
|
||||
#include <jasper/world/worldobject.hpp>
|
||||
#include <jasper/world/worldstatic.hpp>
|
||||
|
||||
#include "typeconv.hpp"
|
||||
@ -23,13 +24,15 @@ class WorldModule: public keel::Module {
|
||||
[[nodiscard]]
|
||||
ox::Vector<keel::TypeDescGenerator> types() const noexcept override {
|
||||
return {
|
||||
keel::generateTypeDesc<PaletteCycle>,
|
||||
keel::generateTypeDesc<WorldObjectSet>,
|
||||
keel::generateTypeDesc<WorldDoc>,
|
||||
keel::generateTypeDesc<WorldStatic>,
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
ox::Vector<const keel::BaseConverter*> converters() const noexcept override {
|
||||
ox::Vector<keel::BaseConverter const*> converters() const noexcept override {
|
||||
return {
|
||||
&m_worldDocToWorldStaticConverter,
|
||||
};
|
||||
|
@ -16,5 +16,5 @@ install(
|
||||
${NOSTALGIA_DIST_MODULE}
|
||||
)
|
||||
|
||||
add_subdirectory(worldobjecteditor)
|
||||
add_subdirectory(worldobjectseteditor)
|
||||
add_subdirectory(worldeditor)
|
||||
|
@ -6,22 +6,22 @@
|
||||
|
||||
#include <jasper/world/consts.hpp>
|
||||
|
||||
#include "worldobjecteditor/worldobjecteditor-imgui.hpp"
|
||||
#include "worldobjectseteditor/worldobjectseteditor-imgui.hpp"
|
||||
#include "worldeditor/worldeditor-imgui.hpp"
|
||||
|
||||
namespace jasper::world {
|
||||
|
||||
class StudioModule: public studio::Module {
|
||||
public:
|
||||
ox::Vector<studio::EditorMaker> editors(turbine::Context &ctx) const noexcept override {
|
||||
ox::Vector<studio::EditorMaker> editors(studio::StudioContext &ctx) const noexcept override {
|
||||
return {
|
||||
studio::editorMaker<WorldObjectEditorImGui>(ctx, FileExt_jwob),
|
||||
studio::editorMaker<WorldObjectSetEditorImGui>(ctx, FileExt_jwob),
|
||||
studio::editorMaker<WorldEditorImGui>(ctx, FileExt_jwld),
|
||||
};
|
||||
}
|
||||
ox::Vector<ox::UPtr<studio::ItemMaker>> itemMakers(turbine::Context&) const noexcept override {
|
||||
ox::Vector<ox::UPtr<studio::ItemMaker>> itemMakers(studio::StudioContext&) const noexcept override {
|
||||
ox::Vector<ox::UPtr<studio::ItemMaker>> out;
|
||||
out.emplace_back(ox::make<studio::ItemMakerT<PrefabDoc>>("World Object", "WorldObjects", FileExt_jwob, ox::ClawFormat::Organic));
|
||||
out.emplace_back(ox::make<studio::ItemMakerT<WorldObjectSet>>("World Object Set", "WorldObjectSets", FileExt_jwob, ox::ClawFormat::Organic));
|
||||
out.emplace_back(ox::make<studio::ItemMakerT<WorldDoc>>("World", "Worlds", FileExt_jwld, ox::ClawFormat::Organic));
|
||||
return out;
|
||||
}
|
||||
|
@ -10,9 +10,9 @@
|
||||
|
||||
namespace jasper::world {
|
||||
|
||||
WorldEditorImGui::WorldEditorImGui(turbine::Context &ctx, ox::StringView path):
|
||||
WorldEditorImGui::WorldEditorImGui(studio::StudioContext &ctx, ox::StringView path):
|
||||
Editor(path),
|
||||
m_ctx(ctx),
|
||||
m_ctx(ctx.tctx),
|
||||
m_editor(m_ctx, path),
|
||||
m_view(m_ctx, m_editor.world()) {
|
||||
setRequiresConstantRefresh(false);
|
||||
|
@ -21,7 +21,7 @@ class WorldEditorImGui: public studio::Editor {
|
||||
WorldEditorView m_view;
|
||||
|
||||
public:
|
||||
WorldEditorImGui(turbine::Context &ctx, ox::StringView path);
|
||||
WorldEditorImGui(studio::StudioContext &ctx, ox::StringView path);
|
||||
|
||||
void draw(turbine::Context&) noexcept final;
|
||||
|
||||
|
@ -1,5 +0,0 @@
|
||||
target_sources(
|
||||
JasperWorld-Studio PRIVATE
|
||||
worldobjecteditor.cpp
|
||||
worldobjecteditor-imgui.cpp
|
||||
)
|
@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include "worldobjecteditor-imgui.hpp"
|
||||
|
||||
namespace jasper::world {
|
||||
|
||||
WorldObjectEditorImGui::WorldObjectEditorImGui(turbine::Context &ctx, ox::StringView path):
|
||||
Editor(path),
|
||||
m_sctx(*applicationData<studio::StudioContext>(ctx)),
|
||||
m_editor(ctx, path) {
|
||||
}
|
||||
|
||||
void WorldObjectEditorImGui::draw(turbine::Context&) noexcept {
|
||||
const auto paneSize = ImGui::GetContentRegionAvail();
|
||||
constexpr auto attrEditorWidth = 400.f;
|
||||
ImGui::BeginChild("Preview", ImVec2(paneSize.x - attrEditorWidth, 0));
|
||||
ImGui::EndChild();
|
||||
ImGui::SameLine();
|
||||
ImGui::BeginChild("AttrEditor", ImVec2(attrEditorWidth, 0), true);
|
||||
drawAttrEditor();
|
||||
drawTileSheetSelector();
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
void WorldObjectEditorImGui::onActivated() noexcept {
|
||||
}
|
||||
|
||||
void WorldObjectEditorImGui::drawAttrEditor() noexcept {
|
||||
static constexpr auto boundDimension = [](int &v) noexcept {
|
||||
v = ox::clamp(v, 1, 4);
|
||||
};
|
||||
auto &doc = m_editor.doc();
|
||||
ImGui::InputInt("Footprint Width", &doc.footprint.width);
|
||||
ImGui::InputInt("Footprint Height", &doc.footprint.height);
|
||||
ImGui::InputInt("Visible Width", &doc.visibleSz.width);
|
||||
ImGui::InputInt("Visible Height", &doc.visibleSz.height);
|
||||
boundDimension(doc.footprint.width);
|
||||
boundDimension(doc.footprint.height);
|
||||
boundDimension(doc.visibleSz.width);
|
||||
boundDimension(doc.visibleSz.height);
|
||||
}
|
||||
|
||||
void WorldObjectEditorImGui::drawTileSheetSelector() noexcept {
|
||||
auto const&tilesheetList = m_sctx.project->fileList(ncore::FileExt_ng);
|
||||
auto const first = m_selectedTilesheetIdx < tilesheetList.size() ?
|
||||
tilesheetList[m_selectedTilesheetIdx].c_str() : "";
|
||||
if (ImGui::BeginCombo("Tile Sheet", first, 0)) {
|
||||
for (auto i = 0u; i < tilesheetList.size(); ++i) {
|
||||
auto const selected = (m_selectedTilesheetIdx == i);
|
||||
if (ImGui::Selectable(tilesheetList[i].c_str(), selected) &&
|
||||
m_selectedTilesheetIdx != i) {
|
||||
m_selectedTilesheetIdx = i;
|
||||
//oxLogError(m_model.setPalette(tilesheetList[n]));
|
||||
}
|
||||
if (selected) {
|
||||
ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
}
|
||||
|
||||
ox::Error WorldObjectEditorImGui::saveItem() noexcept {
|
||||
return m_editor.saveItem();
|
||||
}
|
||||
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <studio/studio.hpp>
|
||||
|
||||
#include <turbine/context.hpp>
|
||||
|
||||
#include "worldobjecteditor.hpp"
|
||||
|
||||
namespace jasper::world {
|
||||
|
||||
class WorldObjectEditorImGui: public studio::Editor {
|
||||
|
||||
private:
|
||||
studio::StudioContext &m_sctx;
|
||||
PrefabEditor m_editor;
|
||||
uint_t m_selectedTilesheetIdx{};
|
||||
|
||||
public:
|
||||
WorldObjectEditorImGui(turbine::Context &ctx, ox::StringView path);
|
||||
|
||||
void draw(turbine::Context&) noexcept final;
|
||||
|
||||
void onActivated() noexcept override;
|
||||
|
||||
protected:
|
||||
void drawAttrEditor() noexcept;
|
||||
|
||||
void drawTileSheetSelector() noexcept;
|
||||
|
||||
ox::Error saveItem() noexcept final;
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include <turbine/turbine.hpp>
|
||||
|
||||
#include "worldobjecteditor.hpp"
|
||||
|
||||
namespace jasper::world {
|
||||
|
||||
PrefabEditor::PrefabEditor(turbine::Context &ctx, ox::StringView path):
|
||||
m_ctx(*applicationData<studio::StudioContext>(ctx)),
|
||||
m_itemPath(path),
|
||||
m_doc(*readObj<PrefabDoc>(keelCtx(ctx), path).unwrapThrow()) {
|
||||
}
|
||||
|
||||
PrefabDoc const&PrefabEditor::doc() const noexcept {
|
||||
return m_doc;
|
||||
}
|
||||
|
||||
PrefabDoc &PrefabEditor::doc() noexcept {
|
||||
return m_doc;
|
||||
}
|
||||
|
||||
ox::Error PrefabEditor::saveItem() noexcept {
|
||||
return m_ctx.project->writeObj(m_itemPath, m_doc, ox::ClawFormat::Organic);
|
||||
}
|
||||
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <turbine/context.hpp>
|
||||
#include <studio/context.hpp>
|
||||
|
||||
#include <jasper/world/world.hpp>
|
||||
|
||||
namespace jasper::world {
|
||||
|
||||
class PrefabEditor {
|
||||
|
||||
private:
|
||||
studio::StudioContext &m_ctx;
|
||||
ox::String m_itemPath;
|
||||
PrefabDoc m_doc;
|
||||
|
||||
public:
|
||||
PrefabEditor(turbine::Context &ctx, ox::StringView path);
|
||||
|
||||
[[nodiscard]]
|
||||
PrefabDoc const&doc() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
PrefabDoc &doc() noexcept;
|
||||
|
||||
ox::Error saveItem() noexcept;
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
target_sources(
|
||||
JasperWorld-Studio PRIVATE
|
||||
commands/addobject.cpp
|
||||
commands/rmobject.cpp
|
||||
commands/editobject.cpp
|
||||
commands/addpalette.cpp
|
||||
commands/rmpalette.cpp
|
||||
commands/editpalette.cpp
|
||||
commands/changetilesheet.cpp
|
||||
collisionmapview.cpp
|
||||
worldobjectseteditor-imgui.cpp
|
||||
)
|
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright 2023 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include "collisionmapview.hpp"
|
||||
|
||||
namespace jasper::world {
|
||||
|
||||
uint64_t mapIdx(auto x, auto y) noexcept {
|
||||
return static_cast<uint64_t>(y) * 8 + static_cast<uint64_t>(x);
|
||||
}
|
||||
|
||||
const glutils::ProgramSource CollisionView::s_programSrc = {
|
||||
.shaderParams = {
|
||||
{
|
||||
.len = 2,
|
||||
.name = ox::String("vPosition"),
|
||||
},
|
||||
{
|
||||
.len = 1,
|
||||
.name = ox::String("vSelection"),
|
||||
},
|
||||
},
|
||||
.vertShader = ox::sfmt(R"(
|
||||
{}
|
||||
in vec2 vPosition;
|
||||
in float vSelection;
|
||||
out float fSelection;
|
||||
void main() {
|
||||
gl_Position = vec4(vPosition, 0.0, 1.0);
|
||||
fSelection = vSelection;
|
||||
})", ncore::gl::GlslVersion),
|
||||
.fragShader = ox::sfmt(R"(
|
||||
{}
|
||||
in float fSelection;
|
||||
out vec4 outColor;
|
||||
void main() {
|
||||
outColor = vec4(0.0, 0.7, 1.0, 0.4) * fSelection;
|
||||
})", ncore::gl::GlslVersion),
|
||||
};
|
||||
|
||||
CollisionView::CollisionView(studio::StudioContext &sctx):
|
||||
m_nctx(ncore::init(sctx.tctx, {
|
||||
.glInstallDrawer = false,
|
||||
.glSpriteCount = 0,
|
||||
}).unwrapThrow()),
|
||||
m_frameBuffer(glutils::generateFrameBuffer(240 * s_scale, 160 * s_scale)) {
|
||||
m_bufferSet.vao = glutils::generateVertexArrayObject();
|
||||
m_bufferSet.vbo = glutils::generateBuffer();
|
||||
m_bufferSet.ebo = glutils::generateBuffer();
|
||||
glBindVertexArray(m_bufferSet.vao);
|
||||
sendVbo(m_bufferSet);
|
||||
sendEbo(m_bufferSet);
|
||||
m_shader = glutils::buildShaderProgram(s_programSrc).unwrapThrow();
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
ox::Error CollisionView::setup(
|
||||
ox::FileAddress const&tsAddr,
|
||||
ox::FileAddress const&palAddr,
|
||||
int const w,
|
||||
int h,
|
||||
uint_t tile,
|
||||
uint64_t colMap) noexcept {
|
||||
m_subsheetTilesWidth = w;
|
||||
m_subsheetTilesHeight = h;
|
||||
m_sheetTileStart = tile;
|
||||
ncore::setBgStatus(*m_nctx, 0, true);
|
||||
oxReturnError(ncore::loadBgTileSheet(*m_nctx, 0, tsAddr));
|
||||
oxReturnError(ncore::loadBgPalette(*m_nctx, 0, palAddr));
|
||||
buildGlBuffers(colMap);
|
||||
return {};
|
||||
}
|
||||
|
||||
void CollisionView::draw() noexcept {
|
||||
glutils::FrameBufferBind const frameBufferBind(m_frameBuffer);
|
||||
ncore::gl::draw(*m_nctx, s_scale);
|
||||
//glViewport(0, 0, m_frameBuffer.width, m_frameBuffer.height);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glUseProgram(m_shader);
|
||||
glBindVertexArray(m_bufferSet.vao);
|
||||
sendVbo(m_bufferSet);
|
||||
sendEbo(m_bufferSet);
|
||||
auto const elmCnt = static_cast<GLsizei>(m_bufferSet.elements.size());
|
||||
glDrawElements(GL_TRIANGLES, elmCnt, GL_UNSIGNED_INT, nullptr);
|
||||
glBindVertexArray(0);
|
||||
glUseProgram(0);
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
int CollisionView::scale() const noexcept {
|
||||
return s_scale;
|
||||
}
|
||||
|
||||
glutils::FrameBuffer const&CollisionView::framebuffer() const noexcept {
|
||||
return m_frameBuffer;
|
||||
}
|
||||
|
||||
void CollisionView::setPixelBufferObject(
|
||||
unsigned vertexRow,
|
||||
float x, float y,
|
||||
bool const selected,
|
||||
float *vbo,
|
||||
GLuint *ebo) const noexcept {
|
||||
auto constexpr xmod = static_cast<float>(ncore::TileWidth) / 240.f * 2;
|
||||
auto constexpr ymod = static_cast<float>(ncore::TileHeight) / 160.f * 2;
|
||||
x *= xmod;
|
||||
y *= -ymod;
|
||||
x -= 1.0f;
|
||||
y += 1.0f - ymod;
|
||||
//x = -1;
|
||||
//y = -1;
|
||||
auto const selection = 1.f * static_cast<float>(selected);
|
||||
// don't worry, these memcpys gets optimized to something much more ideal
|
||||
std::array const vertices{
|
||||
x, y, selection, // bottom left
|
||||
x + xmod, y, selection, // bottom right
|
||||
x + xmod, y + ymod, selection, // top right
|
||||
x, y + ymod, selection, // top left
|
||||
};
|
||||
memcpy(vbo, vertices.data(), sizeof(vertices));
|
||||
ox::Array<GLuint, 6> const elms{
|
||||
vertexRow + 0, vertexRow + 1, vertexRow + 2,
|
||||
vertexRow + 2, vertexRow + 3, vertexRow + 0,
|
||||
};
|
||||
memcpy(ebo, elms.data(), sizeof(elms));
|
||||
}
|
||||
|
||||
bool CollisionView::click(ox::Vec2 const&pos, uint64_t &colMap) noexcept {
|
||||
auto const inMap = colMap;
|
||||
auto const x = static_cast<uint64_t>(pos.x * static_cast<float>(m_subsheetTilesWidth));
|
||||
auto const y = static_cast<uint64_t>(pos.y * static_cast<float>(m_subsheetTilesHeight));
|
||||
uint64_t const idx = mapIdx(x, y);
|
||||
uint64_t const colOn = (colMap >> idx) & 1;
|
||||
colMap ^= uint64_t{1} << idx;
|
||||
colMap |= static_cast<uint64_t>(!colOn) << idx;
|
||||
buildGlBuffers(colMap);
|
||||
return inMap != colMap;
|
||||
}
|
||||
|
||||
void CollisionView::buildGlBuffers(uint64_t colMap) noexcept {
|
||||
auto const vboLength = static_cast<size_t>(s_programSrc.rowLen) * 4;
|
||||
auto constexpr eboLength = 6;
|
||||
auto const tileCnt =
|
||||
static_cast<size_t>(m_subsheetTilesWidth) * static_cast<size_t>(m_subsheetTilesHeight);
|
||||
m_bufferSet.vertices.resize(tileCnt * vboLength);
|
||||
m_bufferSet.elements.resize(tileCnt * eboLength);
|
||||
size_t i = 0;
|
||||
auto tile = m_sheetTileStart;
|
||||
for (auto y = 0; y < m_subsheetTilesHeight; ++y) {
|
||||
for (auto x = 0; x < m_subsheetTilesWidth; ++x) {
|
||||
ncore::setBgTile(*m_nctx, 0, x, y, tile);
|
||||
auto const vbo = &m_bufferSet.vertices[i * vboLength];
|
||||
auto const ebo = &m_bufferSet.elements[i * eboLength];
|
||||
setPixelBufferObject(
|
||||
static_cast<uint_t>(i * 4),
|
||||
static_cast<float>(x),
|
||||
static_cast<float>(y),
|
||||
(colMap >> mapIdx(x, y)) & 1,
|
||||
vbo,
|
||||
ebo);
|
||||
++i;
|
||||
++tile;
|
||||
}
|
||||
}
|
||||
glUseProgram(m_shader);
|
||||
glBindVertexArray(m_bufferSet.vao);
|
||||
sendVbo(m_bufferSet);
|
||||
sendEbo(m_bufferSet);
|
||||
glBindVertexArray(0);
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2023 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <glutils/glutils.hpp>
|
||||
|
||||
#include <studio/context.hpp>
|
||||
#include <nostalgia/core/core.hpp>
|
||||
|
||||
namespace jasper::world {
|
||||
|
||||
namespace ncore = nostalgia::core;
|
||||
|
||||
class CollisionView {
|
||||
private:
|
||||
static const glutils::ProgramSource s_programSrc;
|
||||
static constexpr int s_scale = 10;
|
||||
ncore::ContextUPtr m_nctx;
|
||||
glutils::FrameBuffer m_frameBuffer;
|
||||
glutils::GLProgram m_shader;
|
||||
glutils::BufferSet m_bufferSet;
|
||||
uint_t m_sheetTileStart{};
|
||||
int m_subsheetTilesWidth{};
|
||||
int m_subsheetTilesHeight{};
|
||||
|
||||
public:
|
||||
CollisionView(studio::StudioContext &sctx);
|
||||
|
||||
ox::Error setup(
|
||||
ox::FileAddress const&tsAddr,
|
||||
ox::FileAddress const&palAddr,
|
||||
int w,
|
||||
int h,
|
||||
uint_t tile,
|
||||
uint64_t colMap) noexcept;
|
||||
|
||||
void draw() noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
int scale() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
glutils::FrameBuffer const&framebuffer() const noexcept;
|
||||
|
||||
void setPixelBufferObject(
|
||||
unsigned vertexRow,
|
||||
float x, float y,
|
||||
bool selected,
|
||||
float *vbo,
|
||||
GLuint *ebo) const noexcept;
|
||||
|
||||
/**
|
||||
* @return true if colMap changes
|
||||
*/
|
||||
bool click(ox::Vec2 const&pos, uint64_t &colMap) noexcept;
|
||||
|
||||
private:
|
||||
void buildGlBuffers(uint64_t colMap) noexcept;
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2023 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include "commands.hpp"
|
||||
#include "addobject.hpp"
|
||||
|
||||
namespace jasper::world {
|
||||
|
||||
AddObject::AddObject(WorldObjectSet &doc) noexcept:
|
||||
m_doc(doc) {
|
||||
m_obj.id = m_doc.objIdIdx + 1;
|
||||
m_obj.name = ox::sfmt("Object {}", m_obj.id);
|
||||
for (auto const&o : m_doc.objects) {
|
||||
if (o.name > m_obj.name) {
|
||||
break;
|
||||
}
|
||||
++m_insertIdx;
|
||||
}
|
||||
}
|
||||
|
||||
void AddObject::redo() noexcept {
|
||||
++m_doc.objIdIdx;
|
||||
m_doc.objects.emplace(m_insertIdx, std::move(m_obj));
|
||||
}
|
||||
|
||||
void AddObject::undo() noexcept {
|
||||
m_obj = std::move(m_doc.objects[m_insertIdx]);
|
||||
oxIgnoreError(m_doc.objects.erase(m_insertIdx));
|
||||
--m_doc.objIdIdx;
|
||||
}
|
||||
|
||||
int AddObject::commandId() const noexcept {
|
||||
return static_cast<int>(WorldObjCommand::AddObject);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2023 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/fs/fs.hpp>
|
||||
|
||||
#include <studio/undostack.hpp>
|
||||
|
||||
#include <jasper/world/worldobject.hpp>
|
||||
|
||||
namespace jasper::world {
|
||||
|
||||
class AddObject: public studio::UndoCommand {
|
||||
private:
|
||||
WorldObjectSet &m_doc;
|
||||
size_t m_insertIdx{};
|
||||
WorldObject m_obj{};
|
||||
public:
|
||||
AddObject(WorldObjectSet &doc) noexcept;
|
||||
void redo() noexcept override;
|
||||
void undo() noexcept override;
|
||||
[[nodiscard]]
|
||||
int commandId() const noexcept override;
|
||||
[[nodiscard]]
|
||||
inline size_t insertIdx() const noexcept {
|
||||
return m_insertIdx;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2023 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "commands.hpp"
|
||||
#include "addpalette.hpp"
|
||||
|
||||
namespace jasper::world {
|
||||
|
||||
AddPalette::AddPalette(WorldObjectSet &doc, ox::FileAddress palAddr) noexcept:
|
||||
m_doc(doc),
|
||||
m_palAddr(std::move(palAddr)) {
|
||||
}
|
||||
|
||||
void AddPalette::redo() noexcept {
|
||||
m_doc.palettes.emplace_back(PaletteCycle{
|
||||
.palette = m_palAddr
|
||||
});
|
||||
std::sort(m_doc.palettes.begin(), m_doc.palettes.end(), [](PaletteCycle const&a, PaletteCycle const&b) {
|
||||
return a.palette.getPath().or_value("") < b.palette.getPath().or_value("");
|
||||
});
|
||||
}
|
||||
|
||||
void AddPalette::undo() noexcept {
|
||||
auto const idx = std::find_if(m_doc.palettes.begin(), m_doc.palettes.end(), [this](PaletteCycle const&v) {
|
||||
return v.palette == m_palAddr;
|
||||
});
|
||||
oxIgnoreError(m_doc.palettes.erase(idx));
|
||||
}
|
||||
|
||||
int AddPalette::commandId() const noexcept {
|
||||
return static_cast<int>(WorldObjCommand::AddPalette);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2023 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/fs/fs.hpp>
|
||||
|
||||
#include <studio/undostack.hpp>
|
||||
|
||||
#include <jasper/world/worldobject.hpp>
|
||||
|
||||
namespace jasper::world {
|
||||
|
||||
class AddPalette: public studio::UndoCommand {
|
||||
private:
|
||||
WorldObjectSet &m_doc;
|
||||
ox::FileAddress m_palAddr;
|
||||
public:
|
||||
AddPalette(WorldObjectSet &doc, ox::FileAddress palAddr) noexcept;
|
||||
void redo() noexcept override;
|
||||
void undo() noexcept override;
|
||||
[[nodiscard]]
|
||||
int commandId() const noexcept override;
|
||||
};
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2023 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include "commands.hpp"
|
||||
#include "changetilesheet.hpp"
|
||||
|
||||
namespace jasper::world {
|
||||
|
||||
ChangeTileSheet::ChangeTileSheet(
|
||||
WorldObjectSet &doc,
|
||||
ox::FileAddress newVal,
|
||||
size_t newIdx,
|
||||
size_t &selectedTilesheetIdx) noexcept:
|
||||
m_doc(doc),
|
||||
m_newVal(std::move(newVal)),
|
||||
m_oldIdx(selectedTilesheetIdx),
|
||||
m_newIdx(newIdx),
|
||||
m_selectedTilesheetIdx(selectedTilesheetIdx) {
|
||||
}
|
||||
|
||||
void ChangeTileSheet::redo() noexcept {
|
||||
m_oldVal = std::move(m_doc.tilesheet);
|
||||
m_doc.tilesheet = std::move(m_newVal);
|
||||
m_selectedTilesheetIdx = m_newIdx;
|
||||
}
|
||||
|
||||
void ChangeTileSheet::undo() noexcept {
|
||||
m_newVal = std::move(m_doc.tilesheet);
|
||||
m_doc.tilesheet = std::move(m_oldVal);
|
||||
m_selectedTilesheetIdx = m_oldIdx;
|
||||
}
|
||||
|
||||
int ChangeTileSheet::commandId() const noexcept {
|
||||
return static_cast<int>(WorldObjCommand::ChangeTileSheet);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2023 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/fs/fs.hpp>
|
||||
|
||||
#include <studio/undostack.hpp>
|
||||
|
||||
#include <jasper/world/worldobject.hpp>
|
||||
|
||||
namespace jasper::world {
|
||||
|
||||
class ChangeTileSheet: public studio::UndoCommand {
|
||||
private:
|
||||
WorldObjectSet &m_doc;
|
||||
ox::FileAddress m_newVal;
|
||||
ox::FileAddress m_oldVal;
|
||||
size_t m_oldIdx{};
|
||||
size_t m_newIdx{};
|
||||
size_t &m_selectedTilesheetIdx;
|
||||
public:
|
||||
ChangeTileSheet(
|
||||
WorldObjectSet &doc,
|
||||
ox::FileAddress newVal,
|
||||
size_t newIdx,
|
||||
size_t &selectedTilesheetIdx) noexcept;
|
||||
void redo() noexcept override;
|
||||
void undo() noexcept override;
|
||||
[[nodiscard]]
|
||||
int commandId() const noexcept override;
|
||||
};
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2023 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace jasper::world {
|
||||
|
||||
enum class WorldObjCommand {
|
||||
None,
|
||||
AddObject,
|
||||
RmObject,
|
||||
EditObjectName,
|
||||
EditObjectPalette,
|
||||
EditObjectSubSheet,
|
||||
EditObjectCollisionMap,
|
||||
AddPalette,
|
||||
RmPalette,
|
||||
EditPalette,
|
||||
ChangeTileSheet,
|
||||
};
|
||||
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2023 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include "commands.hpp"
|
||||
#include "editobject.hpp"
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace jasper::world {
|
||||
|
||||
EditObjectName::EditObjectName(WorldObjectSet &doc, size_t objIdx, ox::String newName) noexcept:
|
||||
m_doc(doc),
|
||||
m_objIdx(objIdx),
|
||||
m_oldVal(m_doc.objects[objIdx].name),
|
||||
m_newVal(std::move(newName)) {
|
||||
}
|
||||
|
||||
void EditObjectName::redo() noexcept {
|
||||
auto &obj = m_doc.objects[m_objIdx];
|
||||
obj.name = std::move(m_newVal);
|
||||
}
|
||||
|
||||
void EditObjectName::undo() noexcept {
|
||||
auto &obj = m_doc.objects[m_objIdx];
|
||||
m_newVal = std::move(obj.name);
|
||||
obj.name = m_oldVal;
|
||||
}
|
||||
|
||||
int EditObjectName::commandId() const noexcept {
|
||||
return static_cast<int>(WorldObjCommand::EditObjectName);
|
||||
}
|
||||
|
||||
bool EditObjectName::mergeWith(UndoCommand const*cmd) noexcept {
|
||||
auto const en = dynamic_cast<EditObjectName const*>(cmd);
|
||||
if (en && m_objIdx == en->m_objIdx) {
|
||||
m_newVal = en->m_newVal;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
EditObjectSubSheet::EditObjectSubSheet(
|
||||
WorldObjectSet &doc,
|
||||
size_t objIdx,
|
||||
ncore::SubSheetId subSheetId) noexcept:
|
||||
m_doc(doc),
|
||||
m_objIdx(objIdx),
|
||||
m_newVal(subSheetId),
|
||||
m_oldVal(m_doc.objects[m_objIdx].subsheetId) {
|
||||
}
|
||||
|
||||
void EditObjectSubSheet::redo() noexcept {
|
||||
auto &obj = m_doc.objects[m_objIdx];
|
||||
obj.subsheetId = m_newVal;
|
||||
}
|
||||
|
||||
void EditObjectSubSheet::undo() noexcept {
|
||||
auto &obj = m_doc.objects[m_objIdx];
|
||||
obj.subsheetId = m_oldVal;
|
||||
}
|
||||
|
||||
int EditObjectSubSheet::commandId() const noexcept {
|
||||
return static_cast<int>(WorldObjCommand::EditObjectSubSheet);
|
||||
}
|
||||
|
||||
|
||||
EditObjectPalette::EditObjectPalette(WorldObjectSet &doc, size_t objIdx, uint16_t palBank) noexcept:
|
||||
m_doc(doc),
|
||||
m_objIdx(objIdx),
|
||||
m_oldVal(m_doc.objects[m_objIdx].palBank),
|
||||
m_newVal(palBank) {
|
||||
}
|
||||
|
||||
void EditObjectPalette::redo() noexcept {
|
||||
auto &obj = m_doc.objects[m_objIdx];
|
||||
obj.palBank = m_newVal;
|
||||
}
|
||||
|
||||
void EditObjectPalette::undo() noexcept {
|
||||
auto &obj = m_doc.objects[m_objIdx];
|
||||
obj.palBank = m_oldVal;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
int EditObjectPalette::commandId() const noexcept {
|
||||
return static_cast<int>(WorldObjCommand::EditObjectPalette);
|
||||
}
|
||||
|
||||
|
||||
EditObjectCollisionMap::EditObjectCollisionMap(WorldObjectSet &doc, size_t objIdx, uint64_t colMap) noexcept:
|
||||
m_doc(doc),
|
||||
m_objIdx(objIdx),
|
||||
m_oldVal(m_doc.objects[m_objIdx].collisionMap),
|
||||
m_newVal(colMap) {
|
||||
}
|
||||
|
||||
void EditObjectCollisionMap::redo() noexcept {
|
||||
auto &obj = m_doc.objects[m_objIdx];
|
||||
obj.collisionMap = m_newVal;
|
||||
}
|
||||
|
||||
void EditObjectCollisionMap::undo() noexcept {
|
||||
auto &obj = m_doc.objects[m_objIdx];
|
||||
obj.collisionMap = m_oldVal;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
int EditObjectCollisionMap::commandId() const noexcept {
|
||||
return static_cast<int>(WorldObjCommand::EditObjectCollisionMap);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2023 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/fs/fs.hpp>
|
||||
|
||||
#include <studio/undostack.hpp>
|
||||
|
||||
#include <jasper/world/worldobject.hpp>
|
||||
|
||||
namespace jasper::world {
|
||||
|
||||
class EditObjectName: public studio::UndoCommand {
|
||||
private:
|
||||
WorldObjectSet &m_doc;
|
||||
size_t const m_objIdx{};
|
||||
ox::String const m_oldVal{};
|
||||
ox::String m_newVal{};
|
||||
public:
|
||||
EditObjectName(WorldObjectSet &doc, size_t objIdx, ox::String newName) noexcept;
|
||||
void redo() noexcept override;
|
||||
void undo() noexcept override;
|
||||
[[nodiscard]]
|
||||
int commandId() const noexcept override;
|
||||
bool mergeWith(UndoCommand const*cmd) noexcept override;
|
||||
};
|
||||
|
||||
class EditObjectSubSheet: public studio::UndoCommand {
|
||||
private:
|
||||
WorldObjectSet &m_doc;
|
||||
size_t const m_objIdx{};
|
||||
ncore::SubSheetId const m_newVal{};
|
||||
ncore::SubSheetId const m_oldVal{};
|
||||
public:
|
||||
EditObjectSubSheet(WorldObjectSet &doc, size_t objIdx, ncore::SubSheetId subSheetId) noexcept;
|
||||
void redo() noexcept override;
|
||||
void undo() noexcept override;
|
||||
[[nodiscard]]
|
||||
int commandId() const noexcept override;
|
||||
};
|
||||
|
||||
class EditObjectPalette: public studio::UndoCommand {
|
||||
private:
|
||||
WorldObjectSet &m_doc;
|
||||
size_t const m_objIdx{};
|
||||
uint16_t const m_oldVal{};
|
||||
uint16_t const m_newVal{};
|
||||
public:
|
||||
EditObjectPalette(WorldObjectSet &doc, size_t objIdx, uint16_t palBank) noexcept;
|
||||
void redo() noexcept override;
|
||||
void undo() noexcept override;
|
||||
[[nodiscard]]
|
||||
int commandId() const noexcept override;
|
||||
};
|
||||
|
||||
class EditObjectCollisionMap: public studio::UndoCommand {
|
||||
private:
|
||||
WorldObjectSet &m_doc;
|
||||
size_t const m_objIdx{};
|
||||
uint64_t const m_oldVal{};
|
||||
uint64_t const m_newVal{};
|
||||
public:
|
||||
EditObjectCollisionMap(WorldObjectSet &doc, size_t objIdx, uint64_t colMap) noexcept;
|
||||
void redo() noexcept override;
|
||||
void undo() noexcept override;
|
||||
[[nodiscard]]
|
||||
int commandId() const noexcept override;
|
||||
};
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2023 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include "commands.hpp"
|
||||
#include "editpalette.hpp"
|
||||
|
||||
namespace jasper::world {
|
||||
|
||||
EditPalette::EditPalette(PaletteCycle &doc, uint16_t interval) noexcept:
|
||||
m_doc(doc),
|
||||
m_oldVal(doc.intervalMs),
|
||||
m_newVal(interval) {
|
||||
}
|
||||
|
||||
void EditPalette::redo() noexcept {
|
||||
m_doc.intervalMs = m_newVal;
|
||||
}
|
||||
|
||||
void EditPalette::undo() noexcept {
|
||||
m_doc.intervalMs = m_oldVal;
|
||||
}
|
||||
|
||||
int EditPalette::commandId() const noexcept {
|
||||
return static_cast<int>(WorldObjCommand::EditPalette);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2023 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/fs/fs.hpp>
|
||||
|
||||
#include <studio/undostack.hpp>
|
||||
|
||||
#include <jasper/world/worldobject.hpp>
|
||||
|
||||
namespace jasper::world {
|
||||
|
||||
class EditPalette: public studio::UndoCommand {
|
||||
private:
|
||||
PaletteCycle &m_doc;
|
||||
uint16_t const m_oldVal{};
|
||||
uint16_t const m_newVal{};
|
||||
public:
|
||||
EditPalette(PaletteCycle &doc, uint16_t interval) noexcept;
|
||||
void redo() noexcept override;
|
||||
void undo() noexcept override;
|
||||
[[nodiscard]]
|
||||
int commandId() const noexcept override;
|
||||
};
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2023 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include "commands.hpp"
|
||||
#include "rmobject.hpp"
|
||||
|
||||
namespace jasper::world {
|
||||
|
||||
RmObject::RmObject(WorldObjectSet &doc, size_t const rmIdx) noexcept:
|
||||
m_doc(doc),
|
||||
m_rmIdx(rmIdx) {
|
||||
}
|
||||
|
||||
void RmObject::redo() noexcept {
|
||||
m_obj = std::move(m_doc.objects[m_rmIdx]);
|
||||
oxIgnoreError(m_doc.objects.erase(m_rmIdx));
|
||||
}
|
||||
|
||||
void RmObject::undo() noexcept {
|
||||
m_doc.objects.emplace(m_rmIdx, std::move(m_obj));
|
||||
}
|
||||
|
||||
int RmObject::commandId() const noexcept {
|
||||
return static_cast<int>(WorldObjCommand::RmObject);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2023 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/fs/fs.hpp>
|
||||
|
||||
#include <studio/undostack.hpp>
|
||||
|
||||
#include <jasper/world/worldobject.hpp>
|
||||
|
||||
namespace jasper::world {
|
||||
|
||||
class RmObject: public studio::UndoCommand {
|
||||
private:
|
||||
WorldObjectSet &m_doc;
|
||||
size_t const m_rmIdx{};
|
||||
WorldObject m_obj{};
|
||||
public:
|
||||
RmObject(WorldObjectSet &doc, size_t rmIdx) noexcept;
|
||||
void redo() noexcept override;
|
||||
void undo() noexcept override;
|
||||
[[nodiscard]]
|
||||
int commandId() const noexcept override;
|
||||
};
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2023 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "commands.hpp"
|
||||
#include "rmpalette.hpp"
|
||||
|
||||
namespace jasper::world {
|
||||
|
||||
RmPalette::RmPalette(WorldObjectSet &doc, size_t rmIdx) noexcept:
|
||||
m_doc(doc),
|
||||
m_rmIdx(rmIdx) {
|
||||
}
|
||||
|
||||
void RmPalette::redo() noexcept {
|
||||
m_pal = std::move(m_doc.palettes[m_rmIdx]);
|
||||
oxIgnoreError(m_doc.palettes.erase(m_rmIdx));
|
||||
}
|
||||
|
||||
void RmPalette::undo() noexcept {
|
||||
m_doc.palettes.emplace_back(std::move(m_pal));
|
||||
std::sort(m_doc.palettes.begin(), m_doc.palettes.end(), [](PaletteCycle const&a, PaletteCycle const&b) {
|
||||
return a.palette.getPath().or_value("") < b.palette.getPath().or_value("");
|
||||
});
|
||||
}
|
||||
|
||||
int RmPalette::commandId() const noexcept {
|
||||
return static_cast<int>(WorldObjCommand::RmPalette);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2023 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ox/fs/fs.hpp>
|
||||
|
||||
#include <studio/undostack.hpp>
|
||||
|
||||
#include <jasper/world/worldobject.hpp>
|
||||
|
||||
namespace jasper::world {
|
||||
|
||||
class RmPalette: public studio::UndoCommand {
|
||||
private:
|
||||
WorldObjectSet &m_doc;
|
||||
PaletteCycle m_pal;
|
||||
size_t const m_rmIdx = 0;
|
||||
public:
|
||||
RmPalette(WorldObjectSet &doc, size_t rmIdx) noexcept;
|
||||
void redo() noexcept override;
|
||||
void undo() noexcept override;
|
||||
[[nodiscard]]
|
||||
int commandId() const noexcept override;
|
||||
};
|
||||
|
||||
}
|
@ -0,0 +1,360 @@
|
||||
/*
|
||||
* Copyright 2023 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#include <studio/imguiutil.hpp>
|
||||
|
||||
#include "commands/addobject.hpp"
|
||||
#include "commands/rmobject.hpp"
|
||||
#include "commands/editobject.hpp"
|
||||
#include "commands/addpalette.hpp"
|
||||
#include "commands/rmpalette.hpp"
|
||||
#include "commands/editpalette.hpp"
|
||||
#include "commands/changetilesheet.hpp"
|
||||
|
||||
#include "worldobjectseteditor-imgui.hpp"
|
||||
|
||||
namespace jasper::world {
|
||||
|
||||
namespace ig = studio::ig;
|
||||
|
||||
constexpr auto btnSize = ImVec2(22, ig::BtnSz.y);
|
||||
|
||||
WorldObjectSetEditorImGui::WorldObjectSetEditorImGui(
|
||||
studio::StudioContext &ctx,
|
||||
ox::StringView path):
|
||||
Editor(path),
|
||||
m_sctx(ctx),
|
||||
m_itemPath(path),
|
||||
m_doc(*readObj<WorldObjectSet>(keelCtx(ctx.tctx), path).unwrapThrow()),
|
||||
m_tileSheet(readObj<ncore::TileSheet>(keelCtx(m_sctx.tctx), m_doc.tilesheet).unwrapThrow()) {
|
||||
auto const&tilesheetList = m_sctx.project->fileList(ncore::FileExt_ng);
|
||||
auto const tsPath = m_doc.tilesheet.getPath().or_value("");
|
||||
m_selectedTilesheetIdx = std::find(tilesheetList.begin(), tilesheetList.end(), tsPath).offset();
|
||||
loadObj();
|
||||
undoStack()->changeTriggered.connect(this, &WorldObjectSetEditorImGui::handleCmd);
|
||||
buildPaletteDisplayNameList();
|
||||
}
|
||||
|
||||
void WorldObjectSetEditorImGui::draw(turbine::Context&) noexcept {
|
||||
const auto paneSize = ImGui::GetContentRegionAvail();
|
||||
constexpr auto resourcesWidth = 300.f;
|
||||
ImGui::BeginChild("ObjEditor", ImVec2(paneSize.x - resourcesWidth, 0));
|
||||
if (m_selectedObj < m_doc.objects.size()) {
|
||||
drawObjEditor();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
ImGui::SameLine();
|
||||
ImGui::BeginChild("Resources", ImVec2(0, 0));
|
||||
{
|
||||
{
|
||||
ig::IDStackItem const idStackItem("TileSheetSelector");
|
||||
drawTileSheetSelector();
|
||||
}
|
||||
ImGui::Separator();
|
||||
{
|
||||
ig::IDStackItem const idStackItem("ObjSelector");
|
||||
drawObjSelector();
|
||||
}
|
||||
ImGui::Separator();
|
||||
{
|
||||
ig::IDStackItem const idStackItem("PaletteList");
|
||||
drawPaletteList();
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
drawAddPalettePopup();
|
||||
drawEditPalettePopup();
|
||||
}
|
||||
|
||||
void WorldObjectSetEditorImGui::onActivated() noexcept {
|
||||
}
|
||||
|
||||
WorldObject const&WorldObjectSetEditorImGui::activeObj() const noexcept {
|
||||
return m_doc.objects[m_selectedObj];
|
||||
}
|
||||
|
||||
WorldObject &WorldObjectSetEditorImGui::activeObj() noexcept {
|
||||
return m_doc.objects[m_selectedObj];
|
||||
}
|
||||
|
||||
void WorldObjectSetEditorImGui::buildPaletteDisplayNameList() noexcept {
|
||||
m_paletteDisplayNames.clear();
|
||||
for (auto i = 1u; auto const&pal : m_doc.palettes) {
|
||||
auto path = pal.palette.getPath().or_value("n/a");
|
||||
m_paletteDisplayNames.emplace_back(ox::sfmt("{}: {} - {} ms", i, path, pal.intervalMs));
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
void WorldObjectSetEditorImGui::drawTileSheetSelector() noexcept {
|
||||
auto const&tilesheetList = m_sctx.project->fileList(ncore::FileExt_ng);
|
||||
auto sel = m_selectedTilesheetIdx;
|
||||
if (ig::ComboBox("Tile Sheet", tilesheetList, sel)) {
|
||||
undoStack()->push(ox::make_unique<ChangeTileSheet>
|
||||
(m_doc, ox::FileAddress(tilesheetList[sel]), sel, m_selectedTilesheetIdx));
|
||||
loadObj();
|
||||
}
|
||||
}
|
||||
|
||||
void WorldObjectSetEditorImGui::drawObjSelector() noexcept {
|
||||
ig::IDStackItem const idStackItem1("ObjSelector");
|
||||
if (ig::PushButton("+", btnSize)) {
|
||||
auto cmd = ox::make_unique<AddObject>(m_doc);
|
||||
m_selectedObj = cmd->insertIdx();
|
||||
undoStack()->push(std::move(cmd));
|
||||
loadObj();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ig::PushButton("-", btnSize)) {
|
||||
undoStack()->push(ox::make_unique<RmObject>(m_doc, m_selectedObj));
|
||||
}
|
||||
if (ImGui::BeginListBox("Objects")) {
|
||||
for (auto i = 0u; auto const&obj : m_doc.objects) {
|
||||
ig::IDStackItem const idStackItem2(static_cast<int>(i));
|
||||
if (ImGui::Selectable(obj.name.c_str(), m_selectedObj == i)) {
|
||||
if (i != m_selectedObj) {
|
||||
m_selectedObj = i;
|
||||
loadObj();
|
||||
}
|
||||
}
|
||||
++i;
|
||||
}
|
||||
ImGui::EndListBox();
|
||||
}
|
||||
}
|
||||
|
||||
void WorldObjectSetEditorImGui::loadObj() noexcept {
|
||||
if (m_selectedObj >= m_doc.objects.size()) {
|
||||
return;
|
||||
}
|
||||
auto const&obj = activeObj();
|
||||
auto &nameBuff = m_objEditor.nameBuff;
|
||||
ox_strncpy(nameBuff.data(), obj.name.data(), nameBuff.size());
|
||||
m_objEditor.palette = obj.palBank;
|
||||
m_subsheet = getSubsheet(*m_tileSheet, obj.subsheetId);
|
||||
int w = 0;
|
||||
int h = 0;
|
||||
if (m_subsheet) {
|
||||
w = m_subsheet->columns;
|
||||
h = m_subsheet->rows;
|
||||
}
|
||||
auto const idx = getTileIdx(*m_tileSheet, obj.subsheetId);
|
||||
oxLogError(m_colView.setup(
|
||||
m_doc.tilesheet,
|
||||
m_doc.palettes[obj.palBank].palette,
|
||||
w,
|
||||
h,
|
||||
static_cast<uint_t>(idx),
|
||||
obj.collisionMap));
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
static ox::Vec2 clickPos(ImVec2 const&imgSz, ImVec2 const&offset, ox::Vec2 clickPos) noexcept {
|
||||
clickPos.x -= offset.x;
|
||||
clickPos.y -= offset.y;
|
||||
clickPos.x /= imgSz.x;
|
||||
clickPos.y /= imgSz.y;
|
||||
return clickPos;
|
||||
}
|
||||
|
||||
void WorldObjectSetEditorImGui::drawObjEditor() noexcept {
|
||||
ig::IDStackItem const idStackItem("ObjEditor");
|
||||
ig::IndentStackItem const indent1(10);
|
||||
ImGui::NewLine();
|
||||
auto &nameBuff = m_objEditor.nameBuff;
|
||||
if (ImGui::InputText("Name", nameBuff.data(), nameBuff.size())) {
|
||||
undoStack()->push(ox::make_unique<EditObjectName>(
|
||||
m_doc,
|
||||
m_selectedObj,
|
||||
ox::String(nameBuff.data(), ox_strnlen(nameBuff.data(), nameBuff.size()))));
|
||||
}
|
||||
// SubSheet Selector
|
||||
{
|
||||
ig::IDStackItem const subsheetSelectorStackItem("SubsheetSelector");
|
||||
if (ig::ComboBox("Palette", m_paletteDisplayNames, m_objEditor.palette)) {
|
||||
undoStack()->push(ox::make_unique<EditObjectPalette>(
|
||||
m_doc, m_selectedObj, static_cast<uint16_t>(m_objEditor.palette)));
|
||||
}
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Subsheet:");
|
||||
ImGui::BeginChild("SubsheetSelector", ImVec2{300, 300});
|
||||
{
|
||||
ImGui::SetNextItemOpen(true);
|
||||
if (m_tileSheet) {
|
||||
drawSubSheetNode(m_tileSheet->subsheet);
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
// collision map
|
||||
if (m_subsheet) {
|
||||
ImGui::NewLine();
|
||||
ImGui::Text("Collision Map:");
|
||||
ig::IndentStackItem const indent2(30);
|
||||
m_colView.draw();
|
||||
auto const&fb = m_colView.framebuffer();
|
||||
uintptr_t const buffId = fb.color.id;
|
||||
auto const scale = static_cast<float>(m_colView.scale());
|
||||
auto const width = static_cast<float>(m_subsheet->columns * ncore::TileWidth);
|
||||
auto const height = static_cast<float>(m_subsheet->rows * ncore::TileHeight);
|
||||
auto const horzPct = width / 240.f;
|
||||
auto const vertPct = height / 160.f;
|
||||
auto const imageSz = ImVec2(width * scale, height * scale);
|
||||
auto const imagePos = ImGui::GetCursorScreenPos();
|
||||
ImGui::Image(
|
||||
std::bit_cast<void*>(buffId),
|
||||
imageSz,
|
||||
ImVec2(0, 1.0),
|
||||
ImVec2(horzPct, 1 - vertPct));
|
||||
auto const&io = ImGui::GetIO();
|
||||
if (ImGui::IsItemClicked()) {
|
||||
auto const mousePos = clickPos(imageSz, imagePos, ox::Vec2(io.MousePos));
|
||||
auto &obj = activeObj();
|
||||
auto intermediate = obj.collisionMap;
|
||||
if (m_colView.click(mousePos, intermediate)) {
|
||||
undoStack()->push(ox::make_unique<EditObjectCollisionMap>(m_doc, m_selectedObj, intermediate));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WorldObjectSetEditorImGui::drawSubSheetNode(ncore::TileSheet::SubSheet const&ss) noexcept {
|
||||
auto constexpr dirFlags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;
|
||||
auto &obj = activeObj();
|
||||
auto const selected = ss.id == obj.subsheetId ? ImGuiTreeNodeFlags_Selected : 0;
|
||||
ig::IDStackItem const idStackItem(ss.name);
|
||||
if (!ss.subsheets.empty()) {
|
||||
if (ImGui::TreeNodeEx(ss.name.c_str(), dirFlags)) {
|
||||
for (auto const&child : ss.subsheets) {
|
||||
drawSubSheetNode(child);
|
||||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
} else if (ImGui::TreeNodeEx(ss.name.c_str(), ImGuiTreeNodeFlags_Leaf | selected)) {
|
||||
if (ImGui::IsItemClicked()) {
|
||||
undoStack()->push(ox::make_unique<EditObjectSubSheet>(m_doc, m_selectedObj, ss.id));
|
||||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
|
||||
void WorldObjectSetEditorImGui::drawPaletteList() noexcept {
|
||||
ig::IDStackItem const idStackItem("PaletteList");
|
||||
if (ig::PushButton("+", btnSize)) {
|
||||
addPalette();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ig::PushButton("-", btnSize)) {
|
||||
rmPalette();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ig::PushButton("Edit")) {
|
||||
editPalette();
|
||||
}
|
||||
constexpr auto flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody;
|
||||
if (ImGui::BeginTable("Subsheets", 2, flags)) {
|
||||
ImGui::TableSetupColumn("Palette", ImGuiTableColumnFlags_NoHide);
|
||||
ImGui::TableSetupColumn("Interval", ImGuiTableColumnFlags_WidthFixed, 100);
|
||||
ImGui::TableHeadersRow();
|
||||
drawPaletteListItems();
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
void WorldObjectSetEditorImGui::drawPaletteListItems() noexcept {
|
||||
for (auto i = 0u; auto const&pal : m_doc.palettes) {
|
||||
ig::IDStackItem const idStackItem(static_cast<int>(i));
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", pal.palette.getPath().value.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d ms", pal.intervalMs);
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Selectable("##PaletteSelection", i == m_selectedPal, ImGuiSelectableFlags_SpanAllColumns)) {
|
||||
m_selectedPal = i;
|
||||
}
|
||||
if (ImGui::IsMouseDoubleClicked(0) && ImGui::IsItemHovered()) {
|
||||
editPalette();
|
||||
}
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
void WorldObjectSetEditorImGui::drawAddPalettePopup() noexcept {
|
||||
if (!m_addPalPopup.show) {
|
||||
return;
|
||||
}
|
||||
auto constexpr popupName = ox::CStringView("Add Palette");
|
||||
auto constexpr popupSz = ImVec2{285.f, 0};
|
||||
ig::IDStackItem const idStackItem("AddPalette");
|
||||
if (ig::BeginPopup(m_sctx.tctx, popupName, m_addPalPopup.show, popupSz)) {
|
||||
auto const&palettes = m_sctx.project->fileList(ncore::FileExt_npal);
|
||||
ig::ComboBox("Palette", palettes, m_addPalPopup.selectedIdx);
|
||||
if (ig::PopupControlsOkCancel(popupSz.x, m_addPalPopup.show) == ig::PopupResponse::OK) {
|
||||
undoStack()->push(ox::make_unique<AddPalette>(
|
||||
m_doc,
|
||||
ox::FileAddress(palettes[m_addPalPopup.selectedIdx])));
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
void WorldObjectSetEditorImGui::drawEditPalettePopup() noexcept {
|
||||
if (!m_editPalPopup.show) {
|
||||
return;
|
||||
}
|
||||
auto constexpr popupName = ox::CStringView("Edit Palette");
|
||||
auto constexpr popupSz = ImVec2{300.f, 0};
|
||||
ig::IDStackItem const idStackItem("EditPalette");
|
||||
if (ig::BeginPopup(m_sctx.tctx, popupName, m_editPalPopup.show, popupSz)) {
|
||||
auto &pal = m_doc.palettes[m_selectedPal];
|
||||
ImGui::InputInt("Interval (ms)", &m_editPalPopup.intervalInputVal, 1000);
|
||||
m_editPalPopup.intervalInputVal =
|
||||
static_cast<uint16_t>(ox::clamp(m_editPalPopup.intervalInputVal, 0, 60000));
|
||||
if (ig::PopupControlsOkCancel(popupSz.x, m_editPalPopup.show) == ig::PopupResponse::OK) {
|
||||
undoStack()->push(ox::make_unique<EditPalette>(
|
||||
pal, static_cast<uint16_t>(m_editPalPopup.intervalInputVal)));
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
void WorldObjectSetEditorImGui::addPalette() noexcept {
|
||||
m_addPalPopup.show = true;
|
||||
m_addPalPopup.selectedIdx = 0;
|
||||
}
|
||||
|
||||
void WorldObjectSetEditorImGui::rmPalette() noexcept {
|
||||
undoStack()->push(ox::make_unique<RmPalette>(
|
||||
m_doc, m_selectedPal));
|
||||
}
|
||||
|
||||
void WorldObjectSetEditorImGui::editPalette() noexcept {
|
||||
auto &pal = m_doc.palettes[m_selectedPal];
|
||||
m_editPalPopup.show = true;
|
||||
m_editPalPopup.intervalInputVal = pal.intervalMs;
|
||||
}
|
||||
|
||||
ox::Error WorldObjectSetEditorImGui::handleCmd(studio::UndoCommand const*cmd) noexcept {
|
||||
if (dynamic_cast<ChangeTileSheet const*>(cmd)) {
|
||||
oxLogError(readObj<ncore::TileSheet>(keelCtx(m_sctx.tctx), m_doc.tilesheet).moveTo(m_tileSheet));
|
||||
}
|
||||
if (dynamic_cast<AddPalette const*>(cmd) ||
|
||||
dynamic_cast<RmPalette const*>(cmd) ||
|
||||
dynamic_cast<EditPalette const*>(cmd)) {
|
||||
buildPaletteDisplayNameList();
|
||||
}
|
||||
if (dynamic_cast<EditObjectPalette const*>(cmd)) {
|
||||
auto const&obj = activeObj();
|
||||
m_objEditor.palette = obj.palBank;
|
||||
}
|
||||
loadObj();
|
||||
return {};
|
||||
}
|
||||
|
||||
ox::Error WorldObjectSetEditorImGui::saveItem() noexcept {
|
||||
return m_sctx.project->writeObj(m_itemPath, m_doc, ox::ClawFormat::Organic);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2023 - 2024 Gary Talent (gary@drinkingtea.net). All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <turbine/context.hpp>
|
||||
#include <studio/studio.hpp>
|
||||
|
||||
#include <jasper/world/worldobject.hpp>
|
||||
|
||||
#include "collisionmapview.hpp"
|
||||
|
||||
namespace jasper::world {
|
||||
|
||||
class WorldObjectSetEditorImGui: public studio::Editor {
|
||||
|
||||
private:
|
||||
studio::StudioContext &m_sctx;
|
||||
ox::String m_itemPath;
|
||||
WorldObjectSet m_doc;
|
||||
keel::AssetRef<ncore::TileSheet> m_tileSheet;
|
||||
ncore::TileSheet::SubSheet const*m_subsheet = nullptr;
|
||||
ox::Vector<ox::String> m_paletteDisplayNames;
|
||||
size_t m_selectedObj{};
|
||||
size_t m_selectedPal{};
|
||||
size_t m_selectedTilesheetIdx{};
|
||||
struct {
|
||||
bool show{};
|
||||
size_t selectedIdx{};
|
||||
} m_addPalPopup;
|
||||
struct {
|
||||
bool show{};
|
||||
int intervalInputVal{};
|
||||
} m_editPalPopup;
|
||||
CollisionView m_colView{m_sctx};
|
||||
struct {
|
||||
ox::Buffer nameBuff = ox::Buffer(256);
|
||||
size_t palette{};
|
||||
} m_objEditor;
|
||||
|
||||
public:
|
||||
WorldObjectSetEditorImGui(studio::StudioContext &ctx, ox::StringView path);
|
||||
|
||||
void draw(turbine::Context&) noexcept final;
|
||||
|
||||
void onActivated() noexcept override;
|
||||
|
||||
private:
|
||||
WorldObject const&activeObj() const noexcept;
|
||||
|
||||
WorldObject &activeObj() noexcept;
|
||||
|
||||
void buildPaletteDisplayNameList() noexcept;
|
||||
|
||||
void drawTileSheetSelector() noexcept;
|
||||
|
||||
void drawObjSelector() noexcept;
|
||||
|
||||
void loadObj() noexcept;
|
||||
|
||||
void drawObjEditor() noexcept;
|
||||
|
||||
void drawSubSheetNode(ncore::TileSheet::SubSheet const&ss) noexcept;
|
||||
|
||||
void drawPaletteList() noexcept;
|
||||
|
||||
void drawPaletteListItems() noexcept;
|
||||
|
||||
void drawAddPalettePopup() noexcept;
|
||||
|
||||
void drawEditPalettePopup() noexcept;
|
||||
|
||||
void addPalette() noexcept;
|
||||
|
||||
void rmPalette() noexcept;
|
||||
|
||||
void editPalette() noexcept;
|
||||
|
||||
ox::Error handleCmd(studio::UndoCommand const*cmd) noexcept;
|
||||
|
||||
ox::Error saveItem() noexcept final;
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -42,9 +42,9 @@ void World::setupLayer(
|
||||
const auto width = m_worldStatic.rows[layerNo];
|
||||
for (auto const&tile : layer) {
|
||||
const auto tile8 = static_cast<uint8_t>(tile);
|
||||
ncore::setBgTile(ctx, layerNo, x, y, tile8);
|
||||
ncore::setBgTile(ctx, layerNo, x + 1, y, tile8 + 1);
|
||||
ncore::setBgTile(ctx, layerNo, x, y + 1, tile8 + 2);
|
||||
ncore::setBgTile(ctx, layerNo, x + 0, y + 0, tile8 + 0);
|
||||
ncore::setBgTile(ctx, layerNo, x + 1, y + 0, tile8 + 1);
|
||||
ncore::setBgTile(ctx, layerNo, x + 0, y + 1, tile8 + 2);
|
||||
ncore::setBgTile(ctx, layerNo, x + 1, y + 1, tile8 + 3);
|
||||
x += 2;
|
||||
if (x >= width * 2) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user