diff --git a/data/themes/default.cfg b/data/themes/default.cfg index ccd489647c7..f7dca58004a 100644 --- a/data/themes/default.cfg +++ b/data/themes/default.cfg @@ -145,15 +145,21 @@ height=600 xanchor=right yanchor=fixed [/unit_traits] - [unit_status] + [unit_abilities] font_size=14 rect=897,514,1024,530 xanchor=right yanchor=fixed + [/unit_abilities] + [unit_status] + font_size=14 + rect=897,530,1024,546 + xanchor=right + yanchor=fixed [/unit_status] [unit_moves] font_size=14 - rect=897,530,1024,546 + rect=897,546,1024,562 prefix=movement prefix_literal=": " xanchor=right @@ -161,7 +167,7 @@ height=600 [/unit_moves] [unit_hp] font_size=14 - rect=897,546,1024,562 + rect=897,562,1024,578 prefix=hp prefix_literal=": " xanchor=right @@ -169,7 +175,7 @@ height=600 [/unit_hp] [unit_xp] font_size=14 - rect=897,562,1024,578 + rect=897,578,1024,594 prefix=xp prefix_literal=": " xanchor=right @@ -177,7 +183,7 @@ height=600 [/unit_xp] [unit_weapons] font_size=12 - rect=897,578,1024,690 + rect=897,594,1024,706 xanchor=right yanchor=fixed [/unit_weapons] diff --git a/data/translations/english.cfg b/data/translations/english.cfg index 1445d97f5f2..5ee9464a41a 100644 --- a/data/translations/english.cfg +++ b/data/translations/english.cfg @@ -125,6 +125,58 @@ EASY="Fighter (easy)" NORMAL="*Hero (medium)" HARD="Champion (hard)" +lawful_description="Lawful units fight better at day, and worse at night. + +Day: +25% Damage +Night: -25% Damage" +neutral_description="Neutral units are unaffected by day and night, fighting equally well under both conditions." +chaotic_description="Chaotic units fight better at night, and worse at day. + +Day: -25% Damage +Night: +25% Damage" + +invisible_description="This unit is invisible. It cannot be seen or attacked by enemy units." +slowed_description="This unit has been slowed. It moves at half normal speed and receives one less attack than normal in combat." +poisoned_description="This unit is poisoned. It will lose 8 HP every turn until it can seek a cure to the poison in a village or from a friendly unit with the 'cures' ability. + +Units cannot be killed by poison alone. The poison will not reduce it below 1 HP." + +heals_description="Heals: +Allows the unit to heal adjacent friendly units at the beginning of the turn. + +A unit cared for by a healer may heal up to 4 HP per turn. +A healer may heal a total of 8 HP per turn, for all units it cares for. +A poisoned unit cannot be cured of its poison by a healer, and must seek the care of a village or a unit that can cure." + +cures_description="Cures: +This unit combines herbal remedies with magic to heal units more quickly than is normally possible on the battlefield. + +This unit will care for all adjacent friendly units that are injured at the beginning of the turn. +A unit cared for by a curer may heal up to 8 HP per turn. +A curer may heal a total of 18 HP per turn, for all units it cares for. +A curer can cure a unit of poison, although that unit will receive no additional healing on the turn it is cured of the poison." + +teleport_description="Teleport: +This unit may teleport between any two friendly villages instantly." + +leadership_description="Leadership: +This unit can lead friendly units that are next to it, making them fight better. + +Adjacent friendly units of lower level will do more damage in battle." + +ambush_description="Ambush: +This unit can hide in forest, and remain undetected by its enemies. + +Enemy units cannot see or attack this unit when it is in forest, except for any turn immediately after this unit has attacked." + +illuminates_description="Illuminates: +This unit illuminates the surrounding area, making lawful units fight better, and chaotic units fight worse. + +Any units adjacent to this unit will fight as if it were dusk when it is night, and as if it were day when it is dusk." + +skirmisher_description="Skirmishes: +This unit is skilled in moving past enemies quickly, and ignores all Enemy Zones of Control." + no_leader_to_recruit="You don't have a leader to recruit with." leader_not_on_start="You must have your leader on a keep to recruit or recall units." no_recruit_location="There are no vacant castle tiles in which to recruit a unit." diff --git a/src/display.cpp b/src/display.cpp index f30134a5c1a..6980ff7f0bb 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -751,6 +751,8 @@ void display::draw_report(reports::TYPE report_num) tooltips::clear_tooltips(rect); + SDL_Rect area = rect; + if(report.text.empty() == false) { std::string str = item->prefix(); @@ -764,7 +766,11 @@ void display::draw_report(reports::TYPE report_num) str += report.text.substr(nchop) + item->postfix(); - font::draw_text(this,rect,item->font_size(),font::NORMAL_COLOUR,str,rect.x,rect.y); + area = font::draw_text(this,rect,item->font_size(),font::NORMAL_COLOUR,str,rect.x,rect.y); + } + + if(report.tooltip.empty() == false) { + tooltips::add_tooltip(area,report.tooltip); } if(report.image.empty() == false) { @@ -816,7 +822,6 @@ void display::draw_unit_details(int x, int y, const gamemap::location& loc, if(teams_.empty()) return; - tooltips::clear_tooltips(description_rect); SDL_Rect clipRect = clip_rect != NULL ? *clip_rect : screen_area(); @@ -858,7 +863,7 @@ void display::draw_unit_details(int x, int y, const gamemap::location& loc, << "\n-(" << string_table["level"] << " " << u.type().level() << ")\n" << status << "\n" - << unit_type::alignment_description(u.type().alignment()) + << translate_string(unit_type::alignment_description(u.type().alignment())) << "\n" << u.traits_description() << "\n"; diff --git a/src/reports.cpp b/src/reports.cpp index 1b468a552b4..42f202f529a 100644 --- a/src/reports.cpp +++ b/src/reports.cpp @@ -8,8 +8,8 @@ namespace { const std::string report_names[] = { "unit_description", "unit_type", "unit_level", - "unit_traits","unit_status","unit_alignment","unit_hp","unit_xp","unit_moves", - "unit_weapons","unit_image","unit_profile","time_of_day", + "unit_traits","unit_status","unit_alignment","unit_abilities","unit_hp","unit_xp", + "unit_moves","unit_weapons","unit_image","unit_profile","time_of_day", "turn","gold","villages","num_units","upkeep", "expenses", "income", "terrain", "position" }; } @@ -46,24 +46,55 @@ report generate_report(TYPE type, const gamemap& map, const unit_map& units, switch(type) { case UNIT_DESCRIPTION: return report(u->second.description()); - case UNIT_TYPE: - return report(u->second.type().language_name()); + case UNIT_TYPE: { + report res(u->second.type().language_name()); + res.tooltip = u->second.type().unit_description(); + return res; + } case UNIT_LEVEL: str << u->second.type().level(); break; case UNIT_TRAITS: return report(u->second.traits_description()); - case UNIT_STATUS: - if(map.on_board(loc) && u->second.invisible(map.underlying_terrain(map[loc.x][loc.y]))) - return report("@" + string_table["invisible"]); - else if(u->second.has_flag("slowed")) - return report("#" + string_table["slowed"]); - else if(u->second.has_flag("poisoned")) + case UNIT_STATUS: { + std::string status = "healthy", prefix = ""; + if(map.on_board(loc) && u->second.invisible(map.underlying_terrain(map[loc.x][loc.y]))) { + status = "invisible"; + prefix = "@"; + } else if(u->second.has_flag("slowed")) { + status = "slowed"; + prefix = "#"; return report("#" + string_table["poisoned"]); - else - return report(string_table["healthy"]); - case UNIT_ALIGNMENT: - return report(unit_type::alignment_description(u->second.type().alignment())); + } else if(u->second.has_flag("poisoned")) { + status = "poisoned"; + prefix = "#"; + } + + report res(prefix + string_table[status]); + res.tooltip = string_table[status + "_description"]; + return res; + } + case UNIT_ALIGNMENT: { + const std::string& align = unit_type::alignment_description(u->second.type().alignment()); + report res(translate_string(align)); + res.tooltip = string_table[align + "_description"]; + return res; + } + case UNIT_ABILITIES: { + std::stringstream tooltip; + const std::vector& abilities = u->second.type().abilities(); + for(std::vector::const_iterator i = abilities.begin(); i != abilities.end(); ++i) { + str << translate_string(*i); + if(i+1 != abilities.end()) + str << ","; + + tooltip << string_table[*i + "_description"] << "\n\n"; + } + + report res(str.str()); + res.tooltip = tooltip.str(); + return res; + } case UNIT_HP: if(u->second.hitpoints() <= u->second.max_hitpoints()/3) str << "#"; @@ -118,8 +149,20 @@ report generate_report(TYPE type, const gamemap& map, const unit_map& units, return report("",u->second.type().image()); case UNIT_PROFILE: return report("",u->second.type().image_profile()); - case TIME_OF_DAY: - return report("",timeofday_at(status,units,mouseover).image); + case TIME_OF_DAY: { + const time_of_day& tod = timeofday_at(status,units,mouseover); + report res("",tod.image); + std::stringstream tooltip; + + tooltip << "+" << translate_string_default(tod.id,tod.name) << "\n" + << translate_string("lawful") << " " << string_table["units"] << ": " + << (tod.lawful_bonus > 0 ? "+" : "") << tod.lawful_bonus << "%\n" + << translate_string("neutral") << " " << string_table["units"] << ": " << "0%\n" + << translate_string("chaotic") << " " << string_table["units"] << ": " + << (tod.lawful_bonus < 0 ? "+" : "") << (tod.lawful_bonus*-1) << "%"; + res.tooltip = tooltip.str(); + return res; + } case TURN: str << status.turn() << "/" << status.number_of_turns() << "\n"; break; diff --git a/src/reports.hpp b/src/reports.hpp index 89a3a375eda..87da1496a57 100644 --- a/src/reports.hpp +++ b/src/reports.hpp @@ -13,7 +13,7 @@ namespace reports { enum TYPE { UNIT_DESCRIPTION, UNIT_TYPE, UNIT_LEVEL, UNIT_TRAITS, UNIT_STATUS, - UNIT_ALIGNMENT, UNIT_HP, UNIT_XP, UNIT_MOVES, UNIT_WEAPONS, + UNIT_ALIGNMENT, UNIT_ABILITIES, UNIT_HP, UNIT_XP, UNIT_MOVES, UNIT_WEAPONS, UNIT_IMAGE, UNIT_PROFILE, TIME_OF_DAY, TURN, GOLD, VILLAGES, NUM_UNITS, UPKEEP, EXPENSES, INCOME, TERRAIN, POSITION, NUM_REPORTS}; @@ -28,9 +28,9 @@ namespace reports { explicit report(const std::string& text) : text(text) {} report(const std::string& text, const std::string& image) : text(text), image(image) {} bool empty() const { return text.empty() && image.empty(); } - bool operator==(const report& o) const { return o.text == text && o.image == image; } + bool operator==(const report& o) const { return o.text == text && o.image == image && o.tooltip == tooltip; } bool operator!=(const report& o) const { return !(o == *this); } - std::string text, image; + std::string text, image, tooltip; }; report generate_report(TYPE type, const gamemap& map, const unit_map& units, diff --git a/src/show_dialog.cpp b/src/show_dialog.cpp index 382de4df81b..4583e9ffd5c 100644 --- a/src/show_dialog.cpp +++ b/src/show_dialog.cpp @@ -180,6 +180,27 @@ void draw_solid_tinted_rectangle(int x, int y, int w, int h, namespace gui { +size_t text_to_lines(std::string& message, size_t max_length) +{ + size_t cur_line = 0, longest_line = 0; + for(std::string::iterator i = message.begin(); i != message.end(); ++i) { + if(*i == ' ' && cur_line > max_length) + *i = '\n'; + + if(*i == '\n' || i+1 == message.end()) { + if(cur_line > longest_line) + longest_line = cur_line; + + cur_line = 0; + + } else { + ++cur_line; + } + } + + return longest_line; +} + int show_dialog(display& disp, SDL_Surface* image, const std::string& caption, const std::string& msg, DIALOG_TYPE type, @@ -229,29 +250,11 @@ int show_dialog(display& disp, SDL_Surface* image, menu menu_(disp,menu_items,type == MESSAGE); const int border_size = 6; - int nlines = 1; - int longest_line = 0; - int cur_line = 0; const int max_line_length = 58; std::string message = msg; - for(std::string::iterator message_it = message.begin(); - message_it != message.end(); ++message_it) { - if(*message_it == ' ' && cur_line > max_line_length) - *message_it = '\n'; - - if(*message_it == '\n') { - if(cur_line > longest_line) - longest_line = cur_line; - - cur_line = 0; - - ++nlines; - } else { - ++cur_line; - } - } + const size_t longest_line = text_to_lines(message,max_line_length); SDL_Rect text_size = { 0, 0, 0, 0 }; if(!message.empty()) { @@ -324,9 +327,6 @@ int show_dialog(display& disp, SDL_Surface* image, button_heights += button_height_padding; } - if(cur_line > longest_line) - longest_line = cur_line; - int check_button_height = 0; int check_button_width = 0; diff --git a/src/show_dialog.hpp b/src/show_dialog.hpp index c917177ceb9..75fe1336a17 100644 --- a/src/show_dialog.hpp +++ b/src/show_dialog.hpp @@ -63,6 +63,9 @@ struct check_item { bool checked; }; +//function to chop up one long string of text into lines +size_t text_to_lines(std::string& text, size_t max_length); + //if a menu is given, then returns -1 if the dialog was cancelled, and the //index of the selection otherwise. If no menu is given, returns the index //of the button that was pressed diff --git a/src/tooltips.cpp b/src/tooltips.cpp index fc12dff36a9..79254d62140 100644 --- a/src/tooltips.cpp +++ b/src/tooltips.cpp @@ -24,7 +24,9 @@ display* display_ = NULL; struct tooltip { tooltip(const SDL_Rect& r, const std::string& msg) : rect(r), message(msg) - {} + { + gui::text_to_lines(message,60); + } SDL_Rect rect; std::string message; }; @@ -143,7 +145,7 @@ void clear_tooltips() void clear_tooltips(const SDL_Rect& rect) { - clear_tooltips(); + clear_tooltip(); for(std::vector::iterator i = tips.begin(); i != tips.end(); ) { if(rectangles_overlap(i->rect,rect)) { i = tips.erase(i); @@ -169,8 +171,7 @@ void add_tooltip(const SDL_Rect& rect, const std::string& message) void process(int mousex, int mousey, bool lbutton) { - for(std::vector::const_iterator i = tips.begin(); - i != tips.end(); ++i) { + for(std::vector::const_iterator i = tips.begin(); i != tips.end(); ++i) { if(mousex > i->rect.x && mousey > i->rect.y && mousex < i->rect.x + i->rect.w && mousey < i->rect.y + i->rect.h) { show_tooltip(*i); diff --git a/src/unit_types.cpp b/src/unit_types.cpp index d294a35cb6f..b978e9fd8d3 100644 --- a/src/unit_types.cpp +++ b/src/unit_types.cpp @@ -611,11 +611,7 @@ unit_type::ALIGNMENT unit_type::alignment() const const std::string& unit_type::alignment_description(unit_type::ALIGNMENT align) { static const std::string aligns[] = { "lawful", "neutral", "chaotic" }; - const string_map::const_iterator i = string_table.find(aligns[align]); - if(i != string_table.end()) - return i->second; - else - return aligns[align]; + return aligns[align]; } double unit_type::alpha() const