void game_display::display_unit_hex(map_location hex) { if (!hex.valid()) return; wb::future_map future; /**< Lasts for whole method. */ const unit *u = resources::gameboard->get_visible_unit(hex, dc_->teams()[viewing_team()], !dont_show_all_); if (u) { displayedUnitHex_ = hex; invalidate_unit(); } }
void game_display::display_unit_hex(map_location hex) { if (!hex.valid()) return; wb::future_map future; //< Lasts for whole method. const unit *u = get_visible_unit(hex, (*teams_)[viewing_team()], !viewpoint_); if (u) { displayedUnitHex_ = hex; invalidate_unit(); } }
void highlighter::set_mouseover_hex(const map_location& hex) { clear(); if(!hex.valid()) { return; } real_map ensure_real_map; mouseover_hex_ = hex; //if we're right over a unit, just highlight all of this unit's actions unit_map::iterator it = get_unit_map().find(hex); if(it != get_unit_map().end()) { selection_candidate_ = it.get_shared_ptr(); if(resources::gameboard->teams().at(it->side()-1).get_side_actions()->unit_has_actions(*it)) { owner_unit_ = it.get_shared_ptr(); } //commented code below is to also select the first action of this unit as //the main highlight; it doesn't fit too well in the UI // side_actions::iterator action_it = side_actions_->find_first_action_of(*it); // if(action_it != side_actions_->end()) { // main_highlight_ = *action_it; // } } //Set the execution/deletion/bump targets. if(owner_unit_) { side_actions::iterator itor = side_actions_->find_first_action_of(*owner_unit_); if(itor != side_actions_->end()) { selected_action_ = *itor; } } //Overwrite the above selected_action_ if we find a better one if(side_actions_->empty()) { return; } for(action_ptr act : boost::adaptors::reverse(*side_actions_)) { /**@todo "is_numbering_hex" is not the "correct" criterion by which to * select the hightlighted/selected action. It's just convenient for me * to use at the moment since it happens to coincide with the "correct" * criterion, which is to use find_main_highlight.*/ if(act->is_numbering_hex(hex)) { selected_action_ = act; break; } } }
void get_tile_ring(const map_location& a, const int r, std::vector<map_location>& res) { if(r <= 0) { return; } map_location loc = a.get_direction(map_location::SOUTH_WEST, r); for(int n = 0; n != 6; ++n) { const map_location::DIRECTION dir = static_cast<map_location::DIRECTION>(n); for(int i = 0; i != r; ++i) { res.push_back(loc); loc = loc.get_direction(dir, 1); } } }
void gamemap::set_special_location(const std::string& id, const map_location& loc) { bool valid = loc.valid(); auto it_left = starting_positions_.left.find(id); if (it_left != starting_positions_.left.end()) { if (valid) { starting_positions_.left.replace_data(it_left, loc); } else { starting_positions_.left.erase(it_left); } } else { starting_positions_.left.insert(it_left, std::make_pair(id, loc)); } }
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]); } }
const time_of_day tod_manager::get_illuminated_time_of_day(const map_location& loc, int for_turn) const { // get ToD ignoring illumination time_of_day tod = get_time_of_day(loc, for_turn); // now add illumination const gamemap& map = *resources::game_map; const unit_map& units = *resources::units; int light_modif = map.get_terrain_info(map.get_terrain(loc)).light_modification(); int light = tod.lawful_bonus + light_modif; int illum_light = light; if(loc.valid()) { map_location locs[7]; locs[0] = loc; get_adjacent_tiles(loc,locs+1); for(int i = 0; i != 7; ++i) { const unit_map::const_iterator itor = units.find(locs[i]); if(itor != units.end() && itor->get_ability_bool("illuminates") && !itor->incapacitated()) { unit_ability_list illum = itor->get_abilities("illuminates"); unit_abilities::effect illum_effect(illum, light, false); illum_light = light + illum_effect.get_composite_value(); //max_value and min_value control the final result //unless ToD + terrain effect is stronger int max = std::max(light, illum.highest("max_value").first); int min = std::min(light, illum.lowest("min_value").first); if(illum_light > max) { illum_light = max; } else if (illum_light < min) { illum_light = min; } } } } tod.bonus_modified = illum_light - tod.lawful_bonus; tod.lawful_bonus = illum_light; return tod; }
/** * Function that will add to @a result all locations exactly @a radius tiles * from @a center (or nothing if @a radius is not positive). @a result must be * a std::vector of locations. */ void get_tile_ring(const map_location& center, const int radius, std::vector<map_location>& result) { if ( radius <= 0 ) { return; } map_location loc = center.get_direction(map_location::SOUTH_WEST, radius); for(int n = 0; n != 6; ++n) { const map_location::DIRECTION dir = static_cast<map_location::DIRECTION>(n); for(int i = 0; i != radius; ++i) { result.push_back(loc); loc = loc.get_direction(dir, 1); } } }
void base_map::insert(const map_location loc, base_unit* u) { std::stringstream err; if (!loc.valid()) { err << "Trying to add " << u->name() << " at an invalid location; Discarding."; delete u; VALIDATE(false, err.str()); } bool base = u->base(); if ((base && coor_map_[index(loc.x, loc.y)].base) || (!base && coor_map_[index(loc.x, loc.y)].overlay)) { err << "trying to overwrite existing unit at (" << loc.x << ", " << loc.y << ")"; delete u; VALIDATE(false, err.str()); } // some application maybe require coor_map_ valid before set_location. // but touch_locs is got after set_location. only valid on center location. if (base) { coor_map_[index(loc.x, loc.y)].base = u; } else { coor_map_[index(loc.x, loc.y)].overlay = u; } u->set_location(loc); // it is called after set_location directly, use touch_locs safely. const std::set<map_location>& touch_locs = u->get_touch_locations(); if (touch_locs.size() != 1) { for (std::set<map_location>::const_iterator itor = touch_locs.begin(); itor != touch_locs.end(); ++ itor) { if (base) { coor_map_[index(itor->x, itor->y)].base = u; } else { coor_map_[index(itor->x, itor->y)].overlay = u; } } } // insert p into time-axis.* u->map_index_ = map_vsize_; map_[map_vsize_ ++] = u; if (u->require_sort()) { sort_map(*u); } }
map_location::DIRECTION map_location::get_relative_dir(map_location loc) const { map_location diff = loc.legacy_difference(*this); if(diff == map_location(0,0)) return NDIRECTIONS; if( diff.y < 0 && diff.x >= 0 && abs(diff.x) >= abs(diff.y)) return NORTH_EAST; if( diff.y < 0 && diff.x < 0 && abs(diff.x) >= abs(diff.y)) return NORTH_WEST; if( diff.y < 0 && abs(diff.x) < abs(diff.y)) return NORTH; if( diff.y >= 0 && diff.x >= 0 && abs(diff.x) >= abs(diff.y)) return SOUTH_EAST; if( diff.y >= 0 && diff.x < 0 && abs(diff.x) >= abs(diff.y)) return SOUTH_WEST; if( diff.y >= 0 && abs(diff.x) < abs(diff.y)) return SOUTH; // Impossible assert(false); return NDIRECTIONS; }
bool terrain_filter::match_internal(const map_location& loc, const bool ignore_xy) const { //Filter Areas if (cfg_.has_attribute("area") && resources::tod_manager->get_area_by_id(cfg_["area"]).count(loc) == 0) return false; if(cfg_.has_attribute("terrain")) { if(cache_.parsed_terrain == NULL) { cache_.parsed_terrain = new t_translation::t_match(cfg_["terrain"]); } if(!cache_.parsed_terrain->is_empty) { const t_translation::t_terrain letter = resources::game_map->get_terrain_info(loc).number(); if(!t_translation::terrain_matches(letter, *cache_.parsed_terrain)) { return false; } } } //Allow filtering on location ranges if(!ignore_xy) { if(!loc.matches_range(cfg_["x"], cfg_["y"])) { return false; } //allow filtering by searching a stored variable of locations if(cfg_.has_attribute("find_in")) { variable_info vi(cfg_["find_in"], false, variable_info::TYPE_CONTAINER); if(!vi.is_valid) return false; if(vi.explicit_index) { if(map_location(vi.as_container(),NULL) != loc) { return false; } } else { bool found = false; BOOST_FOREACH(const config &cfg, vi.as_array()) { if (map_location(cfg, NULL) == loc) { found = true; break; } } if (!found) return false; } } }
time_of_day tod_manager::get_time_of_day(int illuminated, const map_location& loc, int n_turn) const { time_of_day res = get_time_of_day_turn(n_turn); if(loc.valid()) { for(std::vector<area_time_of_day>::const_iterator i = areas_.begin(); i != areas_.end(); ++i) { if(i->hexes.count(loc) == 1) { VALIDATE(i->times.size(), _("No time of day has been defined.")); res = i->times[(n_turn-1)%i->times.size()]; break; } } } if(illuminated) { res.bonus_modified=illuminated; res.lawful_bonus += illuminated; } return res; }
void unit_attack(display * disp, game_board & board, const map_location& a, const map_location& b, int damage, const attack_type& attack, const attack_type* secondary_attack, int swing,std::string hit_text,int drain_amount,std::string att_text, const std::vector<std::string>* extra_hit_sounds) { if(!disp ||disp->video().update_locked() || disp->video().faked() || (disp->fogged(a) && disp->fogged(b)) || preferences::show_combat() == false) { return; } //const unit_map& units = disp->get_units(); disp->select_hex(map_location::null_location()); // scroll such that there is at least half a hex spacing around fighters disp->scroll_to_tiles(a,b,game_display::ONSCREEN,true,0.5,false); log_scope("unit_attack"); const unit_map::const_iterator att = board.units().find(a); assert(att.valid()); const unit& attacker = *att; const unit_map::iterator def = board.find_unit(b); assert(def.valid()); unit &defender = *def; int def_hitpoints = defender.hitpoints(); att->set_facing(a.get_relative_dir(b)); def->set_facing(b.get_relative_dir(a)); defender.set_facing(b.get_relative_dir(a)); unit_animator animator; unit_ability_list leaders = attacker.get_abilities("leadership"); unit_ability_list helpers = defender.get_abilities("resistance"); std::string text = number_and_text(damage, hit_text); std::string text_2 = number_and_text(abs(drain_amount), att_text); unit_animation::hit_type hit_type; if(damage >= defender.hitpoints()) { hit_type = unit_animation::hit_type::KILL; } else if(damage > 0) { hit_type = unit_animation::hit_type::HIT; }else { hit_type = unit_animation::hit_type::MISS; } animator.add_animation(&attacker, "attack", att->get_location(), def->get_location(), damage, true, text_2, (drain_amount >= 0) ? display::rgb(0, 255, 0) : display::rgb(255, 0, 0), hit_type, &attack, secondary_attack, swing); // note that we take an anim from the real unit, we'll use it later const unit_animation *defender_anim = def->anim_comp().choose_animation(*disp, def->get_location(), "defend", att->get_location(), damage, hit_type, &attack, secondary_attack, swing); animator.add_animation(&defender, defender_anim, def->get_location(), true, text , display::rgb(255, 0, 0)); for (const unit_ability & ability : leaders) { if(ability.second == a) continue; if(ability.second == b) continue; unit_map::const_iterator leader = board.units().find(ability.second); assert(leader.valid()); leader->set_facing(ability.second.get_relative_dir(a)); animator.add_animation(&*leader, "leading", ability.second, att->get_location(), damage, true, "", 0, hit_type, &attack, secondary_attack, swing); } for (const unit_ability & ability : helpers) { if(ability.second == a) continue; if(ability.second == b) continue; unit_map::const_iterator helper = board.units().find(ability.second); assert(helper.valid()); helper->set_facing(ability.second.get_relative_dir(b)); animator.add_animation(&*helper, "resistance", ability.second, def->get_location(), damage, true, "", 0, hit_type, &attack, secondary_attack, swing); } animator.start_animations(); animator.wait_until(0); int damage_left = damage; bool extra_hit_sounds_played = false; while(damage_left > 0 && !animator.would_end()) { if(!extra_hit_sounds_played && extra_hit_sounds != nullptr) { for (std::string hit_sound : *extra_hit_sounds) { sound::play_sound(hit_sound); } extra_hit_sounds_played = true; } int step_left = (animator.get_end_time() - animator.get_animation_time() )/50; if(step_left < 1) step_left = 1; int removed_hp = damage_left/step_left ; if(removed_hp < 1) removed_hp = 1; defender.take_hit(removed_hp); damage_left -= removed_hp; animator.wait_until(animator.get_animation_time_potential() +50); } animator.wait_for_end(); // pass the animation back to the real unit def->anim_comp().start_animation(animator.get_end_time(), defender_anim, true); reset_helpers(&*att, &*def); def->set_hitpoints(def_hitpoints); }
bool gamemap::on_board(const map_location& loc) const { return loc.valid() && loc.x < w_ && loc.y < h_; }
pathfind::plain_route pathfind::a_star_search(const map_location& src, const map_location& dst, double stop_at, const cost_calculator *calc, const size_t width, const size_t height, const teleport_map *teleports) { //----------------- PRE_CONDITIONS ------------------ assert(src.valid(width, height)); assert(dst.valid(width, height)); assert(calc != NULL); assert(stop_at <= calc->getNoPathValue()); //--------------------------------------------------- DBG_PF << "A* search: " << src << " -> " << dst << '\n'; if (calc->cost(dst, 0) >= stop_at) { LOG_PF << "aborted A* search because Start or Dest is invalid\n"; pathfind::plain_route locRoute; locRoute.move_cost = int(calc->getNoPathValue()); return locRoute; } // increment search_counter but skip the range equivalent to uninitialized search_counter += 2; if (search_counter - bad_search_counter <= 1u) search_counter += 2; static std::vector<node> nodes; nodes.resize(width * height); // this create uninitalized nodes indexer index(width, height); comp node_comp(nodes); nodes[index(dst)].g = stop_at + 1; nodes[index(src)] = node(0, src, map_location::null_location, dst, true, teleports); std::vector<int> pq; pq.push_back(index(src)); while (!pq.empty()) { node& n = nodes[pq.front()]; n.in = search_counter; std::pop_heap(pq.begin(), pq.end(), node_comp); pq.pop_back(); if (n.t >= nodes[index(dst)].g) break; std::vector<map_location> locs; int i; if (teleports && !teleports->empty()) { std::set<map_location> allowed_teleports; teleports->get_adjacents(allowed_teleports, n.curr); i = allowed_teleports.size() +6; locs = std::vector<map_location>(i); std::copy(allowed_teleports.begin(), allowed_teleports.end(), locs.begin() + 6); } else { locs = std::vector<map_location>(6); i = 6;} get_adjacent_tiles(n.curr, &locs[0]); for (; i-- > 0;) { if (!locs[i].valid(width, height)) continue; if (locs[i] == n.curr) continue; node& next = nodes[index(locs[i])]; double thresh = (next.in - search_counter <= 1u) ? next.g : stop_at + 1; // cost() is always >= 1 (assumed and needed by the heuristic) if (n.g + 1 >= thresh) continue; double cost = n.g + calc->cost(locs[i], n.g); if (cost >= thresh) continue; bool in_list = next.in == search_counter + 1; next = node(cost, locs[i], n.curr, dst, true, teleports); if (in_list) { std::push_heap(pq.begin(), std::find(pq.begin(), pq.end(), static_cast<int>(index(locs[i]))) + 1, node_comp); } else { pq.push_back(index(locs[i])); std::push_heap(pq.begin(), pq.end(), node_comp); } } } pathfind::plain_route route; if (nodes[index(dst)].g <= stop_at) { DBG_PF << "found solution; calculating it...\n"; route.move_cost = static_cast<int>(nodes[index(dst)].g); for (node curr = nodes[index(dst)]; curr.prev != map_location::null_location; curr = nodes[index(curr.prev)]) { route.steps.push_back(curr.curr); } route.steps.push_back(src); std::reverse(route.steps.begin(), route.steps.end()); } else { LOG_PF << "aborted a* search " << "\n"; route.move_cost = static_cast<int>(calc->getNoPathValue()); } return route; }
bool basic_unit_filter_impl::internal_matches_filter(const unit & u, const map_location& loc, const unit* u2) const { if (!vcfg["name"].blank() && vcfg["name"].t_str() != u.name()) { return false; } if (!vcfg["id"].empty()) { std::vector<std::string> id_list = utils::split(vcfg["id"]); if (std::find(id_list.begin(), id_list.end(), u.id()) == id_list.end()) { return false; } } // Allow 'speaker' as an alternative to id, since people use it so often if (!vcfg["speaker"].blank() && vcfg["speaker"].str() != u.id()) { return false; } if (vcfg.has_child("filter_location")) { if (vcfg.count_children("filter_location") > 1) { FAIL("Encountered multiple [filter_location] children of a standard unit filter. " "This is not currently supported and in all versions of wesnoth would have " "resulted in the later children being ignored. You must use [and] or similar " "to achieve the desired result."); } terrain_filter filt(vcfg.child("filter_location"), &fc_, use_flat_tod_); if (!filt.match(loc)) { return false; } } if(vcfg.has_child("filter_side")) { if (vcfg.count_children("filter_side") > 1) { FAIL("Encountered multiple [filter_side] children of a standard unit filter. " "This is not currently supported and in all versions of wesnoth would have " "resulted in the later children being ignored. You must use [and] or similar " "to achieve the desired result."); } side_filter filt(vcfg.child("filter_side"), &fc_); if(!filt.match(u.side())) return false; } // Also allow filtering on location ranges outside of the location filter if (!vcfg["x"].blank() || !vcfg["y"].blank()){ if(vcfg["x"] == "recall" && vcfg["y"] == "recall") { //locations on the map are considered to not be on a recall list if (fc_.get_disp_context().map().on_board(loc)) { return false; } } else if(vcfg["x"].empty() && vcfg["y"].empty()) { return false; } else if(!loc.matches_range(vcfg["x"], vcfg["y"])) { return false; } } // The type could be a comma separated list of types if (!vcfg["type"].empty()) { std::vector<std::string> types = utils::split(vcfg["type"]); if (std::find(types.begin(), types.end(), u.type_id()) == types.end()) { return false; } } // Shorthand for all advancements of a given type if (!vcfg["type_tree"].empty()) { std::set<std::string> types; for(const std::string type : utils::split(vcfg["type_tree"])) { if(types.count(type)) { continue; } if(const unit_type* ut = unit_types.find(type)) { const auto& tree = ut->advancement_tree(); types.insert(tree.begin(), tree.end()); types.insert(type); } } if(types.find(u.type_id()) == types.end()) { return false; } } // The variation_type could be a comma separated list of types if (!vcfg["variation"].empty()) { std::vector<std::string> types = utils::split(vcfg["variation"]); if (std::find(types.begin(), types.end(), u.variation()) == types.end()) { return false; } } // The has_variation_type could be a comma separated list of types if (!vcfg["has_variation"].empty()) { bool match = false; // If this unit is a variation itself then search in the base unit's variations. const unit_type* const type = u.variation().empty() ? &u.type() : unit_types.find(u.type().base_id()); assert(type); for (const std::string& variation_id : utils::split(vcfg["has_variation"])) { if (type->has_variation(variation_id)) { match = true; break; } } if (!match) return false; } if (!vcfg["ability"].empty()) { bool match = false; for (const std::string& ability_id : utils::split(vcfg["ability"])) { if (u.has_ability_by_id(ability_id)) { match = true; break; } } if (!match) return false; } if (!vcfg["race"].empty()) { std::vector<std::string> races = utils::split(vcfg["race"]); if (std::find(races.begin(), races.end(), u.race()->id()) == races.end()) { return false; } } if (!vcfg["gender"].blank() && string_gender(vcfg["gender"]) != u.gender()) { return false; } if (!vcfg["side"].empty() && vcfg["side"].to_int(-999) != u.side()) { std::vector<std::string> sides = utils::split(vcfg["side"]); const std::string u_side = std::to_string(u.side()); if (std::find(sides.begin(), sides.end(), u_side) == sides.end()) { return false; } } // handle statuses list if (!vcfg["status"].empty()) { bool status_found = false; for (const std::string status : utils::split(vcfg["status"])) { if(u.get_state(status)) { status_found = true; break; } } if(!status_found) { return false; } } if (vcfg.has_child("has_attack")) { const vconfig& weap_filter = vcfg.child("has_attack"); bool has_weapon = false; for(const attack_type& a : u.attacks()) { if(a.matches_filter(weap_filter.get_parsed_config())) { has_weapon = true; break; } } if(!has_weapon) { return false; } } else if (!vcfg["has_weapon"].blank()) { std::string weapon = vcfg["has_weapon"]; bool has_weapon = false; for(const attack_type& a : u.attacks()) { if(a.id() == weapon) { has_weapon = true; break; } } if(!has_weapon) { return false; } } if (!vcfg["role"].blank() && vcfg["role"].str() != u.get_role()) { return false; } if (!vcfg["ai_special"].blank() && ((vcfg["ai_special"].str() == "guardian") != u.get_state(unit::STATE_GUARDIAN))) { return false; } if (!vcfg["canrecruit"].blank() && vcfg["canrecruit"].to_bool() != u.can_recruit()) { return false; } if (!vcfg["recall_cost"].blank() && vcfg["recall_cost"].to_int(-1) != u.recall_cost()) { return false; } if (!vcfg["level"].blank() && vcfg["level"].to_int(-1) != u.level()) { return false; } if (!vcfg["defense"].blank() && vcfg["defense"].to_int(-1) != u.defense_modifier(fc_.get_disp_context().map().get_terrain(loc))) { return false; } if (!vcfg["movement_cost"].blank() && vcfg["movement_cost"].to_int(-1) != u.movement_cost(fc_.get_disp_context().map().get_terrain(loc))) { return false; } // Now start with the new WML based comparison. // If a key is in the unit and in the filter, they should match // filter only => not for us // unit only => not filtered config unit_cfg; // No point in serializing the unit once for each [filter_wml]! for (const vconfig& wmlcfg : vcfg.get_children("filter_wml")) { config fwml = wmlcfg.get_parsed_config(); /* Check if the filter only cares about variables. If so, no need to serialize the whole unit. */ config::all_children_itors ci = fwml.all_children_range(); if (fwml.all_children_count() == 1 && fwml.attribute_count() == 1 && ci.front().key == "variables") { if (!u.variables().matches(ci.front().cfg)) return false; } else { if (unit_cfg.empty()) u.write(unit_cfg); if (!unit_cfg.matches(fwml)) return false; } } for (const vconfig& vision : vcfg.get_children("filter_vision")) { std::set<int> viewers; // Use standard side filter side_filter ssf(vision, &fc_); std::vector<int> sides = ssf.get_teams(); viewers.insert(sides.begin(), sides.end()); bool found = false; for (const int viewer : viewers) { bool fogged = fc_.get_disp_context().teams()[viewer - 1].fogged(loc); bool hiding = u.invisible(loc, fc_.get_disp_context()); bool unit_hidden = fogged || hiding; if (vision["visible"].to_bool(true) != unit_hidden) { found = true; break; } } if (!found) {return false;} } if (vcfg.has_child("filter_adjacent")) { const unit_map& units = fc_.get_disp_context().units(); map_location adjacent[6]; get_adjacent_tiles(loc, adjacent); for (const vconfig& adj_cfg : vcfg.get_children("filter_adjacent")) { int match_count=0; unit_filter filt(adj_cfg, &fc_, use_flat_tod_); config::attribute_value i_adjacent = adj_cfg["adjacent"]; std::vector<map_location::DIRECTION> dirs; if (i_adjacent.blank()) { dirs = map_location::default_dirs(); } else { dirs = map_location::parse_directions(i_adjacent); } std::vector<map_location::DIRECTION>::const_iterator j, j_end = dirs.end(); for (j = dirs.begin(); j != j_end; ++j) { unit_map::const_iterator unit_itor = units.find(adjacent[*j]); if (unit_itor == units.end() || !filt(*unit_itor, u)) { continue; } boost::optional<bool> is_enemy; if (!adj_cfg["is_enemy"].blank()) { is_enemy = adj_cfg["is_enemy"].to_bool(); } if (!is_enemy || *is_enemy == fc_.get_disp_context().teams()[u.side() - 1].is_enemy(unit_itor->side())) { ++match_count; } } static std::vector<std::pair<int,int> > default_counts = utils::parse_ranges("1-6"); config::attribute_value i_count = adj_cfg["count"]; if(!in_ranges(match_count, !i_count.blank() ? utils::parse_ranges(i_count) : default_counts)) { return false; } } } if (!vcfg["find_in"].blank()) { // Allow filtering by searching a stored variable of units if (const game_data * gd = fc_.get_game_data()) { try { variable_access_const vi = gd->get_variable_access_read(vcfg["find_in"]); bool found_id = false; for (const config& c : vi.as_array()) { if(c["id"] == u.id()) found_id = true; } if(!found_id) { return false; } } catch(const invalid_variablename_exception&) { return false; } } } if (!vcfg["formula"].blank()) { try { const unit_callable main(loc,u); game_logic::map_formula_callable callable(&main); if (u2) { std::shared_ptr<unit_callable> secondary(new unit_callable(*u2)); callable.add("other", variant(secondary.get())); // It's not destroyed upon scope exit because the variant holds a reference } const game_logic::formula form(vcfg["formula"]); if(!form.evaluate(callable).as_bool()) { return false; } return true; } catch(game_logic::formula_error& e) { lg::wml_error() << "Formula error in unit filter: " << e.type << " at " << e.filename << ':' << e.line << ")\n"; // Formulae with syntax errors match nothing return false; } } if (!vcfg["lua_function"].blank()) { if (game_lua_kernel * lk = fc_.get_lua_kernel()) { bool b = lk->run_filter(vcfg["lua_function"].str().c_str(), u); if (!b) return false; } } return true; }
void unit_attack( const map_location& a, const map_location& b, int damage, const attack_type& attack, const attack_type* secondary_attack, int swing,std::string hit_text,bool drain,std::string att_text) { game_display* disp = game_display::get_singleton(); if(!disp ||disp->video().update_locked() || disp->video().faked() || (disp->fogged(a) && disp->fogged(b)) || preferences::show_combat() == false) { return; } unit_map& units = disp->get_units(); disp->select_hex(map_location::null_location); // scroll such that there is at least half a hex spacing around fighters disp->scroll_to_tiles(a,b,game_display::ONSCREEN,true,0.5,false); log_scope("unit_attack"); const unit_map::iterator att = units.find(a); assert(att != units.end()); unit& attacker = *att; const unit_map::iterator def = units.find(b); assert(def != units.end()); unit &defender = *def; int def_hitpoints = defender.hitpoints(); att->set_facing(a.get_relative_dir(b)); def->set_facing(b.get_relative_dir(a)); defender.set_facing(b.get_relative_dir(a)); unit_animator animator; unit_ability_list leaders = attacker.get_abilities("leadership"); unit_ability_list helpers = defender.get_abilities("resistance"); std::string text ; if(damage) text = lexical_cast<std::string>(damage); if(!hit_text.empty()) { text.insert(text.begin(),hit_text.size()/2,' '); text = text + "\n" + hit_text; } std::string text_2 ; if(drain && damage) text_2 = lexical_cast<std::string>(std::min<int>(damage,defender.hitpoints())/2); if(!att_text.empty()) { text_2.insert(text_2.begin(),att_text.size()/2,' '); text_2 = text_2 + "\n" + att_text; } unit_animation::hit_type hit_type; if(damage >= defender.hitpoints()) { hit_type = unit_animation::KILL; } else if(damage > 0) { hit_type = unit_animation::HIT; }else { hit_type = unit_animation::MISS; } animator.add_animation(&attacker, "attack", att->get_location(), def->get_location(), damage, true, text_2, display::rgb(0, 255, 0), hit_type, &attack, secondary_attack, swing); // note that we take an anim from the real unit, we'll use it later const unit_animation *defender_anim = def->choose_animation(*disp, def->get_location(), "defend", att->get_location(), damage, hit_type, &attack, secondary_attack, swing); animator.add_animation(&defender, defender_anim, def->get_location(), true, text , display::rgb(255, 0, 0)); for (std::vector<std::pair<const config *, map_location> >::iterator itor = leaders.cfgs.begin(); itor != leaders.cfgs.end(); ++itor) { if(itor->second == a) continue; if(itor->second == b) continue; unit_map::iterator leader = units.find(itor->second); assert(leader != units.end()); leader->set_facing(itor->second.get_relative_dir(a)); animator.add_animation(&*leader, "leading", itor->second, att->get_location(), damage, true, "", 0, hit_type, &attack, secondary_attack, swing); } for (std::vector<std::pair<const config *, map_location> >::iterator itor = helpers.cfgs.begin(); itor != helpers.cfgs.end(); ++itor) { if(itor->second == a) continue; if(itor->second == b) continue; unit_map::iterator helper = units.find(itor->second); assert(helper != units.end()); helper->set_facing(itor->second.get_relative_dir(b)); animator.add_animation(&*helper, "resistance", itor->second, def->get_location(), damage, true, "", 0, hit_type, &attack, secondary_attack, swing); } animator.start_animations(); animator.wait_until(0); int damage_left = damage; while(damage_left > 0 && !animator.would_end()) { int step_left = (animator.get_end_time() - animator.get_animation_time() )/50; if(step_left < 1) step_left = 1; int removed_hp = damage_left/step_left ; if(removed_hp < 1) removed_hp = 1; defender.take_hit(removed_hp); damage_left -= removed_hp; animator.wait_until(animator.get_animation_time_potential() +50); } animator.wait_for_end(); // pass the animation back to the real unit def->start_animation(animator.get_end_time(), defender_anim, true); reset_helpers(&*att, &*def); def->set_hitpoints(def_hitpoints); }
map_location map_location::legacy_difference(const map_location &a) const { return legacy_sum(a.legacy_negation()); }
bool on_map(const map_location& loc) const { return loc.wml_x() >= 0 && loc.wml_x() < total_width() && loc.wml_y() >= 0 && loc.wml_y() < total_height(); }
void gamemap::overlay_impl( // const but changed via set_terrain const t_translation::ter_map& m1, starting_positions& m1_st, const t_translation::ter_map& m2, const starting_positions& m2_st, std::function<void (const map_location&, const t_translation::terrain_code&, terrain_type_data::merge_mode, bool)> set_terrain, map_location loc, const std::vector<overlay_rule>& rules, bool m_is_odd, bool ignore_special_locations) { int xpos = loc.wml_x(); int ypos = loc.wml_y(); const int xstart = std::max<int>(0, -xpos); const int xend = std::min<int>(m2.w, m1.w -xpos); const int xoffset = xpos; const int ystart_even = std::max<int>(0, -ypos); const int yend_even = std::min<int>(m2.h, m1.h - ypos); const int yoffset_even = ypos; const int ystart_odd = std::max<int>(0, -ypos +(xpos & 1) -(m_is_odd ? 1 : 0)); const int yend_odd = std::min<int>(m2.h, m1.h - ypos +(xpos & 1) -(m_is_odd ? 1 : 0)); const int yoffset_odd = ypos -(xpos & 1) + (m_is_odd ? 1 : 0); for(int x1 = xstart; x1 != xend; ++x1) { int ystart, yend, yoffset; if(x1 & 1) { ystart = ystart_odd , yend = yend_odd , yoffset = yoffset_odd; } else { ystart = ystart_even, yend = yend_even, yoffset = yoffset_even; } for(int y1 = ystart; y1 != yend; ++y1) { const int x2 = x1 + xoffset; const int y2 = y1 + yoffset; const t_translation::terrain_code t = m2.get(x1,y1); const t_translation::terrain_code current = m1.get(x2, y2); if(t == t_translation::FOGGED || t == t_translation::VOID_TERRAIN) { continue; } // See if there is a matching rule const overlay_rule* rule = nullptr; for(const overlay_rule& current_rule : rules) { if(!current_rule.old_.empty() && !t_translation::terrain_matches(current, current_rule.old_)) { continue; } if(!current_rule.new_.empty() && !t_translation::terrain_matches(t, current_rule.new_)) { continue; } rule = ¤t_rule; break; } if (!rule) { set_terrain(map_location(x2, y2, wml_loc()), t, terrain_type_data::BOTH, false); } else if(!rule->use_old_) { set_terrain(map_location(x2, y2, wml_loc()), rule->terrain_ ? *rule->terrain_ : t , rule->mode_, rule->replace_if_failed_); } } } if (!ignore_special_locations) { for(auto& pair : m2_st.left) { int x = pair.second.wml_x(); int y = pair.second.wml_y(); if(x & 1) { if(x < xstart || x >= xend || y < ystart_odd || y >= yend_odd) { continue; } } else { if(x < xstart || x >= xend || y < ystart_even || y >= yend_even) { continue; } } int x_new = x + xoffset; int y_new = y + ((x & 1 ) ? yoffset_odd : yoffset_even); map_location pos_new = map_location(x_new, y_new, wml_loc()); m1_st.left.erase(pair.first); m1_st.insert(starting_positions::value_type(pair.first, t_translation::coordinate(pos_new.x, pos_new.y))); } } }
bool on_map_noborder(const map_location& loc) const { return loc.wml_x() > 0 && loc.wml_x() < total_width() - 1 && loc.wml_y() > 0 && loc.wml_y() < total_height() - 1; }