Fix #1987: halo remained after killing an unit with the context menu

The cause for this bug was the whiteboard.

A unit destroys its halo when the unit itself is destroyed. Makes sense,
right? Unfortunately, units are managed by shared pointers, and therefore
are not destroyed until all references to them are gone. And the whiteboard
retains a reference to the most recently selected unit.

The obvious fix would be to use a weak (non-owning) pointer in the
whiteboard, but it's sadly not possible because unit_ptr is an
intrusive_ptr. It stores the reference count right in the unit itself, and
therefore the refcount is lost when the unit is destroyed: hence weak
pointers are impossible.

Thus, what I ended up doing was explicitly notifying the whiteboard when an
unit is killed, and releasing the reference when it happens. If anyone has
suggestions about a better implementation, I'd love to hear them.
This commit is contained in:
Jyrki Vesterinen 2017-09-14 20:14:08 +03:00
parent 905f1eb391
commit 79554179e9
5 changed files with 15 additions and 0 deletions

View file

@ -69,6 +69,7 @@
#include "recall_list_manager.hpp" // for recall_list_manager
#include "replay.hpp" // for get_user_choice, etc
#include "reports.hpp" // for register_generator, etc
#include "resources.hpp" // for whiteboard
#include "scripting/lua_audio.hpp"
#include "scripting/lua_unit.hpp"
#include "scripting/lua_unit_attacks.hpp"
@ -104,6 +105,7 @@
#include "units/types.hpp" // for unit_type_data, unit_types, etc
#include "variable.hpp" // for vconfig, etc
#include "variable_info.hpp"
#include "whiteboard/manager.hpp" // for whiteboard
#include "wml_exception.hpp"
#include "utils/functional.hpp" // for bind_t, bind
@ -2128,6 +2130,7 @@ int game_lua_kernel::intf_erase_unit(lua_State *L)
}
units().erase(loc);
resources::whiteboard->on_kill_unit();
return 0;
}

View file

@ -36,6 +36,7 @@
#include "formula/string_utils.hpp"
#include "units/types.hpp"
#include "units/udisplay.hpp"
#include "whiteboard/manager.hpp"
#include "font/standard_colors.hpp"
static lg::log_domain log_replay("replay");
@ -533,6 +534,7 @@ SYNCED_COMMAND_HANDLER_FUNCTION(debug_kill, child, use_undo, /*show*/, /*error_h
if (i.valid()) {
resources::gameboard->units().erase(i);
}
resources::whiteboard->on_kill_unit();
actions::recalculate_fog(dying_side);
}
return true;

View file

@ -58,6 +58,8 @@ public:
/// @return the collection of actions that are highlighted but don't have the focus
secondary_highlights_t get_secondary_highlights() { return secondary_highlights_; }
void set_selection_candidate(unit_ptr candidate) { selection_candidate_ = candidate; }
private:
unit_map& get_unit_map();
/** Unhighlight a given action (main or secondary). */

View file

@ -410,6 +410,13 @@ void manager::on_change_controller(int side, const team& t)
}
}
void manager::on_kill_unit()
{
if(highlighter_ != nullptr) {
highlighter_->set_selection_candidate(unit_ptr());
}
}
bool manager::current_side_has_actions()
{
if(current_side_actions()->empty()) {

View file

@ -92,6 +92,7 @@ public:
void on_gamestate_change();
void on_viewer_change(size_t team_index);
void on_change_controller(int side, const team& t);
void on_kill_unit();
/** Handles various cleanup right before removing an action from the queue */
void pre_delete_action(action_ptr action);
/** Handles various cleanup right after removing an action from the queue */