summaryrefslogtreecommitdiffhomepage
path: root/src/world.hpp
blob: 2a3a26f1e5bca0bee327f75a3c4a30abecd1865b (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
#pragma once
#include "compat/safe-ptr.hpp"
#include "compat/base-of.hpp"
#include "chunk.hpp"
#include "global-coords.hpp"
#include "object-type.hpp"
#include "scenery-type.hpp"
#include "loader/policy.hpp"
#include <memory>
#include <unordered_map>

namespace floormat {

template<typename T> struct shared_ptr_wrapper;
struct object;
struct critter;
struct critter_proto;
struct scenery;
struct scenery_proto;

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;
    bool _script_initialized : 1 = false;
    bool _script_finalized : 1 = false;

    explicit world(size_t capacity);

    void do_make_object(const std::shared_ptr<object>& e, global_coords pos, bool sorted); // todo! replace 2nd arg with chunk&
    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);
    [[noreturn]] static void throw_on_wrong_scenery_type(object_id id, scenery_type actual, scenery_type expected);
    [[noreturn]] static void throw_on_empty_scenery_proto(object_id id, global_coords pos, Vector2b offset);

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

    template<bool sorted = true> std::shared_ptr<scenery> make_scenery(object_id id, global_coords pos, scenery_proto&& proto);
    template<typename T = object> std::shared_ptr<T> find_object(object_id id);
    template<typename T> requires is_strict_base_of<scenery, T> 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();

    void init_scripts();
    void finish_scripts();
    bool is_teardown() const;
    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;
};

} // namespace floormat