diff --git a/projectfiles/CodeBlocks-SCons/wesnoth.cbp b/projectfiles/CodeBlocks-SCons/wesnoth.cbp
index e13a17367ad4..e3eb1844b5b0 100644
--- a/projectfiles/CodeBlocks-SCons/wesnoth.cbp
+++ b/projectfiles/CodeBlocks-SCons/wesnoth.cbp
@@ -193,6 +193,8 @@
+
+
diff --git a/projectfiles/CodeBlocks/wesnoth.cbp b/projectfiles/CodeBlocks/wesnoth.cbp
index 568ddc4f674b..5d94e32a01e7 100644
--- a/projectfiles/CodeBlocks/wesnoth.cbp
+++ b/projectfiles/CodeBlocks/wesnoth.cbp
@@ -231,6 +231,8 @@
+
+
diff --git a/projectfiles/VC9/wesnoth.vcproj b/projectfiles/VC9/wesnoth.vcproj
index 83de26d6acee..e773a6d56fdb 100644
--- a/projectfiles/VC9/wesnoth.vcproj
+++ b/projectfiles/VC9/wesnoth.vcproj
@@ -20060,6 +20060,14 @@
RelativePath="..\..\src\display_context.hpp"
>
+
+
+
+
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 127f88b3609d..7e8819a89265 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -700,6 +700,7 @@ set(wesnoth-main_SRC
controller_base.cpp
desktop_util.cpp
dialogs.cpp
+ drawable_unit.cpp
editor/action/action.cpp
editor/action/action_item.cpp
editor/action/action_label.cpp
diff --git a/src/SConscript b/src/SConscript
index a0b8110fd66f..8d5c728f917c 100644
--- a/src/SConscript
+++ b/src/SConscript
@@ -233,6 +233,7 @@ wesnoth_sources = Split("""
controller_base.cpp
desktop_util.cpp
dialogs.cpp
+ drawable_unit.cpp
editor/action/action.cpp
editor/action/action_unit.cpp
editor/action/action_label.cpp
diff --git a/src/actions/move.cpp b/src/actions/move.cpp
index ec380d36c7f6..4e403a51883f 100644
--- a/src/actions/move.cpp
+++ b/src/actions/move.cpp
@@ -1288,37 +1288,5 @@ size_t move_unit_from_replay(const std::vector &steps,
return move_unit_internal(undo_stack, show_move, NULL, mover);
}
-bool unit_can_move(const unit &u)
-{
- const team ¤t_team = (*resources::teams)[u.side() - 1];
-
- if(!u.attacks_left() && u.movement_left()==0)
- return false;
-
- // Units with goto commands that have already done their gotos this turn
- // (i.e. don't have full movement left) should have red globes.
- if(u.has_moved() && u.has_goto()) {
- return false;
- }
-
- map_location locs[6];
- get_adjacent_tiles(u.get_location(), locs);
- for(int n = 0; n != 6; ++n) {
- if (resources::gameboard->map().on_board(locs[n])) {
- const unit_map::const_iterator i = resources::units->find(locs[n]);
- if (i.valid() && !i->incapacitated() &&
- current_team.is_enemy(i->side())) {
- return true;
- }
-
- if (u.movement_cost((resources::gameboard->map())[locs[n]]) <= u.movement_left()) {
- return true;
- }
- }
- }
-
- return false;
-}
-
}//namespace actions
diff --git a/src/actions/move.hpp b/src/actions/move.hpp
index f9c5d4e0e5ad..2e83800c5192 100644
--- a/src/actions/move.hpp
+++ b/src/actions/move.hpp
@@ -114,13 +114,6 @@ size_t move_unit_from_replay(const std::vector &steps,
bool continued_move, bool skip_ally_sighted,
bool show_move = true);
-/**
- * Will return true iff the unit @a u has any possible moves
- * it can do (including attacking etc).
- */
-bool unit_can_move(const unit &u);
-
-
}//namespace actions
diff --git a/src/display.cpp b/src/display.cpp
index 4bbaf7cfd56d..f0dca0a650f0 100644
--- a/src/display.cpp
+++ b/src/display.cpp
@@ -18,6 +18,7 @@
*/
#include "cursor.hpp"
+#include "drawable_unit.hpp"
#include "display.hpp"
#include "fake_unit_manager.hpp"
#include "game_preferences.hpp"
@@ -2553,7 +2554,7 @@ void display::draw_invalidated() {
exclusive_unit_draw_requests_t::iterator request = exclusive_unit_draw_requests_.find(loc);
if (u_it != dc_->units().end()
&& (request == exclusive_unit_draw_requests_.end() || request->second == u_it->id()))
- u_it->redraw_unit();
+ (static_cast (&*u_it))->redraw_unit();
}
}
diff --git a/src/display.hpp b/src/display.hpp
index fca374f5d79c..e4ac9227a5e5 100644
--- a/src/display.hpp
+++ b/src/display.hpp
@@ -165,6 +165,8 @@ class display
void change_display_context(const display_context * dc);
+ const display_context & get_disp_context() { return *dc_; }
+
static Uint32 rgb(Uint8 red, Uint8 green, Uint8 blue)
{ return 0xFF000000 | (red << 16) | (green << 8) | blue; }
static Uint8 red(Uint32 color)
diff --git a/src/display_context.cpp b/src/display_context.cpp
index 0b3690e10ce5..221cd80f7b0b 100644
--- a/src/display_context.cpp
+++ b/src/display_context.cpp
@@ -29,3 +29,41 @@ const unit * display_context::get_visible_unit(const map_location & loc, const t
}
return &*u;
}
+
+/**
+ * Will return true iff the unit @a u has any possible moves
+ * it can do (including attacking etc).
+ */
+
+bool display_context::unit_can_move(const unit &u) const
+{
+ if(!u.attacks_left() && u.movement_left()==0)
+ return false;
+
+ // Units with goto commands that have already done their gotos this turn
+ // (i.e. don't have full movement left) should have red globes.
+ if(u.has_moved() && u.has_goto()) {
+ return false;
+ }
+
+ const team ¤t_team = teams()[u.side() - 1];
+
+ map_location locs[6];
+ get_adjacent_tiles(u.get_location(), locs);
+ for(int n = 0; n != 6; ++n) {
+ if (map().on_board(locs[n])) {
+ const unit_map::const_iterator i = units().find(locs[n]);
+ if (i.valid() && !i->incapacitated() &&
+ current_team.is_enemy(i->side())) {
+ return true;
+ }
+
+ if (u.movement_cost(map()[locs[n]]) <= u.movement_left()) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
diff --git a/src/display_context.hpp b/src/display_context.hpp
index b506fff6e473..f2a3d56b6612 100644
--- a/src/display_context.hpp
+++ b/src/display_context.hpp
@@ -37,8 +37,16 @@ class display_context {
virtual const gamemap & map() const = 0;
virtual const unit_map & units() const = 0;
+ // Needed for reports
+
const unit * get_visible_unit(const map_location &loc, const team ¤t_team, bool see_all = false) const;
+ // From actions:: namespace
+
+ bool unit_can_move(const unit & u) const;
+
+ // Dtor
+
virtual ~display_context() {}
};
diff --git a/src/drawable_unit.cpp b/src/drawable_unit.cpp
new file mode 100644
index 000000000000..e2033da882c8
--- /dev/null
+++ b/src/drawable_unit.cpp
@@ -0,0 +1,294 @@
+/*
+ Copyright (C) 2014 by Chris Beck
+ Part of the Battle for Wesnoth Project http://www.wesnoth.org/
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY.
+
+ See the COPYING file for more details.
+*/
+
+#include "drawable_unit.hpp"
+
+#include "display.hpp"
+#include "display_context.hpp"
+#include "game_preferences.hpp"
+#include "halo.hpp"
+#include "map.hpp"
+#include "team.hpp"
+#include "unit_animation.hpp"
+#include "unit_frame.hpp"
+
+#include
+
+void drawable_unit::redraw_unit () const
+{
+ display &disp = *display::get_singleton();
+ const gamemap &map = disp.get_map();
+
+ if ( hidden_ || disp.is_blindfolded() || !is_visible_to_team(disp.get_teams()[disp.viewing_team()],map, disp.show_everything()) )
+ {
+ clear_haloes();
+ if(anim_) {
+ anim_->update_last_draw_time();
+ }
+ return;
+ }
+
+ if (!anim_) {
+ set_standing();
+ if (!anim_) return;
+ }
+
+ if (refreshing_) return;
+ refreshing_ = true;
+
+ 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(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 = 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(adjusted_params.offset * xdst + (1.0-adjusted_params.offset) * xsrc) + d2;
+ const int y = static_cast(adjusted_params.offset * ydst + (1.0-adjusted_params.offset) * ysrc) + d2;
+
+ if(unit_halo_ == halo::NO_HALO && !image_halo().empty()) {
+ unit_halo_ = halo::add(0, 0, image_halo()+TC_image_mods(), map_location(-1, -1));
+ }
+ if(unit_halo_ != halo::NO_HALO && image_halo().empty()) {
+ halo::remove(unit_halo_);
+ unit_halo_ = halo::NO_HALO;
+ } else if(unit_halo_ != halo::NO_HALO) {
+ halo::set_location(unit_halo_, x, y - height_adjust);
+ }
+
+
+
+ // We draw bars only if wanted, visible on the map view
+ bool draw_bars = 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(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() &&
+ disp.get_teams()[disp.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 ( disp.get_disp_context().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(-5*disp.get_zoom_factor());
+ const int hp_bar_height = static_cast(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(max_experience() * xp_bar_scaling_ / std::max(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::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((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;
+
+ anim_->redraw(params);
+ refreshing_ = false;
+}
+
diff --git a/src/drawable_unit.hpp b/src/drawable_unit.hpp
new file mode 100644
index 000000000000..397549084e37
--- /dev/null
+++ b/src/drawable_unit.hpp
@@ -0,0 +1,41 @@
+/*
+ Copyright (C) 2014 by Chris Beck
+ Part of the Battle for Wesnoth Project http://www.wesnoth.org/
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY.
+
+ See the COPYING file for more details.
+*/
+
+/**
+ *
+ * This wrapper class should be held by the display object when it needs to draw a unit.
+ * The purpose of this is to improve encapsulation -- other parts of the engine like AI
+ * don't need to be exposed to the unit drawing code, and encapsulation like this will
+ * help us to reduce unnecessary includes.
+ *
+ **/
+
+#ifndef DRAWABLE_UNIT_H_INCLUDED
+#define DRAWABLE_UNIT_H_INCLUDED
+
+#include "unit.hpp"
+
+class display;
+class game_display;
+
+class drawable_unit : protected unit //TODO: Get rid of inheritance and use composition instead.
+{ //IMO, it would be better for drawable unit to hold a unit reference, and be marked as a friend class.
+ //But I don't want to rewrite the redraw() function right now.
+ /** draw a unit. */
+ void redraw_unit() const;
+
+ friend class display;
+ friend class game_display;
+};
+#endif
diff --git a/src/game_display.cpp b/src/game_display.cpp
index dfc27f46c4ea..3597cf141269 100644
--- a/src/game_display.cpp
+++ b/src/game_display.cpp
@@ -34,6 +34,7 @@ Growl_Delegate growl_obj;
#endif
#include "cursor.hpp"
+#include "drawable_unit.hpp"
#include "fake_unit.hpp"
#include "fake_unit_manager.hpp"
#include "game_board.hpp"
@@ -246,12 +247,12 @@ void game_display::draw_invalidated()
halo::unrender(invalidated_);
display::draw_invalidated();
- BOOST_FOREACH(unit* temp_unit, fake_unit_man_->get_fake_unit_list_for_invalidation()) {
+ BOOST_FOREACH(const unit* temp_unit, fake_unit_man_->get_fake_unit_list_for_invalidation()) {
const map_location& loc = temp_unit->get_location();
exclusive_unit_draw_requests_t::iterator request = exclusive_unit_draw_requests_.find(loc);
if (invalidated_.find(loc) != invalidated_.end()
&& (request == exclusive_unit_draw_requests_.end() || request->second == temp_unit->id()))
- temp_unit->redraw_unit();
+ (static_cast (temp_unit))->redraw_unit();
}
}
diff --git a/src/menu_events.cpp b/src/menu_events.cpp
index f93a0bdeed05..4ba0f6cecc43 100644
--- a/src/menu_events.cpp
+++ b/src/menu_events.cpp
@@ -810,7 +810,7 @@ namespace { // Helpers for menu_handler::end_turn()
if ( un->side() == side_num ) {
// @todo whiteboard should take into consideration units that have
// a planned move but can still plan more movement in the same turn
- if ( actions::unit_can_move(*un) && !un->user_end_turn()
+ if ( resources::gameboard->unit_can_move(*un) && !un->user_end_turn()
&& !resources::whiteboard->unit_has_actions(&*un) )
return true;
}
@@ -825,7 +825,7 @@ namespace { // Helpers for menu_handler::end_turn()
{
for ( unit_map::const_iterator un = units.begin(); un != units.end(); ++un ) {
if ( un->side() == side_num ) {
- if ( actions::unit_can_move(*un) && !un->has_moved() && !un->user_end_turn()
+ if ( resources::gameboard->unit_can_move(*un) && !un->has_moved() && !un->user_end_turn()
&& !resources::whiteboard->unit_has_actions(&*un) )
return true;
}
diff --git a/src/mouse_events.cpp b/src/mouse_events.cpp
index 614e73eed4c1..7fce837152ff 100644
--- a/src/mouse_events.cpp
+++ b/src/mouse_events.cpp
@@ -1192,7 +1192,7 @@ bool mouse_handler::unit_in_cycle(unit_map::const_iterator it)
return false;
if (it->side() != side_num_ || it->user_end_turn()
- || gui().fogged(it->get_location()) || !actions::unit_can_move(*it))
+ || gui().fogged(it->get_location()) || !board_.unit_can_move(*it))
return false;
if (current_team().is_enemy(int(gui().viewing_team()+1)) &&
diff --git a/src/unit.cpp b/src/unit.cpp
index a424ebce87d9..0e506b413b0d 100644
--- a/src/unit.cpp
+++ b/src/unit.cpp
@@ -1840,273 +1840,6 @@ void unit::set_facing(map_location::DIRECTION dir) const {
// Else look at yourself (not available so continue to face the same direction)
}
-void unit::redraw_unit () const
-{
- display &disp = *display::get_singleton();
- const gamemap &map = disp.get_map();
-
- if ( hidden_ || disp.is_blindfolded() || !is_visible_to_team(disp.get_teams()[disp.viewing_team()],map, disp.show_everything()) )
- {
- clear_haloes();
- if(anim_) {
- anim_->update_last_draw_time();
- }
- return;
- }
-
- if (!anim_) {
- set_standing();
- if (!anim_) return;
- }
-
- if (refreshing_) return;
- refreshing_ = true;
-
- 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(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 = 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(adjusted_params.offset * xdst + (1.0-adjusted_params.offset) * xsrc) + d2;
- const int y = static_cast(adjusted_params.offset * ydst + (1.0-adjusted_params.offset) * ysrc) + d2;
-
- if(unit_halo_ == halo::NO_HALO && !image_halo().empty()) {
- unit_halo_ = halo::add(0, 0, image_halo()+TC_image_mods(), map_location(-1, -1));
- }
- if(unit_halo_ != halo::NO_HALO && image_halo().empty()) {
- halo::remove(unit_halo_);
- unit_halo_ = halo::NO_HALO;
- } else if(unit_halo_ != halo::NO_HALO) {
- halo::set_location(unit_halo_, x, y - height_adjust);
- }
-
-
-
- // We draw bars only if wanted, visible on the map view
- bool draw_bars = 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(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() &&
- disp.get_teams()[disp.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 ( actions::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(-5*disp.get_zoom_factor());
- const int hp_bar_height = static_cast(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(max_experience() * xp_bar_scaling_ / std::max(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::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((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;
-
- anim_->redraw(params);
- refreshing_ = false;
-}
-
void unit::clear_haloes () const
{
if(unit_halo_ != halo::NO_HALO) {
diff --git a/src/unit.hpp b/src/unit.hpp
index 7af60646ab36..365bf389018f 100644
--- a/src/unit.hpp
+++ b/src/unit.hpp
@@ -248,8 +248,6 @@ class unit
/** A SDL surface, ready for display for place where we need a still-image of the unit. */
const surface still_image(bool scaled = false) const;
- /** draw a unit. */
- void redraw_unit() const;
/** Clear unit_halo_ */
void clear_haloes() const;
@@ -420,7 +418,9 @@ class unit
void set_underlying_id();
config cfg_;
+protected:
map_location loc_;
+private:
std::vector advances_to_;
const unit_type * type_;/// Never NULL. Adjusted for gender and variation.
@@ -436,7 +436,9 @@ class unit
int max_hit_points_;
int experience_;
int max_experience_;
+protected:
int level_;
+private:
int recall_cost_;
bool canrecruit_;
std::vector recruit_list_;
@@ -445,7 +447,9 @@ class unit
std::string image_mods_;
bool unrenamable_;
+protected:
int side_;
+private:
const unit_race::GENDER gender_;
fixed_t alpha_;
@@ -469,16 +473,20 @@ class unit
config variables_;
config events_;
config filter_recall_;
+
+protected:
bool emit_zoc_;
mutable STATE state_; //animation state
-
+private:
std::vector overlays_;
std::string role_;
std::vector attacks_;
+protected:
mutable map_location::DIRECTION facing_; //TODO: I think we actually consider this to be part of the gamestate, so it might be better if it's not mutable
//But it's not easy to separate this guy from the animation code right now.
+private:
std::vector trait_names_;
std::vector trait_descriptions_;
@@ -489,6 +497,7 @@ class unit
utils::string_map modification_descriptions_;
// Animations:
+protected:
std::vector animations_;
mutable boost::scoped_ptr anim_;
@@ -503,6 +512,7 @@ class unit
mutable bool draw_bars_; // flag used for drawing / animation
double hp_bar_scaling_, xp_bar_scaling_;
+private:
config modifications_;
/**
diff --git a/src/unit_animation.hpp b/src/unit_animation.hpp
index 124835159514..239a1ad248d5 100644
--- a/src/unit_animation.hpp
+++ b/src/unit_animation.hpp
@@ -67,6 +67,7 @@ class unit_animation
friend std::ostream& operator << (std::ostream& outstream, const unit_animation& u_animation);
friend class unit;
+ friend class drawable_unit;
explicit unit_animation(const config &cfg, const std::string &frame_string = "");