Fix crash in scenario editor when placing units

The previous bugfix fixed a crash when loading savegames with units whose
abilities accessed the map while the display_context was still being
constructed, however it reintroduced a bug in the scenario editor. Using the
unit tool always crashed when adding any unit.

This commit uses 4d1fc268b1's code path whenever possible, however it falls
back to 6a21fdc675's code path when 4d1fc268b1 would assert. In practise one
path is used in game and the other is used in the scenario editor, but doing
the logic in this way ensures that any edge cases are going to affect the
scenario editor rather than the game itself.
This commit is contained in:
Steve Cotton 2023-01-21 02:22:57 +01:00 committed by Steve Cotton
parent 3892b947af
commit 1485cfd625
2 changed files with 43 additions and 16 deletions

View file

@ -0,0 +1,2 @@
### Editor
* Fixed: the unit tool crashes when placing a unit (issue #7296).

View file

@ -18,6 +18,7 @@
* Manage unit-abilities, like heal, cure, and weapon_specials.
*/
#include "display.hpp"
#include "display_context.hpp"
#include "font/text_formatting.hpp"
#include "game_board.hpp"
@ -131,10 +132,41 @@ A poisoned unit cannot be cured of its poison by a healer, and must seek the car
namespace {
const unit_map& get_unit_map()
{
// Used if we're in the game, including during the construction of the display_context
if(resources::gameboard) {
return resources::gameboard->units();
}
// If we get here, we're in the scenario editor
assert(display::get_singleton());
return display::get_singleton()->get_units();
}
const team& get_team(std::size_t side)
{
// Used if we're in the game, including during the construction of the display_context
if(resources::gameboard) {
return resources::gameboard->get_team(side);
}
// If we get here, we're in the scenario editor
assert(display::get_singleton());
return display::get_singleton()->get_disp_context().get_team(side);
}
/**
* Common code for the question "some other unit has an ability, can that ability affect this
* unit" - it's not the full answer to that question, just a part of it.
*
* Although this is called while checking which units' "hides" abilities are active, that's only
* for the question "is this unit next to an ally that has a 'camoflages adjacent allies' ability";
* not the question "is this unit next to an enemy, therefore visible".
*/
bool affects_side(const config& cfg, std::size_t side, std::size_t other_side)
{
assert(resources::gameboard);
const team& side_team = resources::gameboard->get_team(side);
const team& side_team = get_team(side);
if(side == other_side)
return cfg["affect_allies"].to_bool(true);
@ -156,8 +188,7 @@ bool unit::get_ability_bool(const std::string& tag_name, const map_location& loc
}
}
assert(resources::gameboard);
const unit_map& units = resources::gameboard->units();
const unit_map& units = get_unit_map();
const auto adjacent = get_adjacent_tiles(loc);
for(unsigned i = 0; i < adjacent.size(); ++i) {
@ -197,8 +228,7 @@ unit_ability_list unit::get_abilities(const std::string& tag_name, const map_loc
}
}
assert(resources::gameboard);
const unit_map& units = resources::gameboard->units();
const unit_map& units = get_unit_map();
const auto adjacent = get_adjacent_tiles(loc);
for(unsigned i = 0; i < adjacent.size(); ++i) {
@ -361,8 +391,7 @@ bool unit::ability_active(const std::string& ability,const config& cfg,const map
const auto adjacent = get_adjacent_tiles(loc);
assert(resources::gameboard);
const unit_map& units = resources::gameboard->units();
const unit_map& units = get_unit_map();
for (const config &i : cfg.child_range("filter_adjacent"))
{
@ -520,8 +549,7 @@ T get_single_ability_value(const config::attribute_value& v, T def, const unit_a
return v.apply_visitor(get_ability_value_visitor(def, [&](const std::string& s) {
try {
assert(resources::gameboard);
const unit_map& units = resources::gameboard->units();
const unit_map& units = get_unit_map();
auto u_itor = units.find(ability_info.teacher_loc);
@ -881,8 +909,7 @@ std::string attack_type::weapon_specials(bool is_backstab) const
}
std::string weapon_abilities;
std::set<std::string> checking_name;
assert(resources::gameboard);
const unit_map& units = resources::gameboard->units();
const unit_map& units = get_unit_map();
if(self_){
for (const config::any_child &sp : self_->abilities().all_children_range()){
const bool active = check_self_abilities_impl(shared_from_this(), other_attack_, sp.cfg, self_, self_loc_, AFFECT_SELF, sp.key);
@ -1373,8 +1400,7 @@ bool attack_type::check_adj_abilities_impl(const_attack_ptr self_attack, const_a
*/
bool attack_type::has_weapon_ability(const std::string& special, bool special_id, bool special_tags) const
{
assert(resources::gameboard);
const unit_map& units = resources::gameboard->units();
const unit_map& units = get_unit_map();
if(self_){
std::vector<special_match> special_tag_matches_self;
std::vector<special_match> special_id_matches_self;
@ -1531,8 +1557,7 @@ bool attack_type::special_active_impl(const_attack_ptr self_attack, const_attack
}
// Get the units involved.
assert(resources::gameboard);
const unit_map& units = resources::gameboard->units();
const unit_map& units = get_unit_map();
unit_const_ptr self = self_attack ? self_attack->self_ : other_attack->other_;
unit_const_ptr other = self_attack ? self_attack->other_ : other_attack->self_;