void Creature_tracker::remove_dead()
{
    // Can't use game::all_monsters() as it would not contain *dead* monsters.
    for( auto iter = monsters_list.begin(); iter != monsters_list.end(); ) {
        const monster &critter = **iter;
        if( critter.is_dead() ) {
            remove_from_location_map( critter );
            iter = monsters_list.erase( iter );
        } else {
            ++iter;
        }
    }
}
void Creature_tracker::swap_positions( monster &first, monster &second )
{
    const int first_mdex = mon_at( first.pos() );
    const int second_mdex = mon_at( second.pos() );
    remove_from_location_map( first );
    remove_from_location_map( second );
    bool ok = true;
    if( first_mdex == -1 || second_mdex == -1 || first_mdex == second_mdex ) {
        debugmsg( "Tried to swap monsters with invalid positions" );
        ok = false;
    }

    tripoint temp = second.pos();
    second.spawn( first.pos() );
    first.spawn( temp );
    if( ok ) {
        monsters_by_location[first.pos()] = first_mdex;
        monsters_by_location[second.pos()] = second_mdex;
    } else {
        // Try to avoid spamming error messages if something weird happens
        rebuild_cache();
    }
}
void Creature_tracker::remove( const monster &critter )
{
    const auto iter = std::find_if( monsters_list.begin(), monsters_list.end(),
    [&]( const std::shared_ptr<monster> &ptr ) {
        return ptr.get() == &critter;
    } );
    if( iter == monsters_list.end() ) {
        debugmsg( "Tried to remove invalid monster %s", critter.name() );
        return;
    }

    remove_from_location_map( critter );
    monsters_list.erase( iter );
}
bool Creature_tracker::update_pos( const monster &critter, const tripoint &new_pos )
{
    const auto old_pos = critter.pos();
    if( critter.is_dead() ) {
        // mon_at ignores dead critters anyway, changing their position in the
        // monsters_by_location map is useless.
        remove_from_location_map( critter );
        return true;
    }

    const int critter_id = mon_at( old_pos );
    const int new_critter_id = mon_at( new_pos );
    if( new_critter_id >= 0 ) {
        auto &othermon = *monsters_list[new_critter_id];
        if( othermon.is_hallucination() ) {
            othermon.die( nullptr );
        } else {
            debugmsg( "update_zombie_pos: wanted to move %s to %d,%d,%d, but new location already has %s",
                      critter.disp_name().c_str(),
                      new_pos.x, new_pos.y, new_pos.z, othermon.disp_name().c_str() );
            return false;
        }
    }

    if( critter_id >= 0 ) {
        if( &critter == monsters_list[critter_id] ) {
            monsters_by_location.erase( old_pos );
            monsters_by_location[new_pos] = critter_id;
            return true;
        } else {
            const auto &othermon = *monsters_list[critter_id];
            debugmsg( "update_zombie_pos: wanted to move %s from old location %d,%d,%d, but it had %s instead",
                      critter.disp_name().c_str(),
                      old_pos.x, old_pos.y, old_pos.z, othermon.disp_name().c_str() );
            return false;
        }
    } else {
        // We're changing the x/y/z coordinates of a zombie that hasn't been added
        // to the game yet. add_zombie() will update monsters_by_location for us.
        debugmsg( "update_zombie_pos: no %s at %d,%d,%d (moving to %d,%d,%d)",
                  critter.disp_name().c_str(),
                  old_pos.x, old_pos.y, old_pos.z, new_pos.x, new_pos.y, new_pos.z );
        // Rebuild cache in case the monster actually IS in the game, just bugged
        rebuild_cache();
        return false;
    }

    return false;
}
bool Creature_tracker::update_pos( const monster &critter, const tripoint &new_pos )
{
    if( critter.is_dead() ) {
        // find ignores dead critters anyway, changing their position in the
        // monsters_by_location map is useless.
        remove_from_location_map( critter );
        return true;
    }

    if( const std::shared_ptr<monster> new_critter_ptr = find( new_pos ) ) {
        auto &othermon = *new_critter_ptr;
        if( othermon.is_hallucination() ) {
            othermon.die( nullptr );
        } else {
            debugmsg( "update_zombie_pos: wanted to move %s to %d,%d,%d, but new location already has %s",
                      critter.disp_name(),
                      new_pos.x, new_pos.y, new_pos.z, othermon.disp_name() );
            return false;
        }
    }

    const auto iter = std::find_if( monsters_list.begin(), monsters_list.end(),
    [&]( const std::shared_ptr<monster> &ptr ) {
        return ptr.get() == &critter;
    } );
    if( iter != monsters_list.end() ) {
        monsters_by_location.erase( critter.pos() );
        monsters_by_location[new_pos] = *iter;
        return true;
    } else {
        const tripoint &old_pos = critter.pos();
        // We're changing the x/y/z coordinates of a zombie that hasn't been added
        // to the game yet. add_zombie() will update monsters_by_location for us.
        debugmsg( "update_zombie_pos: no %s at %d,%d,%d (moving to %d,%d,%d)",
                  critter.disp_name(),
                  old_pos.x, old_pos.y, old_pos.z, new_pos.x, new_pos.y, new_pos.z );
        // Rebuild cache in case the monster actually IS in the game, just bugged
        rebuild_cache();
        return false;
    }
}
void Creature_tracker::remove( const int idx )
{
    if( idx < 0 || idx >= ( int )monsters_list.size() ) {
        debugmsg( "Tried to remove monster with invalid index %d. Monster num: %d",
                  idx, monsters_list.size() );
        return;
    }

    monster &m = *monsters_list[idx];
    remove_from_location_map( m );

    delete monsters_list[idx];
    monsters_list.erase( monsters_list.begin() + idx );

    // Fix indices in monsters_by_location for any zombies that were just moved down 1 place.
    for( auto &elem : monsters_by_location ) {
        if( elem.second > ( size_t )idx ) {
            --elem.second;
        }
    }
}
bool Creature_tracker::update_pos(const monster &critter, const tripoint &new_pos)
{
    const auto old_pos = critter.pos3();
    if( critter.is_dead() ) {
        // mon_at ignores dead critters anyway, changing their position in the
        // monsters_by_location map is useless.
        remove_from_location_map( critter );
        return true;
    }

    bool success = false;
    const int critter_id = mon_at( old_pos );
    const int new_critter_id = mon_at( new_pos );
    if( new_critter_id >= 0 ) {
        debugmsg("update_zombie_pos: new location %d,%d,%d already has zombie %d",
                 new_pos.x, new_pos.y, new_pos.z, new_critter_id);
    } else if( critter_id >= 0 ) {
        if( &critter == monsters_list[critter_id] ) {
            monsters_by_location.erase( old_pos );
            monsters_by_location[new_pos] = critter_id;
            success = true;
        } else {
            debugmsg("update_zombie_pos: old location %d,%d had zombie %d instead",
                     old_pos.x, old_pos.y, critter_id);
        }
    } else {
        // We're changing the x/y/z coordinates of a zombie that hasn't been added
        // to the game yet. add_zombie() will update monsters_by_location for us.
        debugmsg("update_zombie_pos: no such zombie at %d,%d,%d (moving to %d,%d,%d)",
                 old_pos.x, old_pos.y, old_pos.z, new_pos.x, new_pos.y, new_pos.z );
        // Rebuild cache in case the monster actually IS in the game, just bugged
        rebuild_cache();
    }

    return success;
}