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(); }
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(); }
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; }
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; }
/** * 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 ¤t_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; }
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); } }
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; } }
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; }
/** * 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); }
/** * 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; }
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]); } }
/** * 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); }
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; }
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 ¤t_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; }
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(); }
bool game_state::can_recruit_from(const unit& leader) const { return can_recruit_from(leader.get_location(), leader.side()); }
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) {}
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; }
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) {}
/** * 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()(); }
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_}); }
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; }
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; }
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()); }