summaryrefslogtreecommitdiffhomepage
path: root/src/world.hpp
blob: 0a15cb03b2b6d3cf378db68025c2955745c2ea26 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#pragma once
#include "compat/safe-ptr.hpp"
#include "chunk.hpp"
#include "global-coords.hpp"
#include "object-type.hpp"
#include "loader/policy.hpp"
#include <memory>
#include <unordered_map>

namespace floormat {

struct object;
struct critter;
struct critter_proto;
template<typename T> struct object_type_;
template<typename T> struct shared_ptr_wrapper;

class world final
{
public:
    static constexpr object_id object_counter_init = 1024;
    static constexpr size_t initial_capacity = 4096;
    static constexpr float max_load_factor = .25;
    static constexpr size_t initial_collect_every = 64;

private:
    struct chunk_tuple
    {
        static constexpr chunk_coords_ invalid_coords = { -1 << 15, -1 << 15, chunk_z_min };
        chunk* c = nullptr;
        chunk_coords_ pos = invalid_coords;
    } _last_chunk;

    struct object_id_hasher { size_t operator()(object_id id) const noexcept; };
    struct chunk_coords_hasher { size_t operator()(const chunk_coords_& coord) const noexcept; };
    struct robin_map_wrapper;

    std::unordered_map<chunk_coords_, chunk, chunk_coords_hasher> _chunks;
    safe_ptr<robin_map_wrapper> _objects;
    std::shared_ptr<char> _unique_id = std::make_shared<char>('A');
    object_id _object_counter = object_counter_init;
    uint64_t _current_frame = 1; // zero is special for struct object
    bool _teardown : 1 = false;

    explicit world(size_t capacity);

    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);

    friend struct object;

public:
    explicit world();
    ~world() noexcept;
    explicit world(std::unordered_map<chunk_coords_, chunk>&& chunks);

    struct pair_chunk_tile final { chunk& c; tile_ref t; }; // NOLINT

    chunk& operator[](chunk_coords_ c) noexcept;
    pair_chunk_tile operator[](global_coords pt) noexcept; // todo maybe remove this overload?
    chunk* at(chunk_coords_ c) noexcept;
    bool contains(chunk_coords_ c) const noexcept;
    void clear();
    void collect(bool force = false);
    size_t size() const noexcept { return _chunks.size(); }

    const auto& chunks() const noexcept { return _chunks; }
    auto& chunks() noexcept { return _chunks; }

    void serialize(StringView filename);
    static class world deserialize(StringView filename, loader_policy asset_policy) noexcept(false);
    static void deserialize_old(class world& w, ArrayView<const char> buf, uint16_t proto, enum loader_policy asset_policy) noexcept(false);
    auto frame_no() const { return _current_frame; }
    auto increment_frame_no() { return _current_frame++; }

    template<typename T, bool sorted = true, typename... Xs>
    requires requires(chunk& c) {
        T{object_id(), c, std::declval<Xs&&>()...};
        std::is_base_of_v<object, T>;
    }
    std::shared_ptr<T> make_object(object_id id, global_coords pos, Xs&&... xs) // todo! replace 2nd arg with chunk&
    {
        auto ret = std::shared_ptr<T>(new T{id, operator[](pos.chunk3()), forward<Xs>(xs)...});
        do_make_object(std::static_pointer_cast<object>(ret), pos, sorted);
        return ret;
    }
    void do_make_object(const std::shared_ptr<object>& e, global_coords pos, bool sorted); // todo! replace 2nd arg with chunk&

#if 0
    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_{}), {}, forward<Xs>(xs)...});
    }
#endif

    template<typename T = object> std::shared_ptr<T> find_object(object_id id);

    shared_ptr_wrapper<critter> ensure_player_character(object_id& id, critter_proto p);
    shared_ptr_wrapper<critter> ensure_player_character(object_id& id);
    static critter_proto make_player_proto();

    bool is_teardown() const { return _teardown; }
    object_id object_counter() const { return _object_counter; }
    [[nodiscard]] object_id make_id() { return ++_object_counter; }
    void set_object_counter(object_id value);

    std::array<chunk*, 8> neighbors(chunk_coords_ coord);

    static constexpr std::array<Vector2b, 8> neighbor_offsets = {{
        {-1, -1}, {-1,  0}, { 0, -1}, { 1,  1}, { 1,  0}, { 0,  1}, { 1, -1}, {-1,  1},
    }};

    world& operator=(world&& w) noexcept;
    world(world&& w) noexcept;
};

template<typename T>
std::shared_ptr<T> world::find_object(object_id id)
{
    static_assert(std::is_base_of_v<object, T>);
    // make it a dependent name so that including "src/object.hpp" isn't needed
    using U = std::conditional_t<std::is_same_v<T, object>, T, object>;
    if (std::shared_ptr<U> ptr = find_object_(id); !ptr)
        return {};
    else if constexpr(std::is_same_v<T, object>)
        return ptr;
    else
    {
        if (!(ptr->type() == object_type_<T>::value)) [[unlikely]]
            throw_on_wrong_object_type(id, ptr->type(), object_type_<T>::value);
        return static_pointer_cast<T>(move(ptr));
    }
}

} // namespace floormat