/** * Load data into an empty unit_type (build to FULL). */ void unit_type::build_full(const movement_type_map &mv_types, const race_map &races, const config::const_child_itors &traits) { // Don't build twice. if ( FULL <= build_status_ ) return; // Make sure we are built to the preceding build level. build_help_index(mv_types, races, traits); for (int i = 0; i < 2; ++i) { if (gender_types_[i]) gender_types_[i]->build_full(mv_types, races, traits); } if ( race_ != &unit_race::null_race ) { if (!race_->uses_global_traits()) { possible_traits_.clear(); } if ( cfg_["ignore_race_traits"].to_bool() ) { possible_traits_.clear(); } else { for (const config &t : race_->additional_traits()) { if (alignment_ != unit_type::ALIGNMENT::NEUTRAL || t["id"] != "fearless") possible_traits_.add_child("trait", t); } } if (undead_variation_.empty()) { undead_variation_ = race_->undead_variation(); } } // Insert any traits that are just for this unit type for (const config &trait : cfg_.child_range("trait")) { possible_traits_.add_child("trait", trait); } zoc_ = cfg_["zoc"].to_bool(level_ > 0); const config::attribute_value & alpha_blend = cfg_["alpha"]; if(alpha_blend.empty() == false) { alpha_ = ftofxp(alpha_blend.to_double()); } game_config::add_color_info(cfg_); hp_bar_scaling_ = cfg_["hp_bar_scaling"].to_double(game_config::hp_bar_scaling); xp_bar_scaling_ = cfg_["xp_bar_scaling"].to_double(game_config::xp_bar_scaling); // Propagate the build to the variations. for (variations_map::value_type & variation : variations_) { variation.second->build_full(mv_types, races, traits); } // Deprecation messages, only seen when unit is parsed for the first time. build_status_ = FULL; }
unit_type::unit_type(const config &cfg, const std::string & parent_id) : cfg_(cfg), unit_cfg_(), built_unit_cfg_(false), id_(cfg_.has_attribute("id") ? cfg_["id"].str() : parent_id), debug_id_(), base_id_(!parent_id.empty() ? parent_id : id_), type_name_(cfg_["name"].t_str()), description_(), hitpoints_(0), hp_bar_scaling_(0.0), xp_bar_scaling_(0.0), level_(0), recall_cost_(), movement_(0), vision_(-1), jamming_(0), max_attacks_(0), cost_(0), usage_(), undead_variation_(), image_(cfg_["image"].str()), icon_(), small_profile_(), profile_(), flag_rgb_(cfg_["flag_rgb"].str()), num_traits_(0), gender_types_(), variations_(), default_variation_(cfg_["variation"]), variation_name_(cfg_["variation_name"].t_str()), race_(&unit_race::null_race), alpha_(ftofxp(1.0)), abilities_(), adv_abilities_(), ability_tooltips_(), adv_ability_tooltips_(), zoc_(false), hide_help_(false), do_not_list_(cfg_["do_not_list"].to_bool(false)), advances_to_(), experience_needed_(0), in_advancefrom_(false), alignment_(unit_type::ALIGNMENT::NEUTRAL), movement_type_(), possible_traits_(), genders_(), animations_(), build_status_(NOT_BUILT) { check_id(id_); check_id(base_id_); gender_types_[0] = nullptr; gender_types_[1] = nullptr; }
void game_display::new_turn() { const time_of_day& tod = tod_manager_.get_time_of_day(); if( !first_turn_) { const time_of_day& old_tod = tod_manager_.get_previous_time_of_day(); if(old_tod.image_mask != tod.image_mask) { const surface old_mask(image::get_image(old_tod.image_mask,image::SCALED_TO_HEX)); const surface new_mask(image::get_image(tod.image_mask,image::SCALED_TO_HEX)); const int niterations = static_cast<int>(10/turbo_speed()); const int frame_time = 30; const int starting_ticks = SDL_GetTicks(); for(int i = 0; i != niterations; ++i) { if(old_mask != NULL) { const fixed_t proportion = ftofxp(1.0) - fxpdiv(i,niterations); tod_hex_mask1.assign(adjust_surface_alpha(old_mask,proportion)); } if(new_mask != NULL) { const fixed_t proportion = fxpdiv(i,niterations); tod_hex_mask2.assign(adjust_surface_alpha(new_mask,proportion)); } invalidate_all(); draw(); const int cur_ticks = SDL_GetTicks(); const int wanted_ticks = starting_ticks + i*frame_time; if(cur_ticks < wanted_ticks) { SDL_Delay(wanted_ticks - cur_ticks); } } } tod_hex_mask1.assign(NULL); tod_hex_mask2.assign(NULL); } first_turn_ = false; display::update_tod(); invalidate_all(); draw(); }
/** * Load data into an empty unit_type (build to FULL). */ void unit_type::build_full(const movement_type_map &mv_types, const race_map &races, const config::const_child_itors &traits) { // Don't build twice. if ( FULL <= build_status_ ) return; // Make sure we are built to the preceding build level. build_help_index(mv_types, races, traits); for (int i = 0; i < 2; ++i) { if (gender_types_[i]) gender_types_[i]->build_full(mv_types, races, traits); } if ( race_ != &unit_race::null_race ) { if (undead_variation_.empty()) { undead_variation_ = race_->undead_variation(); } } zoc_ = cfg_["zoc"].to_bool(level_ > 0); const config::attribute_value & alpha_blend = cfg_["alpha"]; if(alpha_blend.empty() == false) { alpha_ = ftofxp(alpha_blend.to_double()); } game_config::add_color_info(cfg_); hp_bar_scaling_ = cfg_["hp_bar_scaling"].to_double(game_config::hp_bar_scaling); xp_bar_scaling_ = cfg_["xp_bar_scaling"].to_double(game_config::xp_bar_scaling); // Propagate the build to the variations. for (variations_map::value_type & variation : variations_) { variation.second->build_full(mv_types, races, traits); } // Deprecation messages, only seen when unit is parsed for the first time. build_status_ = FULL; }
surface o_modification::operator()(const surface& src) const { return adjust_surface_alpha(src, ftofxp(opacity_)); }
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; }
//TODO: proper SDL_gpu implementation void unit_drawer::draw_bar(const std::string& image, int xpos, int ypos, const map_location& loc, size_t height, double filled, const SDL_Color& col, fixed_t alpha) const { filled = std::min<double>(std::max<double>(filled,0.0),1.0); height = static_cast<size_t>(height*zoom_factor); surface surf(image::get_image(image,image::SCALED_TO_HEX)); // We use UNSCALED because scaling (and bilinear interpolation) // is bad for calculate_energy_bar. // But we will do a geometric scaling later. surface bar_surf(image::get_image(image)); if(surf == nullptr || bar_surf == nullptr) { return; } // calculate_energy_bar returns incorrect results if the surface colors // have changed (for example, due to bilinear interpolation) const SDL_Rect& unscaled_bar_loc = calculate_energy_bar(bar_surf); SDL_Rect bar_loc; if (surf->w == bar_surf->w && surf->h == bar_surf->h) bar_loc = unscaled_bar_loc; else { const fixed_t xratio = fxpdiv(surf->w,bar_surf->w); const fixed_t yratio = fxpdiv(surf->h,bar_surf->h); const SDL_Rect scaled_bar_loc = sdl::create_rect( fxptoi(unscaled_bar_loc. x * xratio) , fxptoi(unscaled_bar_loc. y * yratio + 127) , fxptoi(unscaled_bar_loc. w * xratio + 255) , fxptoi(unscaled_bar_loc. h * yratio + 255)); bar_loc = scaled_bar_loc; } if(height > static_cast<size_t>(bar_loc.h)) { height = bar_loc.h; } //if(alpha != ftofxp(1.0)) { // surf.assign(adjust_surface_alpha(surf,alpha)); // if(surf == nullptr) { // return; // } //} const size_t skip_rows = bar_loc.h - height; SDL_Rect top = sdl::create_rect(0, 0, surf->w, bar_loc.y); SDL_Rect bot = sdl::create_rect(0, bar_loc.y + skip_rows, surf->w, 0); bot.h = surf->w - bot.y; #ifdef SDL_GPU sdl::timage img(surf); img.set_clip(top); disp.drawing_buffer_add(display::LAYER_UNIT_BAR, loc, xpos, ypos, surf); img.set_clip(bot); disp.drawing_buffer_add(display::LAYER_UNIT_BAR, loc, xpos, ypos + top.h, surf); #else disp.drawing_buffer_add(display::LAYER_UNIT_BAR, loc, xpos, ypos, surf, top); disp.drawing_buffer_add(display::LAYER_UNIT_BAR, loc, xpos, ypos + top.h, surf, bot); #endif size_t unfilled = static_cast<size_t>(height * (1.0 - filled)); if(unfilled < height && alpha >= ftofxp(0.3)) { const Uint8 r_alpha = std::min<unsigned>(unsigned(fxpmult(alpha,255)),255); surface filled_surf = create_compatible_surface(bar_surf, bar_loc.w, height - unfilled); SDL_Rect filled_area = sdl::create_rect(0, 0, bar_loc.w, height-unfilled); sdl::fill_rect(filled_surf,&filled_area,SDL_MapRGBA(bar_surf->format,col.r,col.g,col.b, r_alpha)); #ifdef SDL_GPU disp.drawing_buffer_add(display::LAYER_UNIT_BAR, loc, xpos + bar_loc.x, ypos + bar_loc.y + unfilled, sdl::timage(filled_surf)); #else disp.drawing_buffer_add(display::LAYER_UNIT_BAR, loc, xpos + bar_loc.x, ypos + bar_loc.y + unfilled, filled_surf); #endif } }
surface floating_label::create_surface() { if(surf_.null()) { font::pango_text text; text.set_foreground_color(color_); text.set_font_size(font_size_); text.set_maximum_width(width_ < 0 ? clip_rect_.w : width_); text.set_maximum_height(height_ < 0 ? clip_rect_.h : height_, true); // ignore last '\n' if(!text_.empty() && *(text_.rbegin()) == '\n') { text.set_text(std::string(text_.begin(), text_.end() - 1), use_markup_); } else { text.set_text(text_, use_markup_); } surface foreground = text.render(); if(foreground == nullptr) { ERR_FT << "could not create floating label's text" << std::endl; return nullptr; } // combine foreground text with its background if(bgalpha_ != 0) { // background is a dark tooltip box surface background = create_neutral_surface(foreground->w + border_ * 2, foreground->h + border_ * 2); if(background == nullptr) { ERR_FT << "could not create tooltip box" << std::endl; return surf_ = foreground; } uint32_t color = SDL_MapRGBA(foreground->format, bgcolor_.r, bgcolor_.g, bgcolor_.b, bgalpha_); sdl::fill_surface_rect(background, nullptr, color); // we make the text less transparent, because the blitting on the // dark background will darken the anti-aliased part. // This 1.13 value seems to restore the brightness of version 1.4 // (where the text was blitted directly on screen) adjust_surface_alpha(foreground, ftofxp(1.13)); SDL_Rect r{border_, border_, 0, 0}; adjust_surface_alpha(foreground, SDL_ALPHA_OPAQUE); sdl_blit(foreground, nullptr, background, &r); surf_ = background; } else { // background is blurred shadow of the text surface background = create_neutral_surface(foreground->w + 4, foreground->h + 4); sdl::fill_surface_rect(background, nullptr, 0); SDL_Rect r{2, 2, 0, 0}; sdl_blit(foreground, nullptr, background, &r); background = shadow_image(background); if(background == nullptr) { ERR_FT << "could not create floating label's shadow" << std::endl; return surf_ = foreground; } sdl_blit(foreground, nullptr, background, &r); surf_ = background; } } return surf_; }
void textbox::draw_contents() { SDL_Rect const &loc = inner_location(); surface surf = video().getSurface(); draw_solid_tinted_rectangle(loc.x,loc.y,loc.w,loc.h,0,0,0, focus(NULL) ? alpha_focus_ : alpha_, surf); SDL_Rect src; if(text_image_ == NULL) { update_text_cache(true); } if(text_image_ != NULL) { src.y = yscroll_; src.w = std::min<size_t>(loc.w,text_image_->w); src.h = std::min<size_t>(loc.h,text_image_->h); src.x = text_pos_; SDL_Rect dest = screen_area(); dest.x = loc.x; dest.y = loc.y; // Fills the selected area if(enabled() && is_selection()) { const int start = std::min<int>(selstart_,selend_); const int end = std::max<int>(selstart_,selend_); int startx = char_x_[start]; int starty = char_y_[start]; const int endx = char_x_[end]; const int endy = char_y_[end]; while(starty <= endy) { const size_t right = starty == endy ? endx : text_image_->w; if(right <= size_t(startx)) { break; } SDL_Rect rect = create_rect(loc.x + startx , loc.y + starty - src.y , right - startx , line_height_); const clip_rect_setter clipper(surf, &loc); Uint32 color = SDL_MapRGB(surf->format, 0, 0, 160); fill_rect_alpha(rect, color, 140, surf); starty += int(line_height_); startx = 0; } } if(enabled()) { sdl_blit(text_image_, &src, surf, &dest); } else { // HACK: using 30% opacity allows white text to look as though it is grayed out, // while not changing any applicable non-grayscale AA. Actual colored text will // not look as good, but this is not currently a concern since GUI1 textboxes // are not used much nowadays, and they will eventually all go away. sdl_blit(adjust_surface_alpha(text_image_, ftofxp(0.3)), &src, surf, &dest); } } draw_cursor((cursor_pos_ == 0 ? 0 : cursor_pos_ - 1), video()); update_rect(loc); }
void drawable_unit::redraw_unit (display & disp) const { const display_context & dc = disp.get_disp_context(); const gamemap &map = dc.map(); const std::vector<team> &teams = dc.teams(); const team & viewing_team = teams[disp.viewing_team()]; unit_animation_component & ac = *anim_comp_; if ( hidden_ || disp.is_blindfolded() || !is_visible_to_team(viewing_team,map, disp.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 (invisible(loc_) && params.highlight_ratio > 0.5) { params.highlight_ratio = 0.5; } if (loc_ == disp.selected_hex() && params.highlight_ratio == 1.0) { params.highlight_ratio = 1.5; } int height_adjust = static_cast<int>(terrain_info.unit_height_adjust() * disp.get_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(get_state(STATE_POISONED)) { green += 255; blend_ratio += 0.25; tints += 1; } if(get_state(STATE_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 = image_mods(); params.halo_mod = TC_image_mods(); params.image= absolute_image(); if(get_state(STATE_PETRIFIED)) 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); int d2 = disp.hex_size() / 2; const int x = static_cast<int>(adjusted_params.offset * xdst + (1.0-adjusted_params.offset) * xsrc) + d2; const int y = static_cast<int>(adjusted_params.offset * ydst + (1.0-adjusted_params.offset) * ysrc) + d2; if(ac.unit_halo_ == halo::NO_HALO && !image_halo().empty()) { ac.unit_halo_ = halo::add(0, 0, image_halo()+TC_image_mods(), map_location(-1, -1)); } if(ac.unit_halo_ != halo::NO_HALO && image_halo().empty()) { halo::remove(ac.unit_halo_); ac.unit_halo_ = halo::NO_HALO; } else if(ac.unit_halo_ != halo::NO_HALO) { halo::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) { const int d = disp.hex_size(); SDL_Rect unit_rect = sdl::create_rect(xsrc, ysrc +adjusted_params.y, d, d); draw_bars = sdl::rects_overlap(unit_rect, disp.map_outside_area()); } surface ellipse_front(NULL); surface ellipse_back(NULL); int ellipse_floating = 0; // Always show the ellipse for selected units if(draw_bars && (preferences::show_side_colors() || disp.selected_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 * disp.hex_size() / 2); } std::string ellipse=image_ellipse(); 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 = disp.selected_hex() == loc_ ? "selected-" : ""; // Load the ellipse parts recolored to match team color char buf[100]; std::string tc=team::get_side_color_index(side_); 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)); } } 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); } if(draw_bars) { const image::locator* orb_img = NULL; /*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()) != disp.viewing_team()+1) { if(disp.team_valid() && viewing_team.is_enemy(side())) { if (preferences::show_enemy_orb() && !get_state(STATE_PETRIFIED)) orb_img = &enemy_orb; else orb_img = NULL; } else { if (preferences::show_allied_orb()) orb_img = &ally_orb; else orb_img = NULL; } } else { if (preferences::show_moved_orb()) orb_img = &moved_orb; else orb_img = NULL; if(disp.playing_team() == disp.viewing_team() && !user_end_turn()) { if (movement_left() == total_movement()) { if (preferences::show_unmoved_orb()) orb_img = &unmoved_orb; else orb_img = NULL; } else if ( dc.unit_can_move(*this) ) { if (preferences::show_partial_orb()) orb_img = &partmoved_orb; else orb_img = NULL; } } } if (orb_img != NULL) { surface orb(image::get_image(*orb_img,image::SCALED_TO_ZOOM)); disp.drawing_buffer_add(display::LAYER_UNIT_BAR, loc_, xsrc, ysrc +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*disp.get_zoom_factor()); const int hp_bar_height = static_cast<int>(max_hitpoints() * hp_bar_scaling_); const fixed_t bar_alpha = (loc_ == disp.mouseover_hex() || loc_ == disp.selected_hex()) ? ftofxp(1.0): ftofxp(0.8); disp.draw_bar(*energy_file, xsrc+bar_shift, ysrc +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() * xp_bar_scaling_ / std::max<int>(level_,1)); SDL_Color color=xp_color(); disp.draw_bar(*energy_file, xsrc, ysrc +adjusted_params.y, loc_, xp_bar_height, filled, color, bar_alpha); } if (can_recruit()) { surface crown(image::get_image(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, ysrc +adjusted_params.y, crown); } } for(std::vector<std::string>::const_iterator ov = overlays().begin(); ov != overlays().end(); ++ov) { const surface ov_img(image::get_image(*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); } } } // 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) * disp.get_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); ac.refreshing_ = false; }