00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "global.hpp"
00024 #include "asserts.hpp"
00025 #include "foreach.hpp"
00026 #include "log.hpp"
00027 #include "storyscreen/part.hpp"
00028 #include "storyscreen/render.hpp"
00029
00030 #include "display.hpp"
00031 #include "image.hpp"
00032 #include "language.hpp"
00033 #include "sound.hpp"
00034 #include "text.hpp"
00035 #include "video.hpp"
00036
00037 static lg::log_domain log_engine("engine");
00038 #define ERR_NG LOG_STREAM(err, log_engine)
00039 #define WARN_NG LOG_STREAM(warn, log_engine)
00040 #define LOG_NG LOG_STREAM(info, log_engine)
00041
00042
00043 namespace {
00044 int const storybox_padding = 10;
00045 double const storyshadow_opacity = 0.5;
00046 int const storyshadow_r = 0;
00047 int const storyshadow_g = 0;
00048 int const storyshadow_b = 0;
00049
00050 int const titlebox_padding = 20;
00051 int const titleshadow_padding = 5;
00052 double const titleshadow_opacity = 0.5;
00053 int const titleshadow_r = 0;
00054 int const titleshadow_g = 0;
00055 int const titleshadow_b = 0;
00056
00057 int const titlebox_font_size = 20;
00058 int const storybox_font_size = 14;
00059
00060 Uint32 const titlebox_font_color = 0xFFFFFFFF;
00061 Uint32 const storybox_font_color = 0xDDDDDDFF;
00062
00063 #ifndef LOW_MEM
00064
00065 std::string const storybox_top_border_path = "dialogs/translucent54-border-top.png";
00066 std::string const storybox_bottom_border_path = "dialogs/translucent54-border-bottom.png";
00067
00068 void blur_area(CVideo& video, int y, int h)
00069 {
00070 SDL_Rect blur_rect = create_rect(0, y, screen_area().w, h);
00071 surface blur = get_surface_portion(video.getSurface(), blur_rect);
00072 blur = blur_surface(blur, 1, false);
00073 video.blit_surface(0, y, blur);
00074 }
00075 #endif
00076 }
00077
00078 namespace storyscreen {
00079
00080 part_ui::part_ui(part &p, display &disp, gui::button &next_button,
00081 gui::button &back_button, gui::button&play_button)
00082 : p_(p)
00083 , disp_(disp)
00084 , video_(disp.video())
00085 , keys_()
00086 , next_button_(next_button)
00087 , back_button_(back_button)
00088 , play_button_(play_button)
00089 , ret_(NEXT), skip_(false), last_key_(false)
00090 , scale_factor_(1.0)
00091 , base_rect_()
00092 , background_(NULL)
00093 , imgs_()
00094 , has_background_(false)
00095 , text_x_(200)
00096 , text_y_(400)
00097 , buttons_x_(0)
00098 , buttons_y_(0)
00099 {
00100 this->prepare_background();
00101 this->prepare_geometry();
00102 this->prepare_floating_images();
00103 }
00104
00105 void part_ui::prepare_background()
00106 {
00107
00108 if(p_.background().empty() != true) {
00109 background_.assign( image::get_image(p_.background()) );
00110 }
00111 has_background_ = !background_.null();
00112 if(background_.null() || background_->w * background_-> h == 0) {
00113 background_.assign( create_neutral_surface(video_.getx(), video_.gety()) );
00114 }
00115
00116 const double xscale = 1.0 * video_.getx() / background_->w;
00117 const double yscale = 1.0 * video_.gety() / background_->h;
00118 scale_factor_ = p_.scale_background() ? std::min<double>(xscale,yscale) : 1.0;
00119
00120 background_ =
00121 scale_surface(background_, static_cast<int>(background_->w*scale_factor_), static_cast<int>(background_->h*scale_factor_));
00122
00123 ASSERT_LOG(background_.null() == false, "Oops: storyscreen part background got NULL");
00124 }
00125
00126 void part_ui::prepare_geometry()
00127 {
00128 base_rect_.x = (video_.getx() - background_->w) / 2;
00129 base_rect_.y = (video_.gety() - background_->h) / 2;
00130 base_rect_.w = background_->w;
00131 base_rect_.h = background_->h;
00132
00133 if(video_.getx() <= 800) {
00134 text_x_ = 10;
00135 buttons_x_ = video_.getx() - 100 - 20;
00136 }
00137 else {
00138 text_x_ = 100 - 40;
00139 buttons_x_ = video_.getx() - 100 - 40;
00140 }
00141
00142 switch(p_.story_text_location())
00143 {
00144 case part::BLOCK_TOP:
00145 text_y_ = 0;
00146 buttons_y_ = 40;
00147 break;
00148 case part::BLOCK_MIDDLE:
00149 text_y_ = video_.gety() / 3;
00150 buttons_y_ = video_.gety() / 2 + 15;
00151 break;
00152 default:
00153 text_y_ = video_.gety() - 200;
00154 buttons_y_ = video_.gety() - 40;
00155 break;
00156 }
00157
00158 back_button_.set_location(buttons_x_, buttons_y_ - 30);
00159 next_button_.set_location(buttons_x_ + play_button_.width() - next_button_.width(), buttons_y_ - 30);
00160 play_button_.set_location(buttons_x_, buttons_y_);
00161
00162 next_button_.set_volatile(true);
00163 play_button_.set_volatile(true);
00164 back_button_.set_volatile(true);
00165 }
00166
00167 void part_ui::prepare_floating_images()
00168 {
00169
00170 foreach(const floating_image& fi, p_.get_floating_images()) {
00171 imgs_.push_back( fi.get_render_input(scale_factor_, base_rect_) );
00172 }
00173 }
00174
00175 void part_ui::render_background()
00176 {
00177 draw_solid_tinted_rectangle(
00178 0, 0, video_.getx(), video_.gety(), 0, 0, 0, 1.0,
00179 video_.getSurface()
00180 );
00181 sdl_blit(background_, NULL, video_.getSurface(), &base_rect_);
00182 }
00183
00184 bool part_ui::render_floating_images()
00185 {
00186 events::raise_draw_event();
00187 update_whole_screen();
00188
00189 skip_ = false;
00190 last_key_ = true;
00191
00192 size_t fi_n = 0;
00193 foreach(floating_image::render_input& ri, imgs_) {
00194 const floating_image& fi = p_.get_floating_images()[fi_n];
00195
00196 if(!ri.image.null()) {
00197 sdl_blit(ri.image, NULL, video_.getSurface(), &ri.rect);
00198 update_rect(ri.rect);
00199 }
00200
00201 if (!skip_)
00202 {
00203 int delay = fi.display_delay(), delay_step = 20;
00204 for (int i = 0; i != (delay + delay_step - 1) / delay_step; ++i)
00205 {
00206 if (handle_interface()) return false;
00207 if (skip_) break;
00208 disp_.delay(std::min<int>(delay_step, delay - i * delay_step));
00209 }
00210 }
00211
00212 ++fi_n;
00213 }
00214
00215 return true;
00216 }
00217
00218 void part_ui::render_title_box()
00219 {
00220 const std::string& titletxt = p_.title();
00221 if(titletxt.empty()) {
00222 return;
00223 }
00224
00225 int titlebox_x, titlebox_y, titlebox_max_w, titlebox_max_h;
00226
00227
00228
00229 titlebox_x = titlebox_padding;
00230 titlebox_max_w = base_rect_.w - 2*titlebox_padding;
00231 titlebox_y = titlebox_padding;
00232 titlebox_max_h = base_rect_.h - 2*titlebox_padding;
00233
00234 font::ttext t;
00235 if(!t.set_text(titletxt, true)) {
00236 ERR_NG << "Text: Invalid markup in '"
00237 << titletxt << "' rendered as is.\n";
00238 t.set_text(titletxt, false);
00239 }
00240
00241 t.set_font_style(font::ttext::STYLE_NORMAL)
00242 .set_font_size(titlebox_font_size)
00243 .set_foreground_color(titlebox_font_color)
00244 .set_maximum_width(titlebox_max_w)
00245 .set_maximum_height(titlebox_max_h);
00246 surface txtsurf = t.render();
00247
00248 if(txtsurf.null()) {
00249 ERR_NG << "storyscreen titlebox rendering resulted in a null surface\n";
00250 return;
00251 }
00252
00253 const int titlebox_w = txtsurf->w;
00254 const int titlebox_h = txtsurf->h;
00255
00256 switch(p_.title_text_alignment()) {
00257 case part::TEXT_CENTERED:
00258 titlebox_x = base_rect_.w / 2 - titlebox_w / 2 - titlebox_padding;
00259 break;
00260 case part::TEXT_RIGHT:
00261 titlebox_x = base_rect_.w - titlebox_padding - titlebox_w;
00262 break;
00263 default:
00264 break;
00265 }
00266
00267 draw_solid_tinted_rectangle(
00268 base_rect_.x + titlebox_x - titleshadow_padding,
00269 base_rect_.y + titlebox_y - titleshadow_padding,
00270 titlebox_w + 2*titleshadow_padding,
00271 titlebox_h + 2*titleshadow_padding,
00272 titleshadow_r, titleshadow_g, titleshadow_b,
00273 titleshadow_opacity,
00274 video_.getSurface()
00275 );
00276
00277 video_.blit_surface(base_rect_.x + titlebox_x, base_rect_.y + titlebox_y, txtsurf);
00278
00279 update_rect(
00280 static_cast<size_t>(std::max(0, base_rect_.x + titlebox_x)),
00281 static_cast<size_t>(std::max(0, base_rect_.y + titlebox_y)),
00282 static_cast<size_t>(std::max(0, titlebox_w)),
00283 static_cast<size_t>(std::max(0, titlebox_h))
00284 );
00285 }
00286
00287 #ifdef LOW_MEM
00288 void part_ui::render_story_box_borders(SDL_Rect& )
00289 {}
00290 #else
00291 void part_ui::render_story_box_borders(SDL_Rect& update_area)
00292 {
00293 const part::BLOCK_LOCATION tbl = p_.story_text_location();
00294
00295 if(has_background_) {
00296 surface border_top = NULL;
00297 surface border_bottom = NULL;
00298
00299 if(tbl == part::BLOCK_BOTTOM || tbl == part::BLOCK_MIDDLE) {
00300 border_top = image::get_image(storybox_top_border_path);
00301 }
00302
00303 if(tbl == part::BLOCK_TOP || tbl == part::BLOCK_MIDDLE) {
00304 border_bottom = image::get_image(storybox_bottom_border_path);
00305 }
00306
00307
00308
00309
00310
00311
00312
00313 if(border_top.null() != true) {
00314 if((border_top = scale_surface(border_top, screen_area().w, border_top->h)).null()) {
00315 WARN_NG << "storyscreen got a null top border surface after rescaling\n";
00316 }
00317 else {
00318 update_area.y -= border_top->h;
00319 update_area.h += border_top->h;
00320 blur_area(video_, update_area.y, border_top->h);
00321 video_.blit_surface(0, update_area.y, border_top);
00322 }
00323 }
00324
00325 if(border_bottom.null() != true) {
00326 if((border_bottom = scale_surface(border_bottom, screen_area().w, border_bottom->h)).null()) {
00327 WARN_NG << "storyscreen got a null bottom border surface after rescaling\n";
00328 }
00329 else {
00330 blur_area(video_, update_area.h, border_bottom->h);
00331 video_.blit_surface(0, update_area.y+update_area.h, border_bottom);
00332 update_area.h += border_bottom->h;
00333 }
00334 }
00335 }
00336 }
00337 #endif
00338
00339 void part_ui::render_story_box()
00340 {
00341 LOG_NG << "ENTER part_ui()::render_story_box()\n";
00342
00343 const std::string& storytxt = p_.text();
00344 if(storytxt.empty()) {
00345 update_whole_screen();
00346 wait_for_input();
00347 return;
00348 }
00349
00350 const part::BLOCK_LOCATION tbl = p_.story_text_location();
00351 const int max_width = buttons_x_ - storybox_padding - text_x_;
00352 const int max_height = screen_area().h - storybox_padding;
00353
00354 skip_ = false;
00355 last_key_ = true;
00356
00357 font::ttext t;
00358 if(!t.set_text(p_.text(), true)) {
00359 ERR_NG << "Text: Invalid markup in '"
00360 << p_.text() << "' rendered as is.\n";
00361 t.set_text(p_.text(), false);
00362 }
00363 t.set_font_style(font::ttext::STYLE_NORMAL)
00364 .set_font_size(storybox_font_size)
00365 .set_foreground_color(storybox_font_color)
00366 .set_maximum_width(max_width)
00367 .set_maximum_height(max_height);
00368 surface txtsurf = t.render();
00369
00370 if(txtsurf.null()) {
00371 ERR_NG << "storyscreen text area rendering resulted in a null surface\n";
00372 return;
00373 }
00374
00375 int fix_text_y = text_y_;
00376 if(fix_text_y + storybox_padding + txtsurf->h > screen_area().h && tbl != part::BLOCK_TOP) {
00377 fix_text_y =
00378 (screen_area().h > txtsurf->h + 1) ?
00379 (std::max(0, screen_area().h - txtsurf->h - (storybox_padding+1))) :
00380 (0);
00381 }
00382 int fix_text_h;
00383 switch(tbl) {
00384 case part::BLOCK_TOP:
00385 fix_text_h = std::max(txtsurf->h + 2*storybox_padding, screen_area().h/4);
00386 break;
00387 case part::BLOCK_MIDDLE:
00388 fix_text_h = std::max(txtsurf->h + 2*storybox_padding, screen_area().h/3);
00389 break;
00390 default:
00391 fix_text_h = screen_area().h - fix_text_y;
00392 break;
00393 }
00394
00395 SDL_Rect update_area = create_rect(0
00396 , fix_text_y
00397 , screen_area().w
00398 , fix_text_h);
00399
00400 {
00401
00402
00403 update_locker locker(video_);
00404
00405 next_button_.hide();
00406 back_button_.hide();
00407 play_button_.hide();
00408
00409 #ifndef LOW_MEM
00410 blur_area(video_, fix_text_y, fix_text_h);
00411 #endif
00412
00413 draw_solid_tinted_rectangle(
00414 0, fix_text_y, screen_area().w, fix_text_h,
00415 storyshadow_r, storyshadow_g, storyshadow_b,
00416 storyshadow_opacity,
00417 video_.getSurface()
00418 );
00419
00420 render_story_box_borders(update_area);
00421
00422 next_button_.hide(false);
00423 back_button_.hide(false);
00424 play_button_.hide(false);
00425 }
00426
00427 if(imgs_.empty()) {
00428 update_whole_screen();
00429 } else if(update_area.h > 0) {
00430 update_rect(update_area);
00431 }
00432
00433
00434 const int scan_height = 1, scan_width = txtsurf->w;
00435 SDL_Rect scan = create_rect(0, 0, scan_width, scan_height);
00436 SDL_Rect dstrect = create_rect(text_x_, 0, scan_width, scan_height);
00437 surface scan_dst = video_.getSurface();
00438 bool scan_finished = false;
00439 while(true) {
00440 scan_finished = scan.y >= txtsurf->h;
00441 if (!scan_finished)
00442 {
00443
00444 dstrect.y = fix_text_y + scan.y + storybox_padding;
00445
00446
00447
00448
00449 video_.blit_surface(dstrect.x, dstrect.y, txtsurf, &scan);
00450 update_rect(dstrect);
00451 ++scan.y;
00452 }
00453 else skip_ = true;
00454
00455 if (handle_interface()) break;
00456
00457 if (!skip_ || scan_finished) {
00458 disp_.delay(20);
00459 }
00460 }
00461
00462 draw_solid_tinted_rectangle(
00463 0, 0, video_.getx(), video_.gety(), 0, 0, 0,
00464 1.0, video_.getSurface()
00465 );
00466 }
00467
00468 void part_ui::wait_for_input()
00469 {
00470 LOG_NG << "ENTER part_ui()::wait_for_input()\n";
00471
00472 last_key_ = true;
00473 skip_ = true;
00474 while (!handle_interface()) {
00475 disp_.delay(20);
00476 }
00477 }
00478
00479 bool part_ui::handle_interface()
00480 {
00481 bool result = false;
00482
00483 bool next_keydown = keys_[SDLK_SPACE] || keys_[SDLK_RETURN] ||
00484 keys_[SDLK_KP_ENTER] || keys_[SDLK_RIGHT];
00485 bool back_keydown = keys_[SDLK_BACKSPACE] || keys_[SDLK_LEFT];
00486 bool play_keydown = keys_[SDLK_ESCAPE];
00487
00488 if ((next_keydown && !last_key_) || next_button_.pressed())
00489 {
00490 next_button_.release();
00491 if (skip_) {
00492 ret_ = NEXT;
00493 result = true;
00494 } else {
00495 skip_ = true;
00496 }
00497 }
00498
00499 if ((play_keydown && !last_key_) || play_button_.pressed()) {
00500 play_button_.release();
00501 ret_ = QUIT;
00502 result = true;
00503 }
00504
00505 if ((back_keydown && !last_key_) || back_button_.pressed()) {
00506 back_button_.release();
00507 ret_ = BACK;
00508 result = true;
00509 }
00510
00511 last_key_ = next_keydown || back_keydown || play_keydown;
00512
00513 events::pump();
00514 events::raise_process_event();
00515 events::raise_draw_event();
00516 disp_.flip();
00517
00518 return result;
00519 }
00520
00521 part_ui::RESULT part_ui::show()
00522 {
00523 if(p_.music().empty() != true) {
00524 sound::play_music_repeatedly(p_.music());
00525 }
00526
00527 if(p_.sound().empty() != true) {
00528 sound::play_sound(p_.sound());
00529 }
00530
00531 render_background();
00532
00533 if(p_.show_title()) {
00534 render_title_box();
00535 }
00536
00537 if(!imgs_.empty()) {
00538 if(!render_floating_images()) {
00539 return ret_;
00540 }
00541 }
00542
00543 try {
00544 render_story_box();
00545 }
00546 catch(utils::invalid_utf8_exception const&) {
00547 ERR_NG << "invalid UTF-8 sequence in story text, skipping part...\n";
00548 }
00549
00550 return ret_;
00551 }
00552
00553 }