Example #1
0
static void teleport_unit_between( const map_location& a, const map_location& b, unit& temp_unit)
{
	game_display* disp = game_display::get_singleton();
	if(!disp || disp->video().update_locked() || disp->video().faked() || (disp->fogged(a) && disp->fogged(b))) {
		return;
	}
	disp->scroll_to_tiles(a,b,game_display::ONSCREEN,true,0.0,false);

	temp_unit.set_location(a);
	if (!disp->fogged(a)) { // teleport
		disp->invalidate(temp_unit.get_location());
		temp_unit.set_facing(a.get_relative_dir(b));
		unit_animator animator;
		animator.add_animation(&temp_unit,"pre_teleport",a);
		animator.start_animations();
		animator.wait_for_end();
	}

	temp_unit.set_location(b);
	if (!disp->fogged(b)) { // teleport
		disp->invalidate(temp_unit.get_location());
		temp_unit.set_facing(a.get_relative_dir(b));
		disp->scroll_to_tiles(b,a,game_display::ONSCREEN,true,0.0,false);
		unit_animator animator;
		animator.add_animation(&temp_unit,"post_teleport",b);
		animator.start_animations();
		animator.wait_for_end();
	}

	temp_unit.set_standing();
	disp->update_display();
	events::pump();
}
Example #2
0
unit_const_ptr find_backup_leader(const unit & leader)
{
	assert(leader.can_recruit());
	assert(resources::gameboard->map().is_keep(leader.get_location()));
	for (unit_map::const_iterator unit = resources::units->begin(); unit != resources::units->end(); unit++)
	{
		if (unit->can_recruit() && unit->id() != leader.id())
		{
			if ( dynamic_cast<game_state*>(resources::filter_con)->can_recruit_on(*unit, leader.get_location()) )
				return unit.get_shared_ptr();
		}
	}
	return unit_const_ptr();
}
Example #3
0
bool recruit_result::test_suitable_recruit_location(const gamemap &map, const unit_map &units, const unit &my_leader, bool)
{
	recruit_location_ = where_;

	//if we have not-on-board location, such as null_location, then the caller wants us to recruit on 'any' possible tile.
	if (!map.on_board(recruit_location_)) {
		recruit_location_ = pathfind::find_vacant_tile(map, units, my_leader.get_location(), pathfind::VACANT_CASTLE);
	}

	if (!can_recruit_on(map, my_leader.get_location(), recruit_location_)) {
		set_error(E_BAD_RECRUIT_LOCATION);
		return false;
	}
	return true;
}
Example #4
0
bool move_result::test_route(const unit &un, const team &my_team, const unit_map &units, const std::vector<team> &teams, const gamemap &map, bool)
{
	if (from_== to_) {
		if (!remove_movement_ || (un.movement_left() == 0) ) {
			set_error(E_EMPTY_MOVE);
			return false;
		}
		return true;
	}

	if (un.movement_left() == 0 ) {
		set_error(E_EMPTY_MOVE);
		return false;
	}

	if (!to_.valid()) {
		set_error(E_NO_ROUTE);
		return false;
	}
	const pathfind::shortest_path_calculator calc(un, my_team, units, teams,map);

	//allowed teleports
	std::set<map_location> allowed_teleports = pathfind::get_teleport_locations(un, units, my_team, true);//@todo 1.9: see_all -> false

	//do an A*-search
	route_ = pathfind::a_star_search(un.get_location(), to_, 10000.0, &calc, map.w(), map.h(), &allowed_teleports);
	if (route_.steps.empty()) {
		set_error(E_NO_ROUTE);
		return false;
	}
	return true;
}
Example #5
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;
}
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;
}
Example #7
0
void pathfind::teleport_group::get_teleport_pair(
		  teleport_pair& loc_pair
		, const unit& u
		, const bool ignore_units) const
{
	const map_location &loc = u.get_location();
	static unit_map empty_unit_map;
	unit_map *units;
	if (ignore_units) {
		units = &empty_unit_map;
	} else {
		units = resources::units;
	}
	vconfig filter(cfg_.child_or_empty("filter"), true);
	vconfig source(cfg_.child_or_empty("source"), true);
	vconfig target(cfg_.child_or_empty("target"), true);
	if (u.matches_filter(filter, loc)) {

		scoped_xy_unit teleport_unit("teleport_unit", loc.x, loc.y, *resources::units);

		terrain_filter source_filter(source, *units);
		source_filter.get_locations(reversed_ ? loc_pair.second : loc_pair.first);

		terrain_filter target_filter(target, *units);
		target_filter.get_locations(reversed_ ? loc_pair.first : loc_pair.second);
	}
}
Example #8
0
void teleport_group::get_teleport_pair(
		  teleport_pair& loc_pair
		, const unit& u
		, const bool ignore_units) const
{
	const map_location &loc = u.get_location();

	const filter_context * fc = resources::filter_con;
	assert(fc);

	if (ignore_units) {
		fc = new ignore_units_filter_context(*resources::filter_con);
	}

	vconfig filter(cfg_.child_or_empty("filter"), true);
	vconfig source(cfg_.child_or_empty("source"), true);
	vconfig target(cfg_.child_or_empty("target"), true);
	const unit_filter ufilt(filter, resources::filter_con); //Note: Don't use the ignore units filter context here, only for the terrain filters. (That's how it worked before the filter contexts were introduced)
	if (ufilt.matches(u, loc)) {

		scoped_xy_unit teleport_unit("teleport_unit", loc.x, loc.y, *resources::units);

		terrain_filter source_filter(source, fc);
		source_filter.get_locations(reversed_ ? loc_pair.second : loc_pair.first);

		terrain_filter target_filter(target, fc);
		target_filter.get_locations(reversed_ ? loc_pair.first : loc_pair.second);
	}

	if (ignore_units) {
		delete fc;
	}
}
Example #9
0
bool recruit_result::test_leader_on_keep(const gamemap &map, const unit &my_leader, bool)
{
	if (!map.is_keep(my_leader.get_location())) {
		set_error(E_LEADER_NOT_ON_KEEP);
		return false;
	}
	return true;
}
Example #10
0
/**
 * Clears shroud (and fog) around the provided location for @a view_team
 * as if @a viewer was standing there.
 * This will also record sighted events, which should be either fired or
 * explicitly dropped.
 *
 * This should only be called if delayed shroud updates is off.
 * It is wasteful to call this if view_team uses neither fog nor shroud.
 *
 * @param known_units      These locations are not checked for uncovered units.
 * @param enemy_count      Incremented for each enemy uncovered (excluding known_units).
 * @param friend_count     Incremented for each friend uncovered (excluding known_units).
 * @param spectator        Will be told of uncovered units (excluding known_units).
 * @param instant          If false, then drawing delays (used to make movement look better) are allowed.
 *
 * @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, team &view_team,
                                const std::set<map_location>* known_units,
                                std::size_t * enemy_count, std::size_t * friend_count,
                                move_unit_spectator * spectator, bool instant)
{
	// This is just a translation to the more general interface. It is
	// not inlined so that vision.hpp does not have to include unit.hpp.
	return clear_unit(view_loc, view_team, viewer.underlying_id(),
	                  viewer.vision(), viewer.get_state(unit::STATE_SLOWED),
	                  viewer.movement_type().get_vision(), viewer.get_location(),
	                  known_units, enemy_count, friend_count, spectator, instant);
}
Example #11
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;
}
Example #12
0
static void move_unit_between(const map_location& a, const map_location& b, unit& temp_unit,unsigned int step_num,unsigned int step_left)
{
	game_display* disp = game_display::get_singleton();
	if(!disp || disp->video().update_locked() || disp->video().faked() || (disp->fogged(a) && disp->fogged(b))) {
		return;
	}

	temp_unit.set_location(a);
	disp->invalidate(temp_unit.get_location());
	temp_unit.set_facing(a.get_relative_dir(b));
	unit_animator animator;
	animator.replace_anim_if_invalid(&temp_unit,"movement",a,b,step_num,
			false,"",0,unit_animation::INVALID,NULL,NULL,step_left);
	animator.start_animations();
        animator.pause_animation();
	disp->scroll_to_tiles(a,b,game_display::ONSCREEN,true,0.0,false);
        animator.restart_animation();

	// useless now, previous short draw() just did one
	// new_animation_frame();

	int target_time = animator.get_animation_time_potential();

		// target_time must be short to avoid jumpy move
		// std::cout << "target time: " << target_time << "\n";
	// we round it to the next multile of 200
	target_time += 200;
	target_time -= target_time%200;

	// This code causes backwards teleport because the time > 200 causes offset > 1.0
	// which will not match with the following -1.0
	// if(  target_time - animator.get_animation_time_potential() < 100 ) target_time +=200;

	animator.wait_until(target_time);
		// debug code, see unit_frame::redraw()
		// std::cout << "   end\n";
	map_location arr[6];
	get_adjacent_tiles(a, arr);
	unsigned int i;
	for (i = 0; i < 6; ++i) {
		disp->invalidate(arr[i]);
	}
	get_adjacent_tiles(b, arr);
	for (i = 0; i < 6; ++i) {
		disp->invalidate(arr[i]);
	}
}
Example #13
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);
}
Example #14
0
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;
}
Example #15
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;
}
Example #16
0
void unit_healing(unit &healed, const std::vector<unit *> &healers, int healing,
                  const std::string & extra_text)
{
	game_display* disp = game_display::get_singleton();
	const map_location &healed_loc = healed.get_location();
	if(!disp || disp->video().update_locked() || disp->video().faked() || disp->fogged(healed_loc)) return;

	// This is all the pretty stuff.
	disp->scroll_to_tile(healed_loc, game_display::ONSCREEN,true,false);
	disp->display_unit_hex(healed_loc);
	unit_animator animator;

	for (unit *h : healers) {
		h->set_facing(h->get_location().get_relative_dir(healed_loc));
		animator.add_animation(h, "healing", h->get_location(),
			healed_loc, healing);
	}

	if (healing < 0) {
		animator.add_animation(&healed, "poisoned", healed_loc,
		                       map_location::null_location(), -healing, false,
		                       number_and_text(-healing, extra_text),
		                       display::rgb(255,0,0));
	} else if ( healing > 0 ) {
		animator.add_animation(&healed, "healed", healed_loc,
		                       map_location::null_location(), healing, false,
		                       number_and_text(healing, extra_text),
		                       display::rgb(0,255,0));
	} else {
		animator.add_animation(&healed, "healed", healed_loc,
		                       map_location::null_location(), 0, false,
		                       extra_text, display::rgb(0,255,0));
	}
	animator.start_animations();
	animator.wait_for_end();
	animator.set_all_standing();
}
Example #17
0
bool game_state::can_recruit_from(const unit& leader) const
{
	return can_recruit_from(leader.get_location(), leader.side());
}
Example #18
0
bool unit_filter::matches(const unit & u, const unit & u2) const {
	return matches(u, u.get_location(), u2);
}
/**
 * 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)
{}
Example #20
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;
}
Example #21
0
static void find_routes(const gamemap& map, const unit_map& /*units*/,
		const unit& u,
		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 map_location loc = u.get_location();
	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);
		}
	}
}
/**
 * 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)
{}
Example #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()();
}
Example #24
0
bool unit_filter::matches(const unit & u, const unit & u2) const {
	return impl_.matches(unit_filter_impl::unit_filter_args{u, u.get_location(), &u2, fc_, use_flat_tod_});
}
Example #25
0
bool move_result::test_route(const unit &un)
{
	if (from_== to_) {
		if (!remove_movement_ || (un.movement_left() == 0) ) {
			set_error(E_EMPTY_MOVE);
			return false;
		}
		return true;
	}

	if (un.movement_left() == 0 ) {
		set_error(E_EMPTY_MOVE);
		return false;
	}

	if (!to_.valid()) {
		set_error(E_NO_ROUTE);
		return false;
	}

	team &my_team = get_my_team();
	const pathfind::shortest_path_calculator calc(un, my_team, resources::gameboard->teams(), resources::gameboard->map());

	//allowed teleports
	pathfind::teleport_map allowed_teleports = pathfind::get_teleport_locations(un, my_team, true);///@todo 1.9: see_all -> false

	//do an A*-search
	route_ = std::shared_ptr<pathfind::plain_route>( new pathfind::plain_route(pathfind::a_star_search(un.get_location(), to_, 10000.0, calc, resources::gameboard->map().w(), resources::gameboard->map().h(), &allowed_teleports)));
	if (route_->steps.empty()) {
		set_error(E_NO_ROUTE);
		return false;
	}
	return true;
}
Example #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;
}
Example #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());
}