-Incorporated Marcin Ko��cielnicki patch that reintroduces the flying
[supertux.git] / lib / video / font.cpp
1 //  $Id$
2 // 
3 //  SuperTux
4 //  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.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
19 //  02111-1307, USA.
20
21 #include <config.h>
22
23 #include <cstdlib>
24 #include <cstring>
25 #include <stdexcept>
26
27 #include "app/globals.h"
28 #include "lisp/parser.h"
29 #include "lisp/lisp.h"
30 #include "screen.h"
31 #include "font.h"
32 #include "drawing_context.h"
33
34 using namespace SuperTux;
35
36 Font::Font(const std::string& file, FontType ntype, int nw, int nh,
37         int nshadowsize)
38     : chars(0), shadow_chars(0), type(ntype), w(nw), h(nh),
39       shadowsize(nshadowsize)
40 {
41   chars = new Surface(file, true);
42  
43   switch(type) {
44     case TEXT:
45       first_char = 32;
46       break;
47     case NUM:
48       first_char = 48;
49       break;
50   }
51   last_char = first_char + (chars->h / h) * 16;
52   if(last_char > 127) // we have left out some control chars at 128-159
53     last_char += 32;
54    
55   // Load shadow font.
56   if(shadowsize > 0) {
57     SDL_Surface* conv = SDL_DisplayFormatAlpha(chars->impl->get_sdl_surface());
58     int pixels = conv->w * conv->h;
59     SDL_LockSurface(conv);
60     for(int i = 0; i < pixels; ++i) {
61       Uint32 *p = (Uint32 *)conv->pixels + i;
62       *p = *p & conv->format->Amask;
63     }
64     SDL_UnlockSurface(conv);
65     SDL_SetAlpha(conv, SDL_SRCALPHA, 128);
66     shadow_chars = new Surface(conv, true);
67     SDL_FreeSurface(conv);
68   }
69 }
70
71 Font::~Font()
72 {
73   delete chars;
74   delete shadow_chars;
75 }
76
77 float
78 Font::get_text_width(const std::string& text) const
79 {
80   /** Let's calculate the size of the biggest paragraph */
81   std::string::size_type l, hl, ol;
82   hl = 0; l = 0;
83   while(true)
84     {
85     ol = l;
86     l = text.find("\n", l+1);
87     if(l == std::string::npos)
88       break;
89     if(hl < l-ol)
90       hl = l-ol;
91     }
92   if(hl == 0)
93     hl = text.size();
94
95   return hl * w;
96 }
97
98 float
99 Font::get_text_height(const std::string& text) const
100 {
101   /** Let's calculate height of the text */
102   std::string::size_type l, hh;
103   hh = h; l = 0;
104   while(true)
105     {
106     l = text.find("\n", l+1);
107     if(l == std::string::npos)
108       break;
109     hh += h + 2;
110     }
111
112   return hh;
113 }
114
115 float
116 Font::get_height() const
117 {
118   return h;
119 }
120
121 void
122 Font::draw(const std::string& text, const Vector& pos_, int allignment, Uint32 drawing_effect, int alpha)
123 {
124   /* Cut lines changes into seperate strings, needed to support center/right text
125      allignments with break lines.
126      Feel free to replace this hack with a more elegant solution
127   */
128   char temp[1024];
129   std::string::size_type l, i, y;
130   bool done = false;
131   i = y = 0;
132
133   while(!done)
134     {
135     l = text.find("\n", i);
136     if(l == std::string::npos)
137       {
138       l = text.size();
139       done = true;
140       }
141
142     temp[text.copy(temp, l - i, i)] = '\0';
143
144     // calculate X positions based on the allignment type
145     Vector pos = Vector(pos_);
146     if(allignment == CENTER_ALLIGN)
147       pos.x -= get_text_width(temp) / 2;
148     else if(allignment == RIGHT_ALLIGN)
149       pos.x -= get_text_width(temp);
150
151     draw_text(temp, pos + Vector(0,y), drawing_effect, alpha);
152
153     i = l+1;
154     y += h + 2;
155     }
156 }
157
158 void
159 Font::draw_text(const std::string& text, const Vector& pos, Uint32 drawing_effect, int alpha)
160 {
161   if(shadowsize > 0)
162     draw_chars(shadow_chars, text, pos + Vector(shadowsize, shadowsize),
163                drawing_effect, alpha);
164
165   draw_chars(chars, text, pos, drawing_effect, alpha);
166 }
167
168 void
169 Font::draw_chars(Surface* pchars, const std::string& text, const Vector& pos,
170                  Uint32 drawing_effect, int alpha)
171 {
172   SurfaceImpl* impl = pchars->impl;
173
174   Vector p = pos;
175   for(size_t i = 0; i < text.size(); ++i)
176   {
177     int c = (unsigned char) text[i];
178     if(c > 127) // correct for the 32 controlchars at 128-159
179       c -= 32;
180     // a non-printable character?
181     if(c == '\n') {
182       p.x = pos.x;
183       p.y += h + 2;
184       continue;
185     }
186     if(c == ' ' || c < first_char || c > last_char) {
187       p.x += w;
188       continue;
189     }
190     
191     int index = c - first_char;
192     int source_x = (index % 16) * w;
193     int source_y = (index / 16) * h;
194
195     impl->draw_part(source_x, source_y, p.x, p.y, w, h, alpha, drawing_effect);
196     p.x += w;
197   }
198 }
199
200 /* --- SCROLL TEXT FUNCTION --- */
201
202 #define MAX_VEL     10
203 #define SPEED_INC   0.01
204 #define SCROLL      60
205 #define ITEMS_SPACE 4
206
207 void SuperTux::display_text_file(const std::string& file, float scroll_speed,
208     Font* heading_font, Font* normal_font, Font* small_font,
209     Font* reference_font)
210 {
211   std::string text;
212   std::string background_file;
213   std::vector<std::string> names;
214
215   std::string filename = datadir + "/" + file;
216   lisp::Parser parser;
217   try {
218     std::auto_ptr<lisp::Lisp> root (parser.parse(filename));
219
220     const lisp::Lisp* text_lisp = root->get_lisp("supertux-text");
221     if(!text_lisp)
222       throw std::runtime_error("File isn't a supertux-text file");
223     
224     if(!text_lisp->get("text", text))
225       throw std::runtime_error("file doesn't contain a text field");
226     if(!text_lisp->get("background", background_file))
227       throw std::runtime_error("file doesn't contain a background file");
228   } catch(std::exception& e) {
229     std::cerr << "Couldn't load file '" << filename << "': " << e.what() <<
230       "\n";
231     return;
232   }
233
234   // Split text string lines into a vector
235   names.clear();
236   std::string::size_type i, l;
237   i = 0;
238   while(true)
239     {
240     l = text.find("\n", i);
241
242     if(l == std::string::npos)
243       {
244       char temp[1024];
245       temp[text.copy(temp, text.size() - i, i)] = '\0';
246       names.push_back(temp);
247       break;
248       }
249
250     char temp[1024];
251     temp[text.copy(temp, l-i, i)] = '\0';
252     names.push_back(temp);
253
254     i = l+1;
255     }
256
257   // load background image
258   Surface* background = new Surface(datadir + "/images/background/" + background_file, false);
259
260   int done = 0;
261   float scroll = 0;
262   float speed = scroll_speed / 50;
263   float left_border = 50;
264
265   DrawingContext context;
266   SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
267
268   Uint32 lastticks = SDL_GetTicks();
269   while(!done)
270     {
271       /* in case of input, exit */
272       SDL_Event event;
273       while(SDL_PollEvent(&event))
274         switch(event.type)
275           {
276           case SDL_KEYDOWN:
277             switch(event.key.keysym.sym)
278               {
279               case SDLK_UP:
280                 speed -= SPEED_INC;
281                 break;
282               case SDLK_DOWN:
283                 speed += SPEED_INC;
284                 break;
285               case SDLK_SPACE:
286               case SDLK_RETURN:
287                 if(speed >= 0)
288                   scroll += SCROLL;
289                 break;
290               case SDLK_ESCAPE:
291                 done = 1;
292                 break;
293               default:
294                 break;
295               }
296             break;
297           case SDL_QUIT:
298             done = 1;
299             break;
300           default:
301             break;
302           }
303
304       if(speed > MAX_VEL)
305         speed = MAX_VEL;
306       else if(speed < -MAX_VEL)
307         speed = -MAX_VEL;
308
309       /* draw the credits */
310       context.draw_surface(background, Vector(0,0), 0);
311
312       float y = 0;
313       for(size_t i = 0; i < names.size(); i++) {
314         if(names[i].size() == 0) {
315           y += normal_font->get_height() + ITEMS_SPACE;
316           continue;
317         }
318
319         Font* font = 0;
320         bool center = true;
321         switch(names[i][0])
322         {
323           case ' ': font = small_font; break;
324           case '\t': font = normal_font; break;
325           case '-': font = heading_font; break;
326           case '*': font = reference_font; break;
327           case '#': font = normal_font; center = false; break;
328           default: 
329             break;
330         }
331         
332         if(font) {
333           if(center) {
334             context.draw_text(font,
335                               names[i].substr(1, names[i].size()-1),
336                               Vector(screen->w/2, screen->h + y - scroll),
337                               CENTER_ALLIGN, LAYER_FOREGROUND1);
338           } else {
339             context.draw_text(font,
340                               names[i].substr(1, names[i].size()-1),
341                               Vector(left_border, screen->h + y - scroll),
342                               LEFT_ALLIGN, LAYER_FOREGROUND1);
343           }
344         }                   
345           
346         y += font->get_height() + ITEMS_SPACE;
347       }
348
349       context.do_drawing();
350
351       if(screen->h+y-scroll < 0 && 20+screen->h+y-scroll < 0)
352         done = 1;
353
354       Uint32 ticks = SDL_GetTicks();
355       scroll += speed * (ticks - lastticks);
356       lastticks = ticks;
357       if(scroll < 0)
358         scroll = 0;
359
360       SDL_Delay(10);
361     }
362
363   SDL_EnableKeyRepeat(0, 0);    // disables key repeating
364   delete background;
365 }
366