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