static std::string get_image_mods(const unit& u) { std::string res = "~RC(" + u.team_color() + ">" + team::get_side_color_index(u.side()) + ")"; if (u.can_recruit()) { res += "~BLIT(" + unit::leader_crown() + ")"; } BOOST_FOREACH(const std::string& overlay, u.overlays()) { res += "~BLIT(" + overlay + ")"; } return res; }
void unit_drawer::redraw_unit (const unit & u) const { unit_animation_component & ac = u.anim_comp(); map_location loc = u.get_location(); int side = u.side(); bool hidden = u.get_hidden(); bool is_flying = u.is_flying(); map_location::DIRECTION facing = u.facing(); int hitpoints = u.hitpoints(); int max_hitpoints = u.max_hitpoints(); int movement_left = u.movement_left(); int total_movement = u.total_movement(); bool can_recruit = u.can_recruit(); bool can_advance = u.can_advance(); int experience = u.experience(); int max_experience = u.max_experience(); bool emit_zoc = u.emits_zoc(); SDL_Color hp_color=u.hp_color(); SDL_Color xp_color=u.xp_color(); std::string ellipse=u.image_ellipse(); if ( hidden || is_blindfolded || !u.is_visible_to_team(viewing_team_ref,map, show_everything) ) { ac.clear_haloes(); if(ac.anim_) { ac.anim_->update_last_draw_time(); } return; } if (!ac.anim_) { ac.set_standing(); if (!ac.anim_) return; } if (ac.refreshing_) return; ac.refreshing_ = true; ac.anim_->update_last_draw_time(); frame_parameters params; const t_translation::t_terrain terrain = map.get_terrain(loc); const terrain_type& terrain_info = map.get_terrain_info(terrain); // do not set to 0 so we can distinguish the flying from the "not on submerge terrain" // instead use -1.0 (as in "negative depth", it will be ignored by rendering) params.submerge= is_flying ? -1.0 : terrain_info.unit_submerge(); if (u.invisible(loc) && params.highlight_ratio > 0.5) { params.highlight_ratio = 0.5; } if (loc == sel_hex && params.highlight_ratio == 1.0) { params.highlight_ratio = 1.5; } int height_adjust = static_cast<int>(terrain_info.unit_height_adjust() * zoom_factor); if (is_flying && height_adjust < 0) { height_adjust = 0; } params.y -= height_adjust; params.halo_y -= height_adjust; int red = 0,green = 0,blue = 0,tints = 0; double blend_ratio = 0; // Add future colored states here if(u.poisoned()) { green += 255; blend_ratio += 0.25; tints += 1; } if(u.slowed()) { red += 191; green += 191; blue += 255; blend_ratio += 0.25; tints += 1; } if(tints > 0) { params.blend_with = disp.rgb((red/tints),(green/tints),(blue/tints)); params.blend_ratio = ((blend_ratio/tints)); } //hackish : see unit_frame::merge_parameters // we use image_mod on the primary image // and halo_mod on secondary images and all haloes params.image_mod = u.image_mods(); params.halo_mod = u.TC_image_mods(); params.image= u.default_anim_image(); if(u.incapacitated()) params.image_mod +="~GS()"; params.primary_frame = t_true; const frame_parameters adjusted_params = ac.anim_->get_current_params(params); const map_location dst = loc.get_direction(facing); const int xsrc = disp.get_location_x(loc); const int ysrc = disp.get_location_y(loc); const int xdst = disp.get_location_x(dst); const int ydst = disp.get_location_y(dst); const int x = static_cast<int>(adjusted_params.offset * xdst + (1.0-adjusted_params.offset) * xsrc) + hex_size_by_2; const int y = static_cast<int>(adjusted_params.offset * ydst + (1.0-adjusted_params.offset) * ysrc) + hex_size_by_2; bool has_halo = ac.unit_halo_ && ac.unit_halo_->valid(); if(!has_halo && !u.image_halo().empty()) { ac.unit_halo_ = halo_man.add(0, 0, u.image_halo()+u.TC_image_mods(), map_location(-1, -1)); } if(has_halo && u.image_halo().empty()) { halo_man.remove(ac.unit_halo_); ac.unit_halo_ = halo::handle(); //halo::NO_HALO; } else if(has_halo) { halo_man.set_location(ac.unit_halo_, x, y - height_adjust); } // We draw bars only if wanted, visible on the map view bool draw_bars = ac.draw_bars_ ; if (draw_bars) { SDL_Rect unit_rect = sdl::create_rect(xsrc, ysrc +adjusted_params.y, hex_size, hex_size); draw_bars = sdl::rects_overlap(unit_rect, disp.map_outside_area()); } #ifdef SDL_GPU sdl::timage ellipse_front; sdl::timage ellipse_back; #else surface ellipse_front(nullptr); surface ellipse_back(nullptr); #endif int ellipse_floating = 0; // Always show the ellipse for selected units if(draw_bars && (preferences::show_side_colors() || sel_hex == loc)) { if(adjusted_params.submerge > 0.0) { // The division by 2 seems to have no real meaning, // It just works fine with the current center of ellipse // and prevent a too large adjust if submerge = 1.0 ellipse_floating = static_cast<int>(adjusted_params.submerge * hex_size_by_2); } if(ellipse.empty()){ ellipse="misc/ellipse"; } if(ellipse != "none") { // check if the unit has a ZoC or can recruit const char* const nozoc = emit_zoc ? "" : "nozoc-"; const char* const leader = can_recruit ? "leader-" : ""; const char* const selected = sel_hex == loc ? "selected-" : ""; // Load the ellipse parts recolored to match team color char buf[100]; std::string tc=team::get_side_color_index(side); #ifdef SDL_GPU snprintf(buf,sizeof(buf),"%s-%s%s%stop.png~RC(ellipse_red>%s)",ellipse.c_str(),leader,nozoc,selected,tc.c_str()); ellipse_back = image::get_texture(image::locator(buf), image::SCALED_TO_ZOOM); snprintf(buf,sizeof(buf),"%s-%s%s%sbottom.png~RC(ellipse_red>%s)",ellipse.c_str(),leader,nozoc,selected,tc.c_str()); ellipse_front = image::get_texture(image::locator(buf), image::SCALED_TO_ZOOM); #else snprintf(buf,sizeof(buf),"%s-%s%s%stop.png~RC(ellipse_red>%s)",ellipse.c_str(),leader,nozoc,selected,tc.c_str()); ellipse_back.assign(image::get_image(image::locator(buf), image::SCALED_TO_ZOOM)); snprintf(buf,sizeof(buf),"%s-%s%s%sbottom.png~RC(ellipse_red>%s)",ellipse.c_str(),leader,nozoc,selected,tc.c_str()); ellipse_front.assign(image::get_image(image::locator(buf), image::SCALED_TO_ZOOM)); #endif } } #ifdef SDL_GPU if (!ellipse_back.null()) { //disp.drawing_buffer_add(display::LAYER_UNIT_BG, loc, disp.drawing_buffer_add(display::LAYER_UNIT_FIRST, loc, xsrc, ysrc +adjusted_params.y-ellipse_floating, ellipse_back); } if (!ellipse_front.null()) { //disp.drawing_buffer_add(display::LAYER_UNIT_FG, loc, disp.drawing_buffer_add(display::LAYER_UNIT_FIRST, loc, xsrc, ysrc +adjusted_params.y-ellipse_floating, ellipse_front); } #else if (ellipse_back != nullptr) { //disp.drawing_buffer_add(display::LAYER_UNIT_BG, loc, disp.drawing_buffer_add(display::LAYER_UNIT_FIRST, loc, xsrc, ysrc +adjusted_params.y-ellipse_floating, ellipse_back); } if (ellipse_front != nullptr) { //disp.drawing_buffer_add(display::LAYER_UNIT_FG, loc, disp.drawing_buffer_add(display::LAYER_UNIT_FIRST, loc, xsrc, ysrc +adjusted_params.y-ellipse_floating, ellipse_front); } #endif if(draw_bars) { const image::locator* orb_img = nullptr; const surface unit_img = image::get_image(u.default_anim_image(), image::SCALED_TO_ZOOM); const int xoff = (hex_size - unit_img->w)/2; const int yoff = (hex_size - unit_img->h)/2; /*static*/ const image::locator partmoved_orb(game_config::images::orb + "~RC(magenta>" + preferences::partial_color() + ")" ); /*static*/ const image::locator moved_orb(game_config::images::orb + "~RC(magenta>" + preferences::moved_color() + ")" ); /*static*/ const image::locator ally_orb(game_config::images::orb + "~RC(magenta>" + preferences::allied_color() + ")" ); /*static*/ const image::locator enemy_orb(game_config::images::orb + "~RC(magenta>" + preferences::enemy_color() + ")" ); /*static*/ const image::locator unmoved_orb(game_config::images::orb + "~RC(magenta>" + preferences::unmoved_color() + ")" ); const std::string* energy_file = &game_config::images::energy; if(size_t(side) != viewing_team+1) { if(disp.team_valid() && viewing_team_ref.is_enemy(side)) { if (preferences::show_enemy_orb() && !u.incapacitated()) orb_img = &enemy_orb; else orb_img = nullptr; } else { if (preferences::show_allied_orb()) orb_img = &ally_orb; else orb_img = nullptr; } } else { if (preferences::show_moved_orb()) orb_img = &moved_orb; else orb_img = nullptr; if(playing_team == viewing_team && !u.user_end_turn()) { if (movement_left == total_movement) { if (preferences::show_unmoved_orb()) orb_img = &unmoved_orb; else orb_img = nullptr; } else if ( dc.unit_can_move(u) ) { if (preferences::show_partial_orb()) orb_img = &partmoved_orb; else orb_img = nullptr; } } } if (orb_img != nullptr) { surface orb(image::get_image(*orb_img,image::SCALED_TO_ZOOM)); disp.drawing_buffer_add(display::LAYER_UNIT_BAR, loc, xsrc + xoff, ysrc + yoff + adjusted_params.y, orb); } double unit_energy = 0.0; if(max_hitpoints > 0) { unit_energy = double(hitpoints)/double(max_hitpoints); } const int bar_shift = static_cast<int>(-5*zoom_factor); const int hp_bar_height = static_cast<int>(max_hitpoints * u.hp_bar_scaling()); const fixed_t bar_alpha = (loc == mouse_hex || loc == sel_hex) ? ftofxp(1.0): ftofxp(0.8); draw_bar(*energy_file, xsrc+xoff+bar_shift, ysrc+yoff+adjusted_params.y, loc, hp_bar_height, unit_energy,hp_color, bar_alpha); if(experience > 0 && can_advance) { const double filled = double(experience)/double(max_experience); const int xp_bar_height = static_cast<int>(max_experience * u.xp_bar_scaling() / std::max<int>(u.level(),1)); draw_bar(*energy_file, xsrc+xoff, ysrc+yoff+adjusted_params.y, loc, xp_bar_height, filled, xp_color, bar_alpha); } if (can_recruit) { surface crown(image::get_image(u.leader_crown(),image::SCALED_TO_ZOOM)); if(!crown.null()) { //if(bar_alpha != ftofxp(1.0)) { // crown = adjust_surface_alpha(crown, bar_alpha); //} disp.drawing_buffer_add(display::LAYER_UNIT_BAR, loc, xsrc+xoff, ysrc+yoff+adjusted_params.y, crown); } } for(std::vector<std::string>::const_iterator ov = u.overlays().begin(); ov != u.overlays().end(); ++ov) { #ifdef SDL_GPU const sdl::timage ov_img(image::get_texture(*ov, image::SCALED_TO_ZOOM)); if(!ov_img.null()) { disp.drawing_buffer_add(display::LAYER_UNIT_BAR, loc, xsrc, ysrc +adjusted_params.y, ov_img); } #else const surface ov_img(image::get_image(*ov, image::SCALED_TO_ZOOM)); if(ov_img != nullptr) { disp.drawing_buffer_add(display::LAYER_UNIT_BAR, loc, xsrc+xoff, ysrc+yoff+adjusted_params.y, ov_img); } #endif } } // Smooth unit movements from terrain of different elevation. // Do this separately from above so that the health bar doesn't go up and down. const t_translation::t_terrain terrain_dst = map.get_terrain(dst); const terrain_type& terrain_dst_info = map.get_terrain_info(terrain_dst); int height_adjust_unit = static_cast<int>((terrain_info.unit_height_adjust() * (1.0 - adjusted_params.offset) + terrain_dst_info.unit_height_adjust() * adjusted_params.offset) * zoom_factor); if (is_flying && height_adjust_unit < 0) { height_adjust_unit = 0; } params.y -= height_adjust_unit - height_adjust; params.halo_y -= height_adjust_unit - height_adjust; ac.anim_->redraw(params, halo_man); ac.refreshing_ = false; }
void tunit_preview_pane::set_displayed_unit(const unit& u) { // Sets the current type id for the profile button callback to use current_type_ = u.type_id(); if(icon_type_) { std::string mods = u.image_mods(); if(u.can_recruit()) { mods += "~BLIT(" + unit::leader_crown() + ")"; } for(const std::string& overlay : u.overlays()) { mods += "~BLIT(" + overlay + ")"; } mods += "~SCALE_INTO_SHARP(144,144)" + image_mods_; icon_type_->set_label(u.absolute_image() + mods); } if(label_name_) { std::string name; if(!u.name().empty()) { name = "<span size='large'>" + u.name() + "</span>" + "\n" + "<small><span color='#a69275'>" + u.type_name() + "</span></small>"; } else { name = "<span size='large'>" + u.type_name() + "</span>\n"; } label_name_->set_label(name); label_name_->set_use_markup(true); } if(label_level_) { std::string l_str = vgettext("Lvl $lvl", {{"lvl", std::to_string(u.level())}}); label_level_->set_label("<b>" + l_str + "</b>"); label_level_->set_use_markup(true); } if(icon_race_) { icon_race_->set_label("icons/unit-groups/race_" + u.race()->id() + "_30.png"); icon_race_->set_tooltip(u.race()->name(u.gender())); } if(icon_alignment_) { const std::string& alignment_name = u.alignment().to_string(); icon_alignment_->set_label("icons/alignments/alignment_" + alignment_name + "_30.png"); icon_alignment_->set_tooltip(unit_type::alignment_description( u.alignment(), u.gender())); } if(label_details_minimal_) { std::stringstream str; const std::string name = "<span size='large'>" + (!u.name().empty() ? u.name() : " ") + "</span>"; str << name << "\n"; str << "<span color='#a69275'>" << u.type_name() << "</span>" << "\n"; str << "Lvl " << u.level() << "\n"; str << u.alignment() << "\n"; str << utils::join(u.trait_names(), ", ") << "\n"; str << font::span_color(u.hp_color()) << _("HP: ") << u.hitpoints() << "/" << u.max_hitpoints() << "</span>" << "\n"; str << font::span_color(u.xp_color()) << _("XP: ") << u.experience() << "/" << u.max_experience() << "</span>"; label_details_minimal_->set_label(str.str()); label_details_minimal_->set_use_markup(true); } if(tree_details_) { std::stringstream str; str << "<small>"; str << font::span_color(u.hp_color()) << "<b>" << _("HP: ") << "</b>" << u.hitpoints() << "/" << u.max_hitpoints() << "</span>" << " | "; str << font::span_color(u.xp_color()) << "<b>" << _("XP: ") << "</b>" << u.experience() << "/" << u.max_experience() << "</span>" << " | "; str << "<b>" << _("MP: ") << "</b>" << u.movement_left() << "/" << u.total_movement(); str << "</small>"; tree_details_->clear(); add_name_tree_node( tree_details_->get_root_node(), "item", str.str() ); if (!u.trait_names().empty()) { auto& header_node = add_name_tree_node( tree_details_->get_root_node(), "header", "<b>" + _("Traits") + "</b>" ); assert(u.trait_names().size() == u.trait_descriptions().size()); for (size_t i = 0; i < u.trait_names().size(); ++i) { add_name_tree_node( header_node, "item", u.trait_names()[i], u.trait_descriptions()[i] ); } } if (!u.get_ability_list().empty()) { auto& header_node = add_name_tree_node( tree_details_->get_root_node(), "header", "<b>" + _("Abilities") + "</b>" ); for (const auto& ab : u.ability_tooltips()) { add_name_tree_node( header_node, "item", std::get<1>(ab), std::get<2>(ab) ); } } print_attack_details(u.attacks(), tree_details_->get_root_node()); } }