1// SuperTux
2// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17#include "object/background.hpp"
18
19#include "editor/editor.hpp"
20#include "supertux/d_scope.hpp"
21#include "supertux/globals.hpp"
22#include "util/reader.hpp"
23#include "util/reader_mapping.hpp"
24#include "util/writer.hpp"
25#include "video/drawing_context.hpp"
26#include "video/surface.hpp"
27
28Background::Background() :
29 ExposedObject<Background, scripting::Background>(this),
30 m_alignment(NO_ALIGNMENT),
31 m_fill(false),
32 m_layer(LAYER_BACKGROUND0),
33 m_imagefile_top(),
34 m_imagefile(),
35 m_imagefile_bottom(),
36 m_pos(),
37 m_parallax_speed(),
38 m_scroll_speed(),
39 m_scroll_offset(),
40 m_image_top(),
41 m_image(),
42 m_image_bottom(),
43 m_has_pos_x(false),
44 m_has_pos_y(false),
45 m_blend(),
46 m_target(DrawingTarget::COLORMAP)
47{
48}
49
50Background::Background(const ReaderMapping& reader) :
51 GameObject(reader),
52 ExposedObject<Background, scripting::Background>(this),
53 m_alignment(NO_ALIGNMENT),
54 m_fill(false),
55 m_layer(LAYER_BACKGROUND0),
56 m_imagefile_top(),
57 m_imagefile(),
58 m_imagefile_bottom(),
59 m_pos(),
60 m_parallax_speed(1.0f, 1.0f),
61 m_scroll_speed(),
62 m_scroll_offset(),
63 m_image_top(),
64 m_image(),
65 m_image_bottom(),
66 m_has_pos_x(false),
67 m_has_pos_y(false),
68 m_blend(),
69 m_target(DrawingTarget::COLORMAP)
70{
71 // read position, defaults to (0,0)
72 float px = 0;
73 float py = 0;
74 m_has_pos_x = reader.get("x", px);
75 m_has_pos_y = reader.get("y", py);
76 m_pos = Vector(px,py);
77
78 reader.get("fill", m_fill);
79
80 std::string alignment_str;
81 if (reader.get("alignment", alignment_str))
82 {
83 if (alignment_str == "left")
84 {
85 m_alignment = LEFT_ALIGNMENT;
86 }
87 else if (alignment_str == "right")
88 {
89 m_alignment = RIGHT_ALIGNMENT;
90 }
91 else if (alignment_str == "top")
92 {
93 m_alignment = TOP_ALIGNMENT;
94 }
95 else if (alignment_str == "bottom")
96 {
97 m_alignment = BOTTOM_ALIGNMENT;
98 }
99 else if (alignment_str == "none")
100 {
101 m_alignment = NO_ALIGNMENT;
102 }
103 else
104 {
105 log_warning << "Background: invalid alignment: '" << alignment_str << "'" << std::endl;
106 m_alignment = NO_ALIGNMENT;
107 }
108 }
109
110 reader.get("scroll-offset-x", m_scroll_offset.x, 0.0f);
111 reader.get("scroll-offset-y", m_scroll_offset.y, 0.0f);
112
113 reader.get("scroll-speed-x", m_scroll_speed.x, 0.0f);
114 reader.get("scroll-speed-y", m_scroll_speed.y, 0.0f);
115
116 m_layer = reader_get_layer(reader, LAYER_BACKGROUND0);
117
118 reader.get("image", m_imagefile, "images/background/transparent_up.png");
119 m_image = Surface::from_file(m_imagefile);
120
121 if(!reader.get("speed-x", m_parallax_speed.x))
122 {
123 // for backward compatibilty
124 reader.get("speed", m_parallax_speed.x, 0.5f);
125 };
126
127 reader.get("speed-y", m_parallax_speed.y, m_parallax_speed.x);
128
129 if (reader.get("image-top", m_imagefile_top)) {
130 m_image_top = Surface::from_file(m_imagefile_top);
131 } else {
132 if (!Editor::is_active()) {
133 m_imagefile_top = m_imagefile;
134 }
135 }
136
137 if (reader.get("image-bottom", m_imagefile_bottom)) {
138 m_image_bottom = Surface::from_file(m_imagefile_bottom);
139 } else {
140 if (!Editor::is_active()) {
141 m_imagefile_bottom = m_imagefile;
142 }
143 }
144
145 reader.get_custom("blend", m_blend, Blend_from_string);
146 reader.get_custom("target", m_target, DrawingTarget_from_string);
147}
148
149Background::~Background()
150{
151}
152
153ObjectSettings
154Background::get_settings()
155{
156 ObjectSettings result = GameObject::get_settings();
157
158 result.add_float(_("X"), &m_pos.x, "x", 0.0f, OPTION_HIDDEN);
159 result.add_float(_("Y"), &m_pos.y, "y", 0.0f, OPTION_HIDDEN);
160
161 result.add_bool(_("Fill"), &m_fill, "fill", false);
162 result.add_int(_("Z-pos"), &m_layer, "z-pos", LAYER_BACKGROUND0);
163 result.add_enum(_("Alignment"), reinterpret_cast<int*>(&m_alignment),
164 {_("none"), _("left"), _("right"), _("top"), _("bottom")},
165 {"none", "left", "right", "top", "bottom"},
166 static_cast<int>(NO_ALIGNMENT), "alignment");
167 result.add_float(_("Scroll offset x"), &m_scroll_offset.x, "scroll-offset-x", 0.0f);
168 result.add_float(_("Scroll offset y"), &m_scroll_offset.y, "scroll-offset-y", 0.0f);
169 result.add_float(_("Scroll speed x"), &m_scroll_speed.x, "scroll-speed-x", 0.0f);
170 result.add_float(_("Scroll speed y"), &m_scroll_speed.y, "scroll-speed-y", 0.0f);
171 result.add_float(_("Parallax Speed x"), &m_parallax_speed.x, "speed", boost::none);
172 result.add_float(_("Parallax Speed y"), &m_parallax_speed.y, "speed-y", m_parallax_speed.x);
173 result.add_surface(_("Top image"), &m_imagefile_top, "image-top", std::string());
174 result.add_surface(_("Image"), &m_imagefile, "image");
175 result.add_surface(_("Bottom image"), &m_imagefile_bottom, "image-bottom", std::string());
176 result.add_enum(_("Draw target"), reinterpret_cast<int*>(&m_target),
177 {_("Normal"), _("Lightmap")},
178 {"normal", "lightmap"},
179 static_cast<int>(DrawingTarget::COLORMAP),
180 "target");
181
182 result.reorder({"x", "y", "alignment", "scroll-speed-x", "scroll-speed-y", "speed", "speed-y", "fill", "target", "image-top", "image", "image-bottom", "z-pos"});
183
184 result.add_remove();
185
186 return result;
187}
188
189void
190Background::after_editor_set()
191{
192 m_image_top = Surface::from_file(m_imagefile_top);
193 m_image = Surface::from_file(m_imagefile);
194 m_image_bottom = Surface::from_file(m_imagefile_bottom);
195}
196
197void
198Background::update(float dt_sec)
199{
200 m_scroll_offset += m_scroll_speed * dt_sec;
201}
202
203void
204Background::set_image(const std::string& name)
205{
206 m_imagefile = name;
207 m_image = Surface::from_file(name);
208}
209
210void
211Background::set_images(const std::string& name_top,
212 const std::string& name_middle,
213 const std::string& name_bottom)
214{
215 m_image_top = Surface::from_file(name_top);
216 m_imagefile_top = name_top;
217
218 m_image = Surface::from_file(name_middle);
219 m_imagefile = name_middle;
220
221 m_image_bottom = Surface::from_file(name_bottom);
222 m_imagefile_bottom = name_bottom;
223}
224
225void
226Background::set_speed(float speed)
227{
228 m_parallax_speed.x = speed;
229 m_parallax_speed.y = speed;
230}
231
232void
233Background::draw_image(DrawingContext& context, const Vector& pos_)
234{
235 const Sizef level(d_gameobject_manager->get_width(), d_gameobject_manager->get_height());
236 const Sizef screen(static_cast<float>(context.get_width()),
237 static_cast<float>(context.get_height()));
238 const Sizef parallax_image_size((1.0f - m_parallax_speed.x) * screen.width + level.width * m_parallax_speed.x,
239 (1.0f - m_parallax_speed.y) * screen.height + level.height * m_parallax_speed.y);
240
241 const Rectf cliprect = context.get_cliprect();
242 const float img_w = static_cast<float>(m_image->get_width());
243 const float img_h = static_cast<float>(m_image->get_height());
244
245 const float img_w_2 = img_w / 2.0f;
246 const float img_h_2 = img_h / 2.0f;
247
248 const int start_x = static_cast<int>(floorf((cliprect.get_left() - (pos_.x - img_w /2.0f)) / img_w));
249 const int end_x = static_cast<int>(ceilf((cliprect.get_right() - (pos_.x + img_w /2.0f)) / img_w)) + 1;
250 const int start_y = static_cast<int>(floorf((cliprect.get_top() - (pos_.y - img_h/2.0f)) / img_h));
251 const int end_y = static_cast<int>(ceilf((cliprect.get_bottom() - (pos_.y + img_h/2.0f)) / img_h)) + 1;
252
253 Canvas& canvas = context.get_canvas(m_target);
254
255 if (m_fill)
256 {
257 Rectf dstrect(Vector(pos_.x - static_cast<float>(context.get_width()) / 2.0f,
258 pos_.y - static_cast<float>(context.get_height()) / 2.0f),
259 Sizef(static_cast<float>(context.get_width()),
260 static_cast<float>(context.get_height())));
261 canvas.draw_surface_scaled(m_image, dstrect, m_layer);
262 }
263 else
264 {
265 switch (m_alignment)
266 {
267 case LEFT_ALIGNMENT:
268 for (int y = start_y; y < end_y; ++y)
269 {
270 Vector p(pos_.x - parallax_image_size.width / 2.0f,
271 pos_.y + static_cast<float>(y) * img_h - img_h_2);
272 canvas.draw_surface(m_image, p, m_layer);
273 }
274 break;
275
276 case RIGHT_ALIGNMENT:
277 for (int y = start_y; y < end_y; ++y)
278 {
279 Vector p(pos_.x + parallax_image_size.width / 2.0f - img_w,
280 pos_.y + static_cast<float>(y) * img_h - img_h_2);
281 canvas.draw_surface(m_image, p, m_layer);
282 }
283 break;
284
285 case TOP_ALIGNMENT:
286 for (int x = start_x; x < end_x; ++x)
287 {
288 Vector p(pos_.x + static_cast<float>(x) * img_w - img_w_2,
289 pos_.y - parallax_image_size.height / 2.0f);
290 canvas.draw_surface(m_image, p, m_layer);
291 }
292 break;
293
294 case BOTTOM_ALIGNMENT:
295 for (int x = start_x; x < end_x; ++x)
296 {
297 Vector p(pos_.x + static_cast<float>(x) * img_w - img_w_2,
298 pos_.y - img_h + parallax_image_size.height / 2.0f);
299 canvas.draw_surface(m_image, p, m_layer);
300 }
301 break;
302
303 case NO_ALIGNMENT:
304 for (int y = start_y; y < end_y; ++y)
305 for (int x = start_x; x < end_x; ++x)
306 {
307 Vector p(pos_.x + static_cast<float>(x) * img_w - img_w_2,
308 pos_.y + static_cast<float>(y) * img_h - img_h_2);
309
310 if (m_image_top.get() != nullptr && (y < 0))
311 {
312 canvas.draw_surface(m_image_top, p, m_layer);
313 }
314 else if (m_image_bottom.get() != nullptr && (y > 0))
315 {
316 canvas.draw_surface(m_image_bottom, p, m_layer);
317 }
318 else
319 {
320 canvas.draw_surface(m_image, p, m_layer);
321 }
322 }
323 break;
324 }
325 }
326}
327
328void
329Background::draw(DrawingContext& context)
330{
331 if (Editor::is_active() && !EditorOverlayWidget::render_background)
332 return;
333
334 if (m_image.get() == nullptr)
335 return;
336
337 Sizef level_size(d_gameobject_manager->get_width(),
338 d_gameobject_manager->get_height());
339 Sizef screen(static_cast<float>(context.get_width()),
340 static_cast<float>(context.get_height()));
341 Sizef translation_range = level_size - screen;
342 Vector center_offset(context.get_translation().x - translation_range.width / 2.0f,
343 context.get_translation().y - translation_range.height / 2.0f);
344
345 Vector pos(m_has_pos_x ? m_pos.x : level_size.width / 2,
346 m_has_pos_y ? m_pos.y : level_size.height / 2);
347 draw_image(context, pos + m_scroll_offset + Vector(center_offset.x * (1.0f - m_parallax_speed.x),
348 center_offset.y * (1.0f - m_parallax_speed.y)));
349}
350
351/* EOF */
352