summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorStanislaw Halik <sthalik@misaki.pl>2024-01-22 18:55:41 +0100
committerStanislaw Halik <sthalik@misaki.pl>2024-01-22 18:55:41 +0100
commit906f56a842c0e0e706087f80607ff6d816a23280 (patch)
tree730ed20091649482691baeaeae753dd40c468402
parent49f455887a29a2a879e330dca9e050296e617720 (diff)
w
-rw-r--r--bench/dijkstra.cpp2
-rw-r--r--loader/atlas.cpp2
-rw-r--r--loader/impl.hpp2
-rw-r--r--loader/loader.hpp2
-rw-r--r--loader/wall-atlas.cpp43
-rw-r--r--serialize/savegame.cpp362
-rw-r--r--src/chunk-render.cpp2
-rw-r--r--src/object.cpp9
-rw-r--r--src/scenery.cpp5
-rw-r--r--src/world.hpp7
-rw-r--r--test/dijkstra.cpp2
-rw-r--r--test/loader.cpp4
-rw-r--r--test/path-search.cpp2
-rw-r--r--test/serializer.cpp2
-rw-r--r--test/wall-atlas2.cpp2
15 files changed, 346 insertions, 102 deletions
diff --git a/bench/dijkstra.cpp b/bench/dijkstra.cpp
index 4296e71c..a50548d0 100644
--- a/bench/dijkstra.cpp
+++ b/bench/dijkstra.cpp
@@ -24,7 +24,7 @@ void Dijkstra(benchmark::State& state)
constexpr auto wpos = global_coords{wch, wt};
auto& ch = w[wch];
- auto metal2 = wall_image_proto{loader.wall_atlas("empty", false), 0};
+ auto metal2 = wall_image_proto{loader.wall_atlas("empty", loader_policy::warn), 0};
for (int16_t j = wcy - 1; j <= wcy + 1; j++)
for (int16_t i = wcx - 1; i <= wcx + 1; i++)
diff --git a/loader/atlas.cpp b/loader/atlas.cpp
index 100f638e..555a2b02 100644
--- a/loader/atlas.cpp
+++ b/loader/atlas.cpp
@@ -56,7 +56,7 @@ ArrayView<const String> loader_impl::anim_atlas_list()
std::shared_ptr<anim_atlas> loader_impl::anim_atlas(StringView name, StringView dir) noexcept(false)
{
- fm_soft_assert(dir && dir[dir.size()-1] == '/');
+ fm_soft_assert(!dir || dir[dir.size()-1] == '/');
char buf[FILENAME_MAX];
auto path = make_atlas_path(buf, dir, name);
diff --git a/loader/impl.hpp b/loader/impl.hpp
index fd51b4f3..9308baba 100644
--- a/loader/impl.hpp
+++ b/loader/impl.hpp
@@ -50,7 +50,7 @@ struct loader_impl final : loader_
std::vector<wall_info> wall_atlas_array;
std::vector<String> missing_wall_atlases;
Pointer<wall_info> invalid_wall_atlas;
- std::shared_ptr<class wall_atlas> wall_atlas(StringView name, bool fail_ok = true) override;
+ std::shared_ptr<class wall_atlas> wall_atlas(StringView name, loader_policy policy) override;
ArrayView<const wall_info> wall_atlas_list() override;
void get_wall_atlas_list();
const wall_info& make_invalid_wall_atlas();
diff --git a/loader/loader.hpp b/loader/loader.hpp
index be132751..63740aa7 100644
--- a/loader/loader.hpp
+++ b/loader/loader.hpp
@@ -41,7 +41,7 @@ struct loader_
virtual std::shared_ptr<class ground_atlas> ground_atlas(StringView filename, loader_policy policy = loader_policy::DEFAULT) noexcept(false) = 0;
virtual ArrayView<const String> anim_atlas_list() = 0;
virtual std::shared_ptr<class anim_atlas> anim_atlas(StringView name, StringView dir = ANIM_PATH) noexcept(false) = 0;
- virtual std::shared_ptr<class wall_atlas> wall_atlas(StringView name, bool fail_ok = false) noexcept(false) = 0;
+ virtual std::shared_ptr<class wall_atlas> wall_atlas(StringView name, loader_policy policy = loader_policy::DEFAULT) noexcept(false) = 0;
virtual ArrayView<const wall_info> wall_atlas_list() = 0;
virtual void destroy() = 0;
static loader_& default_loader() noexcept;
diff --git a/loader/wall-atlas.cpp b/loader/wall-atlas.cpp
index b2505fbc..6e66478b 100644
--- a/loader/wall-atlas.cpp
+++ b/loader/wall-atlas.cpp
@@ -68,21 +68,38 @@ const wall_info& loader_impl::make_invalid_wall_atlas()
return *invalid_wall_atlas;
}
-std::shared_ptr<class wall_atlas> loader_impl::wall_atlas(StringView name, bool fail_ok) noexcept(false)
+std::shared_ptr<class wall_atlas> loader_impl::wall_atlas(StringView name, loader_policy policy) noexcept(false)
{
- fm_assert(fail_ok || name != INVALID);
- fm_soft_assert(check_atlas_name(name));
+ (void)wall_atlas_list();
+ switch (policy)
+ {
+ case loader_policy::error:
+ fm_assert(name != INVALID);
+ break;
+ case loader_policy::ignore:
+ case loader_policy::warn:
+ break;
+ default:
+ fm_abort("invalid loader_policy");
+ }
+
+ fm_soft_assert(check_atlas_name(name));
auto it = wall_atlas_map.find(name);
if (it != wall_atlas_map.end()) [[likely]]
{
if (it->second == (wall_info*)-1) [[unlikely]]
{
- if (fail_ok) [[likely]]
- goto missing_ok;
- else
+ switch (policy)
+ {
+ case loader_policy::error:
goto error;
+ case loader_policy::warn:
+ case loader_policy::ignore:
+ goto missing_ok;
+ }
+ std::unreachable();
}
else if (!it->second->atlas)
return it->second->atlas = get_wall_atlas(name, loader.WALL_TILESET_PATH);
@@ -91,13 +108,19 @@ std::shared_ptr<class wall_atlas> loader_impl::wall_atlas(StringView name, bool
}
else
{
- if (fail_ok) [[likely]]
- goto missing;
- else
+ switch (policy)
+ {
+ case loader_policy::error:
goto error;
+ case loader_policy::warn:
+ goto missing_warn;
+ case loader_policy::ignore:
+ goto missing_ok;
+ }
+ std::unreachable();
}
-missing:
+missing_warn:
{
missing_wall_atlases.push_back(String { AllocatedInit, name });
auto string_view = StringView{missing_wall_atlases.back()};
diff --git a/serialize/savegame.cpp b/serialize/savegame.cpp
index af7e223a..81df0d08 100644
--- a/serialize/savegame.cpp
+++ b/serialize/savegame.cpp
@@ -84,19 +84,6 @@ struct buffer
}
};
-struct serialized_atlas
-{
- buffer buf;
- const void* atlas;
- atlas_type type;
-};
-
-struct serialized_chunk
-{
- buffer buf{};
- chunk* c;
-};
-
template<typename Derived>
struct visitor_
{
@@ -150,16 +137,17 @@ struct visitor_
}
template<typename F>
- void visit(object& obj, F&& f)
+ void visit_object_internal(object& obj, F&& f, object_id id, object_type type)
{
- auto type = obj.type();
-
- do_visit(obj.id, f);
- do_visit(type, f);
- fm_assert(obj.atlas);
- do_visit(*obj.atlas, f);
+ non_const(obj.id) = id;
+ do_visit_nonconst(obj.atlas, f);
//do_visit(*obj.c, f);
- do_visit(obj.coord.local(), f);
+ {
+ chunk_coords_ ch = obj.coord.chunk3();
+ local_coords pt = obj.coord.local();
+ do_visit(pt, f);
+ non_const(obj.coord) = {ch, pt};
+ }
do_visit_nonconst(obj.offset, f);
do_visit_nonconst(obj.bbox_offset, f);
do_visit_nonconst(obj.bbox_size, f);
@@ -178,7 +166,7 @@ struct visitor_
case object_type::none:
break;
}
- fm_abort("invalid object type '%d'", (int)obj.type());
+ fm_abort("invalid object type '%d'", (int)type);
}
template<typename F>
@@ -188,6 +176,51 @@ struct visitor_
do_visit(c.wall_north(), f);
do_visit(c.wall_west(), f);
}
+
+ template<typename F> void visit(chunk_coords_& coord, F&& f)
+ {
+ f(coord.x);
+ f(coord.y);
+ f(coord.z);
+ }
+
+ template<typename F>
+ void visit(critter& obj, F&& f)
+ {
+ uint8_t flags = 0;
+ flags |= (1 << 0) * obj.playable;
+ do_visit(flags, f);
+ obj.playable = flags & (1 << 0);
+ do_visit(obj.name, f);
+ do_visit(obj.offset_frac, f);
+ }
+
+ template<typename F> void visit(scenery& obj, F&& f)
+ {
+ auto sc_type = obj.sc_type;
+ do_visit(sc_type, f);
+ uint8_t flags = 0;
+ flags |= obj.active * (1 << 0);
+ flags |= obj.closing * (1 << 1);
+ flags |= obj.interactive * (1 << 2);
+ do_visit(flags, f);
+ obj.active = flags & (1 << 0);
+ obj.closing = flags & (1 << 1);
+ obj.interactive = flags & (1 << 2);
+ }
+
+ template<typename F> void visit(light& obj, F&& f)
+ {
+ do_visit(obj.max_distance, f);
+ do_visit(obj.color, f);
+ auto falloff = obj.falloff;
+ do_visit(falloff, f);
+ obj.falloff = falloff;
+ uint8_t flags = 0;
+ flags |= obj.enabled * (1 << 0);
+ do_visit(flags, f);
+ obj.enabled = flags & (1 << 0);
+ }
};
constexpr size_t vector_initial_size = 128, hash_initial_size = vector_initial_size*2;
@@ -196,6 +229,19 @@ struct writer final : visitor_<writer>
{
const world& w;
+ struct serialized_atlas
+ {
+ buffer buf;
+ const void* atlas;
+ atlas_type type;
+ };
+
+ struct serialized_chunk
+ {
+ buffer buf{};
+ chunk* c;
+ };
+
std::vector<StringView> string_array{};
tsl::robin_map<StringView, uint32_t, string_hasher> string_map{hash_initial_size};
@@ -232,61 +278,31 @@ struct writer final : visitor_<writer>
using visitor_<writer>::visit;
template<typename F>
- void visit(critter& obj, F&& f)
+ void visit(std::shared_ptr<anim_atlas>& a, F&& f)
{
- uint8_t flags = 0;
- flags |= (1 << 0) * obj.playable;
- do_visit(flags, f);
- do_visit(obj.name, f);
- do_visit(obj.offset_frac, f);
+ atlasid id = intern_atlas(a, atlas_type::object);
+ do_visit(id, f);
}
- template<typename F>
- void visit(scenery& obj, F&& f)
+ template<typename F> void visit(const local_coords& pt, F&& f)
{
- auto sc_type = obj.sc_type;
- do_visit(sc_type, f);
- uint8_t flags = 0;
- flags |= obj.active * (1 << 0);
- flags |= obj.closing * (1 << 1);
- flags |= obj.interactive * (1 << 2);
- do_visit(flags, f);
+ f(pt.to_index());
}
- template<typename F>
- void visit(light& obj, F&& f)
+ template<typename F> void visit(StringView name, F&& f)
{
- do_visit(obj.max_distance, f);
- do_visit(obj.color, f);
- auto falloff = obj.falloff;
- do_visit(falloff, f);
- uint8_t flags = 0;
- flags |= obj.enabled * (1 << 0);
- do_visit(flags, f);
+ f(intern_string(name));
}
template<typename F>
- void visit(anim_atlas& a, F&& f)
- {
- atlasid id = intern_atlas(&a, atlas_type::object);
- do_visit(id, f);
- }
-
- template<typename F> void visit(const chunk_coords_& coord, F&& f)
+ void visit(object& obj, F&& f)
{
- f(coord.x);
- f(coord.y);
- f(coord.z);
- }
+ auto type = obj.type();
- template<typename F> void visit(const local_coords& pt, F&& f)
- {
- f(pt.to_index());
- }
+ do_visit_nonconst(obj.id, f);
+ do_visit(type, f);
- template<typename F> void visit(StringView name, F&& f)
- {
- f(intern_string(name));
+ visit_object_internal(obj, f, obj.id, obj.type());
}
template<typename F>
@@ -309,8 +325,9 @@ ok:
do_visit(intern_string(name), f);
}
- [[nodiscard]] atlasid intern_atlas(const void* atlas, atlas_type type)
+ template<typename T> [[nodiscard]] atlasid intern_atlas(const std::shared_ptr<T>& atlas_, atlas_type type)
{
+ const void* atlas = atlas_.get();
atlas_array.reserve(vector_initial_size);
fm_assert(atlas != nullptr);
auto [kv, fresh] = atlas_map.try_emplace(atlas, (uint32_t)-1);
@@ -337,7 +354,7 @@ ok:
}
}
- atlasid maybe_intern_atlas(const void* atlas, atlas_type type)
+ template<typename T> atlasid maybe_intern_atlas(const std::shared_ptr<T>& atlas, atlas_type type)
{
if (!atlas)
return null<atlasid>;
@@ -363,7 +380,7 @@ ok:
for (const std::shared_ptr<object>& obj : c.objects())
{
fm_assert(obj != nullptr);
- do_visit(object_magic, f); // todo remove this
+ do_visit(object_magic, f); // todo move before all objects
do_visit(*obj, f);
}
}
@@ -392,10 +409,7 @@ ok:
do_visit(num, f);
}
- if (a != null)
- do_visit(a, f);
- else
- do_visit(null, f);
+ do_visit(a, f);
i += num_idempotent;
fm_debug_assert(i <= TILE_COUNT);
@@ -412,15 +426,15 @@ ok:
// todo do atlases and variants separately
for (uint32_t i = 0; i < TILE_COUNT; i++)
serialize_tile_([&](uint32_t i) {
- return maybe_intern_atlas(c[i].ground_atlas().get(), atlas_type::ground);
+ return maybe_intern_atlas(c[i].ground_atlas(), atlas_type::ground);
}, i, f);
for (uint32_t i = 0; i < TILE_COUNT; i++)
serialize_tile_([&](uint32_t i) {
- return maybe_intern_atlas(c[i].wall_north_atlas().get(), atlas_type::wall);
+ return maybe_intern_atlas(c[i].wall_north_atlas(), atlas_type::wall);
}, i, f);
for (uint32_t i = 0; i < TILE_COUNT; i++)
serialize_tile_([&](uint32_t i) {
- return maybe_intern_atlas(c[i].wall_west_atlas().get(), atlas_type::wall);
+ return maybe_intern_atlas(c[i].wall_west_atlas(), atlas_type::wall);
}, i, f);
for (uint32_t i = 0; i < TILE_COUNT; i++)
serialize_tile_([&](uint32_t i) {
@@ -576,15 +590,87 @@ void world::serialize(StringView filename)
namespace {
+template<atlas_type Type> struct atlas_from_type;
+template<> struct atlas_from_type<atlas_type::ground> { using Type = ground_atlas; static StringView name(void* ptr) { return reinterpret_cast<Type*>(ptr)->name(); } };
+template<> struct atlas_from_type<atlas_type::wall> { using Type = wall_atlas; static StringView name(void* ptr) { return reinterpret_cast<Type*>(ptr)->name(); }};
+template<> struct atlas_from_type<atlas_type::object> { using Type = anim_atlas; static StringView name(void* ptr) { return reinterpret_cast<Type*>(ptr)->name(); } };
+
struct reader final : visitor_<reader>
{
+ struct atlas_pair
+ {
+ void* atlas;
+ atlas_type type;
+ };
+
std::vector<StringView> strings;
+ std::vector<atlas_pair> atlases;
proto_t PROTO = (proto_t)-1;
uint32_t nstrings = 0, natlases = 0, nchunks = 0;
class world& w;
reader(class world& w) : w{w} {}
+ using visitor_<reader>::visit;
+
+ struct byte_reader
+ {
+ binary_reader<const char*>& s;
+
+ template<typename T>
+ requires (std::is_fundamental_v<T> && std::is_arithmetic_v<T>)
+ void operator()(T& value)
+ {
+ value << s;
+ }
+ };
+
+ template<typename F>
+ void visit(String& str, F&& f)
+ {
+ atlasid id;
+ f(id);
+ str = get_string(id);
+ }
+
+ template<typename F> void visit(local_coords& pt, F&& f)
+ {
+ uint8_t i;
+ f(i);
+ pt = local_coords{i};
+ }
+
+ template<typename F>
+ void visit(std::shared_ptr<anim_atlas>& a, F&& f)
+ {
+ atlasid id = (atlasid)-1;
+ f(id);
+ a = loader.anim_atlas(get_atlas<atlas_type::object>(id), {});
+ }
+
+ template<typename F>
+ void visit(std::shared_ptr<object>& obj, F&& f)
+ {
+ object_id id;
+ do_visit(id, f);
+ std::underlying_type_t<object_type> type_;
+ do_visit(type_, f);
+ auto type = object_type(type_);
+
+ switch (type)
+ {
+ case object_type::none:
+ case object_type::COUNT:
+ break;
+ case object_type::light: obj = w.make_unconnected_object<light>(); goto ok;
+ case object_type::critter: obj = w.make_unconnected_object<critter>(); goto ok;
+ case object_type::scenery: obj = w.make_unconnected_object<scenery>(); goto ok;
+ }
+ fm_throw("invalid object type {}"_cf, type_);
+ok:
+ visit_object_internal(*obj, f, id, object_type(type));
+ }
+
void deserialize_header_(binary_reader<const char*>& s)
{
fm_assert(PROTO == (proto_t)-1);
@@ -604,6 +690,15 @@ struct reader final : visitor_<reader>
return strings[id];
}
+ template<atlas_type Type>
+ StringView get_atlas(atlasid id)
+ {
+ fm_soft_assert(id < atlases.size());
+ auto a = atlases[id];
+ fm_soft_assert(a.type == Type);
+ return atlas_from_type<Type>::name(a.atlas);
+ }
+
void deserialize_strings_(binary_reader<const char*>& s)
{
fm_assert(strings.empty());
@@ -615,13 +710,128 @@ struct reader final : visitor_<reader>
}
}
+ void deserialize_atlases(binary_reader<const char*>& s)
+ {
+ for (uint32_t i = 0; i < natlases; i++)
+ {
+ atlas_type type = (atlas_type)s.read<std::underlying_type_t<atlas_type>>();
+ atlasid id; id << s;
+ auto str = get_string(id);
+ void* atlas = nullptr;
+ switch (type)
+ {
+ case atlas_type::none: break;
+ case atlas_type::ground: atlas = loader.ground_atlas(str, loader_policy::warn).get(); goto ok;
+ case atlas_type::wall: atlas = loader.wall_atlas(str, loader_policy::warn).get(); goto ok;
+ case atlas_type::object: atlas = loader.anim_atlas(str, {}).get(); goto ok;
+ }
+ fm_throw("invalid atlas_type {}"_cf, (size_t)type);
+ok:
+ DBG << str << (int)type;
+ atlases.push_back({.atlas = atlas, .type = type });
+ }
+ }
+
+ template<typename INT>
+ void deserialize_tile_part(auto&& g, uint32_t& i, byte_reader& r)
+ {
+ constexpr auto highbit = reader::highbit<INT>;
+ constexpr auto null = reader::null<INT>;
+
+ INT num;
+ uint32_t num_idempotent = 0;
+
+ do_visit(num, r);
+
+ if (num & highbit)
+ {
+ num_idempotent = num & ~highbit;
+ do_visit(num, r);
+ }
+
+ if (num != null)
+ for (uint32_t j = 0; j <= num_idempotent; j++)
+ g(i+j, num);
+
+ i += num_idempotent;
+ }
+
+ void deserialize_objects_(chunk& c, byte_reader& r)
+ {
+ uint32_t count;
+ do_visit(count, r);
+
+ for (uint32_t i = 0; i < count; i++)
+ {
+ using magic_type = std::decay_t<decltype(object_magic)>;
+ magic_type magic;
+ r(magic);
+ fm_soft_assert(magic == object_magic);
+
+ std::shared_ptr<object> obj;
+ do_visit(obj, r);
+ w.do_make_object(std::move(obj), obj->position().coord(), false);
+ }
+ c.sort_objects();
+ }
+
+ void deserialize_chunk_(binary_reader<const char*>& s)
+ {
+ auto r = byte_reader{s};
+
+ using magic_type = std::decay_t<decltype(chunk_magic)>;
+ magic_type magic; magic << s;
+ fm_soft_assert(magic == chunk_magic);
+
+ chunk_coords_ coord;
+ do_visit(coord, r);
+ auto& c = w[coord];
+
+ for (uint32_t i = 0; i < TILE_COUNT; i++)
+ deserialize_tile_part<atlasid>([&](uint32_t i, uint32_t id) {
+ auto name = get_atlas<atlas_type::ground>(id);
+ auto a = loader.ground_atlas(name, loader_policy::warn);
+ c[i].ground() = { std::move(a), (variant_t)-1 };
+ }, i, r);
+ for (uint32_t i = 0; i < TILE_COUNT; i++)
+ deserialize_tile_part<atlasid>([&](uint32_t i, uint32_t id) {
+ auto name = get_atlas<atlas_type::wall>(id);
+ auto a = loader.wall_atlas(name, loader_policy::warn);
+ c[i].wall_north() = { std::move(a), (variant_t)-1 };
+ }, i, r);
+ for (uint32_t i = 0; i < TILE_COUNT; i++)
+ deserialize_tile_part<atlasid>([&](uint32_t i, uint32_t id) {
+ auto name = get_atlas<atlas_type::wall>(id);
+ auto a = loader.wall_atlas(name, loader_policy::warn);
+ c[i].wall_west() = { std::move(a), (variant_t)-1 };
+ }, i, r);
+ for (uint32_t i = 0; i < TILE_COUNT; i++)
+ deserialize_tile_part<variant_t>([&](uint32_t i, variant_t id) {
+ c[i].ground().variant = id;
+ }, i, r);
+ for (uint32_t i = 0; i < TILE_COUNT; i++)
+ deserialize_tile_part<variant_t>([&](uint32_t i, variant_t id) {
+ c[i].wall_north().variant = id;
+ }, i, r);
+ for (uint32_t i = 0; i < TILE_COUNT; i++)
+ deserialize_tile_part<variant_t>([&](uint32_t i, variant_t id) {
+ c[i].wall_west().variant = id;
+ }, i, r);
+
+ deserialize_objects_(c, r);
+ }
+
void deserialize_world(ArrayView<const char> buf)
{
binary_reader s{buf.data(), buf.data() + buf.size()};
deserialize_header_(s);
deserialize_strings_(s);
- // atlases
- // chunks
+ deserialize_atlases(s);
+ for (uint32_t i = 0; i < nchunks; i++)
+ {
+ deserialize_chunk_(s);
+ fm_assert(false && "todo");
+ }
// assert end
}
};
diff --git a/src/chunk-render.cpp b/src/chunk-render.cpp
index 459f51b7..3072f472 100644
--- a/src/chunk-render.cpp
+++ b/src/chunk-render.cpp
@@ -57,7 +57,7 @@ auto chunk::ensure_ground_mesh() noexcept -> ground_mesh_tuple
const auto& atlas = _ground->atlases[i];
const local_coords pos{i};
const auto quad = floor_quad(Vector3(pos) * TILE_SIZE, TILE_SIZE2);
- const auto texcoords = atlas->texcoords_for_id(_ground->variants[i]);
+ const auto texcoords = atlas->texcoords_for_id(_ground->variants[i] % _ground->atlases[i]->num_tiles());
const float depth = tile_shader::depth_value(pos, tile_shader::ground_depth_offset + hack_offset);
auto& v = vertexes[k];
for (auto j = 0uz; j < 4; j++)
diff --git a/src/object.cpp b/src/object.cpp
index 0139362c..ce3bb51f 100644
--- a/src/object.cpp
+++ b/src/object.cpp
@@ -55,9 +55,12 @@ object::object(object_id id, struct chunk& c, const object_proto& proto) :
bbox_size{proto.bbox_size}, delta{proto.delta},
frame{proto.frame}, r{proto.r}, pass{proto.pass}
{
- fm_soft_assert(atlas);
- fm_soft_assert(atlas->check_rotation(r));
- fm_soft_assert(frame < atlas->info().nframes);
+ if (id != 0)
+ {
+ fm_soft_assert(atlas);
+ fm_soft_assert(atlas->check_rotation(r));
+ fm_soft_assert(frame < atlas->info().nframes);
+ }
}
object::~object() noexcept
diff --git a/src/scenery.cpp b/src/scenery.cpp
index f3a11f6d..24431a6b 100644
--- a/src/scenery.cpp
+++ b/src/scenery.cpp
@@ -152,7 +152,10 @@ scenery::scenery(object_id id, struct chunk& c, const scenery_proto& proto) :
object{id, c, proto}, sc_type{proto.sc_type}, active{proto.active},
closing{proto.closing}, interactive{proto.interactive}
{
- fm_debug_assert(atlas); // todo add placeholder graphic
+#ifndef FM_NO_DEBUG
+ if (id != 0)
+ fm_debug_assert(atlas); // todo add placeholder graphic
+#endif
}
} // namespace floormat
diff --git a/src/world.hpp b/src/world.hpp
index 88927166..6bb85a18 100644
--- a/src/world.hpp
+++ b/src/world.hpp
@@ -43,7 +43,6 @@ private:
explicit world(size_t capacity);
- void do_make_object(const std::shared_ptr<object>& e, global_coords pos, bool sorted);
void do_kill_object(object_id id);
std::shared_ptr<object> find_object_(object_id id);
[[noreturn]] static void throw_on_wrong_object_type(object_id id, object_type actual, object_type expected);
@@ -86,6 +85,12 @@ public:
do_make_object(static_pointer_cast<object>(ret), pos, sorted);
return ret;
}
+ void do_make_object(const std::shared_ptr<object>& e, global_coords pos, bool sorted);
+
+ template<typename T, typename... Xs> std::shared_ptr<object> make_unconnected_object(Xs&&... xs)
+ {
+ return std::shared_ptr<T>(new T{0, operator[](chunk_coords_{}), {}, Utility::forward<Xs>(xs)...});
+ }
template<typename T = object> std::shared_ptr<T> find_object(object_id id);
diff --git a/test/dijkstra.cpp b/test/dijkstra.cpp
index a880ebbc..b09d7fd5 100644
--- a/test/dijkstra.cpp
+++ b/test/dijkstra.cpp
@@ -19,7 +19,7 @@ void test_app::test_dijkstra()
constexpr auto wpos = global_coords{wch, wt};
auto& ch = w[wch];
- auto metal2 = wall_image_proto{loader.wall_atlas("empty", false), 0};
+ auto metal2 = wall_image_proto{loader.wall_atlas("empty", loader_policy::warn), 0};
for (int16_t j = wcy - 1; j <= wcy + 1; j++)
for (int16_t i = wcx - 1; i <= wcx + 1; i++)
diff --git a/test/loader.cpp b/test/loader.cpp
index 8910df72..e6d9f07d 100644
--- a/test/loader.cpp
+++ b/test/loader.cpp
@@ -19,9 +19,9 @@ void test_app::test_loader()
{ auto walls = loader.wall_atlas_list();
fm_assert(!walls.isEmpty());
fm_assert(loader.wall_atlas("test1"_s));
- fm_assert(loader.wall_atlas(loader.INVALID, true));
+ fm_assert(loader.wall_atlas(loader.INVALID, loader_policy::ignore));
fm_assert(loader.wall_atlas("test1"_s) == loader.wall_atlas("test1"_s));
- fm_assert(loader.wall_atlas("test1"_s) != loader.wall_atlas(loader.INVALID, true));
+ fm_assert(loader.wall_atlas("test1"_s) != loader.wall_atlas(loader.INVALID, loader_policy::ignore));
}
for (const auto& info : loader.wall_atlas_list())
fm_assert(loader.wall_atlas(info.name));
diff --git a/test/path-search.cpp b/test/path-search.cpp
index 772de5fb..e281f95b 100644
--- a/test/path-search.cpp
+++ b/test/path-search.cpp
@@ -170,7 +170,7 @@ void test_bbox()
return neighbor_tiles(w, { ch, pos }, {}, (object_id)-1, path_search::never_continue());
};
- const auto metal2 = loader.wall_atlas("empty", false);
+ const auto metal2 = loader.wall_atlas("empty", loader_policy::warn);
const auto table = loader.scenery("table1");
{
diff --git a/test/serializer.cpp b/test/serializer.cpp
index 5b06baf1..e14f5c1e 100644
--- a/test/serializer.cpp
+++ b/test/serializer.cpp
@@ -18,7 +18,7 @@ chunk& test_app::make_test_chunk(world& w, chunk_coords_ ch)
chunk& c = w[ch];
c.mark_modified();
- auto metal2 = loader.wall_atlas("empty", false);
+ auto metal2 = loader.wall_atlas("empty", loader_policy::warn);
auto tiles = loader.ground_atlas("tiles");
auto door = loader.scenery("door1");
auto table = loader.scenery("table1");
diff --git a/test/wall-atlas2.cpp b/test/wall-atlas2.cpp
index 98921f1d..665f0570 100644
--- a/test/wall-atlas2.cpp
+++ b/test/wall-atlas2.cpp
@@ -54,7 +54,7 @@ void test_concrete_wall()
using enum Wall::Direction_;
constexpr auto name = "concrete1"_s;
- auto& wall = *loader.wall_atlas(name, false);
+ auto& wall = *loader.wall_atlas(name);
fm_assert(wall.name() == name);
fm_assert(wall.info().depth == 20);
fm_assert(wall.raw_frame_array().size() >= 3);