6ef8168845e67a40c4cf5c600b37fbe65d575e37
[supertux.git] / src / camera.cpp
1 //  $Id$
2 //
3 //  SuperTux -  A Jump'n Run
4 //  Copyright (C) 2004 Matthias Braun <matze@braunis.de
5 //
6 //  This program is free software; you can redistribute it and/or
7 //  modify it under the terms of the GNU General Public License
8 //  as published by the Free Software Foundation; either version 2
9 //  of the License, or (at your option) any later version.
10 //
11 //  This program is distributed in the hope that it will be useful,
12 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //  GNU General Public License for more details.
15 //
16 //  You should have received a copy of the GNU General Public License
17 //  along with this program; if not, write to the Free Software
18 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19 #include "camera.h"
20
21 #include <math.h>
22 #include "lispwriter.h"
23 #include "player.h"
24 #include "level.h"
25 #include "globals.h"
26
27 Camera::Camera(Player* newplayer, Level* newlevel)
28   : player(newplayer), level(newlevel), scrollchange(NONE)
29 {
30   if(!player || !level)
31     mode = MANUAL;
32   else
33     mode = NORMAL;
34 }
35
36 Camera::~Camera()
37 {
38 }
39
40 void
41 Camera::set_translation(const Vector& newtranslation)
42 {
43   translation = newtranslation;
44 }
45
46 void
47 Camera::write(LispWriter& writer)
48 {
49   writer.start_list("camera");
50   
51   if(mode == NORMAL) {
52     writer.write_string("mode", "normal");
53   } else if(mode == AUTOSCROLL) {
54     writer.write_string("mode", "autoscroll");
55   } else if(mode == MANUAL) {
56     writer.write_string("mode", "manual");
57   }
58                      
59   writer.end_list("camera");
60 }
61
62 static const float EPSILON = .00001;
63 static const float max_speed_y = 1.4;
64
65 void
66 Camera::action(float elapsed_time)
67 {
68   if(mode == NORMAL)
69     scroll_normal(elapsed_time);
70   else if(mode == AUTOSCROLL)
71     scroll_autoscroll(elapsed_time);
72 }
73
74 void
75 Camera::scroll_normal(float elapsed_time)
76 {
77   assert(level != 0 && player != 0);
78   
79   // check that we don't have division by zero later
80   if(elapsed_time < EPSILON)
81     return;
82
83   /****** Vertical Scrolling part ******/
84   bool do_y_scrolling = true;
85
86   if(player->dying || level->height == 19)
87     do_y_scrolling = false;
88
89   if(do_y_scrolling) {
90     // target_y is the high we target our scrolling at. This is not always the
91     // high of the player, but if he is jumping upwards we should use the
92     // position where he last touched the ground.
93     float target_y; 
94     if(player->fall_mode == Player::JUMPING)
95       target_y = player->last_ground_y + player->base.height;
96     else
97       target_y = player->base.y + player->base.height;
98
99     // delta_y is the distance we'd have to travel to directly reach target_y
100     float delta_y = translation.y - (target_y - screen->h/2);
101     // speed is the speed the camera would need to reach target_y in this frame
102     float speed_y = delta_y / elapsed_time;
103
104     // limit the camera speed when jumping upwards
105     if(player->fall_mode != Player::FALLING 
106         && player->fall_mode != Player::TRAMPOLINE_JUMP) {
107       if(speed_y > max_speed_y)
108         speed_y = max_speed_y;
109       else if(speed_y < -max_speed_y)
110         speed_y = -max_speed_y;
111     }
112
113     // finally scroll with calculated speed
114     translation.y -= speed_y * elapsed_time;
115
116     // don't scroll before the start or after the level's end
117     if(translation.y > level->height * 32 - screen->h)
118       translation.y = level->height * 32 - screen->h;
119     if(translation.y < 0)
120       translation.y = 0; 
121   }
122
123   /****** Horizontal scrolling part *******/
124
125   // our camera is either in leftscrolling, rightscrolling or nonscrollingmode.
126   
127   // when suddenly changing directions while scrolling into the other direction.
128   // abort scrolling, since tux might be going left/right at a relatively small
129   // part of the map (like when jumping upwards)
130   if((player->dir == ::LEFT && scrollchange == RIGHT)
131       || (player->dir == ::RIGHT && scrollchange == LEFT))
132     scrollchange = NONE;
133   // when in left 1/3rd of screen scroll left
134   if(player->base.x < translation.x + screen->w/3 && level->back_scrolling)
135     scrollchange = LEFT;
136   // scroll right when in right 1/3rd of screen
137   else if(player->base.x > translation.x + screen->w/3*2)
138     scrollchange = RIGHT;
139
140   // calculate our scroll target depending on scroll mode
141   float target_x;
142   if(scrollchange == LEFT)
143     target_x = player->base.x - screen->w/3*2;
144   else if(scrollchange == RIGHT)
145     target_x = player->base.x - screen->w/3;
146   else
147     target_x = translation.x;
148
149   // that's the distance we would have to travel to reach target_x
150   float delta_x = translation.x - target_x;
151   // the speed we'd need to travel to reach target_x in this frame
152   float speed_x = delta_x / elapsed_time;
153
154   // limit our speed
155   float maxv = 1 + fabsf(player->physic.get_velocity_x() * 1.3);
156   if(speed_x > maxv)
157     speed_x = maxv;
158   else if(speed_x < -maxv)
159     speed_x = -maxv;
160  
161   // apply scrolling
162   translation.x -= speed_x * elapsed_time;
163
164   // don't scroll before the start or after the level's end
165   if(translation.x > level->width * 32 - screen->w)
166     translation.x = level->width * 32 - screen->w;
167   if(translation.x < 0)
168     translation.x = 0;   
169 }
170
171 void
172 Camera::scroll_autoscroll(float elapsed_time)
173 {
174   // TODO
175 }