1 | // SuperTux |
2 | // Copyright (C) 2006 Matthias Braun <matze@braunis.de> |
3 | // 2018 Ingo Ruhnke <grumbel@gmail.com> |
4 | // |
5 | // This program is free software: you can redistribute it and/or modify |
6 | // it under the terms of the GNU General Public License as published by |
7 | // the Free Software Foundation, either version 3 of the License, or |
8 | // (at your option) any later version. |
9 | // |
10 | // This program is distributed in the hope that it will be useful, |
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | // GNU General Public License for more details. |
14 | // |
15 | // You should have received a copy of the GNU General Public License |
16 | // along with this program. If not, see <http://www.gnu.org/licenses/>. |
17 | |
18 | #ifndef HEADER_SUPERTUX_SUPERTUX_GAME_OBJECT_MANAGER_HPP |
19 | #define |
20 | |
21 | #include <functional> |
22 | #include <typeindex> |
23 | #include <unordered_map> |
24 | #include <vector> |
25 | |
26 | #include "supertux/game_object.hpp" |
27 | #include "util/uid_generator.hpp" |
28 | |
29 | class DrawingContext; |
30 | class TileMap; |
31 | |
32 | template<class T> class GameObjectRange; |
33 | |
34 | class GameObjectManager |
35 | { |
36 | public: |
37 | static bool s_draw_solids_only; |
38 | |
39 | private: |
40 | struct NameResolveRequest |
41 | { |
42 | std::string name; |
43 | std::function<void (UID)> callback; |
44 | }; |
45 | |
46 | public: |
47 | GameObjectManager(); |
48 | virtual ~GameObjectManager(); |
49 | |
50 | /** Queue an object up to be added to the object list */ |
51 | GameObject& add_object(std::unique_ptr<GameObject> object); |
52 | void clear_objects(); |
53 | |
54 | template<typename T, typename... Args> |
55 | T& add(Args&&... args) |
56 | { |
57 | auto obj = std::make_unique<T>(std::forward<Args>(args)...); |
58 | T& obj_ref = *obj; |
59 | add_object(std::move(obj)); |
60 | return obj_ref; |
61 | } |
62 | |
63 | void update(float dt_sec); |
64 | void draw(DrawingContext& context); |
65 | |
66 | const std::vector<std::unique_ptr<GameObject> >& get_objects() const; |
67 | |
68 | /** Commit the queued up additions and deletions to the object list */ |
69 | void flush_game_objects(); |
70 | |
71 | float get_width() const; |
72 | float get_height() const; |
73 | |
74 | /** returns the width (in tiles) of a worldmap */ |
75 | float get_tiles_width() const; |
76 | |
77 | /** returns the height (in tiles) of a worldmap */ |
78 | float get_tiles_height() const; |
79 | |
80 | /** Hook that is called before an object is added to the vector */ |
81 | virtual bool before_object_add(GameObject& object) = 0; |
82 | |
83 | /** Hook that is called before an object is removed from the vector */ |
84 | virtual void before_object_remove(GameObject& object) = 0; |
85 | |
86 | template<class T> |
87 | GameObjectRange<T> get_objects_by_type() const |
88 | { |
89 | return GameObjectRange<T>(*this); |
90 | } |
91 | |
92 | const std::vector<GameObject*>& |
93 | get_objects_by_type_index(std::type_index type_idx) const |
94 | { |
95 | auto it = m_objects_by_type_index.find(type_idx); |
96 | if (it == m_objects_by_type_index.end()) { |
97 | // use a dummy return value to avoid making this method non-const |
98 | static std::vector<GameObject*> dummy; |
99 | return dummy; |
100 | } else { |
101 | return it->second; |
102 | } |
103 | } |
104 | |
105 | template<class T> |
106 | T& get_singleton_by_type() const |
107 | { |
108 | const auto& range = get_objects_by_type<T>(); |
109 | assert(range.begin() != range.end()); |
110 | assert(range.begin()->is_singleton()); |
111 | return *range.begin(); |
112 | } |
113 | |
114 | template<class T> |
115 | T* get_object_by_uid(const UID& uid) const |
116 | { |
117 | auto it = m_objects_by_uid.find(uid); |
118 | if (it == m_objects_by_uid.end()) |
119 | { |
120 | // FIXME: Is this a good idea? Should gameobjects be made |
121 | // accessible even when not fully inserted into the manager? |
122 | for (auto&& itnew : m_gameobjects_new) |
123 | { |
124 | if (itnew->get_uid() == uid) |
125 | return static_cast<T*>(itnew.get()); |
126 | } |
127 | return nullptr; |
128 | } |
129 | else |
130 | { |
131 | #ifdef NDEBUG |
132 | return static_cast<T*>(it->second); |
133 | #else |
134 | // Since uids should be unique, there should be no need to guess |
135 | // the type, thus we assert() when the object type is not what |
136 | // we expected. |
137 | auto ptr = dynamic_cast<T*>(it->second); |
138 | assert(ptr != nullptr); |
139 | return ptr; |
140 | #endif |
141 | } |
142 | } |
143 | |
144 | /** Register a callback to be called once the given name can be |
145 | resolsed to a UID. Note that this function is only valid in the |
146 | construction phase, not during draw() or update() calls, use |
147 | get_object_by_uid() instead. */ |
148 | void request_name_resolve(const std::string& name, std::function<void (UID)> callback); |
149 | |
150 | template<class T> |
151 | T* get_object_by_name(const std::string& name) const |
152 | { |
153 | auto it = m_objects_by_name.find(name); |
154 | if (it == m_objects_by_name.end()) |
155 | { |
156 | return nullptr; |
157 | } |
158 | else |
159 | { |
160 | return dynamic_cast<T*>(it->second); |
161 | } |
162 | } |
163 | |
164 | /** Get total number of GameObjects of given type */ |
165 | template<class T> |
166 | int get_object_count(std::function<bool(const T&)> predicate = nullptr) const |
167 | { |
168 | int total = 0; |
169 | for (const auto& obj : m_gameobjects) { |
170 | auto object = dynamic_cast<T*>(obj.get()); |
171 | if (object && (predicate == nullptr || predicate(*object))) |
172 | { |
173 | total += 1; |
174 | } |
175 | } |
176 | return total; |
177 | } |
178 | |
179 | const std::vector<TileMap*>& get_solid_tilemaps() const { return m_solid_tilemaps; } |
180 | |
181 | protected: |
182 | void process_resolve_requests(); |
183 | |
184 | template<class T> |
185 | T* get_object_by_type() const |
186 | { |
187 | const auto& range = get_objects_by_type<T>(); |
188 | if (range.begin() == range.end()) { |
189 | return nullptr; |
190 | } else { |
191 | return &*range.begin(); |
192 | } |
193 | } |
194 | |
195 | private: |
196 | void this_before_object_add(GameObject& object); |
197 | void this_before_object_remove(GameObject& object); |
198 | |
199 | private: |
200 | UIDGenerator m_uid_generator; |
201 | |
202 | std::vector<std::unique_ptr<GameObject>> m_gameobjects; |
203 | |
204 | /** container for newly created objects, they'll be added in flush_game_objects() */ |
205 | std::vector<std::unique_ptr<GameObject>> m_gameobjects_new; |
206 | |
207 | /** Fast access to solid tilemaps */ |
208 | std::vector<TileMap*> m_solid_tilemaps; |
209 | |
210 | std::unordered_map<std::string, GameObject*> m_objects_by_name; |
211 | std::unordered_map<UID, GameObject*> m_objects_by_uid; |
212 | std::unordered_map<std::type_index, std::vector<GameObject*> > m_objects_by_type_index; |
213 | |
214 | std::vector<NameResolveRequest> m_name_resolve_requests; |
215 | |
216 | private: |
217 | GameObjectManager(const GameObjectManager&) = delete; |
218 | GameObjectManager& operator=(const GameObjectManager&) = delete; |
219 | }; |
220 | |
221 | #include "supertux/game_object_iterator.hpp" |
222 | |
223 | #endif |
224 | |
225 | /* EOF */ |
226 | |