Beispiel #1
0
pathfind::paths::paths(gamemap const &map, unit_map const &units,
		const unit& u, std::vector<team> const &teams,
		bool force_ignore_zoc, bool allow_teleport, const team &viewing_team,
		int additional_turns, bool see_all, bool ignore_units)
	: destinations()
{
	if (u.side() < 1 || u.side() > int(teams.size())) {
		return;
	}

	find_routes(map, units, u,
		u.movement_left(), destinations, teams, force_ignore_zoc,
		allow_teleport,additional_turns,viewing_team,
		see_all, ignore_units);
}
Beispiel #2
0
int aspect_attacks::rate_terrain(const unit& u, const map_location& loc)
{
	gamemap &map_ = *resources::game_map;
	const t_translation::t_terrain terrain = map_.get_terrain(loc);
	const int defense = u.defense_modifier(terrain);
	int rating = 100 - defense;

	const int healing_value = 10;
	const int friendly_village_value = 5;
	const int neutral_village_value = 10;
	const int enemy_village_value = 15;

	if(map_.gives_healing(terrain) && u.get_ability_bool("regenerate",loc) == false) {
		rating += healing_value;
	}

	if(map_.is_village(terrain)) {
		int owner = village_owner(loc, *resources::teams) + 1;

		if(owner == u.side()) {
			rating += friendly_village_value;
		} else if(owner == 0) {
			rating += neutral_village_value;
		} else {
			rating += enemy_village_value;
		}
	}

	return rating;
}
Beispiel #3
0
/**
 * Clears shroud (and fog) at the provided location and its immediate neighbors.
 * This is an aid for the [teleport] action, allowing the destination to be
 * cleared before teleporting, while the unit's full visual range gets cleared
 * after.
 * The @a viewer is needed for correct firing of sighted events.
 *
 * @return whether or not information was uncovered (i.e. returns true if the
 *         locations in question were fogged/shrouded under shared vision/maps).
 */
bool shroud_clearer::clear_dest(const map_location &dest, const unit &viewer)
{
	team & viewing_team = resources::gameboard->get_team(viewer.side());
	// A pair of dummy variables needed to simplify some logic.
	std::size_t enemies, friends;

	// Abort if there is nothing to clear.
	if ( !viewing_team.fog_or_shroud() )
		return false;

	// Cache some values.
	const map_location & real_loc = viewer.get_location();
	const std::size_t viewer_id = viewer.underlying_id();

	// Clear the destination.
	bool cleared_something = clear_loc(viewing_team, dest, dest, real_loc,
	                                   viewer_id, true, enemies, friends);

	// Clear the adjacent hexes (will be seen even if vision is 0, and the
	// graphics do not work so well for an isolated cleared hex).
	adjacent_loc_array_t adjacent;
	get_adjacent_tiles(dest, adjacent.data());
	for (unsigned i = 0; i < adjacent.size(); ++i )
		if ( clear_loc(viewing_team, adjacent[i], dest, real_loc, viewer_id,
		               true, enemies, friends) )
			cleared_something = true;

	if ( cleared_something )
		invalidate_after_clear();

	return cleared_something;
}
Beispiel #4
0
ttactic2::ttactic2(game_display& gui, std::vector<team>& teams, unit_map& units, hero_map& heros, unit& tactician, int operate)
	: gui_(gui)
	, teams_(teams)
	, units_(units)
	, heros_(heros)
	, tactician_(tactician)
	, operate_(operate)
	, candidate_()
	, valid_()
	, cannot_valid_(false)
	, selected_(-1)
{
	if (tactician_.master().tactic_ != HEROS_NO_TACTIC) {
		candidate_.push_back(&tactician_.master());
	}
	if (tactician_.second().valid() && tactician_.second().tactic_ != HEROS_NO_TACTIC) {
		candidate_.push_back(&tactician_.second());
	}
	if (tactician_.third().valid() && tactician_.third().tactic_ != HEROS_NO_TACTIC) {
		candidate_.push_back(&tactician_.third());
	}
	if (operate_ == ACTIVE) {
		std::vector<unit*> actives = teams_[tactician.side() - 1].active_tactics();
		if ((int)actives.size() >= game_config::active_tactic_slots || 
			std::find(actives.begin(), actives.end(), &tactician) != actives.end()) {
			cannot_valid_ = true;
		}
	}
}
Beispiel #5
0
pathfind::teleport_map::teleport_map(
		  const std::vector<teleport_group>& groups
		, const unit& u
		, const team &viewing_team
		, const bool see_all
		, const bool ignore_units)
	: teleport_map_()
	, sources_()
	, targets_()
{

	foreach(const teleport_group& group, groups) {

		teleport_pair locations;
		group.get_teleport_pair(locations, u, ignore_units);
		if (!see_all && !group.always_visible() && viewing_team.is_enemy(u.side())) {
			teleport_pair filter_locs;
			foreach(const map_location &loc, locations.first)
				if(!viewing_team.fogged(loc))
					filter_locs.first.insert(loc);
			foreach(const map_location &loc, locations.second)
				if(!viewing_team.fogged(loc))
					filter_locs.second.insert(loc);
			locations.first.swap(filter_locs.first);
			locations.second.swap(filter_locs.second);
		}
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 &current_team = get_team(u.side());

	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;
}
Beispiel #7
0
int path_cost(std::vector<map_location> const& path, unit const& u)
{
	if(path.size() < 2)
		return 0;

	map_location const& dest = path.back();
	if((resources::game_map->is_village(dest) && !resources::teams->at(u.side()-1).owns_village(dest))
			|| pathfind::enemy_zoc(*resources::teams,dest,resources::teams->at(u.side()-1),u.side()))
		return u.total_movement();

	int result = 0;
	gamemap const& map = *resources::game_map;
	BOOST_FOREACH(map_location const& loc, std::make_pair(path.begin()+1,path.end()))
		result += u.movement_cost(map[loc]);
	return result;
}
Beispiel #8
0
void unit_creator::post_create(const map_location &loc, const unit &new_unit, bool anim)
{

	if (discover_) {
		preferences::encountered_units().insert(new_unit.type_id());
	}

	bool show = show_ && (resources::screen !=nullptr) && !resources::screen->fogged(loc);
	bool animate = show && anim;

	if (get_village_) {
		assert(resources::gameboard);
		if (board_->map().is_village(loc)) {
			actions::get_village(loc, new_unit.side());
		}
	}

	// Only fire the events if it's safe; it's not if we're in the middle of play_controller::reset_gamestate()
	if (resources::lua_kernel != nullptr) {
		resources::game_events->pump().fire("unit_placed", loc);
	}

	if (resources::screen!=nullptr) {

		if (invalidate_ ) {
			resources::screen->invalidate(loc);
		}

		if (animate) {
			unit_display::unit_recruited(loc);
		} else if (show) {
			resources::screen->draw();
		}
	}
}
Beispiel #9
0
	std::vector<map_location> fake_unit_path(const unit& fake_unit, const std::vector<std::string>& xvals, const std::vector<std::string>& yvals)
	{
		const gamemap *game_map = & resources::gameboard->map();
		std::vector<map_location> path;
		map_location src;
		map_location dst;
		for(size_t i = 0; i != std::min(xvals.size(),yvals.size()); ++i) {
			if(i==0){
				src.x = atoi(xvals[i].c_str())-1;
				src.y = atoi(yvals[i].c_str())-1;
				if (!game_map->on_board(src)) {
					ERR_CF << "invalid move_unit_fake source: " << src << '\n';
					break;
				}
				path.push_back(src);
				continue;
			}
			pathfind::shortest_path_calculator calc(fake_unit,
					(*resources::teams)[fake_unit.side()-1],
					*resources::teams,
					*game_map);

			dst.x = atoi(xvals[i].c_str())-1;
			dst.y = atoi(yvals[i].c_str())-1;
			if (!game_map->on_board(dst)) {
				ERR_CF << "invalid move_unit_fake destination: " << dst << '\n';
				break;
			}

			pathfind::plain_route route = pathfind::a_star_search(src, dst, 10000, &calc,
				game_map->w(), game_map->h());

			if (route.steps.empty()) {
				WRN_NG << "Could not find move_unit_fake route from " << src << " to " << dst << ": ignoring complexities" << std::endl;
				pathfind::emergency_path_calculator calc(fake_unit, *game_map);

				route = pathfind::a_star_search(src, dst, 10000, &calc,
						game_map->w(), game_map->h());
				if(route.steps.empty()) {
					// This would occur when trying to do a MUF of a unit
					// over locations which are unreachable to it (infinite movement
					// costs). This really cannot fail.
					WRN_NG << "Could not find move_unit_fake route from " << src << " to " << dst << ": ignoring terrain" << std::endl;
					pathfind::dummy_path_calculator calc(fake_unit, *game_map);
					route = a_star_search(src, dst, 10000, &calc, game_map->w(), game_map->h());
					assert(!route.steps.empty());
				}
			}
			// we add this section to the end of the complete path
			// skipping section's head because already included
			// by the previous iteration
			path.insert(path.end(),
					route.steps.begin()+1, route.steps.end());

			src = dst;
		}
		return path;
	}
Beispiel #10
0
/**
 * Construct a list of paths for the specified unit.
 *
 * This function is used for several purposes, including showing a unit's
 * potential moves and generating currently possible paths.
 * @param u                The unit whose moves and movement type will be used.
 * @param force_ignore_zoc Set to true to completely ignore zones of control.
 * @param allow_teleport   Set to true to consider teleportation abilities.
 * @param viewing_team     Usually the current team, except for "show enemy moves", etc.
 * @param additional_turns The number of turns to account for, in addition to the current.
 * @param see_all          Set to true to remove unit visibility from consideration.
 * @param ignore_units     Set to true if units should never obstruct paths (implies ignoring ZoC as well).
 */
paths::paths(const unit& u, bool force_ignore_zoc,
		bool allow_teleport, const team &viewing_team,
		int additional_turns, bool see_all, bool ignore_units)
	: destinations()
{
	std::vector<team> const &teams = *resources::teams;
	if (u.side() < 1 || u.side() > int(teams.size())) {
		return;
	}

	find_routes(u.get_location(), u.movement_type().get_movement(),
	            u.get_state(unit::STATE_SLOWED), u.movement_left(),
	            u.total_movement(), additional_turns, destinations, NULL,
	            allow_teleport ? &u : NULL,
	            ignore_units ? NULL : &teams[u.side()-1],
	            force_ignore_zoc ? NULL : &u,
	            see_all ? NULL : &viewing_team);
}
Beispiel #11
0
/**
 * Clears shroud (and fog) around the provided location as if @a viewer
 * was standing there.
 * This version of shroud_clearer::clear_unit() will abort if the viewer's
 * team uses neither fog nor shroud. If @a can_delay is left as true, then
 * this function also aborts on the viewing team's turn if delayed shroud
 * updates is on. (Not supplying a team suggests that it would be inconvenient
 * for the caller to check these.)
 * In addition, if @a invalidate is left as true, invalidate_after_clear()
 * will be called.
 * Setting @a instant to false allows some drawing delays that are used to
 * make movement look better.
 *
 * @return whether or not information was uncovered (i.e. returns true if any
 *         locations in visual range were fogged/shrouded under shared vision/maps).
 */
bool shroud_clearer::clear_unit(const map_location &view_loc, const unit &viewer,
                                bool can_delay, bool invalidate, bool instant)
{
	team & viewing_team = resources::gameboard->get_team(viewer.side());

	// Abort if there is nothing to clear.
	if ( !viewing_team.fog_or_shroud() )
		return false;
	if ( can_delay  &&  !viewing_team.auto_shroud_updates()  &&
	     viewer.side() == resources::controller->current_side()  )
		return false;

	if ( !clear_unit(view_loc, viewer, viewing_team, instant) )
		// Nothing uncovered.
		return false;

	if ( invalidate )
		invalidate_after_clear();

	return true;
}
static stuff_list_adder add_unit_entry(stuff_list_adder& progress, const unit& u, const display_context& dc)
{

	Uint32 team_color = game_config::tc_info(dc.get_team(u.side()).color())[0];
	std::stringstream s;

	s << '(' << u.get_location() << ')';
	progress.widget("loc", s.str());

	s.str("");
	s << "<span color='#" << std::hex << team_color << std::dec;
	s << "'>side=" << u.side() << "</span>";
	progress.widget("side", s.str(), true);

	if(u.can_recruit()) {
		progress.widget("leader", "<span color='yellow'>LEADER</span> ", true);
	}

	s.str("");
	s << "id=\"" << u.id() << '"';
	progress.widget("id", s.str());

	progress.widget("type", u.type_id());

	s.str("");
	s << "L" << u.level();
	progress.widget("level", s.str());

	s.str("");
	s << u.experience() << '/' << u.max_experience() << " xp";
	progress.widget("xp", s.str());

	s.str("");
	s << u.hitpoints() << '/' << u.max_hitpoints() << " hp";
	progress.widget("hp", s.str());

	progress.widget("traits", utils::join(u.get_traits_list(), ", "));

	return progress;
}
Beispiel #13
0
std::set<map_location> get_teleport_locations(const unit &u,
	const unit_map &units, const team &viewing_team,
	bool see_all, bool ignore_units)
{
	std::set<map_location> res;
	if (!u.get_ability_bool("teleport")) return res;

	const team &current_team = (*resources::teams)[u.side() - 1];
	const map_location &loc = u.get_location();
	foreach (const map_location &l, current_team.villages())
	{
		// This must be a vacant village (or occupied by the unit)
		// to be able to teleport.
		if (!see_all && viewing_team.is_enemy(u.side()) && viewing_team.fogged(l))
			continue;
		if (!ignore_units && l != loc &&
		    get_visible_unit(units, l, viewing_team, see_all))
			continue;
		res.insert(l);
	}
	return res;
}
Beispiel #14
0
/**
 * Determines if @a loc is within @a viewer's visual range.
 * This is a moderately expensive function (vision is recalculated
 * with each call), so avoid using it heavily.
 * If @a jamming is left as nullptr, the jamming map is also calculated
 * with each invocation.
 */
static bool can_see(const unit & viewer, const map_location & loc,
                    const std::map<map_location, int> * jamming = nullptr)
{
	// Make sure we have a "jamming" map.
	std::map<map_location, int> local_jamming;
	if ( jamming == nullptr ) {
		create_jamming_map(local_jamming, resources::gameboard->get_team(viewer.side()));
		jamming = &local_jamming;
	}

	// Determine which hexes this unit can see.
	pathfind::vision_path sight(viewer, viewer.get_location(), *jamming);

	return sight.destinations.contains(loc)  ||  sight.edges.count(loc) != 0;
}
Beispiel #15
0
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;
}
Beispiel #16
0
int path_cost(std::vector<map_location> const& path, unit const& u)
{
	if(path.size() < 2)
		return 0;

	team const& u_team = resources::teams->at(u.side()-1);
	map_location const& dest = path.back();
	if ( (resources::gameboard->map().is_village(dest) && !u_team.owns_village(dest))
	     || pathfind::enemy_zoc(u_team, dest, u_team) )
		return u.total_movement();

	int result = 0;
	gamemap const& map = resources::gameboard->map();
	for(map_location const& loc : std::make_pair(path.begin()+1,path.end())) {
		result += u.movement_cost(map[loc]);
	}
	return result;
}
Beispiel #17
0
bool game_state::can_recruit_from(const unit& leader) const
{
	return can_recruit_from(leader.get_location(), leader.side());
}
Beispiel #18
0
marked_route mark_route(const plain_route &rt,
	const std::vector<map_location>& waypoints, const unit &u,
	const team &viewing_team, const unit_map &units,
	const std::vector<team> &teams, const gamemap &map)
{
	marked_route res;

	if (rt.steps.empty()) return res;
	res.steps = rt.steps;

	int turns = 0;
	int movement = u.movement_left();
	const team& unit_team = teams[u.side()-1];
	bool zoc = false;

	std::vector<map_location>::const_iterator i = rt.steps.begin(),
			w = waypoints.begin();

	// TODO fix the name confusion with waypoints and route.waypoints
	for (; i !=rt.steps.end(); i++) {
		bool last_step = (i+1 == rt.steps.end());

		// move_cost of the next step is irrelevant for the last step
		assert(last_step || map.on_board(*(i+1)));
		const int move_cost = last_step ? 0 : u.movement_cost(map[*(i+1)]);
		bool capture = false;
		bool pass_here = false;
		if (w != waypoints.end() && *i == *w) {
			w++;
			pass_here = true;
		}

		if (last_step || zoc || move_cost > movement) {
			// check if we stop an a village and so maybe capture it
			// if it's an enemy unit and a fogged village, we assume a capture
			// (if he already owns it, we can't know that)
			// if it's not an enemy, we can always know if he owns the village
			bool capture = map.is_village(*i) && ( !unit_team.owns_village(*i)
				 || (viewing_team.is_enemy(u.side()) && viewing_team.fogged(*i)) );

			++turns;

			bool invisible = u.invisible(*i,units,teams,false);

			res.waypoints[*i] = marked_route::waypoint(turns, pass_here, zoc, capture, invisible);

			if (last_step) break; // finished and we used dummy move_cost

			movement = u.total_movement();
			if(move_cost > movement) {
				return res; //we can't reach destination
			}
		} else if (pass_here) {
			bool invisible = u.invisible(*i,units,teams,false);
			res.waypoints[*i] = marked_route::waypoint(0, pass_here, zoc, false, invisible);
		}

		zoc = enemy_zoc(units, teams, *(i + 1), viewing_team,u.side())
					&& !u.get_ability_bool("skirmisher", *(i+1));

		if (zoc || capture) {
			movement = 0;
		} else {
			movement -= move_cost;
		}
	}

	return res;
}
Beispiel #19
0
static void find_routes(const gamemap& map, const unit_map& units,
		const unit& u, const map_location& loc,
		int move_left, paths::dest_vect &destinations,
		std::vector<team> const &teams,
		bool force_ignore_zocs, bool allow_teleport, int turns_left,
		const team &viewing_team,
		bool see_all, bool ignore_units)
{
	const team& current_team = teams[u.side() - 1];
	std::set<map_location> teleports;
	if (allow_teleport) {
		teleports = get_teleport_locations(u, units, viewing_team, see_all, ignore_units);
	}

	const int total_movement = u.total_movement();

	std::vector<map_location> locs(6 + teleports.size());
	std::copy(teleports.begin(), teleports.end(), locs.begin() + 6);

	search_counter += 2;
	if (search_counter == 0) search_counter = 2;

	static std::vector<node> nodes;
	nodes.resize(map.w() * map.h());

	indexer index(map.w(), map.h());
	comp node_comp(nodes);

	int xmin = loc.x, xmax = loc.x, ymin = loc.y, ymax = loc.y, nb_dest = 1;

	nodes[index(loc)] = node(move_left, turns_left, map_location::null_location, loc);
	std::vector<int> pq;
	pq.push_back(index(loc));

	while (!pq.empty()) {
		node& n = nodes[pq.front()];
		std::pop_heap(pq.begin(), pq.end(), node_comp);
		pq.pop_back();
		n.in = search_counter;

		get_adjacent_tiles(n.curr, &locs[0]);
		for (int i = teleports.count(n.curr) ? locs.size() : 6; i-- > 0; ) {
			if (!locs[i].valid(map.w(), map.h())) continue;

			node& next = nodes[index(locs[i])];

			bool next_visited = next.in - search_counter <= 1u;

			// Classic Dijkstra allow to skip chosen nodes (with next.in==search_counter)
			// But the cost function and hex grid allow to also skip visited nodes:
			// if next was visited, then we already have a path 'src-..-n2-next'
			// - n2 was chosen before n, meaning that it is nearer to src.
			// - the cost of 'n-next' can't be smaller than 'n2-next' because
			//   cost is independent of direction and we don't have more MP at n
			//   (important because more MP may allow to avoid waiting next turn)
			// Thus, 'src-..-n-next' can't be shorter.
			if (next_visited) continue;

			const int move_cost = u.movement_cost(map[locs[i]]);

			node t = node(n.movement_left, n.turns_left, n.curr, locs[i]);
			if (t.movement_left < move_cost) {
				t.movement_left = total_movement;
				t.turns_left--;
			}

			if (t.movement_left < move_cost || t.turns_left < 0) continue;

			t.movement_left -= move_cost;

			if (!ignore_units) {
				const unit *v =
					get_visible_unit(units, locs[i], viewing_team, see_all);
				if (v && current_team.is_enemy(v->side()))
					continue;

				if (!force_ignore_zocs && t.movement_left > 0
						&& enemy_zoc(units, teams, locs[i], viewing_team, u.side(), see_all)
						&& !u.get_ability_bool("skirmisher", locs[i])) {
					t.movement_left = 0;
				}
			}

			++nb_dest;
			int x = locs[i].x;
			if (x < xmin) xmin = x;
			if (xmax < x) xmax = x;
			int y = locs[i].y;
			if (y < ymin) ymin = y;
			if (ymax < y) ymax = y;

			bool in_list = next.in == search_counter + 1;
			t.in = search_counter + 1;
			next = t;

			// if already in the priority queue then we just update it, else push it.
			if (in_list) { // never happen see next_visited above
				std::push_heap(pq.begin(), std::find(pq.begin(), pq.end(), index(locs[i])) + 1, node_comp);
			} else {
				pq.push_back(index(locs[i]));
				std::push_heap(pq.begin(), pq.end(), node_comp);
			}
		}
	}

	// Build the routes for every map_location that we reached.
	// The ordering must be compatible with map_location::operator<.
	destinations.reserve(nb_dest);
	for (int x = xmin; x <= xmax; ++x) {
		for (int y = ymin; y <= ymax; ++y)
		{
			const node &n = nodes[index(map_location(x, y))];
			if (n.in - search_counter > 1u) continue;
			paths::step s =
				{ n.curr, n.prev, n.movement_left + n.turns_left * total_movement };
			destinations.push_back(s);
		}
	}
}
Beispiel #20
0
std::deque<action_ptr> find_actions_of(unit const &target)
{
	return resources::gameboard->teams()[target.side()-1].get_side_actions()->actions_of(target);
}
Beispiel #21
0
static int attack_info(reports::context & rc, const attack_type &at, config &res, const unit &u, const map_location &displayed_unit_hex)
{
	std::ostringstream str, tooltip;

	at.set_specials_context(displayed_unit_hex, u.side() == rc.screen().playing_side());
	int base_damage = at.damage();
	int specials_damage = at.modified_damage(false);
	int damage_multiplier = 100;
	int tod_bonus = combat_modifier(rc.units(), rc.map(), displayed_unit_hex, u.alignment(), u.is_fearless());
	damage_multiplier += tod_bonus;
	int leader_bonus = 0;
	if (under_leadership(rc.units(), displayed_unit_hex, &leader_bonus).valid())
		damage_multiplier += leader_bonus;

	bool slowed = u.get_state(unit::STATE_SLOWED);
	int damage_divisor = slowed ? 20000 : 10000;
	// Assume no specific resistance (i.e. multiply by 100).
	int damage = round_damage(specials_damage, damage_multiplier * 100, damage_divisor);

	// Hit points are used to calculate swarm, so they need to be bounded.
	unsigned max_hp = u.max_hitpoints();
	unsigned cur_hp = std::min<unsigned>(std::max(0, u.hitpoints()), max_hp);

	unsigned base_attacks = at.num_attacks();
	unsigned min_attacks, max_attacks;
	at.modified_attacks(false, min_attacks, max_attacks);
	unsigned num_attacks = swarm_blows(min_attacks, max_attacks, cur_hp, max_hp);

	SDL_Color dmg_color = font::weapon_color;
	if ( damage > specials_damage )
		dmg_color = font::good_dmg_color;
	else if ( damage < specials_damage )
		dmg_color = font::bad_dmg_color;

	str << span_color(dmg_color) << "  " << damage << naps << span_color(font::weapon_color)
		<< font::weapon_numbers_sep << num_attacks << ' ' << at.name()
		<< "</span>\n";
	tooltip << _("Weapon: ") << "<b>" << at.name() << "</b>\n"
		<< _("Damage: ") << "<b>" << damage << "</b>\n";

	if ( tod_bonus || leader_bonus || slowed || specials_damage != base_damage )
	{
		tooltip << '\t' << _("Base damage: ") << base_damage << '\n';
		if ( specials_damage != base_damage ) {
			tooltip << '\t' << _("With specials: ") << specials_damage << '\n';
		}
		if (tod_bonus) {
			tooltip << '\t' << _("Time of day: ")
				<< utils::signed_percent(tod_bonus) << '\n';
		}
		if (leader_bonus) {
			tooltip << '\t' << _("Leadership: ")
				<< utils::signed_percent(leader_bonus) << '\n';
		}
		if (slowed) {
			tooltip << '\t' << _("Slowed: ") << "/ 2" << '\n';
		}
	}

	tooltip << _("Attacks: ") << "<b>" << num_attacks << "</b>\n";
	if ( max_attacks != min_attacks  &&  cur_hp != max_hp ) {
		if ( max_attacks < min_attacks ) {
			// "Reverse swarm"
			tooltip << '\t' << _("Max swarm bonus: ") << (min_attacks-max_attacks) << '\n';
			tooltip << '\t' << _("Swarm: ") << "* "<< (100 - cur_hp*100/max_hp) << "%\n";
			tooltip << '\t' << _("Base attacks: ") << '+' << base_attacks << '\n';
			// The specials line will not necessarily match up with how the
			// specials are calculated, but for an unusual case, simple brevity
			// trumps complexities.
			if ( max_attacks != base_attacks ) {
				int attack_diff = int(max_attacks) - int(base_attacks);
				tooltip << '\t' << _("Specials: ") << utils::signed_value(attack_diff) << '\n';
			}
		}
		else {
			// Regular swarm
			tooltip << '\t' << _("Base attacks: ") << base_attacks << '\n';
			if ( max_attacks != base_attacks ) {
				tooltip << '\t' << _("With specials: ") << max_attacks << '\n';
			}
			if ( min_attacks != 0 ) {
				tooltip << '\t' << _("Subject to swarm: ") << (max_attacks-min_attacks) << '\n';
			}
			tooltip << '\t' << _("Swarm: ") << "* "<< (cur_hp*100/max_hp) << "%\n";
		}
	}
	else if ( num_attacks != base_attacks ) {
		tooltip << '\t' << _("Base attacks: ") << base_attacks << '\n';
		tooltip << '\t' << _("With specials: ") << num_attacks << '\n';
	}

	add_text(res, flush(str), flush(tooltip));

	std::string range = string_table["range_" + at.range()];
	std::string lang_type = string_table["type_" + at.type()];

	str << span_color(font::weapon_details_color) << "  " << "  "
		<< range << font::weapon_details_sep
		<< lang_type << "</span>\n";

	tooltip << _("Weapon range: ") << "<b>" << range << "</b>\n"
		<< _("Damage type: ")  << "<b>" << lang_type << "</b>\n"
		<< _("Damage versus: ") << '\n';

	// Show this weapon damage and resistance against all the different units.
	// We want weak resistances (= good damage) first.
	std::map<int, std::set<std::string>, std::greater<int> > resistances;
	std::set<std::string> seen_types;
	const team &unit_team = rc.teams()[u.side() - 1];
	const team &viewing_team = rc.teams()[rc.screen().viewing_team()];
	for (const unit &enemy : rc.units())
	{
		if (enemy.incapacitated()) //we can't attack statues so don't display them in this tooltip
			continue;
		if (!unit_team.is_enemy(enemy.side()))
			continue;
		const map_location &loc = enemy.get_location();
		if (viewing_team.fogged(loc) ||
		    (viewing_team.is_enemy(enemy.side()) && enemy.invisible(loc)))
			continue;
		bool new_type = seen_types.insert(enemy.type_id()).second;
		if (new_type) {
			int resistance = enemy.resistance_against(at, false, loc);
			resistances[resistance].insert(enemy.type_name());
		}
	}

	typedef std::pair<int, std::set<std::string> > resist_units;
	for (const resist_units &resist : resistances) {
		int damage = round_damage(specials_damage, damage_multiplier * resist.first, damage_divisor);
		tooltip << "<b>" << damage << "</b>  "
			<< "<i>(" << utils::signed_percent(resist.first-100) << ")</i> : "
			<< utils::join(resist.second, ", ") << '\n';
	}
	add_text(res, flush(str), flush(tooltip));

	const std::string &accuracy_parry = at.accuracy_parry_description();
	if (!accuracy_parry.empty())
	{
		str << span_color(font::weapon_details_color)
			<< "  " << accuracy_parry << "</span>\n";
		int accuracy = at.accuracy();
		if (accuracy) {
			tooltip << _("Accuracy:") << "<b>"
				<< utils::signed_percent(accuracy) << "</b>\n";
		}
		int parry = at.parry();
		if (parry) {
			tooltip << _("Parry:") << "<b>"
				<< utils::signed_percent(parry) << "</b>\n";
			}
		add_text(res, flush(str), flush(tooltip));
	}

	at.set_specials_context_for_listing();
	std::vector<bool> active;
	const std::vector<std::pair<t_string, t_string> > &specials = at.special_tooltips(&active);
	const size_t specials_size = specials.size();
	for ( size_t i = 0; i != specials_size; ++i )
	{
		// Aliases for readability:
		const t_string &name = specials[i].first;
		const t_string &description = specials[i].second;
		const SDL_Color &details_color = active[i] ? font::weapon_details_color :
		                                             font::inactive_details_color;

		str << span_color(details_color) << "  " << "  " << name << naps << '\n';
		std::string help_page = "weaponspecial_" + name.base_str();
		tooltip << _("Weapon special: ") << "<b>" << name << "</b>";
		if ( !active[i] )
			tooltip << "<i>" << _(" (inactive)") << "</i>";
		tooltip << '\n' << description;

		add_text(res, flush(str), flush(tooltip), help_page);
	}
	return damage;
}
Beispiel #22
0
std::deque<action_ptr> find_actions_of(unit const &target)
{
	return (*resources::teams)[target.side()-1].get_side_actions()->actions_of(target);
}
Beispiel #23
0
/**
 * Fires sighted events for the sides that can see @a target.
 * If @a cache is supplied, only those sides might get events.
 * If @a cache is nullptr, all sides might get events.
 * This function is for the sighting *of* units that clear the shroud; it is
 * the complement of shroud_clearer::fire_events(), which handles sighting *by*
 * units that clear the shroud.
 *
 * See get_sides_not_seeing() for a way to obtain a cache.
 *
 * @returns true if an event has mutated the game state.
 */
game_events::pump_result_t actor_sighted(const unit & target, const std::vector<int> * cache)
/* Current logic:
 * 1) One event is fired per side that can see the target.
 * 2) The second unit for the event is one that can see the target, if possible.
 * 3) If no units on a side can see the target, a second unit is chosen as
 *    close as possible (but this behavior should not be relied on; it is
 *    subject to change at any time, should it become inconvenient).
 * 4) A side with no units at all will not get a sighted event.
 * 5) Sides that do not use fog or shroud CAN get sighted events.
 */
{
	const std::vector<team> & teams = resources::gameboard->teams();
	const std::size_t teams_size = teams.size();
	const map_location & target_loc = target.get_location();

	// Determine the teams that (probably) should get events.
	boost::dynamic_bitset<> needs_event;
	needs_event.resize(teams_size, cache == nullptr);
	if ( cache != nullptr ) {
		// Flag just the sides in the cache as needing events.
		for (int side : *cache)
			needs_event[side-1] = true;
	}
	// Exclude the target's own team.
	needs_event[target.side()-1] = false;
	// Exclude those teams that cannot see the target.
	for ( std::size_t i = 0; i != teams_size; ++i )
		needs_event[i] = needs_event[i] && target.is_visible_to_team(teams[i], false);

	// Cache "jamming".
	std::vector< std::map<map_location, int>> jamming_cache(teams_size);
	for ( std::size_t i = 0; i != teams_size; ++i )
		if ( needs_event[i] )
			create_jamming_map(jamming_cache[i], teams[i]);

	// Look for units that can be used as the second unit in sighted events.
	std::vector<const unit *> second_units(teams_size, nullptr);
	std::vector<std::size_t> distances(teams_size, UINT_MAX);
	for (const unit & viewer : resources::gameboard->units()) {
		const std::size_t index = viewer.side() - 1;
		// Does viewer belong to a team for which we still need a unit?
		if ( needs_event[index]  &&  distances[index] != 0 ) {
			if ( can_see(viewer, target_loc, &jamming_cache[index]) ) {
				// Definitely use viewer as the second unit.
				second_units[index] = &viewer;
				distances[index] = 0;
			}
			else {
				// Consider viewer as a backup if it is close.
				std::size_t viewer_distance =
					distance_between(target_loc, viewer.get_location());
				if ( viewer_distance < distances[index] ) {
					second_units[index] = &viewer;
					distances[index] = viewer_distance;
				}
			}
		}
	}

	// Raise events for the appropriate teams.
	const game_events::entity_location target_entity(target);
	for ( std::size_t i = 0; i != teams_size; ++i )
		if ( second_units[i] != nullptr ) {
			resources::game_events->pump().raise(sighted_str, target_entity, game_events::entity_location(*second_units[i]));
		}

	// Fire the events and return.
	return resources::game_events->pump()();
}
Beispiel #24
0
	BOOST_FOREACH (unit & i, units_) {
		if (i.side() == player_num) {
			i.new_turn();
		}
	}
Beispiel #25
0
bool basic_unit_filter_impl::internal_matches_filter(const unit & u, const map_location& loc, const unit* u2) const
{
	if (!vcfg["name"].blank() && vcfg["name"].t_str() != u.name()) {
		return false;
	}

	if (!vcfg["id"].empty()) {
		std::vector<std::string> id_list = utils::split(vcfg["id"]);
		if (std::find(id_list.begin(), id_list.end(), u.id()) == id_list.end()) {
			return false;
		}
	}

	// Allow 'speaker' as an alternative to id, since people use it so often
	if (!vcfg["speaker"].blank() && vcfg["speaker"].str() != u.id()) {
		return false;
	}

	if (vcfg.has_child("filter_location")) {
		if (vcfg.count_children("filter_location") > 1) {
			FAIL("Encountered multiple [filter_location] children of a standard unit filter. "
				 "This is not currently supported and in all versions of wesnoth would have "
				 "resulted in the later children being ignored. You must use [and] or similar "
				 "to achieve the desired result.");
		}
		terrain_filter filt(vcfg.child("filter_location"), &fc_, use_flat_tod_);
		if (!filt.match(loc)) {
			return false;
		}
	}

	if(vcfg.has_child("filter_side")) {
		if (vcfg.count_children("filter_side") > 1) {
			FAIL("Encountered multiple [filter_side] children of a standard unit filter. "
				 "This is not currently supported and in all versions of wesnoth would have "
				 "resulted in the later children being ignored. You must use [and] or similar "
				 "to achieve the desired result.");
		}
		side_filter filt(vcfg.child("filter_side"), &fc_);
		if(!filt.match(u.side()))
			return false;
	}

	// Also allow filtering on location ranges outside of the location filter
	if (!vcfg["x"].blank() || !vcfg["y"].blank()){
		if(vcfg["x"] == "recall" && vcfg["y"] == "recall") {
			//locations on the map are considered to not be on a recall list
			if (fc_.get_disp_context().map().on_board(loc))
			{
				return false;
			}
		} else if(vcfg["x"].empty() && vcfg["y"].empty()) {
			return false;
		} else if(!loc.matches_range(vcfg["x"], vcfg["y"])) {
			return false;
		}
	}

	// The type could be a comma separated list of types
	if (!vcfg["type"].empty()) {
		std::vector<std::string> types = utils::split(vcfg["type"]);
		if (std::find(types.begin(), types.end(), u.type_id()) == types.end()) {
			return false;
		}
	}

	// Shorthand for all advancements of a given type
	if (!vcfg["type_tree"].empty()) {
		std::set<std::string> types;
		for(const std::string type : utils::split(vcfg["type_tree"])) {
			if(types.count(type)) {
				continue;
			}
			if(const unit_type* ut = unit_types.find(type)) {
				const auto& tree = ut->advancement_tree();
				types.insert(tree.begin(), tree.end());
				types.insert(type);
			}
		}
		if(types.find(u.type_id()) == types.end()) {
			return false;
		}
	}

	// The variation_type could be a comma separated list of types
	if (!vcfg["variation"].empty())
	{
		std::vector<std::string> types = utils::split(vcfg["variation"]);
		if (std::find(types.begin(), types.end(), u.variation()) == types.end()) {
			return false;
		}
	}

	// The has_variation_type could be a comma separated list of types
	if (!vcfg["has_variation"].empty())
	{
		bool match = false;
		// If this unit is a variation itself then search in the base unit's variations.
		const unit_type* const type = u.variation().empty() ? &u.type() : unit_types.find(u.type().base_id());
		assert(type);

		for (const std::string& variation_id : utils::split(vcfg["has_variation"])) {
			if (type->has_variation(variation_id)) {
				match = true;
				break;
			}
		}
		if (!match) return false;
	}

	if (!vcfg["ability"].empty())
	{
		bool match = false;

		for (const std::string& ability_id : utils::split(vcfg["ability"])) {
			if (u.has_ability_by_id(ability_id)) {
				match = true;
				break;
			}
		}
		if (!match) return false;
	}

	if (!vcfg["race"].empty()) {
		std::vector<std::string> races = utils::split(vcfg["race"]);
		if (std::find(races.begin(), races.end(), u.race()->id()) == races.end()) {
			return false;
		}
	}

	if (!vcfg["gender"].blank() && string_gender(vcfg["gender"]) != u.gender()) {
		return false;
	}

	if (!vcfg["side"].empty() && vcfg["side"].to_int(-999) != u.side()) {
		std::vector<std::string> sides = utils::split(vcfg["side"]);
		const std::string u_side = std::to_string(u.side());
		if (std::find(sides.begin(), sides.end(), u_side) == sides.end()) {
			return false;
		}
	}

	// handle statuses list
	if (!vcfg["status"].empty()) {
		bool status_found = false;

		for (const std::string status : utils::split(vcfg["status"])) {
			if(u.get_state(status)) {
				status_found = true;
				break;
			}
		}

		if(!status_found) {
			return false;
		}
	}

	if (vcfg.has_child("has_attack")) {
		const vconfig& weap_filter = vcfg.child("has_attack");
		bool has_weapon = false;
		for(const attack_type& a : u.attacks()) {
			if(a.matches_filter(weap_filter.get_parsed_config())) {
				has_weapon = true;
				break;
			}
		}
		if(!has_weapon) {
			return false;
		}
	} else if (!vcfg["has_weapon"].blank()) {
		std::string weapon = vcfg["has_weapon"];
		bool has_weapon = false;
		for(const attack_type& a : u.attacks()) {
			if(a.id() == weapon) {
				has_weapon = true;
				break;
			}
		}
		if(!has_weapon) {
			return false;
		}
	}

	if (!vcfg["role"].blank() && vcfg["role"].str() != u.get_role()) {
		return false;
	}

	if (!vcfg["ai_special"].blank() && ((vcfg["ai_special"].str() == "guardian") != u.get_state(unit::STATE_GUARDIAN))) {
		return false;
	}

	if (!vcfg["canrecruit"].blank() && vcfg["canrecruit"].to_bool() != u.can_recruit()) {
		return false;
	}

	if (!vcfg["recall_cost"].blank() && vcfg["recall_cost"].to_int(-1) != u.recall_cost()) {
		return false;
	}

	if (!vcfg["level"].blank() && vcfg["level"].to_int(-1) != u.level()) {
		return false;
	}

	if (!vcfg["defense"].blank() && vcfg["defense"].to_int(-1) != u.defense_modifier(fc_.get_disp_context().map().get_terrain(loc))) {
		return false;
	}

	if (!vcfg["movement_cost"].blank() && vcfg["movement_cost"].to_int(-1) != u.movement_cost(fc_.get_disp_context().map().get_terrain(loc))) {
		return false;
	}

	// Now start with the new WML based comparison.
	// If a key is in the unit and in the filter, they should match
	// filter only => not for us
	// unit only => not filtered
	config unit_cfg; // No point in serializing the unit once for each [filter_wml]!
	for (const vconfig& wmlcfg : vcfg.get_children("filter_wml")) {
			config fwml = wmlcfg.get_parsed_config();
			/* Check if the filter only cares about variables.
			   If so, no need to serialize the whole unit. */
			config::all_children_itors ci = fwml.all_children_range();
			if (fwml.all_children_count() == 1 && 
				fwml.attribute_count() == 1 &&
			    ci.front().key == "variables") {
				if (!u.variables().matches(ci.front().cfg))
					return false;
			} else {
				if (unit_cfg.empty())
					u.write(unit_cfg);
				if (!unit_cfg.matches(fwml))
					return false;
			}
	}

	for (const vconfig& vision : vcfg.get_children("filter_vision")) {
		std::set<int> viewers;

		// Use standard side filter
		side_filter ssf(vision, &fc_);
		std::vector<int> sides = ssf.get_teams();
		viewers.insert(sides.begin(), sides.end());

		bool found = false;
		for (const int viewer : viewers) {
			bool fogged = fc_.get_disp_context().teams()[viewer - 1].fogged(loc);
			bool hiding = u.invisible(loc, fc_.get_disp_context());
			bool unit_hidden = fogged || hiding;
			if (vision["visible"].to_bool(true) != unit_hidden) {
				found = true;
				break;
			}
		}
		if (!found) {return false;}
	}

	if (vcfg.has_child("filter_adjacent")) {
		const unit_map& units = fc_.get_disp_context().units();
		map_location adjacent[6];
		get_adjacent_tiles(loc, adjacent);

		for (const vconfig& adj_cfg : vcfg.get_children("filter_adjacent")) {
			int match_count=0;
			unit_filter filt(adj_cfg, &fc_, use_flat_tod_);

			config::attribute_value i_adjacent = adj_cfg["adjacent"];
			std::vector<map_location::DIRECTION> dirs;
			if (i_adjacent.blank()) {
				dirs = map_location::default_dirs();
			} else {
				dirs = map_location::parse_directions(i_adjacent);
			}

			std::vector<map_location::DIRECTION>::const_iterator j, j_end = dirs.end();
			for (j = dirs.begin(); j != j_end; ++j) {
				unit_map::const_iterator unit_itor = units.find(adjacent[*j]);
				if (unit_itor == units.end() || !filt(*unit_itor, u)) {
					continue;
				}
				boost::optional<bool> is_enemy;
				if (!adj_cfg["is_enemy"].blank()) {
					is_enemy = adj_cfg["is_enemy"].to_bool();
				}
				if (!is_enemy || *is_enemy ==
				    fc_.get_disp_context().teams()[u.side() - 1].is_enemy(unit_itor->side())) {
					++match_count;
				}
			}

			static std::vector<std::pair<int,int> > default_counts = utils::parse_ranges("1-6");
			config::attribute_value i_count = adj_cfg["count"];
			if(!in_ranges(match_count, !i_count.blank() ? utils::parse_ranges(i_count) : default_counts)) {
				return false;
			}
		}
	}

	if (!vcfg["find_in"].blank()) {
		// Allow filtering by searching a stored variable of units
		if (const game_data * gd = fc_.get_game_data()) {
			try
			{
				variable_access_const vi = gd->get_variable_access_read(vcfg["find_in"]);
				bool found_id = false;
				for (const config& c : vi.as_array())
				{
					if(c["id"] == u.id())
						found_id = true;
				}
				if(!found_id)
				{
					return false;
				}
			}
			catch(const invalid_variablename_exception&)
			{
				return false;
			}
		}
	}
	if (!vcfg["formula"].blank()) {
		try {
			const unit_callable main(loc,u);
			game_logic::map_formula_callable callable(&main);
			if (u2) {
				std::shared_ptr<unit_callable> secondary(new unit_callable(*u2));
				callable.add("other", variant(secondary.get()));
				// It's not destroyed upon scope exit because the variant holds a reference
			}
			const game_logic::formula form(vcfg["formula"]);
			if(!form.evaluate(callable).as_bool()) {
				return false;
			}
			return true;
		} catch(game_logic::formula_error& e) {
			lg::wml_error() << "Formula error in unit filter: " << e.type << " at " << e.filename << ':' << e.line << ")\n";
			// Formulae with syntax errors match nothing
			return false;
		}
	}

	if (!vcfg["lua_function"].blank()) {
		if (game_lua_kernel * lk = fc_.get_lua_kernel()) {
			bool b = lk->run_filter(vcfg["lua_function"].str().c_str(), u);
			if (!b) return false;
		}
	}

	return true;
}
Beispiel #26
0
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;
}
Beispiel #27
0
bool game_state::can_recruit_on(const unit& leader, const map_location& recruit_loc) const
{
	return can_recruit_on(leader.get_location(), recruit_loc, leader.side());
}