Implemented background alignment for resolution independent parallax scrolling
[supertux.git] / src / object / background.cpp
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 <iostream>
20 #include "math/sizef.hpp"
21 #include "supertux/globals.hpp"
22 #include "supertux/object_factory.hpp"
23 #include "supertux/sector.hpp"
24 #include "util/log.hpp"
25 #include "util/reader.hpp"
26
27 Background::Background() :
28   alignment(NO_ALIGNMENT),
29   layer(LAYER_BACKGROUND0),
30   imagefile_top(),
31   imagefile(),
32   imagefile_bottom(),
33   pos(),
34   speed(),
35   speed_y(),
36   scroll_speed(),
37   scroll_offset(),
38   image_top(),
39   image(),
40   image_bottom()
41 {
42 }
43
44 Background::Background(const Reader& reader) :
45   alignment(NO_ALIGNMENT),
46   layer(LAYER_BACKGROUND0),
47   imagefile_top(),
48   imagefile(),
49   imagefile_bottom(),
50   pos(),
51   speed(),
52   speed_y(),
53   scroll_speed(),
54   scroll_offset(),
55   image_top(),
56   image(),
57   image_bottom()
58 {
59   // read position, defaults to (0,0)
60   float px = 0;
61   float py = 0;
62   reader.get("x", px);
63   reader.get("y", py);
64   this->pos = Vector(px,py);
65
66   speed = 1.0;
67   speed_y = 1.0;
68
69   std::string alignment_str;
70   if (reader.get("alignment", alignment_str))
71   {
72     if (alignment_str == "left")
73     {
74       alignment = LEFT_ALIGNMENT;
75     }
76     else if (alignment_str == "right")
77     {
78       alignment = RIGHT_ALIGNMENT;
79     }
80     else if (alignment_str == "top")
81     {
82       alignment = TOP_ALIGNMENT;
83     }
84     else if (alignment_str == "bottom")
85     {
86       alignment = BOTTOM_ALIGNMENT;
87     }
88     else
89     {
90       log_warning << "Background: invalid alignment: '" << alignment_str << "'" << std::endl;
91       alignment = NO_ALIGNMENT;
92     }
93   }
94
95   reader.get("scroll-offset-x", scroll_offset.x);
96   reader.get("scroll-offset-y", scroll_offset.y);
97
98   reader.get("scroll-speed-x", scroll_speed.x);
99   reader.get("scroll-speed-y", scroll_speed.y);
100
101   reader.get("layer", layer);
102   if(!reader.get("image", imagefile) || !reader.get("speed", speed))
103     throw std::runtime_error("Must specify image and speed for background");
104
105   set_image(imagefile, speed);
106   if (!reader.get("speed-y", speed_y))
107   {
108     speed_y = speed;
109   }
110
111   if (reader.get("image-top", imagefile_top)) {
112     image_top = Surface::create(imagefile_top);
113   }
114   if (reader.get("image-bottom", imagefile_bottom)) {
115     image_bottom = Surface::create(imagefile_bottom);
116   }
117 }
118
119 Background::~Background()
120 {
121 }
122
123 void
124 Background::update(float delta)
125 {
126   scroll_offset += scroll_speed * delta;
127 }
128
129 void
130 Background::set_image(const std::string& name, float speed)
131 {
132   this->imagefile = name;
133   this->speed = speed;
134
135   image = Surface::create(name);
136 }
137
138 void
139 Background::draw_image(DrawingContext& context, const Vector& pos)
140 {
141   Sizef level(Sector::current()->get_width(), Sector::current()->get_height());
142   Sizef screen(SCREEN_WIDTH, SCREEN_HEIGHT);
143   Sizef parallax_image_size = (1.0f - speed) * screen + level * speed;
144
145   // FIXME: Implement proper clipping here
146   int start_x = -level.width  / image->get_width()  / 2;
147   int end_x   =  level.width  / image->get_width()  / 2;
148   int start_y = -level.height / image->get_height() / 2;
149   int end_y   =  level.height / image->get_height() / 2;
150
151   switch(alignment)
152   {
153     case LEFT_ALIGNMENT:
154       for(int y = start_y; y < end_y; ++y)
155       {
156         Vector p(pos.x - parallax_image_size.width / 2.0f,
157                  pos.y + y * image->get_height()  - image->get_height() / 2.0f);
158         context.draw_surface(image.get(), p, layer);
159       }
160       break;
161
162     case RIGHT_ALIGNMENT:
163       for(int y = start_y; y < end_y; ++y)
164       {
165         Vector p(pos.x + parallax_image_size.width / 2.0f - image->get_width(),
166                  pos.y + y * image->get_height() - image->get_height() / 2.0f);
167         context.draw_surface(image.get(), p, layer);
168       }
169       break;
170
171     case TOP_ALIGNMENT:
172       for(int x = start_x; x < end_x; ++x)
173       {
174         Vector p(pos.x + x * image->get_width() - image->get_width() / 2.0f, 
175                  pos.y - parallax_image_size.height / 2.0f);       
176         context.draw_surface(image.get(), p, layer);
177       }
178       break;
179
180     case BOTTOM_ALIGNMENT:
181       for(int x = start_x; x < end_x; ++x)
182       {
183         Vector p(pos.x + x * image->get_width()  - image->get_width() / 2.0f, 
184                  pos.y - image->get_height() + parallax_image_size.height / 2.0f);       
185         context.draw_surface(image.get(), p, layer);
186       }
187       break;
188
189     case NO_ALIGNMENT:
190       for(int y = start_y; y <= end_y; ++y)
191         for(int x = start_x; x <= end_x; ++x)
192         {
193           Vector p(pos.x + x * image->get_width()  - image->get_width()/2, 
194                    pos.y + y * image->get_height() - image->get_height()/2);
195
196           if (image_top.get() != NULL && (y < 0))
197           {
198             context.draw_surface(image_top.get(), p, layer);
199           }
200           else if (image_bottom.get() != NULL && (y > 0))
201           {
202             context.draw_surface(image_bottom.get(), p, layer);
203           }
204           else
205           {
206             context.draw_surface(image.get(), p, layer);
207           }
208         }
209       break;
210   }
211 }
212
213 void
214 Background::draw(DrawingContext& context)
215 {
216   if(image.get() == NULL)
217     return;
218
219   Sizef level_size(Sector::current()->get_width(),
220                    Sector::current()->get_height());
221   Sizef screen(SCREEN_WIDTH, SCREEN_HEIGHT);
222   Sizef translation_range = level_size - screen;
223   Vector center_offset(context.get_translation().x - translation_range.width  / 2.0f, 
224                        context.get_translation().y - translation_range.height / 2.0f);
225
226   // FIXME: We are not handling 'pos'
227   draw_image(context, Vector(level_size.width / 2.0f, level_size.height / 2.0f) + center_offset * (1.0f - speed));
228 }
229
230 /* EOF */