Fixed off-by-one
[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 if (alignment_str == "none")
89     {
90       alignment = NO_ALIGNMENT;
91     }
92     else
93     {
94       log_warning << "Background: invalid alignment: '" << alignment_str << "'" << std::endl;
95       alignment = NO_ALIGNMENT;
96     }
97   }
98
99   reader.get("scroll-offset-x", scroll_offset.x);
100   reader.get("scroll-offset-y", scroll_offset.y);
101
102   reader.get("scroll-speed-x", scroll_speed.x);
103   reader.get("scroll-speed-y", scroll_speed.y);
104
105   reader.get("layer", layer);
106   if(!reader.get("image", imagefile) || !reader.get("speed", speed))
107     throw std::runtime_error("Must specify image and speed for background");
108
109   set_image(imagefile, speed);
110   if (!reader.get("speed-y", speed_y))
111   {
112     speed_y = speed;
113   }
114
115   if (reader.get("image-top", imagefile_top)) {
116     image_top = Surface::create(imagefile_top);
117   }
118   if (reader.get("image-bottom", imagefile_bottom)) {
119     image_bottom = Surface::create(imagefile_bottom);
120   }
121 }
122
123 Background::~Background()
124 {
125 }
126
127 void
128 Background::update(float delta)
129 {
130   scroll_offset += scroll_speed * delta;
131 }
132
133 void
134 Background::set_image(const std::string& name, float speed)
135 {
136   this->imagefile = name;
137   this->speed = speed;
138
139   image = Surface::create(name);
140 }
141
142 void
143 Background::draw_image(DrawingContext& context, const Vector& pos)
144 {
145   Sizef level(Sector::current()->get_width(), Sector::current()->get_height());
146   Sizef screen(SCREEN_WIDTH, SCREEN_HEIGHT);
147   Sizef parallax_image_size = (1.0f - speed) * screen + level * speed;
148
149   // FIXME: Implement proper clipping here
150   int start_x = -parallax_image_size.width  / 2.0f / image->get_width()  - 1;
151   int end_x   =  parallax_image_size.width  / 2.0f / image->get_width()  + 1;
152   int start_y = -parallax_image_size.height / 2.0f / image->get_height() - 1;
153   int end_y   =  parallax_image_size.height / 2.0f / image->get_height() + 1;
154
155   switch(alignment)
156   {
157     case LEFT_ALIGNMENT:
158       for(int y = start_y; y <= end_y; ++y)
159       {
160         Vector p(pos.x - parallax_image_size.width / 2.0f,
161                  pos.y + y * image->get_height()  - image->get_height() / 2.0f);
162         context.draw_surface(image.get(), p, layer);
163       }
164       break;
165
166     case RIGHT_ALIGNMENT:
167       for(int y = start_y; y <= end_y; ++y)
168       {
169         Vector p(pos.x + parallax_image_size.width / 2.0f - image->get_width(),
170                  pos.y + y * image->get_height() - image->get_height() / 2.0f);
171         context.draw_surface(image.get(), p, layer);
172       }
173       break;
174
175     case TOP_ALIGNMENT:
176       for(int x = start_x; x <= end_x; ++x)
177       {
178         Vector p(pos.x + x * image->get_width() - image->get_width() / 2.0f, 
179                  pos.y - parallax_image_size.height / 2.0f);       
180         context.draw_surface(image.get(), p, layer);
181       }
182       break;
183
184     case BOTTOM_ALIGNMENT:
185       for(int x = start_x; x <= end_x; ++x)
186       {
187         Vector p(pos.x + x * image->get_width()  - image->get_width() / 2.0f, 
188                  pos.y - image->get_height() + parallax_image_size.height / 2.0f);       
189         context.draw_surface(image.get(), p, layer);
190       }
191       break;
192
193     case NO_ALIGNMENT:
194       for(int y = start_y; y <= end_y; ++y)
195         for(int x = start_x; x <= end_x; ++x)
196         {
197           Vector p(pos.x + x * image->get_width()  - image->get_width()/2, 
198                    pos.y + y * image->get_height() - image->get_height()/2);
199
200           if (image_top.get() != NULL && (y < 0))
201           {
202             context.draw_surface(image_top.get(), p, layer);
203           }
204           else if (image_bottom.get() != NULL && (y > 0))
205           {
206             context.draw_surface(image_bottom.get(), p, layer);
207           }
208           else
209           {
210             context.draw_surface(image.get(), p, layer);
211           }
212         }
213       break;
214   }
215 }
216
217 void
218 Background::draw(DrawingContext& context)
219 {
220   if(image.get() == NULL)
221     return;
222
223   Sizef level_size(Sector::current()->get_width(),
224                    Sector::current()->get_height());
225   Sizef screen(SCREEN_WIDTH, SCREEN_HEIGHT);
226   Sizef translation_range = level_size - screen;
227   Vector center_offset(context.get_translation().x - translation_range.width  / 2.0f, 
228                        context.get_translation().y - translation_range.height / 2.0f);
229
230   // FIXME: We are not handling 'pos'
231   draw_image(context, Vector(level_size.width / 2.0f, level_size.height / 2.0f) + center_offset * (1.0f - speed));
232 }
233
234 /* EOF */