00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #include "global.hpp"
00017
00018 #include "actions.hpp"
00019 #include "attack_prediction.hpp"
00020 #include "editor/editor_controller.hpp"
00021 #include "editor/palette/terrain_palettes.hpp"
00022 #include "font.hpp"
00023 #include "foreach.hpp"
00024 #include "game_display.hpp"
00025 #include "game_preferences.hpp"
00026 #include "gettext.hpp"
00027 #include "language.hpp"
00028 #include "map.hpp"
00029 #include "marked-up_text.hpp"
00030 #include "play_controller.hpp"
00031 #include "reports.hpp"
00032 #include "resources.hpp"
00033 #include "team.hpp"
00034 #include "text.hpp"
00035 #include "tod_manager.hpp"
00036 #include "unit.hpp"
00037 #include "whiteboard/manager.hpp"
00038
00039
00040 #include <cassert>
00041 #include <ctime>
00042
00043 static void add_text(config &report, const std::string &text,
00044 const std::string &tooltip, const std::string &help = "")
00045 {
00046 config &element = report.add_child("element");
00047 element["text"] = text;
00048 if (!tooltip.empty()) element["tooltip"] = tooltip;
00049 if (!help.empty()) element["help"] = help;
00050 }
00051
00052 static void add_image(config &report, const std::string &image,
00053 const std::string &tooltip, const std::string &help = "")
00054 {
00055 config &element = report.add_child("element");
00056 element["image"] = image;
00057 if (!tooltip.empty()) element["tooltip"] = tooltip;
00058 if (!help.empty()) element["help"] = help;
00059 }
00060
00061 static config report()
00062 {
00063 return config();
00064 }
00065
00066 static config text_report(const std::string &text,
00067 const std::string &tooltip = "", const std::string &help = "")
00068 {
00069 config r;
00070 add_text(r, text, tooltip, help);
00071 return r;
00072 }
00073
00074 static config image_report(const std::string &image,
00075 const std::string &tooltip = "", const std::string &help = "")
00076 {
00077 config r;
00078 add_image(r, image, tooltip, help);
00079 return r;
00080 }
00081
00082 using font::span_color;
00083
00084 static void add_status(config &r,
00085 char const *path, char const *desc1, char const *desc2)
00086 {
00087 std::ostringstream s;
00088 s << gettext(desc1) << gettext(desc2);
00089 add_image(r, path, s.str());
00090 }
00091
00092 static std::string flush(std::ostringstream &s)
00093 {
00094 std::string r(s.str());
00095 s.str(std::string());
00096 return r;
00097 }
00098
00099 typedef config (*generator_function)();
00100
00101 typedef std::map<std::string, generator_function> static_report_generators;
00102 typedef std::map<std::string, reports::generator *> dynamic_report_generators;
00103 static static_report_generators static_generators;
00104 static dynamic_report_generators dynamic_generators;
00105
00106 struct report_generator_helper
00107 {
00108 report_generator_helper(const char *name, generator_function g)
00109 {
00110 static_generators.insert(static_report_generators::value_type(name, g));
00111 }
00112 };
00113
00114 #define REPORT_GENERATOR(n) \
00115 static config report_##n(); \
00116 static report_generator_helper reg_gen_##n(#n, &report_##n); \
00117 static config report_##n()
00118
00119 static char const *naps = "</span>";
00120
00121 static unit *get_visible_unit()
00122 {
00123 return get_visible_unit(resources::screen->displayed_unit_hex(),
00124 (*resources::teams)[resources::screen->viewing_team()],
00125 resources::screen->show_everything());
00126 }
00127
00128 static unit *get_selected_unit()
00129 {
00130 return get_visible_unit(resources::screen->selected_hex(),
00131 (*resources::teams)[resources::screen->viewing_team()],
00132 resources::screen->show_everything());
00133 }
00134
00135 static config gray_inactive(const std::string &str)
00136 {
00137 if (resources::screen->viewing_side() == resources::screen->playing_side())
00138 return text_report(str);
00139 return text_report(span_color(font::GRAY_COLOR) + str + naps);
00140 }
00141
00142 static config unit_name(unit *u)
00143 {
00144 if (!u) {
00145 return report();
00146 }
00147
00148
00149
00150
00151
00152 const std::string& name = font::escape_text(u->name());
00153 std::ostringstream str, tooltip;
00154 str << "<b>" << name << "</b>";
00155 tooltip << _("Name: ") << "<b>" << name << "</b>";
00156 return text_report(str.str(), tooltip.str());
00157 }
00158
00159 REPORT_GENERATOR(unit_name)
00160 {
00161 unit *u = get_visible_unit();
00162 return unit_name(u);
00163 }
00164 REPORT_GENERATOR(selected_unit_name)
00165 {
00166 unit *u = get_selected_unit();
00167 return unit_name(u);
00168 }
00169
00170 static config unit_type(unit* u)
00171 {
00172 if (!u) return report();
00173 std::ostringstream str, tooltip;
00174 str << span_color(font::unit_type_color) << u->type_name() << naps;
00175 tooltip << _("Type: ") << "<b>" << u->type_name() << "</b>\n"
00176 << u->unit_description();
00177 return text_report(str.str(), tooltip.str(), "unit_" + u->type_id());
00178 }
00179 REPORT_GENERATOR(unit_type)
00180 {
00181 unit *u = get_visible_unit();
00182 return unit_type(u);
00183 }
00184 REPORT_GENERATOR(selected_unit_type)
00185 {
00186 unit *u = get_selected_unit();
00187 return unit_type(u);
00188 }
00189
00190 static config unit_race(unit* u)
00191 {
00192 if (!u) return report();
00193 std::ostringstream str, tooltip;
00194 str << span_color(font::race_color) << u->race()->name(u->gender()) << naps;
00195 tooltip << _("Race: ") << "<b>" << u->race()->name(u->gender()) << "</b>";
00196 return text_report(str.str(), tooltip.str(), "..race_" + u->race()->id());
00197 }
00198 REPORT_GENERATOR(unit_race)
00199 {
00200 unit *u = get_visible_unit();
00201 return unit_race(u);
00202 }
00203 REPORT_GENERATOR(selected_unit_race)
00204 {
00205 unit *u = get_selected_unit();
00206 return unit_race(u);
00207 }
00208
00209 static config unit_side(unit* u)
00210 {
00211 if (!u) return report();
00212 const team &u_team = (*resources::teams)[u->side() - 1];
00213 std::string flag_icon = u_team.flag_icon();
00214 std::string old_rgb = game_config::flag_rgb;
00215 std::string new_rgb = team::get_side_color_index(u->side());
00216 std::string mods = "~RC(" + old_rgb + ">" + new_rgb + ")";
00217 if (flag_icon.empty())
00218 flag_icon = game_config::images::flag_icon;
00219 return image_report(flag_icon + mods, u_team.current_player());
00220 }
00221 REPORT_GENERATOR(unit_side)
00222 {
00223 unit *u = get_visible_unit();
00224 return unit_side(u);
00225 }
00226 REPORT_GENERATOR(selected_unit_side)
00227 {
00228 unit *u = get_selected_unit();
00229 return unit_side(u);
00230 }
00231
00232 static config unit_level(unit* u)
00233 {
00234 if (!u) return report();
00235 std::ostringstream str, tooltip;
00236 str << u->level();
00237 tooltip << _("Level: ") << "<b>" << u->level() << "</b>\n";
00238 const std::vector<std::string> &adv_to = u->advances_to_translated();
00239 if (adv_to.empty())
00240 tooltip << _("No advancement");
00241 else
00242 tooltip << _("Advances to:") << "\n<b>\t"
00243 << utils::join(adv_to, "\n\t") << "</b>";
00244 return text_report(str.str(), tooltip.str());
00245 }
00246 REPORT_GENERATOR(unit_level)
00247 {
00248 unit *u = get_visible_unit();
00249 return unit_level(u);
00250 }
00251 REPORT_GENERATOR(selected_unit_level)
00252 {
00253 unit *u = get_selected_unit();
00254 return unit_level(u);
00255 }
00256
00257 REPORT_GENERATOR(unit_amla)
00258 {
00259 unit *u = get_visible_unit();
00260 if (!u) return report();
00261 config res;
00262 typedef std::pair<std::string, std::string> pair_string;
00263 foreach(const pair_string &ps, u->amla_icons()) {
00264 add_image(res, ps.first, ps.second);
00265 }
00266 return res;
00267 }
00268
00269 static config unit_traits(unit* u)
00270 {
00271
00272 if (!u) return report();
00273 config res;
00274 const std::vector<t_string> &traits = u->trait_names();
00275 const std::vector<t_string> &descriptions = u->trait_descriptions();
00276 unsigned nb = traits.size();
00277 for (unsigned i = 0; i < nb; ++i)
00278 {
00279 std::ostringstream str, tooltip;
00280 str << traits[i];
00281 if (i != nb - 1 ) str << ", ";
00282 tooltip << _("Trait: ") << "<b>" << traits[i] << "</b>\n"
00283 << descriptions[i];
00284 add_text(res, str.str(), tooltip.str());
00285 }
00286 return res;
00287 }
00288 REPORT_GENERATOR(unit_traits)
00289 {
00290 unit *u = get_visible_unit();
00291 return unit_traits(u);
00292 }
00293 REPORT_GENERATOR(selected_unit_traits)
00294 {
00295 unit *u = get_selected_unit();
00296 return unit_traits(u);
00297 }
00298
00299 static config unit_status(unit* u)
00300 {
00301 if (!u) return report();
00302 config res;
00303 map_location displayed_unit_hex = resources::screen->displayed_unit_hex();
00304 if (resources::game_map->on_board(displayed_unit_hex) && u->invisible(displayed_unit_hex)) {
00305 add_status(res, "misc/invisible.png", N_("invisible: "),
00306 N_("This unit is invisible. It cannot be seen or attacked by enemy units."));
00307 }
00308 if (u->get_state(unit::STATE_SLOWED)) {
00309 add_status(res, "misc/slowed.png", N_("slowed: "),
00310 N_("This unit has been slowed. It will only deal half its normal damage when attacking and its movement cost is doubled."));
00311 }
00312 if (u->get_state(unit::STATE_POISONED)) {
00313 add_status(res, "misc/poisoned.png", N_("poisoned: "),
00314 N_("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.\n\nUnits cannot be killed by poison alone. The poison will not reduce it below 1 HP."));
00315 }
00316 if (u->get_state(unit::STATE_PETRIFIED)) {
00317 add_status(res, "misc/petrified.png", N_("petrified: "),
00318 N_("This unit has been petrified. It may not move or attack."));
00319 }
00320 return res;
00321 }
00322 REPORT_GENERATOR(unit_status)
00323 {
00324 unit *u = get_visible_unit();
00325 return unit_status(u);
00326 }
00327 REPORT_GENERATOR(selected_unit_status)
00328 {
00329 unit *u = get_selected_unit();
00330 return unit_status(u);
00331 }
00332
00333 static config unit_alignment(unit* u)
00334 {
00335 if (!u) return report();
00336 std::ostringstream str, tooltip;
00337 char const *align = unit_type::alignment_description(u->alignment(), u->gender());
00338 std::string align_id = unit_type::alignment_id(u->alignment());
00339 int cm = combat_modifier(resources::screen->displayed_unit_hex(), u->alignment(), u->is_fearless());
00340 str << align << " (" << utils::signed_percent(cm) << ")";
00341 tooltip << _("Alignment: ") << "<b>" << align << "</b>\n"
00342 << string_table[align_id + "_description"];
00343 return text_report(str.str(), tooltip.str(), "time_of_day");
00344 }
00345 REPORT_GENERATOR(unit_alignment)
00346 {
00347 unit *u = get_visible_unit();
00348 return unit_alignment(u);
00349 }
00350 REPORT_GENERATOR(selected_unit_alignment)
00351 {
00352 unit *u = get_selected_unit();
00353 return unit_alignment(u);
00354 }
00355
00356
00357 static config unit_abilities(unit* u)
00358 {
00359 if (!u) return report();
00360 config res;
00361 const std::vector<boost::tuple<t_string,t_string,t_string> > &abilities = u->ability_tooltips();
00362 for (std::vector<boost::tuple<t_string,t_string,t_string> >::const_iterator i = abilities.begin(),
00363 i_end = abilities.end(); i != i_end; ++i)
00364 {
00365 std::ostringstream str, tooltip;
00366 const std::string &name = i->get<0>().base_str();
00367 str << i->get<1>().str();
00368 if (i + 1 != i_end) str << ", ";
00369 tooltip << _("Ability: ") << i->get<2>().str();
00370 add_text(res, str.str(), tooltip.str(), "ability_" + name);
00371 }
00372 return res;
00373 }
00374 REPORT_GENERATOR(unit_abilities)
00375 {
00376 unit *u = get_visible_unit();
00377 return unit_abilities(u);
00378 }
00379 REPORT_GENERATOR(selected_unit_abilities)
00380 {
00381 unit *u = get_selected_unit();
00382 return unit_abilities(u);
00383 }
00384
00385
00386 static config unit_hp(unit* u)
00387 {
00388 if (!u) return report();
00389 std::ostringstream str, tooltip;
00390 str << span_color(u->hp_color()) << u->hitpoints()
00391 << '/' << u->max_hitpoints() << naps;
00392
00393 std::set<std::string> resistances_table;
00394 utils::string_map resistances = u->get_base_resistances();
00395
00396 bool att_def_diff = false;
00397 map_location displayed_unit_hex = resources::screen->displayed_unit_hex();
00398 foreach (const utils::string_map::value_type &resist, u->get_base_resistances())
00399 {
00400 std::ostringstream line;
00401 line << gettext(resist.first.c_str()) << ": ";
00402
00403 int res_att = 100 - u->resistance_against(resist.first, true, displayed_unit_hex);
00404 int res_def = 100 - u->resistance_against(resist.first, false, displayed_unit_hex);
00405 if (res_att == res_def) {
00406 line << utils::signed_percent(res_def) << "\n";
00407 } else {
00408 line << utils::signed_percent(res_att) << " / " << utils::signed_percent(res_def) << '\n';
00409 att_def_diff = true;
00410 }
00411 resistances_table.insert(line.str());
00412 }
00413
00414 tooltip << _("Resistances: ");
00415 if (att_def_diff)
00416 tooltip << _("(Att / Def)");
00417 tooltip << '\n';
00418 foreach (const std::string &line, resistances_table) {
00419 tooltip << line;
00420 }
00421 return text_report(str.str(), tooltip.str());
00422 }
00423 REPORT_GENERATOR(unit_hp)
00424 {
00425 unit *u = get_visible_unit();
00426 return unit_hp(u);
00427 }
00428 REPORT_GENERATOR(selected_unit_hp)
00429 {
00430 unit *u = get_selected_unit();
00431 return unit_hp(u);
00432 }
00433
00434 static config unit_xp(unit* u)
00435 {
00436 if (!u) return report();
00437 std::ostringstream str, tooltip;
00438 str << span_color(u->xp_color()) << u->experience()
00439 << '/' << u->max_experience() << naps;
00440
00441 int exp_mod = unit_type::experience_accelerator::get_acceleration();
00442 tooltip << _("Experience Modifier: ") << exp_mod << '%';
00443 return text_report(str.str(), tooltip.str());
00444 }
00445 REPORT_GENERATOR(unit_xp)
00446 {
00447 unit *u = get_visible_unit();
00448 return unit_xp(u);
00449 }
00450 REPORT_GENERATOR(selected_unit_xp)
00451 {
00452 unit *u = get_selected_unit();
00453 return unit_xp(u);
00454 }
00455
00456 static config unit_advancement_options(unit* u)
00457 {
00458 if (!u) return report();
00459 config res;
00460 typedef std::pair<std::string, std::string> pair_string;
00461 foreach (const pair_string &ps, u->advancement_icons()) {
00462 add_image(res, ps.first, ps.second);
00463 }
00464 return res;
00465 }
00466 REPORT_GENERATOR(unit_advancement_options)
00467 {
00468 unit *u = get_visible_unit();
00469 return unit_advancement_options(u);
00470 }
00471 REPORT_GENERATOR(selected_unit_advancement_options)
00472 {
00473 unit *u = get_selected_unit();
00474 return unit_advancement_options(u);
00475 }
00476
00477 static config unit_defense(unit* u, const map_location& displayed_unit_hex)
00478 {
00479 if(!u) {
00480 return report();
00481 }
00482
00483 std::ostringstream str, tooltip;
00484 const gamemap &map = *resources::game_map;
00485 if(!resources::game_map->on_board(displayed_unit_hex)) {
00486 return report();
00487 }
00488
00489 const t_translation::t_terrain &terrain = map[displayed_unit_hex];
00490 int def = 100 - u->defense_modifier(terrain);
00491 SDL_Color color = int_to_color(game_config::red_to_green(def));
00492 str << span_color(color) << def << "%</span>";
00493 tooltip << _("Terrain: ") << "<b>" << map.get_terrain_info(terrain).description() << "</b>\n";
00494
00495 const t_translation::t_list &underlyings = map.underlying_def_terrain(terrain);
00496 std::vector<int> t_defs;
00497 bool revert = false;
00498 if (underlyings.size() != 1 || underlyings.front() != terrain)
00499 {
00500 foreach (const t_translation::t_terrain &t, underlyings)
00501 {
00502 if (t == t_translation::MINUS) {
00503 revert = true;
00504 } else if (t == t_translation::PLUS) {
00505 revert = false;
00506 } else {
00507 int t_def = 100 - u->defense_modifier(t);
00508 SDL_Color color = int_to_color(game_config::red_to_green(t_def));
00509 tooltip << '\t' << map.get_terrain_info(t).description() << ": "
00510 << span_color(color) << t_def << "%</span> "
00511 << (revert ? _("maximum^max.") : _("minimum^min.")) << '\n';
00512 }
00513 }
00514 }
00515
00516 tooltip << "<b>" << _("Defense: ") << span_color(color) << def << "%</span></b>";
00517 return text_report(str.str(), tooltip.str());
00518 }
00519 REPORT_GENERATOR(unit_defense)
00520 {
00521 unit *u = get_visible_unit();
00522 const map_location& displayed_unit_hex = resources::screen->displayed_unit_hex();
00523 return unit_defense(u, displayed_unit_hex);
00524 }
00525 REPORT_GENERATOR(selected_unit_defense)
00526 {
00527 unit *u = get_selected_unit();
00528 const map_location& selected_hex = resources::screen->selected_hex();
00529 return unit_defense(u, selected_hex);
00530 }
00531
00532 static config unit_moves(unit* u)
00533 {
00534 if (!u) return report();
00535 std::ostringstream str;
00536 double movement_frac = 1.0;
00537 if (u->side() == resources::screen->playing_side()) {
00538 movement_frac = double(u->movement_left()) / std::max<int>(1, u->total_movement());
00539 if (movement_frac > 1.0)
00540 movement_frac = 1.0;
00541 }
00542
00543 int grey = 128 + int((255 - 128) * movement_frac);
00544 SDL_Color c = create_color(grey, grey, grey);
00545 str << span_color(c) << u->movement_left() << '/' << u->total_movement() << naps;
00546 return text_report(str.str());
00547 }
00548 REPORT_GENERATOR(unit_moves)
00549 {
00550 unit *u = get_visible_unit();
00551 return unit_moves(u);
00552 }
00553 REPORT_GENERATOR(selected_unit_moves)
00554 {
00555 unit *u = get_selected_unit();
00556 return unit_moves(u);
00557 }
00558
00559 static int attack_info(const attack_type &at, config &res, unit *u, const map_location &displayed_unit_hex)
00560 {
00561 std::ostringstream str, tooltip;
00562
00563 at.set_specials_context(displayed_unit_hex, map_location(), *u);
00564 int base_damage = at.damage();
00565 int damage_multiplier = 100;
00566 int tod_bonus = combat_modifier(displayed_unit_hex, u->alignment(), u->is_fearless());
00567 damage_multiplier += tod_bonus;
00568 int leader_bonus = 0;
00569 if (under_leadership(*resources::units, displayed_unit_hex, &leader_bonus).valid())
00570 damage_multiplier += leader_bonus;
00571
00572
00573 damage_multiplier *= 100;
00574 bool slowed = u->get_state(unit::STATE_SLOWED);
00575 int damage_divisor = slowed ? 20000 : 10000;
00576 int damage = round_damage(base_damage, damage_multiplier, damage_divisor);
00577
00578 int base_nattacks = at.num_attacks();
00579 int nattacks = base_nattacks;
00580 unit_ability_list swarm = at.get_specials("swarm");
00581 if (!swarm.empty())
00582 {
00583 int swarm_max_attacks = swarm.highest("swarm_attacks_max", nattacks).first;
00584 int swarm_min_attacks = swarm.highest("swarm_attacks_min").first;
00585 int hitp = u->hitpoints();
00586 int mhitp = u->max_hitpoints();
00587 nattacks = swarm_min_attacks + (swarm_max_attacks - swarm_min_attacks) * hitp / mhitp;
00588 }
00589 SDL_Color dmg_color = font::weapon_color;
00590 double dmg_bonus = double(damage) / base_damage;
00591 if (dmg_bonus > 1.0)
00592 dmg_color = font::good_dmg_color;
00593 else if (dmg_bonus < 1.0)
00594 dmg_color = font::bad_dmg_color;
00595
00596 str << span_color(dmg_color) << damage << naps << span_color(font::weapon_color)
00597 << font::weapon_numbers_sep << nattacks << ' ' << at.name()
00598 << "</span>\n";
00599 tooltip << _("Weapon: ") << "<b>" << at.name() << "</b>\n"
00600 << _("Damage: ") << "<b>" << damage << "</b>\n";
00601
00602 if (tod_bonus || leader_bonus || slowed)
00603 {
00604 tooltip << '\t' << _("Base damage: ") << base_damage << '\n';
00605 if (tod_bonus) {
00606 tooltip << '\t' << _("Time of day: ")
00607 << utils::signed_percent(tod_bonus) << '\n';
00608 }
00609 if (leader_bonus) {
00610 tooltip << '\t' << _("Leadership: ")
00611 << utils::signed_percent(leader_bonus) << '\n';
00612 }
00613 if (slowed) {
00614 tooltip << '\t' << _("Slowed: ") << "/ 2" << '\n';
00615 }
00616 }
00617
00618 tooltip << _("Attacks: ") << "<b>" << nattacks << "</b>\n";
00619 if (nattacks != base_nattacks){
00620 tooltip << '\t' << _("Base attacks: ") << base_nattacks << '\n';
00621 int hp_ratio = u->hitpoints() * 100 / u->max_hitpoints();
00622 tooltip << '\t' << _("Swarm: ") << "* "<< hp_ratio << "%\n";
00623 }
00624
00625 add_text(res, flush(str), flush(tooltip));
00626
00627 std::string range = string_table["range_" + at.range()];
00628 std::string lang_type = string_table["type_" + at.type()];
00629
00630 str << span_color(font::weapon_details_color) << " "
00631 << range << font::weapon_details_sep
00632 << lang_type << "</span>\n";
00633
00634 tooltip << _("Weapon range: ") << "<b>" << range << "</b>\n"
00635 << _("Damage type: ") << "<b>" << lang_type << "</b>\n"
00636 << _("Damage versus: ") << '\n';
00637
00638
00639
00640 std::map<int, std::set<std::string>, std::greater<int> > resistances;
00641 std::set<std::string> seen_types;
00642 const team &unit_team = (*resources::teams)[u->side() - 1];
00643 const team &viewing_team = (*resources::teams)[resources::screen->viewing_team()];
00644 foreach(const unit &enemy, *resources::units)
00645 {
00646 if (!unit_team.is_enemy(enemy.side()))
00647 continue;
00648 const map_location &loc = enemy.get_location();
00649 if (viewing_team.fogged(loc) ||
00650 (viewing_team.is_enemy(enemy.side()) && enemy.invisible(loc)))
00651 continue;
00652 bool new_type = seen_types.insert(enemy.type_id()).second;
00653 if (new_type) {
00654 int resistance = enemy.resistance_against(at, false, loc);
00655 resistances[resistance].insert(enemy.type_name());
00656 }
00657 }
00658
00659
00660 damage_multiplier = 100;
00661 tod_bonus = combat_modifier(map_location::null_location, u->alignment(), u->is_fearless());
00662 damage_multiplier += tod_bonus;
00663
00664 typedef std::pair<int, std::set<std::string> > resist_units;
00665 foreach (const resist_units &resist, resistances) {
00666 int damage = round_damage(base_damage, damage_multiplier * resist.first, damage_divisor);
00667 tooltip << "<b>" << damage << "</b> "
00668 << "<i>(" << utils::signed_percent(resist.first-100) << ")</i> : "
00669 << utils::join(resist.second, ", ") << '\n';
00670 }
00671 add_text(res, flush(str), flush(tooltip));
00672
00673 const std::string &accuracy_parry = at.accuracy_parry_description();
00674 if (!accuracy_parry.empty())
00675 {
00676 str << span_color(font::weapon_details_color)
00677 << " " << accuracy_parry << "</span>\n";
00678 int accuracy = at.accuracy();
00679 if (accuracy) {
00680 tooltip << _("Accuracy:") << "<b>"
00681 << utils::signed_percent(accuracy) << "</b>\n";
00682 }
00683 int parry = at.parry();
00684 if (parry) {
00685 tooltip << _("Parry:") << "<b>"
00686 << utils::signed_percent(parry) << "</b>\n";
00687 }
00688 add_text(res, flush(str), flush(tooltip));
00689 }
00690
00691 const std::vector<t_string> &specials = at.special_tooltips();
00692 if (!specials.empty())
00693 {
00694 for (std::vector<t_string>::const_iterator sp_it = specials.begin(),
00695 sp_end = specials.end(); sp_it != sp_end; ++sp_it)
00696 {
00697 str << span_color(font::weapon_details_color)
00698 << " " << *sp_it << "</span>\n";
00699 std::string help_page = "weaponspecial_" + sp_it->base_str();
00700 ++sp_it;
00701
00702 tooltip << _("Weapon special: ") << *sp_it << '\n';
00703 add_text(res, flush(str), flush(tooltip), help_page);
00704 }
00705 }
00706 return damage;
00707 }
00708
00709
00710 static void format_prob(char str_buf[10], double prob)
00711 {
00712
00713 if(prob > 0.9995) {
00714 snprintf(str_buf, 10, "100 %%");
00715 } else if(prob >= 0.1) {
00716 snprintf(str_buf, 10, "%4.1f %%", 100.0 * prob);
00717 } else {
00718 snprintf(str_buf, 10, " %3.1f %%", 100.0 * prob);
00719 }
00720
00721 str_buf[9] = '\0';
00722 }
00723
00724 static void format_hp(char str_buf[10], int hp)
00725 {
00726 if(hp < 10) {
00727 snprintf(str_buf, 10, " %i", hp);
00728 } else if(hp < 99) {
00729 snprintf(str_buf, 10, " %i", hp);
00730 } else if(hp < 999) {
00731 snprintf(str_buf, 10, " %i", hp);
00732 } else {
00733 snprintf(str_buf, 10, " %i", hp);
00734 }
00735
00736 str_buf[9] = '\0';
00737 }
00738
00739 static config unit_weapons(unit *attacker, const map_location &attacker_pos, unit *defender, bool show_attacker)
00740 {
00741 if (!attacker || !defender) return report();
00742
00743 unit* u = show_attacker ? attacker : defender;
00744 const map_location unit_loc = show_attacker ? attacker_pos : defender->get_location();
00745
00746 std::ostringstream str, tooltip;
00747 config res;
00748
00749 std::vector<battle_context> weapons;
00750 for (unsigned int i = 0; i < attacker->attacks().size(); i++) {
00751
00752 if (attacker->attacks()[i].attack_weight() > 0) {
00753 battle_context weapon(*resources::units, attacker_pos, defender->get_location(), i, -1, 0.0, NULL, attacker);
00754 weapons.push_back(weapon);
00755 }
00756 }
00757
00758 foreach(const battle_context& weapon, weapons) {
00759
00760
00761 combatant attacker_combatant(weapon.get_attacker_stats());
00762 combatant defender_combatant(weapon.get_defender_stats());
00763 attacker_combatant.fight(defender_combatant);
00764
00765 const battle_context_unit_stats& context_unit_stats =
00766 show_attacker ? weapon.get_attacker_stats() : weapon.get_defender_stats();
00767
00768 int total_damage = 0;
00769 int base_damage = 0;
00770 int num_blows = 0;
00771 int chance_to_hit = 0;
00772 t_string weapon_name = _("None");
00773
00774 SDL_Color dmg_color = font::weapon_color;
00775 if (context_unit_stats.weapon) {
00776 base_damage = attack_info(*context_unit_stats.weapon, res, u, unit_loc);
00777 total_damage = context_unit_stats.damage;
00778 num_blows = context_unit_stats.num_blows;
00779 chance_to_hit = context_unit_stats.chance_to_hit;
00780 weapon_name = context_unit_stats.weapon->name();
00781
00782 double dmg_bonus = double(total_damage) / base_damage;
00783 if (dmg_bonus > 1.0)
00784 dmg_color = font::good_dmg_color;
00785 else if (dmg_bonus < 1.0)
00786 dmg_color = font::bad_dmg_color;
00787 } else {
00788 str << span_color(font::weapon_color) << weapon_name << naps << "\n";
00789 tooltip << _("Weapon: ") << "<b>" << weapon_name << "</b>\n"
00790 << _("Damage: ") << "<b>" << "0" << "</b>\n";
00791 }
00792
00793 SDL_Color chance_color = int_to_color(game_config::red_to_green(chance_to_hit));
00794
00795
00796 str << " " << span_color(dmg_color) << total_damage << naps << span_color(font::weapon_color)
00797 << utils::unicode_en_dash << num_blows
00798 << " (" << span_color(chance_color) << chance_to_hit << "%" << naps << ")"
00799 << naps << "\n";
00800
00801 tooltip << _("Weapon: ") << "<b>" << weapon_name << "</b>\n"
00802 << _("Total damage") << "<b>" << total_damage << "</b>\n";
00803
00804
00805 std::vector<std::pair<int, double> > hp_prob_vector;
00806
00807
00808 std::vector<std::pair<double, int> > prob_hp_vector;
00809 int i;
00810
00811 combatant* c = show_attacker ? &attacker_combatant : &defender_combatant;
00812
00813 for(i = 0; i < static_cast<int>(c->hp_dist.size()); i++) {
00814 double prob = c->hp_dist[i];
00815
00816
00817 if(prob > 0.001)
00818 prob_hp_vector.push_back(std::pair<double, int>(prob, i));
00819 }
00820
00821 std::sort(prob_hp_vector.begin(), prob_hp_vector.end());
00822
00823
00824 int max_hp_distrib_rows_ = 10;
00825
00826
00827 int nb_elem = std::min<int>(max_hp_distrib_rows_, prob_hp_vector.size());
00828
00829 for(i = prob_hp_vector.size() - nb_elem;
00830 i < static_cast<int>(prob_hp_vector.size()); i++) {
00831
00832 hp_prob_vector.push_back(std::pair<int, double>
00833 (prob_hp_vector[i].second, prob_hp_vector[i].first));
00834 }
00835
00836
00837 std::sort(hp_prob_vector.begin(), hp_prob_vector.end());
00838
00839 std::reverse(hp_prob_vector.begin(), hp_prob_vector.end());
00840
00841 for(i = 0;
00842 i < static_cast<int>(hp_prob_vector.size()); i++) {
00843
00844 int hp = hp_prob_vector[i].first;
00845 double prob = hp_prob_vector[i].second;
00846
00847 char prob_buf[10];
00848 format_prob(prob_buf, prob);
00849 char hp_buf[10];
00850 format_hp(hp_buf, hp);
00851
00852 SDL_Color prob_color = int_to_color(game_config::blue_to_white(prob * 100.0, true));
00853
00854 str << span_color(font::weapon_details_color) << " " << " "
00855 << span_color(u->hp_color(hp)) << hp_buf << naps
00856 << " " << font::weapon_numbers_sep << " "
00857 << span_color(prob_color) << prob_buf << naps
00858 << naps << "\n";
00859 }
00860
00861 add_text(res, flush(str), flush(tooltip));
00862 }
00863 return res;
00864 }
00865
00866 static config unit_weapons(unit *u)
00867 {
00868 if (!u) return report();
00869 map_location displayed_unit_hex = resources::screen->displayed_unit_hex();
00870 config res;
00871
00872 foreach (const attack_type &at, u->attacks())
00873 {
00874 attack_info(at, res, u, displayed_unit_hex);
00875 }
00876 return res;
00877 }
00878 REPORT_GENERATOR(unit_weapons)
00879 {
00880 unit *u = get_visible_unit();
00881 if (!u) return config();
00882
00883 return unit_weapons(u);
00884 }
00885 REPORT_GENERATOR(highlighted_unit_weapons)
00886 {
00887 unit *u = get_selected_unit();
00888 unit *sec_u = get_visible_unit();
00889
00890 if (!u) return config();
00891 if (!sec_u || u == sec_u) return unit_weapons(sec_u);
00892
00893 map_location highlighted_hex = resources::screen->displayed_unit_hex();
00894 map_location attack_loc =
00895 resources::controller->get_mouse_handler_base().current_unit_attacks_from(highlighted_hex);
00896
00897 if (!attack_loc.valid())
00898 return unit_weapons(sec_u);
00899
00900 return unit_weapons(u, attack_loc, sec_u, false);
00901 }
00902 REPORT_GENERATOR(selected_unit_weapons)
00903 {
00904 unit *u = get_selected_unit();
00905 unit *sec_u = get_visible_unit();
00906
00907 if (!u) return config();
00908 if (!sec_u || u == sec_u) return unit_weapons(u);
00909
00910 map_location highlighted_hex = resources::screen->displayed_unit_hex();
00911 map_location attack_loc =
00912 resources::controller->get_mouse_handler_base().current_unit_attacks_from(highlighted_hex);
00913
00914 if (!attack_loc.valid())
00915 return unit_weapons(u);
00916
00917 return unit_weapons(u, attack_loc, sec_u, true);
00918 }
00919
00920 REPORT_GENERATOR(unit_image)
00921 {
00922 unit *u = get_visible_unit();
00923 if (!u) return report();
00924 return image_report(u->absolute_image() + u->image_mods());
00925 }
00926 REPORT_GENERATOR(selected_unit_image)
00927 {
00928 unit *u = get_selected_unit();
00929 if (!u) return report();
00930 return image_report(u->absolute_image() + u->image_mods());
00931 }
00932
00933 REPORT_GENERATOR(selected_unit_profile)
00934 {
00935 unit *u = get_selected_unit();
00936 if (!u) return report();
00937 return image_report(u->small_profile());
00938 }
00939 REPORT_GENERATOR(unit_profile)
00940 {
00941 unit *u = get_visible_unit();
00942 if (!u) return report();
00943 return image_report(u->small_profile());
00944 }
00945
00946 static config time_of_day_at(map_location& mouseover_hex)
00947 {
00948 std::ostringstream tooltip;
00949 time_of_day tod;
00950 const team &viewing_team = (*resources::teams)[resources::screen->viewing_team()];
00951 if (viewing_team.shrouded(mouseover_hex)) {
00952
00953 tod = resources::tod_manager->get_time_of_day();
00954 } else if (viewing_team.fogged(mouseover_hex)) {
00955
00956 tod = resources::tod_manager->get_time_of_day(mouseover_hex);
00957 } else {
00958 tod = resources::tod_manager->get_illuminated_time_of_day(mouseover_hex);
00959 }
00960
00961 int b = tod.lawful_bonus;
00962
00963 tooltip << tod.name << '\n'
00964 << _("Lawful units: ") << utils::signed_percent(b) << '\n'
00965 << _("Neutral units: ") << utils::signed_percent(0) << '\n'
00966 << _("Chaotic units: ") << utils::signed_percent(-b) << '\n'
00967 << _("Liminal units: ") << utils::signed_percent(-(abs(b)));
00968
00969 std::string tod_image = tod.image;
00970 if (tod.bonus_modified > 0) tod_image += "~BRIGHTEN()";
00971 else if (tod.bonus_modified < 0) tod_image += "~DARKEN()";
00972 if (preferences::flip_time()) tod_image += "~FL(horiz)";
00973
00974 return image_report(tod_image, tooltip.str(), "time_of_day");
00975 }
00976 REPORT_GENERATOR(time_of_day)
00977 {
00978 map_location mouseover_hex = resources::screen->mouseover_hex();
00979 return time_of_day_at(mouseover_hex);
00980 }
00981 REPORT_GENERATOR(selected_time_of_day)
00982 {
00983 map_location selected_hex = resources::screen->selected_hex();
00984 return time_of_day_at(selected_hex);
00985 }
00986
00987 REPORT_GENERATOR(turn)
00988 {
00989 std::ostringstream str;
00990 str << resources::tod_manager->turn();
00991 int nb = resources::tod_manager->number_of_turns();
00992 if (nb != -1) str << '/' << nb;
00993 return text_report(str.str());
00994 }
00995
00996 REPORT_GENERATOR(gold)
00997 {
00998 std::ostringstream str;
00999 int viewing_side = resources::screen->viewing_side();
01000
01001 int fake_gold = (*resources::teams)[viewing_side - 1].gold() -
01002 resources::whiteboard->get_spent_gold_for(viewing_side);
01003 char const *end = naps;
01004 if (viewing_side != resources::screen->playing_side()) {
01005 str << span_color(font::GRAY_COLOR);
01006 }
01007 else if (fake_gold < 0) {
01008 str << span_color(font::BAD_COLOR);
01009 }
01010 else {
01011 end = "";
01012 }
01013 str << utils::half_signed_value(fake_gold) << end;
01014 return text_report(str.str());
01015 }
01016
01017 REPORT_GENERATOR(villages)
01018 {
01019 std::ostringstream str;
01020 int viewing_side = resources::screen->viewing_side();
01021 const team &viewing_team = (*resources::teams)[viewing_side - 1];
01022 team_data td = calculate_team_data(viewing_team, viewing_side);
01023 str << td.villages << '/';
01024 if (viewing_team.uses_shroud()) {
01025 int unshrouded_villages = 0;
01026 foreach (const map_location &loc, resources::game_map->villages()) {
01027 if (!viewing_team.shrouded(loc))
01028 ++unshrouded_villages;
01029 }
01030 str << unshrouded_villages;
01031 } else {
01032 str << resources::game_map->villages().size();
01033 }
01034 return gray_inactive(str.str());
01035 }
01036
01037 REPORT_GENERATOR(num_units)
01038 {
01039 return gray_inactive(str_cast(side_units(resources::screen->viewing_side())));
01040 }
01041
01042 REPORT_GENERATOR(upkeep)
01043 {
01044 std::ostringstream str;
01045 int viewing_side = resources::screen->viewing_side();
01046 const team &viewing_team = (*resources::teams)[viewing_side - 1];
01047 team_data td = calculate_team_data(viewing_team, viewing_side);
01048 str << td.expenses << " (" << td.upkeep << ")";
01049 return gray_inactive(str.str());
01050 }
01051
01052 REPORT_GENERATOR(expenses)
01053 {
01054 int viewing_side = resources::screen->viewing_side();
01055 const team &viewing_team = (*resources::teams)[viewing_side - 1];
01056 team_data td = calculate_team_data(viewing_team, resources::screen->viewing_side());
01057 return gray_inactive(str_cast(td.expenses));
01058 }
01059
01060 REPORT_GENERATOR(income)
01061 {
01062 std::ostringstream str;
01063 int viewing_side = resources::screen->viewing_side();
01064 const team &viewing_team = (*resources::teams)[viewing_side - 1];
01065 team_data td = calculate_team_data(viewing_team, viewing_side);
01066 char const *end = naps;
01067 if (viewing_side != resources::screen->playing_side()) {
01068 if (td.net_income < 0) {
01069 td.net_income = - td.net_income;
01070 str << span_color(font::GRAY_COLOR);
01071 str << utils::unicode_minus;
01072 }
01073 else {
01074 str << span_color(font::GRAY_COLOR);
01075 }
01076 }
01077 else if (td.net_income < 0) {
01078 td.net_income = - td.net_income;
01079 str << span_color(font::BAD_COLOR);
01080 str << utils::unicode_minus;
01081 }
01082 else {
01083 end = "";
01084 }
01085 str << td.net_income << end;
01086 return text_report(str.str());
01087 }
01088
01089 REPORT_GENERATOR(terrain)
01090 {
01091 gamemap &map = *resources::game_map;
01092 int viewing_side = resources::screen->viewing_side();
01093 const team &viewing_team = (*resources::teams)[viewing_side - 1];
01094 map_location mouseover_hex = resources::screen->mouseover_hex();
01095 if (!map.on_board(mouseover_hex) || viewing_team.shrouded(mouseover_hex))
01096 return report();
01097
01098 t_translation::t_terrain terrain = map.get_terrain(mouseover_hex);
01099 if (terrain == t_translation::OFF_MAP_USER)
01100 return report();
01101
01102 std::ostringstream str;
01103 if (map.is_village(mouseover_hex))
01104 {
01105 int owner = village_owner(mouseover_hex, *resources::teams) + 1;
01106 if (owner == 0 || viewing_team.fogged(mouseover_hex)) {
01107 str << map.get_terrain_info(terrain).income_description();
01108 } else if (owner == viewing_side) {
01109 str << map.get_terrain_info(terrain).income_description_own();
01110 } else if (viewing_team.is_enemy(owner)) {
01111 str << map.get_terrain_info(terrain).income_description_enemy();
01112 } else {
01113 str << map.get_terrain_info(terrain).income_description_ally();
01114 }
01115
01116 const std::string& underlying_desc = map.get_underlying_terrain_string(terrain);
01117 if(!underlying_desc.empty()) {
01118 str << underlying_desc;
01119 }
01120 } else {
01121 str << map.get_terrain_string(terrain);
01122 }
01123
01124 return text_report(str.str());
01125 }
01126
01127 REPORT_GENERATOR(position)
01128 {
01129 gamemap &map = *resources::game_map;
01130 map_location mouseover_hex = resources::screen->mouseover_hex(),
01131 displayed_unit_hex = resources::screen->displayed_unit_hex();
01132 if (!map.on_board(mouseover_hex))
01133 return report();
01134 t_translation::t_terrain terrain = map[mouseover_hex];
01135 if (terrain == t_translation::OFF_MAP_USER)
01136 return report();
01137
01138 std::ostringstream str;
01139 str << mouseover_hex;
01140
01141 const unit *u = get_visible_unit();
01142 const team &viewing_team = (*resources::teams)[resources::screen->viewing_team()];
01143 if (!u ||
01144 (displayed_unit_hex != mouseover_hex &&
01145 displayed_unit_hex != resources::screen->selected_hex()) ||
01146 viewing_team.shrouded(mouseover_hex))
01147 return text_report(str.str());
01148
01149 int move_cost = u->movement_cost(terrain);
01150 int defense = 100 - u->defense_modifier(terrain);
01151 if (move_cost < unit_movement_type::UNREACHABLE) {
01152 str << " (" << defense << "%," << move_cost << ')';
01153 } else if (mouseover_hex == displayed_unit_hex) {
01154 str << " (" << defense << "%,‒)";
01155 } else {
01156 str << " (‒)";
01157 }
01158 return text_report(str.str());
01159 }
01160
01161 REPORT_GENERATOR(side_playing)
01162 {
01163 const team &active_team = (*resources::teams)[resources::screen->playing_team()];
01164 std::string flag_icon = active_team.flag_icon();
01165 std::string old_rgb = game_config::flag_rgb;
01166 std::string new_rgb = team::get_side_color_index(resources::screen->playing_side());
01167 std::string mods = "~RC(" + old_rgb + ">" + new_rgb + ")";
01168 if (flag_icon.empty())
01169 flag_icon = game_config::images::flag_icon;
01170 return image_report(flag_icon + mods, active_team.current_player());
01171 }
01172
01173 REPORT_GENERATOR(observers)
01174 {
01175 const std::set<std::string> &observers = resources::screen->observers();
01176 if (observers.empty())
01177 return report();
01178
01179 std::ostringstream str;
01180 str << _("Observers:") << '\n';
01181 foreach (const std::string &obs, observers) {
01182 str << obs << '\n';
01183 }
01184 return image_report(game_config::images::observer, str.str());
01185 }
01186
01187
01188
01189
01190
01191
01192
01193
01194
01195
01196
01197
01198
01199
01200
01201
01202
01203
01204
01205
01206
01207
01208
01209 REPORT_GENERATOR(editor_tool_hint)
01210 {
01211 return report();
01212 }
01213
01214 REPORT_GENERATOR(report_clock)
01215 {
01216 time_t t = std::time(NULL);
01217 struct tm *lt = std::localtime(&t);
01218 if (!lt) return report();
01219 char temp[15];
01220 size_t s = std::strftime(temp, 15,
01221 (preferences::use_twelve_hour_clock_format() ? _("%I:%M %p") : _("%H:%M")),
01222 lt);
01223 return s ? text_report(temp) : report();
01224
01225 }
01226
01227 REPORT_GENERATOR(report_countdown)
01228 {
01229 int viewing_side = resources::screen->viewing_side();
01230 const team &viewing_team = (*resources::teams)[viewing_side - 1];
01231 int min, sec;
01232 if (viewing_team.countdown_time() == 0)
01233 return report_report_clock();
01234 std::ostringstream str;
01235 sec = viewing_team.countdown_time() / 1000;
01236 char const *end = naps;
01237 if (viewing_side != resources::screen->playing_side())
01238 str << span_color(font::GRAY_COLOR);
01239 else if (sec < 60)
01240 str << "<span foreground=\"#c80000\">";
01241 else if (sec < 120)
01242 str << "<span foreground=\"#c8c800\">";
01243 else
01244 end = "";
01245 min = sec / 60;
01246 str << min << ':';
01247 sec = sec % 60;
01248 if (sec < 10) str << '0';
01249 str << sec << end;
01250 return text_report(str.str());
01251 }
01252
01253 static std::set<std::string> all_reports;
01254
01255 void reports::reset_generators()
01256 {
01257 foreach (dynamic_report_generators::value_type &rg, dynamic_generators) {
01258 delete rg.second;
01259 }
01260 dynamic_generators.clear();
01261 all_reports.clear();
01262 }
01263
01264 void reports::register_generator(const std::string &name, reports::generator *g)
01265 {
01266 std::pair<dynamic_report_generators::iterator, bool> ib =
01267 dynamic_generators.insert(std::make_pair(name, g));
01268 if (!ib.second) {
01269 delete ib.first->second;
01270 ib.first->second = g;
01271 }
01272 }
01273
01274 config reports::generate_report(const std::string &name, bool only_static)
01275 {
01276 if (!only_static) {
01277 dynamic_report_generators::const_iterator i = dynamic_generators.find(name);
01278 if (i != dynamic_generators.end())
01279 return i->second->generate();
01280 }
01281 static_report_generators::const_iterator j = static_generators.find(name);
01282 if (j != static_generators.end())
01283 return j->second();
01284 return report();
01285 }
01286
01287 const std::set<std::string> &reports::report_list()
01288 {
01289 if (!all_reports.empty()) return all_reports;
01290 foreach (const static_report_generators::value_type &v, static_generators) {
01291 all_reports.insert(v.first);
01292 }
01293 foreach (const dynamic_report_generators::value_type &v, dynamic_generators) {
01294 all_reports.insert(v.first);
01295 }
01296 return all_reports;
01297 }