void move::apply_temp_modifier(unit_map& unit_map) { if (get_source_hex() == get_dest_hex()) return; //zero-hex move, used by attack subclass // Safety: Make sure the old temporary_unit_mover (if any) is destroyed // before creating a new one. mover_.reset(); //@todo: deal with multi-turn moves, which may for instance end their first turn // by capturing a village //@todo: we may need to change unit status here and change it back in remove_temp_modifier unit* unit; { unit_map::iterator unit_it = unit_map.find(get_source_hex()); assert(unit_it != unit_map.end()); unit = &*unit_it; } //Modify movement points DBG_WB <<"Move: Changing movement points for unit " << unit->name() << " [" << unit->id() << "] from " << unit->movement_left() << " to " << unit->movement_left() - movement_cost_ << ".\n"; // Move the unit DBG_WB << "Move: Temporarily moving unit " << unit->name() << " [" << unit->id() << "] from (" << get_source_hex() << ") to (" << get_dest_hex() <<")\n"; mover_.reset(new temporary_unit_mover(unit_map, get_source_hex(), get_dest_hex(), unit->movement_left() - movement_cost_)); //Update status of fake unit (not undone by remove_temp_modifiers) //@todo this contradicts the name "temp_modifiers" fake_unit_->set_movement(unit->movement_left(), true); }
void attack::execute(bool& success, bool& complete) { if(!valid()) { success = false; //Setting complete to true signifies to side_actions to delete the planned action: nothing more to do with it. complete = true; return; } LOG_WB << "Executing: " << shared_from_this() << "\n"; if (route_->steps.size() >= 2) { bool m_success, m_complete; move::execute(m_success,m_complete); if(!m_success) { //Move failed for some reason, so don't attack. success = false; complete = true; return; } } resources::controller->get_mouse_handler_base().attack_enemy(get_dest_hex(), get_target_hex(), weapon_choice_); complete = true; //check that attacking unit is still alive, if not, consider the attack a failure unit_map::const_iterator survivor = resources::units->find(get_dest_hex()); if(!survivor.valid() || survivor->id() != unit_id_) { success = false; } success = true; }
void attack::draw_hex(const map_location& hex) { if (hex == get_dest_hex() || hex == target_hex_) //draw attack indicator { //@todo: replace this by either the use of transparency + LAYER_ATTACK_INDICATOR, //or a dedicated layer const display::tdrawing_layer layer = display::LAYER_FOOTSTEPS; //calculate direction (valid for both hexes) std::string direction_text = map_location::write_direction( get_dest_hex().get_relative_dir(target_hex_)); #ifdef SDL_GPU if (hex == get_dest_hex()) //add symbol to attacker hex { int xpos = resources::screen->get_location_x(get_dest_hex()); int ypos = resources::screen->get_location_y(get_dest_hex()); resources::screen->drawing_buffer_add(layer, get_dest_hex(), xpos, ypos, image::get_texture("whiteboard/attack-indicator-src-" + direction_text + ".png", image::SCALED_TO_HEX)); } else if (hex == target_hex_) //add symbol to defender hex { int xpos = resources::screen->get_location_x(target_hex_); int ypos = resources::screen->get_location_y(target_hex_); resources::screen->drawing_buffer_add(layer, target_hex_, xpos, ypos, image::get_texture("whiteboard/attack-indicator-dst-" + direction_text + ".png", image::SCALED_TO_HEX)); } #else if (hex == get_dest_hex()) //add symbol to attacker hex { int xpos = resources::screen->get_location_x(get_dest_hex()); int ypos = resources::screen->get_location_y(get_dest_hex()); resources::screen->drawing_buffer_add(layer, get_dest_hex(), xpos, ypos, image::get_image("whiteboard/attack-indicator-src-" + direction_text + ".png", image::SCALED_TO_HEX)); } else if (hex == target_hex_) //add symbol to defender hex { int xpos = resources::screen->get_location_x(target_hex_); int ypos = resources::screen->get_location_y(target_hex_); resources::screen->drawing_buffer_add(layer, target_hex_, xpos, ypos, image::get_image("whiteboard/attack-indicator-dst-" + direction_text + ".png", image::SCALED_TO_HEX)); } #endif } }
/* private */ void attack::invalidate() { if(resources::screen) { //invalidate dest and target hex so attack indicator is properly cleared resources::screen->invalidate(get_dest_hex()); resources::screen->invalidate(target_hex_); } }
void move::draw_hex(map_location const& hex) { //display turn info for turns 2 and above if (hex == get_dest_hex() && turn_number_ >= 2) { std::stringstream turn_text; turn_text << turn_number_; resources::screen->draw_text_in_hex(hex, display::LAYER_MOVE_INFO, turn_text.str(), 17, font::NORMAL_COLOR, 0.5,0.8); } }
void move::remove_temp_modifier(unit_map&) { if (get_source_hex() == get_dest_hex()) return; //zero-hex move, probably used by attack subclass unit* unit; { unit_map::iterator unit_it = resources::units->find(get_dest_hex()); assert(unit_it != resources::units->end()); unit = &*unit_it; } // Restore movement points DBG_WB << "Move: Changing movement points for unit " << unit->name() << " [" << unit->id() << "] from " << unit->movement_left() << " to " << unit->movement_left() + movement_cost_ << ".\n"; unit->set_movement(unit->movement_left() + movement_cost_); // Restore the unit to its original position mover_.reset(); }
void move::remove_temp_modifier(unit_map&) { if (get_source_hex() == get_dest_hex()) return; //zero-hex move, probably used by attack subclass // Debug movement points if ( !lg::debug.dont_log(log_whiteboard) ) { unit* unit; { unit_map::iterator unit_it = resources::units->find(get_dest_hex()); assert(unit_it != resources::units->end()); unit = &*unit_it; } DBG_WB << "Move: Movement points for unit " << unit->name() << " [" << unit->id() << "] should get changed from " << unit->movement_left() << " to " << unit->movement_left() + movement_cost_ << ".\n"; } // Restore the unit to its original position and movement. mover_.reset(); }
void move::calculate_move_cost() { assert(get_unit()); assert(route_); if (get_source_hex().valid() && get_dest_hex().valid() && get_source_hex() != get_dest_hex()) { // @todo: find a better treatment of movement points when defining moves out-of-turn if(get_unit()->movement_left() - route_->move_cost < 0 && resources::controller->current_side() == resources::screen->viewing_side()) { WRN_WB << "Move defined with insufficient movement left.\n"; } // If unit finishes move in a village it captures, set the move cost to unit's movement_left() if (route_->marks[get_dest_hex()].capture) { movement_cost_ = get_unit()->movement_left(); } else { movement_cost_ = route_->move_cost; } } }
attack::attack(config const& cfg, bool hidden) : move(cfg,hidden) , target_hex_(cfg.child("target_hex_")["x"],cfg.child("target_hex_")["y"]) , weapon_choice_(cfg["weapon_choice_"].to_int(-1)) //default value: -1 , attack_movement_cost_() , temp_movement_subtracted_(0) { // Validate target_hex if(!tiles_adjacent(target_hex_,get_dest_hex())) throw action::ctor_err("attack: Invalid target_hex_"); // Validate weapon_choice_ if(weapon_choice_ < 0 || weapon_choice_ >= static_cast<int>(get_unit()->attacks().size())) throw action::ctor_err("attack: Invalid weapon_choice_"); // Construct attack_movement_cost_ attack_movement_cost_ = get_unit()->attacks()[weapon_choice_].movement_used(); this->init(); }
void move::redraw() { resources::screen->invalidate(get_source_hex()); resources::screen->invalidate(get_dest_hex()); update_arrow_style(); }
std::ostream& move::print(std::ostream &s) const { s << "Move for unit " << get_unit()->name() << " [" << get_unit()->id() << "] " << "from (" << get_source_hex() << ") to (" << get_dest_hex() << ")"; return s; }
action::error move::check_validity() const { // Used to deal with multiple return paths. class arrow_texture_setter { public: arrow_texture_setter(const move *target, move::ARROW_TEXTURE current_texture, move::ARROW_TEXTURE setting_texture): target(target), current_texture(current_texture), setting_texture(setting_texture) {} ~arrow_texture_setter() { if(current_texture!=setting_texture) { target->set_arrow_texture(setting_texture); } } void set_texture(move::ARROW_TEXTURE texture) { setting_texture=texture; } private: const move *target; move::ARROW_TEXTURE current_texture, setting_texture; }; arrow_texture_setter setter(this, arrow_texture_, ARROW_TEXTURE_INVALID); if(!(get_source_hex().valid() && get_dest_hex().valid())) { return INVALID_LOCATION; } //Check that the unit still exists in the source hex unit_map::iterator unit_it; unit_it = resources::units->find(get_source_hex()); if(unit_it == resources::units->end()) { return NO_UNIT; } //check if the unit in the source hex has the same unit id as before, //i.e. that it's the same unit if(unit_id_ != unit_it->id() || unit_underlying_id_ != unit_it->underlying_id()) { return UNIT_CHANGED; } //If the path has at least two hexes (it can have less with the attack subclass), ensure destination hex is free if(get_route().steps.size() >= 2 && get_visible_unit(get_dest_hex(),resources::teams->at(viewer_team())) != NULL) { return LOCATION_OCCUPIED; } //check that the path is good if(get_source_hex() != get_dest_hex()) { //skip zero-hex move used by attack subclass // Mark the plain route to see if the move can still be done in one turn, // which is always the case for planned moves pathfind::marked_route checked_route = pathfind::mark_route(get_route().route); if(checked_route.marks[checked_route.steps.back()].turns != 1) { return TOO_FAR; } } // The move is valid, so correct the setter. setter.set_texture(ARROW_TEXTURE_VALID); return OK; }
map_location move::get_numbering_hex() const { return get_dest_hex(); }
void move::execute(bool& success, bool& complete) { if(!valid()) { success = false; //Setting complete to true signifies to side_actions to delete the planned action. complete = true; return; } if(get_source_hex() == get_dest_hex()) { //zero-hex move, used by attack subclass success = complete = true; return; } LOG_WB << "Executing: " << shared_from_this() << "\n"; // Copy the current route to ensure it remains valid throughout the animation. const std::vector<map_location> steps = route_->steps; set_arrow_brightness(ARROW_BRIGHTNESS_HIGHLIGHTED); hide_fake_unit(); size_t num_steps; bool interrupted; try { events::mouse_handler& mouse_handler = resources::controller->get_mouse_handler_base(); num_steps = mouse_handler.move_unit_along_route(steps, interrupted); } catch (end_turn_exception&) { set_arrow_brightness(ARROW_BRIGHTNESS_STANDARD); throw; // we rely on the caller to delete this action } const map_location & final_location = steps[num_steps]; unit_map::const_iterator unit_it = resources::units->find(final_location); if ( num_steps == 0 ) { LOG_WB << "Move execution resulted in zero movement.\n"; success = false; complete = true; } else if ( unit_it == resources::units->end() || unit_it->id() != unit_id_ ) { WRN_WB << "Unit disappeared from map during move execution.\n"; success = false; complete = true; } else { complete = num_steps + 1 == steps.size(); success = complete && !interrupted; if ( !success ) { if ( complete ) { LOG_WB << "Move completed, but interrupted on final hex. Halting.\n"; //reset to a single-hex path, just in case *this is a wb::attack route_->steps = std::vector<map_location>(1, final_location); arrow_.reset(); } else { LOG_WB << "Move finished at (" << final_location << ") instead of at (" << get_dest_hex() << "). Setting new path.\n"; route_->steps = std::vector<map_location>(steps.begin() + num_steps, steps.end()); //FIXME: probably better to use the new calculate_new_route() instead of the above: //calculate_new_route(final_location, steps.back()); // Of course, "better" would need to be verified. arrow_->set_path(route_->steps); } } } if(!complete) { set_arrow_brightness(ARROW_BRIGHTNESS_STANDARD); show_fake_unit(); } }
void move::execute(bool& success, bool& complete) { if (!valid_) { success = false; //Setting complete to true signifies to side_actions to delete the planned action. complete = true; return; } if (get_source_hex() == get_dest_hex()) { //zero-hex move, used by attack subclass success = complete = true; return; } LOG_WB << "Executing: " << shared_from_this() << "\n"; set_arrow_brightness(ARROW_BRIGHTNESS_HIGHLIGHTED); hide_fake_unit(); events::mouse_handler& mouse_handler = resources::controller->get_mouse_handler_base(); std::set<map_location> adj_enemies = mouse_handler.get_adj_enemies(get_dest_hex(), side_number()); map_location final_location; bool steps_finished; bool enemy_sighted; { team const& owner_team = resources::teams->at(team_index()); try { steps_finished = mouse_handler.move_unit_along_route(*route_, &final_location, owner_team.auto_shroud_updates(), &enemy_sighted); } catch (end_turn_exception&) { set_arrow_brightness(ARROW_BRIGHTNESS_STANDARD); throw; // we rely on the caller to delete this action } // final_location now contains the final unit location // if that isn't needed, pass NULL rather than &final_location // Also, enemy_sighted now tells whether a unit was sighted during the move } if(mouse_handler.get_adj_enemies(final_location,side_number()) != adj_enemies) enemy_sighted = true; //< "ambushed" on last hex unit_map::const_iterator unit_it; if (final_location == route_->steps.front()) { LOG_WB << "Move execution resulted in zero movement.\n"; success = false; complete = true; } else if (final_location.valid() && (unit_it = resources::units->find(final_location)) != resources::units->end() && unit_it->id() == unit_id_) { if (steps_finished && route_->steps.back() == final_location) //reached destination { complete = true; //check if new enemies are now visible if(enemy_sighted) { LOG_WB << "Move completed, but interrupted on final hex. Halting.\n"; //reset to a single-hex path, just in case *this is a wb::attack arrow_.reset(); route_->steps = std::vector<map_location>(1,route_->steps.back()); success = false; } else // Everything went smoothly success = true; } else // Move was interrupted, probably by enemy unit sighted { success = false; LOG_WB << "Move finished at (" << final_location << ") instead of at (" << get_dest_hex() << "), analyzing\n"; std::vector<map_location>::iterator start_new_path; bool found = false; for (start_new_path = route_->steps.begin(); ((start_new_path != route_->steps.end()) && !found); ++start_new_path) { if (*start_new_path == final_location) { found = true; } } if (found) { --start_new_path; //since the for loop incremented the iterator once after we found the right one. std::vector<map_location> new_path(start_new_path, route_->steps.end()); LOG_WB << "Setting new path for this move from (" << new_path.front() << ") to (" << new_path.back() << ").\n"; //FIXME: probably better to use the new calculate_new_route instead of doing this route_->steps = new_path; arrow_->set_path(new_path); complete = false; } else //Unit ended up in location outside path, likely due to a WML event { WRN_WB << "Unit ended up in location outside path during move execution.\n"; complete = true; } } } else //Unit disappeared from the map, likely due to a WML event { WRN_WB << "Unit disappeared from map during move execution.\n"; success = false; complete = true; } if(!complete) { set_arrow_brightness(ARROW_BRIGHTNESS_STANDARD); show_fake_unit(); } }