New grow and skid sounds from remaxim
[supertux.git] / src / textscroller.cpp
index 85b7c08..b460119 100644 (file)
@@ -41,24 +41,22 @@ static const float LEFT_BORDER = 50;
 static const float SCROLL = 60;
 static const float ITEMS_SPACE = 4;
 
-
-
 TextScroller::TextScroller(const std::string& filename)
 {
   defaultspeed = DEFAULT_SPEED;
   speed = defaultspeed;
-  
+
   std::string text;
   std::string background_file;
 
   lisp::Parser parser;
   try {
-    std::auto_ptr<lisp::Lisp> root (parser.parse(filename));
+    const lisp::Lisp* root = parser.parse(filename);
 
     const lisp::Lisp* text_lisp = root->get_lisp("supertux-text");
     if(!text_lisp)
       throw std::runtime_error("File isn't a supertux-text file");
-    
+
     if(!text_lisp->get("text", text))
       throw std::runtime_error("file doesn't contain a text field");
     if(!text_lisp->get("background", background_file))
@@ -72,12 +70,13 @@ TextScroller::TextScroller(const std::string& filename)
   }
 
   // Split text string lines into a vector
-  lines = InfoBoxLine::split(text, 40);
+  lines = InfoBoxLine::split(text, SCREEN_WIDTH - 2*LEFT_BORDER);
 
   // load background image
   background.reset(new Surface("images/background/" + background_file));
 
   scroll = 0;
+  fading = false;
 }
 
 TextScroller::~TextScroller()
@@ -89,7 +88,6 @@ void
 TextScroller::setup()
 {
   sound_manager->play_music(music);
-  Menu::set_current(NULL);
 }
 
 void
@@ -105,13 +103,13 @@ TextScroller::update(float elapsed_time)
   if(main_controller->pressed(Controller::JUMP)
       || main_controller->pressed(Controller::ACTION)
       || main_controller->pressed(Controller::MENU_SELECT))
-    scroll += SCROLL;    
+    scroll += SCROLL;
   if(main_controller->pressed(Controller::PAUSE_MENU)) {
     main_loop->exit_screen(new FadeOut(0.5));
   }
 
   scroll += speed * elapsed_time;
-    
+
   if(scroll < 0)
     scroll = 0;
 }
@@ -119,15 +117,21 @@ TextScroller::update(float elapsed_time)
 void
 TextScroller::draw(DrawingContext& context)
 {
-  context.draw_surface(background.get(), Vector(0,0), 0);
+  context.draw_filled_rect(Vector(0, 0), Vector(SCREEN_WIDTH, SCREEN_HEIGHT),
+      Color(0.6f, 0.7f, 0.8f, 0.5f), 0);
+  context.draw_surface(background.get(), Vector(SCREEN_WIDTH/2 - background->get_width()/2 , SCREEN_HEIGHT/2 - background->get_height()/2), 0);
 
   float y = SCREEN_HEIGHT - scroll;
   for(size_t i = 0; i < lines.size(); i++) {
-    lines[i]->draw(context, Vector(LEFT_BORDER, y), LAYER_GUI);
+    if (y + lines[i]->get_height() >= 0 && SCREEN_HEIGHT - y >= 0) {
+        lines[i]->draw(context, Rect(LEFT_BORDER, y, SCREEN_WIDTH - 2*LEFT_BORDER, y), LAYER_GUI);
+    }
+
     y += lines[i]->get_height();
   }
 
-  if(y < 0) {
+  if(y < 0 && !fading ) {
+    fading = true;
     main_loop->exit_screen(new FadeOut(0.5));
   }
 }
@@ -136,7 +140,7 @@ InfoBox::InfoBox(const std::string& text)
   : firstline(0)
 {
   // Split text string lines into a vector
-  lines = InfoBoxLine::split(text, 23);
+  lines = InfoBoxLine::split(text, 400);
 
   try
   {
@@ -154,7 +158,9 @@ InfoBox::InfoBox(const std::string& text)
 
 InfoBox::~InfoBox()
 {
-  for(std::vector<InfoBoxLine*>::iterator i = lines.begin(); i != lines.end(); i++) delete *i;
+  for(std::vector<InfoBoxLine*>::iterator i = lines.begin();
+      i != lines.end(); i++)
+    delete *i;
   delete arrow_scrollup;
   delete arrow_scrolldown;
 }
@@ -162,8 +168,8 @@ InfoBox::~InfoBox()
 void
 InfoBox::draw(DrawingContext& context)
 {
-  float x1 = 200;
-  float y1 = 100;
+  float x1 = SCREEN_WIDTH/2-200;
+  float y1 = SCREEN_HEIGHT/2-200;
   float width = 400;
   float height = 200;
 
@@ -171,19 +177,25 @@ InfoBox::draw(DrawingContext& context)
       Color(0.6f, 0.7f, 0.8f, 0.5f), LAYER_GUI-1);
 
   float y = y1;
+  bool linesLeft = false;
   for(size_t i = firstline; i < lines.size(); ++i) {
-    if(y >= y1 + height) break;
+    if(y >= y1 + height) {
+      linesLeft = true;
+      break;
+    }
 
-    lines[i]->draw(context, Vector(x1, y), LAYER_GUI);
+    lines[i]->draw(context, Rect(x1, y, x1+width, y), LAYER_GUI);
     y += lines[i]->get_height();
+  }
 
+  {
     // draw the scrolling arrows
     if (arrow_scrollup && firstline > 0)
       context.draw_surface(arrow_scrollup,
       Vector( x1 + width  - arrow_scrollup->get_width(),  // top-right corner of box
               y1), LAYER_GUI);
 
-    if (arrow_scrolldown && firstline < lines.size()-1)
+    if (arrow_scrolldown && linesLeft && firstline < lines.size()-1)
       context.draw_surface(arrow_scrolldown,
       Vector( x1 + width  - arrow_scrolldown->get_width(),  // bottom-light corner of box
               y1 + height - arrow_scrolldown->get_height()),
@@ -215,47 +227,96 @@ InfoBox::pagedown()
 {
 }
 
-InfoBoxLine::InfoBoxLine(char format_char, const std::string& text) : lineType(NORMAL), font(white_text), text(text), image(0)
-{
+namespace {
+Font* get_font_by_format_char(char format_char) {
   switch(format_char)
   {
-    case ' ': 
-      lineType = SMALL;
-      font = white_small_text;
+    case ' ':
+      return small_font;
+      break;
+    case '-':
+      return big_font;
       break;
-    case '\t': 
-      lineType = NORMAL;
-      font = white_text;
+    case '\t':
+    case '*':
+    case '#':
+    case '!':
+      return normal_font;
       break;
-    case '-': 
-      lineType = HEADING;
-      font = white_big_text;
+    default:
+      return normal_font;
+      log_warning << "Unknown format_char: '" << format_char << "'" << std::endl;
       break;
-    case '*': 
-      lineType = REFERENCE;
-      font = blue_text;
+  }
+}
+
+Color get_color_by_format_char(char format_char) {
+  switch(format_char)
+  {
+    case ' ':
+      return TextScroller::small_color;
       break;
-    case '#': 
-      lineType = NORMAL_LEFT;
-      font = white_text;
+    case '-':
+      return TextScroller::heading_color;
       break;
-    case '!': 
-      lineType = IMAGE;
-      image = new Surface(text);
+    case '*':
+      return TextScroller::reference_color;
+    case '\t':
+    case '#':
+    case '!':
+      return TextScroller::normal_color;
       break;
     default:
+      return Color(0,0,0);
       log_warning << "Unknown format_char: '" << format_char << "'" << std::endl;
       break;
   }
 }
 
+InfoBoxLine::LineType get_linetype_by_format_char(char format_char) {
+  switch(format_char)
+  {
+    case ' ':
+      return InfoBoxLine::SMALL;
+      break;
+    case '\t':
+      return InfoBoxLine::NORMAL;
+      break;
+    case '-':
+      return InfoBoxLine::HEADING;
+      break;
+    case '*':
+      return InfoBoxLine::REFERENCE;
+      break;
+    case '#':
+      return InfoBoxLine::NORMAL_LEFT;
+      break;
+    case '!':
+      return InfoBoxLine::IMAGE;
+      break;
+    default:
+      return InfoBoxLine::SMALL;
+      log_warning << "Unknown format_char: '" << format_char << "'" << std::endl;
+      break;
+  }
+}
+}
+
+InfoBoxLine::InfoBoxLine(char format_char, const std::string& text) : lineType(NORMAL), font(normal_font), text(text), image(0)
+{
+  font = get_font_by_format_char(format_char);
+  lineType = get_linetype_by_format_char(format_char);
+  color = get_color_by_format_char(format_char);
+  if (lineType == IMAGE) image = new Surface(text);
+}
+
 InfoBoxLine::~InfoBoxLine()
 {
   delete image;
 }
 
-const std::vector<InfoBoxLine*> 
-InfoBoxLine::split(const std::string& text, int line_length)
+const std::vector<InfoBoxLine*>
+InfoBoxLine::split(const std::string& text, float width)
 {
   std::vector<InfoBoxLine*> lines;
 
@@ -263,7 +324,14 @@ InfoBoxLine::split(const std::string& text, int line_length)
   std::string::size_type l;
   char format_char = '#';
   while(i < text.size()) {
+    // take care of empty lines - represent them as blank lines of normal text
+    if (text[i] == '\n') {
+      lines.push_back(new InfoBoxLine('\t', ""));
+      i++;
+      continue;
+    }
 
+    // extract the format_char
     format_char = text[i];
     i++;
     if (i >= text.size()) break;
@@ -278,38 +346,35 @@ InfoBoxLine::split(const std::string& text, int line_length)
     if (format_char == '!') {
       lines.push_back(new InfoBoxLine(format_char, s));
       continue;
-    } 
-
-    // if we are dealing with text, wrap long lines
-    while ((int)s.length() > line_length) {
-      int split_at = line_length;
-      while ((split_at > 0) && (s[split_at] != ' ')) split_at--;
-      if (split_at == 0) split_at = line_length;
-
-      lines.push_back(new InfoBoxLine(format_char, s.substr(0, split_at)));
-      if (s[split_at] == ' ') split_at++;
-      s = s.substr(split_at);
     }
-    lines.push_back(new InfoBoxLine(format_char, s));
 
+    // append wrapped parts of line into list
+    std::string overflow;
+    do {
+      Font* font = get_font_by_format_char(format_char);
+      std::string s2 = s;
+      if (font) s2 = font->wrap_to_width(s2, width, &overflow);
+      lines.push_back(new InfoBoxLine(format_char, s2));
+      s = overflow;
+    } while (s.length() > 0);
   }
 
   return lines;
 }
 
-void 
-InfoBoxLine::draw(DrawingContext& context, const Vector& position, int layer)
+void
+InfoBoxLine::draw(DrawingContext& context, const Rect& bbox, int layer)
 {
+  Vector position = bbox.p1;
   switch (lineType) {
-    case SPACER:
     case IMAGE:
-      context.draw_surface(image, Vector( (SCREEN_WIDTH - image->get_width()) / 2, position.y), layer);
+      context.draw_surface(image, Vector( (bbox.p1.x + bbox.p2.x - image->get_width()) / 2, position.y), layer);
       break;
     case NORMAL_LEFT:
-      context.draw_text(font, text, Vector(position.x, position.y), LEFT_ALLIGN, layer);
+      context.draw_text(font, text, Vector(position.x, position.y), ALIGN_LEFT, layer, color);
       break;
-    default: 
-      context.draw_text(font, text, Vector(SCREEN_WIDTH/2, position.y), CENTER_ALLIGN, layer);
+    default:
+      context.draw_text(font, text, Vector((bbox.p1.x + bbox.p2.x) / 2, position.y), ALIGN_CENTER, layer, color);
       break;
   }
 }
@@ -318,14 +383,11 @@ float
 InfoBoxLine::get_height()
 {
   switch (lineType) {
-    case SPACER:
-      return font->get_height() + ITEMS_SPACE;    
     case IMAGE:
       return image->get_height() + ITEMS_SPACE;
     case NORMAL_LEFT:
       return font->get_height() + ITEMS_SPACE;
-    default: 
+    default:
       return font->get_height() + ITEMS_SPACE;
   }
 }
-