Exemple #1
0
/**
 * Animates a single step between hexes.
 * This will return before the animation actually finishes, allowing other
 * processing to occur during the animation.
 *
 * @param a          The starting hex.
 * @param b          The ending hex.
 * @param temp_unit  The unit to animate (historically, a temporary unit).
 * @param step_num   The number of steps taken so far (used to pick an animation).
 * @param step_left  The number of steps remaining (used to pick an animation).
 * @param animator   The unit_animator to use. This is assumed clear when we start,
 *                   but will likely not be clear when we return.
 * @param disp       The game display. Assumed neither locked nor faked.
 * @returns  The animation potential until this animation will finish.
 *           INT_MIN indicates that no animation is pending.
 */
static int move_unit_between(const map_location& a, const map_location& b,
                             unit_ptr temp_unit, unsigned int step_num,
                             unsigned int step_left, unit_animator & animator,
                             display& disp)
{
	if ( disp.fogged(a) && disp.fogged(b) ) {
		return INT_MIN;
	}

	temp_unit->set_location(a);
	disp.invalidate(a);
	temp_unit->set_facing(a.get_relative_dir(b));
	animator.replace_anim_if_invalid(temp_unit.get(),"movement",a,b,step_num,
			false,"",0,unit_animation::hit_type::INVALID,nullptr,nullptr,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 multiple 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;

	return target_time;
}
Exemple #2
0
/**
 * Initiates the display of movement for the supplied unit.
 * This should be called before attempting to display moving to a new hex.
 */
void unit_mover::start(unit_ptr u)
{
	// Nothing to do here if there is nothing to animate.
	if ( !can_draw_ )
		return;
	// If no animation then hide unit until end of movement
	if ( !animate_ ) {
		was_hidden_ = u->get_hidden();
		u->set_hidden(true);
		return;
	}

	// This normally does nothing, but just in case...
	wait_for_anims();

	// Visually replace the original unit with the temporary.
	// (Original unit is left on the map, so the unit count is correct.)
	replace_temporary(u);

	// Initialize our temporary unit for the move.
	temp_unit_ptr_->set_location(path_[0]);
	temp_unit_ptr_->set_facing(path_[0].get_relative_dir(path_[1]));
	temp_unit_ptr_->anim_comp().set_standing(false);
	disp_->invalidate(path_[0]);

	// If the unit can be seen here by the viewing side:
	if ( !is_enemy_ || !temp_unit_ptr_->invisible(path_[0]) ) {
		// Scroll to the path, but only if it fully fits on screen.
		// If it does not fit we might be able to do a better scroll later.
		disp_->scroll_to_tiles(path_, game_display::ONSCREEN, true, true, 0.0, false);
	}
	// We need to clear big invalidation before the move and have a smooth animation
	// (mainly black stripes and invalidation after canceling attack dialog).
	// Two draw calls are needed to also redraw the previously invalidated hexes.
	// We use update=false because we don't need delay here (no time wasted)
	// and no screen refresh (will be done by last 3rd draw() and it optimizes
	// the double blitting done by these invalidations).
	disp_->draw(false);
	disp_->draw(false);

	// The last draw() was still slow, and its initial new_animation_frame() call
	// is now old, so we do another draw() to get a fresh one
	// TODO: replace that by a new_animation_frame() before starting anims
	//       don't forget to change the previous draw(false) to true
	disp_->draw(true);

	// extra immobile movement animation for take-off
	animator_.add_animation(temp_unit_ptr_.get(), "pre_movement", path_[0], path_[1]);
	animator_.start_animations();
	animator_.wait_for_end();
	animator_.clear();

	// Switch the display back to the real unit.
	u->set_facing(temp_unit_ptr_->facing());
	u->anim_comp().set_standing(false);	// Need to reset u's animation so the new facing takes effect.
	u->set_hidden(was_hidden_);
	temp_unit_ptr_->set_hidden(true);
}
Exemple #3
0
/**
 * Finishes the display of movement for the supplied unit.
 * If called before showing the unit reach the end of the path, it will be
 * assumed that the movement ended early.
 * If @a dir is not supplied, the final direction will be determined by (the
 * last two traversed hexes of) the path.
 */
void unit_mover::finish(unit_ptr u, map_location::DIRECTION dir)
{
	// Nothing to do here if the display is not valid.
	if ( !can_draw_ ) {
		// Make sure to reset the unit's animation to deal with a quirk in the
		// action engine where it leaves it to us to reenable bars even if the
		// display is initially locked.
		u->anim_comp().set_standing(true);
		return;
	}

	const map_location & end_loc = path_[current_];
	const map_location::DIRECTION final_dir = current_ == 0 ?
		path_[0].get_relative_dir(path_[1]) :
		path_[current_-1].get_relative_dir(end_loc);

	if ( animate_ )
	{
		wait_for_anims(); // In case proceed_to() did not wait for the last animation.

		// Make sure the displayed unit is correct.
		replace_temporary(u);
		temp_unit_ptr_->set_location(end_loc);
		temp_unit_ptr_->set_facing(final_dir);

		// Animation
		animator_.add_animation(temp_unit_ptr_.get(), "post_movement", end_loc);
		animator_.start_animations();
		animator_.wait_for_end();
		animator_.clear();

		// Switch the display back to the real unit.
		u->set_hidden(was_hidden_);
		temp_unit_ptr_->set_hidden(true);

		events::mouse_handler* mousehandler = events::mouse_handler::get_singleton();
		if ( mousehandler ) {
			mousehandler->invalidate_reachmap();
		}
	}
	else
	{
		// Show the unit at end of skipped animation
		u->set_hidden(was_hidden_);
	}

	// Facing gets set even when not animating.
	u->set_facing(dir == map_location::NDIRECTIONS ? final_dir : dir);
	u->anim_comp().set_standing(true);	// Need to reset u's animation so the new facing takes effect.

	// Redraw path ends (even if not animating).
	disp_->invalidate(path_.front());
	disp_->invalidate(end_loc);
}
bool unit::is_ally(const unit_ptr &u) const
{
    if (!u)
        return false;

    if (get_align() == align_neutral || u->get_align() == align_neutral)
        return true;

    if (u->get_align() == get_align())
        return true;

    if (u->get_align() == align_ally || get_align() == align_ally)
        return false;

    return true;
}
Exemple #5
0
unit_map::umap_retval_pair_t unit_map::replace(const map_location& l, unit_ptr p)
{
	self_check();
	p->set_location(l);
	erase(l);
	return insert(p);
}
Exemple #6
0
void world::remove_unit(unit_ptr u)
{
	unit_map::iterator pos = units_.find(u->loc());
	assert(pos != units_.end());
	unit_list::iterator i = std::find(pos->second.begin(), pos->second.end(), u);
	assert(i != pos->second.end());
	pos->second.erase(i);
}
Exemple #7
0
/* Note: Hide the unit in its current location; do not actually remove it.
 * Otherwise the status displays will be wrong during the movement.
 */
void unit_mover::replace_temporary(unit_ptr u)
{
	if ( disp_ == nullptr )
		// No point in creating a temp unit with no way to display it.
		return;

	// Save the hidden state of the unit.
	was_hidden_ = u->get_hidden();

	// Make our temporary unit mostly match u...
	temp_unit_ptr_ = fake_unit_ptr(unit_ptr(new unit(*u)), resources::fake_units);

	// ... but keep the temporary unhidden and hide the original.
	temp_unit_ptr_->set_hidden(false);
	u->set_hidden(true);

	// Update cached data.
	is_enemy_ =	(*resources::teams)[u->side()-1].is_enemy(disp_->viewing_side());
}
Exemple #8
0
/**
 * Visually moves a unit from the last hex we drew to the one specified by
 * @a path_index. If @a path_index points to an earlier hex, we do nothing.
 * The moving unit will only be updated if update is set to true; otherwise,
 * the provided unit is merely hidden during the movement and re-shown after.
 * (Not updating the unit can produce smoother animations in some cases.)
 * If @a wait is set to false, this returns without waiting for the final
 * animation to finish. Call wait_for_anims() to explicitly get this final
 * wait (another call to proceed_to() or finish() will implicitly wait). The
 * unit must remain valid until the wait is finished.
 */
void unit_mover::proceed_to(unit_ptr u, size_t path_index, bool update, bool wait)
{
	// Nothing to do here if animations cannot be shown.
	if ( !can_draw_ || !animate_ )
		return;

	// Handle pending visibility issues before introducing new ones.
	wait_for_anims();

	if ( update  ||  !temp_unit_ptr_ )
		// Replace the temp unit (which also hides u and shows our temporary).
		replace_temporary(u);
	else
	{
		// Just switch the display from the real unit to our fake one.
		temp_unit_ptr_->set_hidden(false);
		u->set_hidden(true);
	}

	// Safety check.
	path_index = std::min(path_index, path_.size()-1);

	for ( ; current_ < path_index; ++current_ )
		// If the unit can be seen by the viewing side while making this step:
		if ( !is_enemy_ || !temp_unit_ptr_->invisible(path_[current_]) ||
		     !temp_unit_ptr_->invisible(path_[current_+1]) )
		{
			// Wait for the previous step to complete before drawing the next one.
			wait_for_anims();

			if ( !disp_->tile_fully_on_screen(path_[current_]) ||
			     !disp_->tile_fully_on_screen(path_[current_+1]))
			{
				// prevent the unit from disappearing if we scroll here with i == 0
				temp_unit_ptr_->set_location(path_[current_]);
				disp_->invalidate(path_[current_]);
				// scroll in as much of the remaining path as possible
				if ( temp_unit_ptr_->anim_comp().get_animation() )
					temp_unit_ptr_->anim_comp().get_animation()->pause_animation();
				disp_->scroll_to_tiles(path_.begin() + current_,
				                       path_.end(), game_display::ONSCREEN,
				                       true, false, 0.0, force_scroll_);
				if ( temp_unit_ptr_->anim_comp().get_animation() )
					temp_unit_ptr_->anim_comp().get_animation()->restart_animation();
			}

			if ( tiles_adjacent(path_[current_], path_[current_+1]) )
				wait_until_ =
					move_unit_between(path_[current_], path_[current_+1],
					                  temp_unit_ptr_.get_unit_ptr(), current_,
					                  path_.size() - (current_+2), animator_,
					                  *disp_);
			else if ( path_[current_] != path_[current_+1] )
				teleport_unit_between(path_[current_], path_[current_+1],
				                      *temp_unit_ptr_, *disp_);
		}

	// Update the unit's facing.
	u->set_facing(temp_unit_ptr_->facing());
	u->anim_comp().set_standing(false);	// Need to reset u's animation so the new facing takes effect.
	// Remember the unit to unhide when the animation finishes.
	shown_unit_ = u;
	if ( wait )
		wait_for_anims();
}
Exemple #9
0
void world::add_unit(const hex::location& loc, unit_ptr u)
{
	units_[loc].push_back(u);
	u->set_loc(loc);
}
Exemple #10
0
/** Inserts the unit pointed to by @a p into the unit_map.

It needs to succeed on the insertion to the umap and to the lmap
otherwise all operations are reverted.
1. Construct a unit_pod
2. Try insertion into the umap
3. Try insertion in the lmap and remove the umap entry on failure

The one oddity is that to facilitate non-invalidating iterators the list
sometimes has NULL pointers which should be used when they correspond
to uids previously used.
 */
std::pair<unit_map::unit_iterator, bool> unit_map::insert(unit_ptr p) {
	self_check();
	assert(p);

	size_t unit_id = p->underlying_id();
	const map_location &loc = p->get_location();

	if (!loc.valid()) {
		ERR_NG << "Trying to add " << p->name()
			<< " - " << p->id() << " at an invalid location; Discarding.\n";
		return std::make_pair(make_unit_iterator(umap_.end()), false);
	}

	unit_pod upod;
	upod.unit = p ;

	DBG_NG << "Adding unit " << p->underlying_id() << " - " << p->id()
		<< " to location: (" << loc << ")\n";

	std::pair<t_umap::iterator, bool> uinsert = umap_.insert(std::make_pair(unit_id, upod ));

	if (! uinsert.second) {
		//If the pod is empty reinsert the unit in the same list element
		if (!uinsert.first->second.unit) {
			unit_pod &opod = uinsert.first->second;
			opod.unit = p ;
			assert(opod.ref_count != 0);
		} else {
			unit_ptr q = uinsert.first->second.unit;
			ERR_NG << "Trying to add " << p->name()
				   << " - " << p->id() << " - " << p->underlying_id()
				   << " ("  << loc << ") over " << q->name()
				   << " - " << q->id() << " - " << q->underlying_id()
				   << " ("  << q->get_location()
				   << ").";

			p->clone(false);
			ERR_NG << "The new unit was assigned underlying_id="
				   << p->underlying_id()
				   << " to prevent duplicate id conflicts.\n";

			uinsert = umap_.insert(std::make_pair(p->underlying_id(), upod ));
			int guard(0);
			while (!uinsert.second && (++guard < 1e6) ) {
				if(guard % 10 == 9){
					ERR_NG << "\n\nPlease Report this error to https://gna.org/bugs/index.php?18591 "
						"\nIn addition to the standard details of operating system and wesnoth version "
						"and how it happened, please answer the following questions "
						"\n 1. Were you playing multi-player?"
						"\n 2. Did you start/restart/reload the game/scenario?"
						"\nThank you for your help in fixing this bug.\n";
				}
				p->clone(false);
				uinsert = umap_.insert(std::make_pair(p->underlying_id(), upod )); }
			if (!uinsert.second) {
				throw game::error("One million collisions in unit_map"); }
		}
	}

	std::pair<t_lmap::iterator,bool> linsert = lmap_.insert(std::make_pair(loc, uinsert.first ));

	//Fail if the location is occupied
	if(! linsert.second) {
		if(upod.ref_count == 0) {
			//Undo a virgin insertion
			umap_.erase(uinsert.first);
		} else {
			//undo a reinsertion
			uinsert.first->second.unit.reset();
		}
		DBG_NG << "Trying to add " << p->name()
			   << " - " << p->id() << " at location ("<<loc <<"); Occupied  by "
			   <<(linsert.first->second->second).unit->name()<< " - " << linsert.first->second->second.unit->id() <<"\n";

		return std::make_pair(make_unit_iterator(umap_.end()), false);
	}

	self_check();
	return std::make_pair( make_unit_iterator( uinsert.first ), true);
}
Exemple #11
0
static bool find_if_matches_uid_helper(const unit_ptr & ptr, size_t uid)
{
	return ptr->underlying_id() == uid;
}
Exemple #12
0
static bool find_if_matches_helper(const unit_ptr & ptr, const std::string & unit_id)
{
	return ptr->matches_id(unit_id);
}
Exemple #13
0
bool game_board::try_add_unit_to_recall_list(const map_location&, const unit_ptr u)
{
	teams_[u->side()-1].recall_list().add(u);
	return true;
}