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#include "supertux/game_object_manager.hpp"
19
20#include <algorithm>
21
22#include "object/tilemap.hpp"
23
24bool GameObjectManager::s_draw_solids_only = false;
25
26GameObjectManager::GameObjectManager() :
27 m_uid_generator(),
28 m_gameobjects(),
29 m_gameobjects_new(),
30 m_solid_tilemaps(),
31 m_objects_by_name(),
32 m_objects_by_uid(),
33 m_objects_by_type_index(),
34 m_name_resolve_requests()
35{
36}
37
38GameObjectManager::~GameObjectManager()
39{
40 // clear_objects() must be called before destructing the GameObjectManager
41 assert(m_gameobjects.size() == 0);
42 assert(m_gameobjects_new.size() == 0);
43}
44
45void
46GameObjectManager::request_name_resolve(const std::string& name, std::function<void (UID)> callback)
47{
48 m_name_resolve_requests.push_back({name, std::move(callback)});
49}
50
51void
52GameObjectManager::process_resolve_requests()
53{
54 assert(m_gameobjects_new.empty());
55
56 for (const auto& request : m_name_resolve_requests)
57 {
58 GameObject* object = get_object_by_name<GameObject>(request.name);
59 if (!object)
60 {
61 log_warning << "GameObjectManager: name resolve for '" << request.name << "' failed" << std::endl;
62 request.callback({});
63 }
64 else
65 {
66 request.callback(object->get_uid());
67 }
68 }
69 m_name_resolve_requests.clear();
70}
71
72const std::vector<std::unique_ptr<GameObject> >&
73GameObjectManager::get_objects() const
74{
75 return m_gameobjects;
76}
77
78GameObject&
79GameObjectManager::add_object(std::unique_ptr<GameObject> object)
80{
81 assert(object);
82 assert(!object->get_uid());
83
84 object->set_uid(m_uid_generator.next());
85
86 // make sure the object isn't already in the list
87#ifndef NDEBUG
88 for (const auto& game_object : m_gameobjects) {
89 assert(game_object != object);
90 }
91 for (const auto& gameobject : m_gameobjects_new) {
92 assert(gameobject != object);
93 }
94#endif
95
96 GameObject& tmp = *object;
97 m_gameobjects_new.push_back(std::move(object));
98 return tmp;
99}
100
101void
102GameObjectManager::clear_objects()
103{
104 flush_game_objects();
105
106 for (const auto& obj: m_gameobjects) {
107 before_object_remove(*obj);
108 }
109 m_gameobjects.clear();
110}
111
112void
113GameObjectManager::update(float dt_sec)
114{
115 for (const auto& object : m_gameobjects)
116 {
117 if (!object->is_valid())
118 continue;
119
120 object->update(dt_sec);
121 }
122}
123
124void
125GameObjectManager::draw(DrawingContext& context)
126{
127 for (const auto& object : m_gameobjects)
128 {
129 if (!object->is_valid())
130 continue;
131
132 if (s_draw_solids_only)
133 {
134 auto tm = dynamic_cast<TileMap*>(object.get());
135 if (tm && !tm->is_solid())
136 continue;
137 }
138
139 object->draw(context);
140 }
141}
142
143void
144GameObjectManager::flush_game_objects()
145{
146 { // cleanup marked objects
147 m_gameobjects.erase(
148 std::remove_if(m_gameobjects.begin(), m_gameobjects.end(),
149 [this](const std::unique_ptr<GameObject>& obj) {
150 if (!obj->is_valid())
151 {
152 this_before_object_remove(*obj);
153 before_object_remove(*obj);
154 return true;
155 } else {
156 return false;
157 }
158 }),
159 m_gameobjects.end());
160 }
161
162 { // add newly created objects
163 // Objects might add new objects in finish_construction(), so we
164 // loop until no new objects show up.
165 while (!m_gameobjects_new.empty()) {
166 auto new_objects = std::move(m_gameobjects_new);
167 for (auto& object : new_objects)
168 {
169 if (before_object_add(*object))
170 {
171 this_before_object_add(*object);
172 m_gameobjects.push_back(std::move(object));
173 }
174 }
175 }
176 }
177
178 { // update solid_tilemaps list
179 m_solid_tilemaps.clear();
180 for (const auto& obj : m_gameobjects)
181 {
182 const auto& tm = dynamic_cast<TileMap*>(obj.get());
183 if (!tm) continue;
184 if (tm->is_solid()) m_solid_tilemaps.push_back(tm);
185 }
186 }
187}
188
189void
190GameObjectManager::this_before_object_add(GameObject& object)
191{
192 { // by_name
193 if (!object.get_name().empty())
194 {
195 m_objects_by_name[object.get_name()] = &object;
196 }
197 }
198
199 { // by_id
200 assert(object.get_uid());
201
202 m_objects_by_uid[object.get_uid()] = &object;
203 }
204
205 { // by_type_index
206 m_objects_by_type_index[std::type_index(typeid(object))].push_back(&object);
207 }
208}
209
210void
211GameObjectManager::this_before_object_remove(GameObject& object)
212{
213 { // by_name
214 const std::string& name = object.get_name();
215 if (!name.empty())
216 {
217 m_objects_by_name.erase(name);
218 }
219 }
220
221 { // by_id
222 m_objects_by_uid.erase(object.get_uid());
223 }
224
225 { // by_type_index
226 auto& vec = m_objects_by_type_index[std::type_index(typeid(object))];
227 auto it = std::find(vec.begin(), vec.end(), &object);
228 assert(it != vec.end());
229 vec.erase(it);
230 }
231}
232
233float
234GameObjectManager::get_width() const
235{
236 float width = 0;
237 for (auto& solids: get_solid_tilemaps()) {
238 width = std::max(width, solids->get_bbox().get_right());
239 }
240
241 return width;
242}
243
244float
245GameObjectManager::get_height() const
246{
247 float height = 0;
248 for (const auto& solids: get_solid_tilemaps()) {
249 height = std::max(height, solids->get_bbox().get_bottom());
250 }
251
252 return height;
253}
254
255float
256GameObjectManager::get_tiles_width() const
257{
258 float width = 0;
259 for (const auto& solids : get_solid_tilemaps()) {
260 if (static_cast<float>(solids->get_width()) > width)
261 width = static_cast<float>(solids->get_width());
262 }
263 return width;
264}
265
266float
267GameObjectManager::get_tiles_height() const
268{
269 float height = 0;
270 for (const auto& solids : get_solid_tilemaps()) {
271 if (static_cast<float>(solids->get_height()) > height)
272 height = static_cast<float>(solids->get_height());
273 }
274 return height;
275}
276
277/* EOF */
278