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 HEADER_SUPERTUX_SUPERTUX_GAME_OBJECT_MANAGER_HPP
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
29class DrawingContext;
30class TileMap;
31
32template<class T> class GameObjectRange;
33
34class GameObjectManager
35{
36public:
37 static bool s_draw_solids_only;
38
39private:
40 struct NameResolveRequest
41 {
42 std::string name;
43 std::function<void (UID)> callback;
44 };
45
46public:
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
181protected:
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
195private:
196 void this_before_object_add(GameObject& object);
197 void this_before_object_remove(GameObject& object);
198
199private:
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
216private:
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