Ejemplo n.º 1
0
/**
 * Convenience constructor for when an event has a unit, saving the caller
 * the need to explicitly get the location and underlying ID.
 */
entity_location::entity_location(const unit &u)
	: map_location(u.get_location())
	, id_(u.underlying_id())
	, filter_loc_(*this)
{}
Ejemplo n.º 2
0
	BOOST_FOREACH (unit & i, units_) {
		if (i.side() == player_num) {
			i.new_turn();
		}
	}
void recall_unit(const unit& u)
{
	stats& s = get_stats(u.side_id());
	s.recalls[u.type_id()]++;
	s.recall_cost += u.cost();
}
Ejemplo n.º 4
0
	unit_movement_resetter(unit& u, bool operate=true) : u_(u), moves_(u.movement_)
	{
		if(operate) {
			u.movement_ = u.total_movement();
		}
	}
Ejemplo n.º 5
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;
		}
	}

	// 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;
		const std::vector<attack_type>& attacks = u.attacks();
		for(std::vector<attack_type>::const_iterator i = attacks.begin();
			i != attacks.end(); ++i) {
			if(i->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;
		const std::vector<attack_type>& attacks = u.attacks();
		for(std::vector<attack_type>::const_iterator i = attacks.begin();
			i != attacks.end(); ++i) {
			if(i->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/*, false(?) */);
			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;
}
Ejemplo n.º 6
0
/** @todo FIXME: Hand previous defender unit in here. */
int battle_context::choose_defender_weapon(const unit& attacker,
		const unit& defender,
		unsigned attacker_weapon,
		const unit_map& units,
		const map_location& attacker_loc,
		const map_location& defender_loc,
		const combatant* prev_def)
{
	VALIDATE(attacker_weapon < attacker.attacks().size(), _("An invalid attacker weapon got selected."));

	const attack_type& att = attacker.attacks()[attacker_weapon];
	std::vector<unsigned int> choices;

	// What options does defender have?
	unsigned int i;

	for(i = 0; i < defender.attacks().size(); ++i) {
		const attack_type& def = defender.attacks()[i];

		if(def.range() == att.range() && def.defense_weight() > 0) {
			choices.push_back(i);
		}
	}

	if(choices.empty()) {
		return -1;
	}

	if(choices.size() == 1) {
		const battle_context_unit_stats def_stats(
				defender, defender_loc, choices[0], false, attacker, attacker_loc, att.shared_from_this(), units);

		return (def_stats.disable) ? -1 : choices[0];
	}

	// Multiple options:
	// First pass : get the best weight and the minimum simple rating for this weight.
	// simple rating = number of blows * damage per blows (resistance taken in account) * cth * weight
	// Eligible attacks for defense should have a simple rating greater or equal to this weight.

	int min_rating = 0;
	{
		double max_weight = 0.0;

		for(i = 0; i < choices.size(); ++i) {
			const attack_type& def = defender.attacks()[choices[i]];

			if(def.defense_weight() >= max_weight) {
				const battle_context_unit_stats def_stats(defender, defender_loc, choices[i], false, attacker,
						attacker_loc, att.shared_from_this(), units);

				if(def_stats.disable) {
					continue;
				}

				max_weight = def.defense_weight();
				int rating = static_cast<int>(
						def_stats.num_blows * def_stats.damage * def_stats.chance_to_hit * def.defense_weight());

				if(def.defense_weight() > max_weight || rating < min_rating) {
					min_rating = rating;
				}
			}
		}
	}

	// Multiple options: simulate them, save best.
	for(i = 0; i < choices.size(); ++i) {
		const attack_type& def = defender.attacks()[choices[i]];

		std::unique_ptr<battle_context_unit_stats> att_stats(new battle_context_unit_stats(
				attacker, attacker_loc, attacker_weapon, true, defender, defender_loc, def.shared_from_this(), units));

		std::unique_ptr<battle_context_unit_stats> def_stats(new battle_context_unit_stats(
				defender, defender_loc, choices[i], false, attacker, attacker_loc, att.shared_from_this(), units));

		if(def_stats->disable) {
			continue;
		}

		std::unique_ptr<combatant> att_comb(new combatant(*att_stats));
		std::unique_ptr<combatant> def_comb(new combatant(*def_stats, prev_def));

		att_comb->fight(*def_comb);

		int simple_rating = static_cast<int>(
				def_stats->num_blows * def_stats->damage * def_stats->chance_to_hit * def.defense_weight());

		if(simple_rating >= min_rating &&
			(!attacker_combatant_ || better_combat(*def_comb, *att_comb, *defender_combatant_, *attacker_combatant_, 1.0))
		) {
			attacker_combatant_ = std::move(att_comb);
			defender_combatant_ = std::move(def_comb);
			attacker_stats_ = std::move(att_stats);
			defender_stats_ = std::move(def_stats);
		}
	}

	return defender_stats_ ? defender_stats_->attack_num : -1;
}
Ejemplo n.º 7
0
static void find_routes(const gamemap& map, const unit_map& /*units*/,
		const unit& u, const map_location& loc,
		int move_left, pathfind::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];
	pathfind::teleport_map teleports;
	if (allow_teleport) {
	  teleports = pathfind::get_teleport_locations(u, viewing_team, see_all, ignore_units);
	}

	const int total_movement = u.total_movement();

	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;

		std::set<map_location> allowed_teleports;
		teleports.get_adjacents(allowed_teleports, n.curr);
		std::vector<map_location> locs(6 + allowed_teleports.size());
		std::copy(allowed_teleports.begin(), allowed_teleports.end(), locs.begin() + 6);
		get_adjacent_tiles(n.curr, &locs[0]);
		for (int i = locs.size(); i-- > 0; ) {
			if (!locs[i].valid(map.w(), map.h())) continue;

			if (locs[i] == n.curr) 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(locs[i], viewing_team, see_all);
				if (v && current_team.is_enemy(v->side()))
					continue;

				if (!force_ignore_zocs && t.movement_left > 0
				    && pathfind::enemy_zoc(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;
			pathfind::paths::step s =
				{ n.curr, n.prev, n.movement_left + n.turns_left * total_movement };
			destinations.push_back(s);
		}
	}
}
Ejemplo n.º 8
0
int un_recall_unit_cost(const unit& u)  // this really belongs elsewhere, perhaps in undo.cpp
{					// but I'm too lazy to do it at the moment
	stats& s = get_stats(get_team_save_id(u));
	s.recalls[u.type_id()]--;
	return u.recall_cost();
}
Ejemplo n.º 9
0
void advance_unit(const unit& u)
{
	stats& s = get_stats(get_team_save_id(u));
	s.advanced_to[u.type_id()]++;
}
Ejemplo n.º 10
0
void un_recall_unit(const unit& u)
{
	stats& s = get_stats(get_team_save_id(u));
	s.recalls[u.type_id()]--;
	s.recall_cost -= u.cost();
}
Ejemplo n.º 11
0
void un_recruit_unit(const unit& u)
{
	stats& s = get_stats(get_team_save_id(u));
	s.recruits[u.type().base_id()]--;
	s.recruit_cost -= u.cost();
}
Ejemplo n.º 12
0
void recall_unit(const unit& u)
{
	stats& s = get_stats(get_team_save_id(u));
	s.recalls[u.type_id()]++;
	s.recall_cost += u.cost();
}
Ejemplo n.º 13
0
bool human_team_can_ai(const unit& u)
{
	return !u.human() || tent::tower_mode() || u.provoked_turns() || u.task() == unit::TASK_GUARD;
}
Ejemplo n.º 14
0
/**
 * Convenience constructor for when an event has a unit that needs to be
 * filtered as if it was in a different location, and the caller does not
 * want to explicitly get the unit's location and underlying ID.
 */
entity_location::entity_location(const unit &u, const map_location & filter_loc)
	: map_location(u.get_location())
	, id_(u.underlying_id())
	, filter_loc_(filter_loc)
{}
Ejemplo n.º 15
0
int battle_context::choose_attacker_weapon(const unit& attacker,
		const unit& defender,
		const unit_map& units,
		const map_location& attacker_loc,
		const map_location& defender_loc,
		double harm_weight,
		int* defender_weapon,
		const combatant* prev_def)
{
	std::vector<unsigned int> choices;

	// What options does attacker have?
	unsigned int i;

	for(i = 0; i < attacker.attacks().size(); ++i) {
		const attack_type& att = attacker.attacks()[i];

		if(att.attack_weight() > 0) {
			choices.push_back(i);
		}
	}

	if(choices.empty()) {
		return -1;
	}

	if(choices.size() == 1) {
		*defender_weapon
				= choose_defender_weapon(attacker, defender, choices[0], units, attacker_loc, defender_loc, prev_def);
		const_attack_ptr def_weapon
				= *defender_weapon >= 0 ? defender.attacks()[*defender_weapon].shared_from_this() : nullptr;
		attacker_stats_.reset(new battle_context_unit_stats(
				attacker, attacker_loc, choices[0], true, defender, defender_loc, def_weapon, units));

		if(attacker_stats_->disable) {
			return -1;
		}

		const attack_type& att = attacker.attacks()[choices[0]];
		defender_stats_.reset(new battle_context_unit_stats(
				defender, defender_loc, *defender_weapon, false, attacker, attacker_loc, att.shared_from_this(), units));

		return choices[0];
	}

	// Multiple options: simulate them, save best.
	std::unique_ptr<battle_context_unit_stats> best_att_stats(nullptr);
	std::unique_ptr<battle_context_unit_stats> best_def_stats(nullptr);

	std::unique_ptr<combatant> best_att_comb(nullptr);
	std::unique_ptr<combatant> best_def_comb(nullptr);

	for(i = 0; i < choices.size(); ++i) {
		const attack_type& att = attacker.attacks()[choices[i]];

		int def_weapon =
			choose_defender_weapon(attacker, defender, choices[i], units, attacker_loc, defender_loc, prev_def);

		// If that didn't simulate, do so now.
		if(!attacker_combatant_) {
			const_attack_ptr def = nullptr;

			if(def_weapon >= 0) {
				def = defender.attacks()[def_weapon].shared_from_this();
			}

			attacker_stats_.reset(new battle_context_unit_stats(
					attacker, attacker_loc, choices[i], true, defender, defender_loc, def, units));

			if(attacker_stats_->disable) {
				continue;
			}

			defender_stats_.reset(new battle_context_unit_stats(
					defender, defender_loc, def_weapon, false, attacker, attacker_loc, att.shared_from_this(), units));

			attacker_combatant_.reset(new combatant(*attacker_stats_));
			defender_combatant_.reset(new combatant(*defender_stats_, prev_def));

			attacker_combatant_->fight(*defender_combatant_);
		} else {
			if(attacker_stats_ != nullptr && attacker_stats_->disable) {
				continue;
			}
		}

		if(!best_att_comb ||
			better_combat(*attacker_combatant_, *defender_combatant_, *best_att_comb, *best_def_comb, harm_weight)
		) {
			best_att_comb = std::move(attacker_combatant_);
			best_def_comb = std::move(defender_combatant_);
			best_att_stats = std::move(attacker_stats_);
			best_def_stats = std::move(defender_stats_);
		}

		attacker_combatant_.reset();
		defender_combatant_.reset();
		attacker_stats_.reset();
		defender_stats_.reset();
	}

	attacker_combatant_ = std::move(best_att_comb);
	defender_combatant_ = std::move(best_def_comb);
	attacker_stats_ = std::move(best_att_stats);
	defender_stats_ = std::move(best_def_stats);

	// These currently mean the same thing, but assumptions like that have been broken before
	if(!defender_stats_ || !attacker_stats_) {
		return -1;
	}

	*defender_weapon = defender_stats_->attack_num;
	return attacker_stats_->attack_num;
}
Ejemplo n.º 16
0
bool animate_unit_advancement(unit& u, size_t choice)
{
	const events::command_disabler cmd_disabler;

	if (u.advances() == false) {
		return false;
	}

	const std::vector<std::string>& options = u.advances_to();
	std::vector<config> mod_options = u.get_modification_advances();

	if (choice >= options.size() + mod_options.size()) {
		return false;
	}

	// When the unit advances, it fades to white, and then switches
	// to the new unit, then fades back to the normal colour

	game_display* disp = resources::screen;
	rect_of_hexes& draw_area = disp->draw_area();
	bool force_scroll = preferences::scroll_to_action();
	bool animate = force_scroll || point_in_rect_of_hexes(u.get_location().x, u.get_location().y, draw_area);
	if (!resources::screen->video().update_locked() && animate) {
		unit_animator animator;
		bool with_bars = true;
		animator.add_animation(&u, "levelout", u.get_location(), map_location(), 0, with_bars);
		animator.start_animations();
		animator.wait_for_end();
	}

	if(choice < options.size()) {
		// chosen_unit is not a reference, since the unit may disappear at any moment.
		std::string chosen_unit = options[choice];
		::advance_unit(u, chosen_unit);
	} else {
		unit* amla_unit = &u;
		const config &mod_option = mod_options[choice - options.size()];
		
		game_events::fire("advance", u.get_location());

		amla_unit->get_experience(increase_xp::attack_ublock(*amla_unit), -amla_unit->max_experience()); // subtract xp required
		// ALMA may want to change status, but add_modification in modify_according_to_hero cannot change state,
		// so it need call amla_unit->add_modification instead of amla_unit->modify_according_to_hero.
		amla_unit->add_modification(mod_option);

		game_events::fire("post_advance", u.get_location());
	}

	resources::screen->invalidate_unit();

	if (!resources::screen->video().update_locked()) {
		if (force_scroll || point_in_rect_of_hexes(u.get_location().x, u.get_location().y, draw_area)) {
			unit_animator animator;
			animator.add_animation(&u, "levelin", u.get_location(), map_location(), 0, true);
			animator.start_animations();
			animator.wait_for_end();
			animator.set_all_standing();
		
			resources::screen->invalidate(u.get_location());
			resources::screen->draw();
		}
		events::pump();
	}

	resources::screen->invalidate_all();
	if (force_scroll || point_in_rect_of_hexes(u.get_location().x, u.get_location().y, draw_area)) {
		resources::screen->draw();
	}

	return true;
}
Ejemplo n.º 17
0
battle_context_unit_stats::battle_context_unit_stats(const unit& u,
		const map_location& u_loc,
		int u_attack_num,
		bool attacking,
		const unit& opp,
		const map_location& opp_loc,
		const_attack_ptr opp_weapon,
		const unit_map& units)
	: weapon(nullptr)
	, attack_num(u_attack_num)
	, is_attacker(attacking)
	, is_poisoned(u.get_state(unit::STATE_POISONED))
	, is_slowed(u.get_state(unit::STATE_SLOWED))
	, slows(false)
	, drains(false)
	, petrifies(false)
	, plagues(false)
	, poisons(false)
	, backstab_pos(false)
	, swarm(false)
	, firststrike(false)
	, disable(false)
	, experience(u.experience())
	, max_experience(u.max_experience())
	, level(u.level())
	, rounds(1)
	, hp(0)
	, max_hp(u.max_hitpoints())
	, chance_to_hit(0)
	, damage(0)
	, slow_damage(0)
	, drain_percent(0)
	, drain_constant(0)
	, num_blows(0)
	, swarm_min(0)
	, swarm_max(0)
	, plague_type()
{
	// Get the current state of the unit.
	if(attack_num >= 0) {
		weapon = u.attacks()[attack_num].shared_from_this();
	}

	if(u.hitpoints() < 0) {
		LOG_CF << "Unit with " << u.hitpoints() << " hitpoints found, set to 0 for damage calculations\n";
		hp = 0;
	} else if(u.hitpoints() > u.max_hitpoints()) {
		// If a unit has more hp than its maximum, the engine will fail with an
		// assertion failure due to accessing the prob_matrix out of bounds.
		hp = u.max_hitpoints();
	} else {
		hp = u.hitpoints();
	}

	// Exit if no weapon.
	if(!weapon) {
		return;
	}

	// Get the weapon characteristics as appropriate.
	weapon->set_specials_context(u_loc, opp_loc, attacking, opp_weapon);

	if(opp_weapon) {
		opp_weapon->set_specials_context(opp_loc, u_loc, !attacking, weapon);
	}

	slows = weapon->get_special_bool("slow");
	drains = !opp.get_state("undrainable") && weapon->get_special_bool("drains");
	petrifies = weapon->get_special_bool("petrifies");
	poisons = !opp.get_state("unpoisonable") && weapon->get_special_bool("poison") && !opp.get_state(unit::STATE_POISONED);
	backstab_pos = is_attacker && backstab_check(u_loc, opp_loc, units, resources::gameboard->teams());
	rounds = weapon->get_specials("berserk").highest("value", 1).first;
	firststrike = weapon->get_special_bool("firststrike");

	{
		const int distance = distance_between(u_loc, opp_loc);
		const bool out_of_range = distance > weapon->max_range() || distance < weapon->min_range();
		disable = weapon->get_special_bool("disable") || out_of_range;
	}

	// Handle plague.
	unit_ability_list plague_specials = weapon->get_specials("plague");
	plagues = !opp.get_state("unplagueable") && !plague_specials.empty() &&
		opp.undead_variation() != "null" && !resources::gameboard->map().is_village(opp_loc);

	if(plagues) {
		plague_type = (*plague_specials.front().first)["type"].str();

		if(plague_type.empty()) {
			plague_type = u.type().base_id();
		}
	}

	// Compute chance to hit.
	chance_to_hit = opp.defense_modifier(resources::gameboard->map().get_terrain(opp_loc)) + weapon->accuracy()
		- (opp_weapon ? opp_weapon->parry() : 0);

	if(chance_to_hit > 100) {
		chance_to_hit = 100;
	}

	unit_ability_list cth_specials = weapon->get_specials("chance_to_hit");
	unit_abilities::effect cth_effects(cth_specials, chance_to_hit, backstab_pos);
	chance_to_hit = cth_effects.get_composite_value();

	if(opp.get_state("invulnerable")) {
		chance_to_hit = 0;
	}

	// Compute base damage done with the weapon.
	int base_damage = weapon->modified_damage(backstab_pos);

	// Get the damage multiplier applied to the base damage of the weapon.
	int damage_multiplier = 100;

	// Time of day bonus.
	damage_multiplier += combat_modifier(
			resources::gameboard->units(), resources::gameboard->map(), u_loc, u.alignment(), u.is_fearless());

	// Leadership bonus.
	int leader_bonus = under_leadership(units, u_loc).first;
	if(leader_bonus != 0) {
		damage_multiplier += leader_bonus;
	}

	// Resistance modifier.
	damage_multiplier *= opp.damage_from(*weapon, !attacking, opp_loc);

	// Compute both the normal and slowed damage.
	damage = round_damage(base_damage, damage_multiplier, 10000);
	slow_damage = round_damage(base_damage, damage_multiplier, 20000);

	if(is_slowed) {
		damage = slow_damage;
	}

	// Compute drain amounts only if draining is possible.
	if(drains) {
		unit_ability_list drain_specials = weapon->get_specials("drains");

		// Compute the drain percent (with 50% as the base for backward compatibility)
		unit_abilities::effect drain_percent_effects(drain_specials, 50, backstab_pos);
		drain_percent = drain_percent_effects.get_composite_value();
	}

	// Add heal_on_hit (the drain constant)
	unit_ability_list heal_on_hit_specials = weapon->get_specials("heal_on_hit");
	unit_abilities::effect heal_on_hit_effects(heal_on_hit_specials, 0, backstab_pos);
	drain_constant += heal_on_hit_effects.get_composite_value();

	drains = drain_constant || drain_percent;

	// Compute the number of blows and handle swarm.
	weapon->modified_attacks(backstab_pos, swarm_min, swarm_max);
	swarm = swarm_min != swarm_max;
	num_blows = calc_blows(hp);
}
Ejemplo n.º 18
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;
}
Ejemplo n.º 19
0
void show_unit_description(CVideo& video, const unit &u)
{
	help::show_unit_description(video, u.type());
}
Ejemplo n.º 20
0
int number_of_possible_advances(const unit &u)
{
	return u.advances_to().size() + u.get_modification_advances().size();
}
Ejemplo n.º 21
0
static stuff_list_adder add_unit_entry(stuff_list_adder& progress, const unit& u, const display_context& dc)
{

	color_t 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 << font::span_color(team_color);
	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;
}
Ejemplo n.º 22
0
temporary_unit_placer::temporary_unit_placer(unit_map& m, const map_location& loc, unit& u)
	: m_(m), loc_(loc), temp_(m_.extract(loc))
{
	u.clone();
	m_.add(loc, u);
}
Ejemplo n.º 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.
 */
bool 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 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 ( size_t i = 0; i != teams_size; ++i )
		needs_event[i] = needs_event[i] && target.is_visible_to_team(teams[i], *resources::gameboard, false);

	// Cache "jamming".
	std::vector< std::map<map_location, int> > jamming_cache(teams_size);
	for ( 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<size_t> distances(teams_size, UINT_MAX);
	for (const unit & viewer : *resources::units) {
		const 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.
				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 ( 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()();
}
Ejemplo n.º 24
0
temporary_unit_placer::temporary_unit_placer(game_board& b, const map_location& loc, unit& u)
	: m_(b.units_), loc_(loc), temp_(m_.extract(loc))
{
	u.clone();
	m_.add(loc, u);
}
Ejemplo n.º 25
0
bool unit_filter::matches(const unit & u, const unit & u2) const {
	return matches(u, u.get_location(), u2);
}
Ejemplo n.º 26
0
void tunit_preview_pane::set_displayed_unit(const unit& u)
{
	// Sets the current type id for the profile button callback to use
	current_type_ = u.type_id();

	if(icon_type_) {
		std::string mods = u.image_mods();

		if(u.can_recruit()) {
			mods += "~BLIT(" + unit::leader_crown() + ")";
		}

		for(const std::string& overlay : u.overlays()) {
			mods += "~BLIT(" + overlay + ")";
		}

		mods += "~SCALE_INTO_SHARP(144,144)" + image_mods_;

		icon_type_->set_label(u.absolute_image() + mods);
	}

	if(label_name_) {
		std::string name;
		if(!u.name().empty()) {
			name = "<span size='large'>" + u.name() + "</span>" + "\n" + "<small><span color='#a69275'>" + u.type_name() + "</span></small>";
		} else {
			name = "<span size='large'>" + u.type_name() + "</span>\n";
		}

		label_name_->set_label(name);
		label_name_->set_use_markup(true);
	}

	if(label_level_) {
		std::string l_str = vgettext("Lvl $lvl", {{"lvl", std::to_string(u.level())}});

		label_level_->set_label("<b>" + l_str + "</b>");
		label_level_->set_use_markup(true);
	}

	if(icon_race_) {
		icon_race_->set_label("icons/unit-groups/race_" + u.race()->id() + "_30.png");
		icon_race_->set_tooltip(u.race()->name(u.gender()));
	}

	if(icon_alignment_) {
		const std::string& alignment_name = u.alignment().to_string();

		icon_alignment_->set_label("icons/alignments/alignment_" + alignment_name + "_30.png");
		icon_alignment_->set_tooltip(unit_type::alignment_description(
			u.alignment(),
			u.gender()));
	}

	if(label_details_minimal_) {
		std::stringstream str;

		const std::string name = "<span size='large'>" + (!u.name().empty() ? u.name() : " ") + "</span>";
		str << name << "\n";

		str << "<span color='#a69275'>" << u.type_name() << "</span>" << "\n";

		str << "Lvl " << u.level() << "\n";

		str << u.alignment() << "\n";

		str << utils::join(u.trait_names(), ", ") << "\n";

		str << font::span_color(u.hp_color())
			<< _("HP: ") << u.hitpoints() << "/" << u.max_hitpoints() << "</span>" << "\n";

		str << font::span_color(u.xp_color())
			<< _("XP: ") << u.experience() << "/" << u.max_experience() << "</span>";

		label_details_minimal_->set_label(str.str());
		label_details_minimal_->set_use_markup(true);
	}

	if(tree_details_) {
		std::stringstream str;
		str << "<small>";

		str << font::span_color(u.hp_color())
			<< "<b>" << _("HP: ") << "</b>" << u.hitpoints() << "/" << u.max_hitpoints() << "</span>" << " | ";

		str << font::span_color(u.xp_color())
			<< "<b>" << _("XP: ") << "</b>" << u.experience() << "/" << u.max_experience() << "</span>" << " | ";

		str << "<b>" << _("MP: ") << "</b>"
			<< u.movement_left() << "/" << u.total_movement();

		str << "</small>";

		tree_details_->clear();

		add_name_tree_node(
			tree_details_->get_root_node(),
			"item",
			str.str()
		);

		if (!u.trait_names().empty()) {
			auto& header_node = add_name_tree_node(
				tree_details_->get_root_node(),
				"header",
				"<b>" + _("Traits") + "</b>"
			);

			assert(u.trait_names().size() == u.trait_descriptions().size());
			for (size_t i = 0; i < u.trait_names().size(); ++i) {
				add_name_tree_node(
					header_node,
					"item",
					u.trait_names()[i],
					u.trait_descriptions()[i]
				);
			}
		}
		if (!u.get_ability_list().empty()) {
			auto& header_node = add_name_tree_node(
				tree_details_->get_root_node(),
				"header",
				"<b>" + _("Abilities") + "</b>"
			);

			for (const auto& ab : u.ability_tooltips()) {
				add_name_tree_node(
					header_node,
					"item",
					std::get<1>(ab),
					std::get<2>(ab)
				);
			}
		}
		print_attack_details(u.attacks(), tree_details_->get_root_node());
	}
}
Ejemplo n.º 27
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.absolute_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(NULL);
	surface ellipse_back(NULL);
#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 != 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);
	}
#endif
	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) != 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 = 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(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 = NULL;
				} else if ( dc.unit_can_move(u) ) {
					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*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+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 * u.xp_bar_scaling() / std::max<int>(u.level(),1));

			draw_bar(*energy_file, xsrc, ysrc +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, ysrc +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 != NULL) {
				disp.drawing_buffer_add(display::LAYER_UNIT_BAR,
					loc, xsrc, ysrc +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;
}
Ejemplo n.º 28
0
static void set_defender_info(twindow& w, unit& u)
{
	set_label<timage>(w, "defender_portrait", u.absolute_image());
	set_label<timage>(w, "defender_icon", u.absolute_image());
	set_label<tcontrol>(w, "defender_name", u.name());
}
void un_recruit_unit(const unit& u)
{
	stats& s = get_stats(u.side_id());
	s.recruits[u.type_id()]--;
	s.recruit_cost -= u.cost();
}
Ejemplo n.º 30
0
std::deque<action_ptr> find_actions_of(unit const &target)
{
	return resources::gameboard->teams()[target.side()-1].get_side_actions()->actions_of(target);
}