intro.cpp

Go to the documentation of this file.
00001 /* $Id: intro.cpp 24854 2008-03-19 17:09:25Z brunowolff $ */
00002 /*
00003    Copyright (C) 2003 - 2008 by David White <dave@whitevine.net>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License version 2
00008    or at your option any later version.
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY.
00011 
00012    See the COPYING file for more details.
00013 */
00014 
00015 //! @file intro.cpp 
00016 //! Introduction sequence at start of a scenario, End-screen after end of campaign.
00017 
00018 #include "global.hpp"
00019 
00020 #include "display.hpp"
00021 #include "events.hpp"
00022 #include "game_config.hpp"
00023 #include "gettext.hpp"
00024 #include "image.hpp"
00025 #include "intro.hpp"
00026 #include "font.hpp"
00027 #include "key.hpp"
00028 #include "log.hpp"
00029 #include "marked-up_text.hpp"
00030 #include "sdl_utils.hpp"
00031 #include "sound.hpp"
00032 #include "util.hpp"
00033 #include "video.hpp"
00034 #include "widgets/button.hpp"
00035 #include "game_events.hpp"
00036 #include "language.hpp"
00037 
00038 #include <cstdlib>
00039 #include <sstream>
00040 #include <vector>
00041 
00042 #define LOG_NG LOG_STREAM(info, engine)
00043 
00044 static bool show_intro_part(display &disp, const config& part,
00045         const std::string& scenario);
00046 
00047 //! Show an introduction sequence at the start of a scenario.
00048 void show_intro(display &disp, const config& data, const config& level)
00049 {
00050     LOG_NG << "showing intro sequence...\n";
00051 
00052     // Stop the screen being resized while we're in this function
00053     const resize_lock stop_resizing;
00054     const events::event_context context;
00055 
00056     bool showing = true;
00057 
00058     const std::string& scenario = level["name"];
00059 
00060     for(config::all_children_iterator i = data.ordered_begin();
00061             i != data.ordered_end() && showing; i++) {
00062         std::pair<const std::string*, const config*> item = *i;
00063 
00064         if(*item.first == "part") {
00065             showing = show_intro_part(disp, (*item.second), scenario);
00066         } else if(*item.first == "if") {
00067             const std::string type = game_events::conditional_passed(
00068                 NULL, item.second) ? "then":"else";
00069             const config* const thens = (*item.second).child(type);
00070             if(thens == NULL) {
00071                 LOG_NG << "no intro story this way...\n";
00072                 return;
00073             }
00074             const config& selection = *thens;
00075             show_intro(disp, selection, level);
00076         }
00077     }
00078 
00079     LOG_NG << "intro sequence finished...\n";
00080 }
00081 
00082 //! show_intro_part() is split into two parts, the second part can cause
00083 //! an utils::invalid_utf8_exception exception and it's to much code
00084 //! to indent. The solution is not very clean but the entire routine could
00085 //! use a cleanup.
00086 static bool show_intro_part_helper(display &disp, const config& part,
00087         int textx, int texty,
00088         gui::button& next_button, gui::button& skip_button,
00089         CKey& key);
00090 
00091 bool show_intro_part(display &disp, const config& part,
00092         const std::string& scenario)
00093 {
00094     LOG_NG << "showing intro part\n";
00095 
00096     CVideo &video = disp.video();
00097     const std::string& music_file = part["music"];
00098 
00099     // Play music if available
00100     if(music_file != "") {
00101         sound::play_music_repeatedly(music_file);
00102     }
00103 
00104     CKey key;
00105 
00106     gui::button next_button(video,_("Next") + std::string(">>>"));
00107     gui::button skip_button(video,_("Skip"));
00108 
00109     draw_solid_tinted_rectangle(0,0,video.getx(),video.gety(),
00110             0,0,0,1.0,video.getSurface());
00111 
00112 
00113     const std::string& background_name = part["background"];
00114     const bool show_title = utils::string_bool(part["show_title"]);
00115 
00116     surface background(NULL);
00117     if(background_name.empty() == false) {
00118         background.assign(image::get_image(background_name));
00119     }
00120 
00121     int textx = 200;
00122     int texty = 400;
00123 
00124     SDL_Rect dstrect;
00125 
00126     if(background.null() || background->w*background->h == 0) {
00127         background.assign(SDL_CreateRGBSurface(SDL_SWSURFACE,video.getx(),video.gety(),32,0xFF0000,0xFF00,0xFF,0xFF000000));
00128     }
00129 
00130     double xscale = 1.0 * video.getx() / background->w;
00131     double yscale = 1.0 * video.gety() / background->h;
00132     double scale = minimum<double>(xscale,yscale);
00133 
00134     background = scale_surface(background, static_cast<int>(background->w*scale), static_cast<int>(background->h*scale));
00135 
00136     dstrect.x = (video.getx() - background->w) / 2;
00137     dstrect.y = (video.gety() - background->h) / 2;
00138     dstrect.w = background->w;
00139     dstrect.h = background->h;
00140 
00141     SDL_BlitSurface(background,NULL,video.getSurface(),&dstrect);
00142 
00143 #ifdef USE_TINY_GUI
00144     textx = 10;
00145     int xbuttons = video.getx() - 50;
00146 
00147     // Use the whole screen for text
00148     texty = 0;
00149 #else
00150     int xbuttons;
00151 
00152     if (background->w > 500) {
00153         textx = dstrect.x + 150;
00154         xbuttons = dstrect.x+dstrect.w-140;
00155     } else {
00156         textx = 200;
00157         xbuttons = video.getx() - 200 - 40;
00158     }
00159 
00160     texty = dstrect.y + dstrect.h - 200;
00161 #endif
00162 
00163     // Darken the area for the text and buttons to be drawn on
00164     if(show_title == false) {
00165         draw_solid_tinted_rectangle(0,texty,video.getx(),video.gety()-texty,0,0,0,0.5,video.getSurface());
00166     }
00167 
00168 #ifdef USE_TINY_GUI
00169     next_button.set_location(xbuttons,dstrect.y+dstrect.h-40);
00170     skip_button.set_location(xbuttons,dstrect.y+dstrect.h-20);
00171 #else
00172     next_button.set_location(xbuttons,dstrect.y+dstrect.h-70);
00173     skip_button.set_location(xbuttons,dstrect.y+dstrect.h-40);
00174 #endif
00175 
00176     // Draw title if needed
00177     if(show_title) {
00178         const SDL_Rect area = {0,0,video.getx(),video.gety()};
00179         const SDL_Rect txt_shadow_rect = font::line_size(scenario, font::SIZE_XLARGE);
00180         draw_solid_tinted_rectangle(dstrect.x + 15,dstrect.y + 15,txt_shadow_rect.w + 10,txt_shadow_rect.h + 10,0,0,0,0.5,video.getSurface());
00181 
00182         font::draw_text(NULL,area,font::SIZE_XLARGE,font::BIGMAP_COLOUR,scenario,0,0);
00183         update_rect(font::draw_text(&video,area,font::SIZE_XLARGE,font::BIGMAP_COLOUR,scenario,
00184                         dstrect.x + 20,dstrect.y + 20));
00185     }
00186 
00187     events::raise_draw_event();
00188     update_whole_screen();
00189     disp.flip();
00190 
00191     if(!background.null()) {
00192         // Draw images
00193         const config::child_list& images = part.get_children("image");
00194 
00195         bool pass = false;
00196 
00197         for(std::vector<config*>::const_iterator i = images.begin(); i != images.end(); ++i){
00198             const std::string& image_name = (**i)["file"];
00199             if(image_name == "") continue;
00200             surface img(image::get_image(image_name));
00201             if(img.null()) continue;
00202 
00203             const std::string& xloc = (**i)["x"];
00204             const std::string& yloc = (**i)["y"];
00205             const std::string& delay_str = (**i)["delay"];
00206             const int delay = (delay_str == "") ? 0: atoi(delay_str.c_str());
00207             const int x = static_cast<int>(atoi(xloc.c_str())*scale);
00208             const int y = static_cast<int>(atoi(yloc.c_str())*scale);
00209 
00210             if (utils::string_bool((**i)["scaled"])){
00211                 img = scale_surface(img, static_cast<int>(img->w*scale), static_cast<int>(img->h*scale));
00212             }
00213 
00214             SDL_Rect image_rect;
00215             image_rect.x = x + dstrect.x;
00216             image_rect.y = y + dstrect.y;
00217             image_rect.w = img->w;
00218             image_rect.h = img->h;
00219 
00220             if (utils::string_bool((**i)["centered"])){
00221                 image_rect.x -= image_rect.w/2;
00222                 image_rect.y -= image_rect.h/2;
00223             }
00224 
00225             SDL_BlitSurface(img,NULL,video.getSurface(),&image_rect);
00226 
00227             update_rect(image_rect);
00228 
00229             if(pass == false) {
00230                 for(int i = 0; i != 50; ++i) {
00231                     if(key[SDLK_ESCAPE] || next_button.pressed() || skip_button.pressed()) {
00232                         return false;
00233                     }
00234 
00235                     disp.delay(delay/50);
00236 
00237                     events::pump();
00238                     events::raise_process_event();
00239                     events::raise_draw_event();
00240 
00241                     int a, b;
00242                     const int mouse_flags = SDL_GetMouseState(&a,&b);
00243                     if(key[SDLK_RETURN] || key[SDLK_KP_ENTER] || key[SDLK_SPACE] || mouse_flags) {
00244                         pass = true;
00245                         continue;
00246                     }
00247 
00248                     disp.flip();
00249                 }
00250             }
00251 
00252             if(key[SDLK_ESCAPE] || next_button.pressed() || skip_button.pressed()) {
00253                 pass = true;
00254                 continue;
00255             }
00256         }
00257     }
00258     try {
00259         return show_intro_part_helper(
00260             disp, part, textx, texty, next_button, skip_button, key);
00261 
00262     } catch (utils::invalid_utf8_exception&) {
00263         LOG_STREAM(err, engine) << "Invalid utf-8 found, story message is ignored.\n";
00264         // stop showing on an error, there might be more badly formed utf-8 messages
00265         return false;
00266     }
00267 }
00268 
00269 static bool show_intro_part_helper(display &disp, const config& part,
00270         int textx, int texty,
00271         gui::button& next_button, gui::button& skip_button,
00272         CKey& key)
00273 {
00274     bool lang_rtl = current_language_rtl();
00275     CVideo &video = disp.video();
00276 
00277 
00278     const int max_width = next_button.location().x - 10 - textx;
00279     const std::string story = 
00280         font::word_wrap_text(part["story"], font::SIZE_PLUS, max_width);
00281 
00282     utils::utf8_iterator itor(story);
00283 
00284     bool skip = false, last_key = true;
00285 
00286     const SDL_Rect total_size = font::draw_text(NULL, screen_area(), font::SIZE_PLUS,
00287             font::NORMAL_COLOUR, story, 0, 0);
00288     if (texty + 20 + total_size.h > screen_area().h) {
00289         texty = screen_area().h > total_size.h + 1 ? screen_area().h - total_size.h - 21 : 0;
00290 
00291         draw_solid_tinted_rectangle(textx, texty, total_size.w, total_size.h,
00292                 0, 0, 0, 128, video.getSurface());
00293         update_rect(textx, texty, total_size.w, total_size.h);
00294     }
00295 
00296     if(lang_rtl)
00297         textx += max_width;
00298 
00299 #ifdef USE_TINY_GUI
00300     int xpos = textx, ypos = texty + 10;
00301 #else
00302     int xpos = textx, ypos = texty + 20;
00303 #endif
00304 
00305     // The maximum position that text can reach before wrapping
00306     size_t height = 0;
00307 
00308     for(;;) {
00309         if(itor != utils::utf8_iterator::end(story)) {
00310             if(*itor == '\n') {
00311                 xpos = textx;
00312                 ypos += height;
00313                 ++itor;
00314             }
00315 
00316             // Output the character
00317             //! @todo  FIXME: this is broken: it does not take kerning into account.
00318             std::string tmp;
00319             tmp.append(itor.substr().first, itor.substr().second);
00320             if(lang_rtl)
00321                 xpos -= font::line_width(tmp, font::SIZE_PLUS);
00322             const SDL_Rect rect = font::draw_text(&video,
00323                     screen_area(),font::SIZE_PLUS,
00324                     font::NORMAL_COLOUR,tmp,xpos,ypos,
00325                     false);
00326 
00327             if(rect.h > height)
00328                 height = rect.h;
00329             if(!lang_rtl)
00330                 xpos += rect.w;
00331             update_rect(rect);
00332 
00333             ++itor;
00334             if(itor == utils::utf8_iterator::end(story))
00335                 skip = true;
00336 
00337         }
00338 
00339         const bool keydown = key[SDLK_SPACE] || key[SDLK_RETURN] || key[SDLK_KP_ENTER];
00340 
00341         if((keydown && !last_key) || next_button.pressed()) {
00342             if(skip == true || itor == utils::utf8_iterator::end(story)) {
00343                 break;
00344             } else {
00345                 skip = true;
00346             }
00347         }
00348 
00349         last_key = keydown;
00350 
00351         if(key[SDLK_ESCAPE] || skip_button.pressed())
00352             return false;
00353 
00354         events::pump();
00355         events::raise_process_event();
00356         events::raise_draw_event();
00357         disp.flip();
00358 
00359         if(!skip || itor == utils::utf8_iterator::end(story))
00360             disp.delay(20);
00361     }
00362 
00363     draw_solid_tinted_rectangle(0,0,video.getx(),video.gety(),0,0,0,1.0,
00364                                      video.getSurface());
00365 
00366     return true;
00367 }
00368 
00369 //! Black screen with "The End", shown at the end of a campaign.
00370 void the_end(display &disp)
00371 {
00372     SDL_Rect area = screen_area();
00373     CVideo &video = disp.video();
00374     SDL_FillRect(video.getSurface(),&area,0);
00375 
00376     update_whole_screen();
00377     disp.flip();
00378 
00379     const std::string text = _("The End");
00380     const size_t font_size = font::SIZE_XLARGE;
00381 
00382     area = font::text_area(text,font_size);
00383     area.x = screen_area().w/2 - area.w/2;
00384     area.y = screen_area().h/2 - area.h/2;
00385 
00386     for(size_t n = 0; n < 255; n += 5) {
00387         const SDL_Color col = {n,n,n,n};
00388         font::draw_text(&video,area,font_size,col,text,area.x,area.y);
00389         update_rect(area);
00390         disp.flip();
00391 
00392         SDL_FillRect(video.getSurface(),&area,0);
00393 
00394         disp.delay(10);
00395     }
00396 
00397     disp.delay(4000);
00398 }
00399 

Generated by doxygen 1.5.5 on 23 May 2008 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs