Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport #70423 and #70685 #73774

Merged
merged 2 commits into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions src/cata_utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,57 @@ std::map<K, V> map_without_keys( const std::map<K, V> &original, const std::vect
return filtered;
}

template<typename Map, typename Set>
bool map_equal_ignoring_keys( const Map &lhs, const Map &rhs, const Set &ignore_keys )
{
// Since map and set are sorted, we can do this as a single pass with only conditional checks into remove_keys
if( ignore_keys.empty() ) {
return lhs == rhs;
}

auto lbegin = lhs.begin();
auto lend = lhs.end();
auto rbegin = rhs.begin();
auto rend = rhs.end();

for( ; lbegin != lend && rbegin != rend; ++lbegin, ++rbegin ) {
// Sanity check keys
if( lbegin->first != rbegin->first ) {
while( lbegin != lend && ignore_keys.count( lbegin->first ) == 1 ) {
++lbegin;
}
if( lbegin == lend ) {
break;
}
if( rbegin->first != lbegin->first ) {
while( rbegin != rend && ignore_keys.count( rbegin->first ) == 1 ) {
++rbegin;
}
if( rbegin == rend ) {
break;
}
}
// If we've skipped ignored keys and the keys still don't match,
// then the maps are unequal.
if( lbegin->first != rbegin->first ) {
return false;
}
}
if( lbegin->second != rbegin->second && ignore_keys.count( lbegin->first ) != 1 ) {
return false;
}
// Either the values were equal, or the key was ignored.
}
// At least one map ran out of keys. The other may still have ignored keys in it.
while( lbegin != lend && ignore_keys.count( lbegin->first ) ) {
++lbegin;
}
while( rbegin != rend && ignore_keys.count( rbegin->first ) ) {
++rbegin;
}
return lbegin == lend && rbegin == rend;
}

int modulo( int v, int m );

/** Add elements from one set to another */
Expand Down
26 changes: 18 additions & 8 deletions src/catacharset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -415,21 +415,31 @@ std::u32string utf8_to_utf32( const std::string_view str )
std::vector<std::string> utf8_display_split( const std::string &s )
{
std::vector<std::string> result;
std::string current_glyph;
std::vector<std::string_view> parts;
utf8_display_split_into( s, parts );
result.reserve( parts.size() );
for( std::string_view part : parts ) {
result.emplace_back( part );
}
return result;
}

void utf8_display_split_into( const std::string &s, std::vector<std::string_view> &result )
{
const char *pos = s.c_str();
const char *glyph_begin = pos;
const char *glyph_end = pos;
int len = s.length();
while( len > 0 ) {
const char *old_pos = pos;
const uint32_t ch = UTF8_getch( &pos, &len );
const int width = mk_wcwidth( ch );
if( width > 0 && !current_glyph.empty() ) {
result.push_back( current_glyph );
current_glyph.clear();
if( width > 0 && glyph_begin != glyph_end ) {
result.emplace_back( glyph_begin, std::distance( glyph_begin, glyph_end ) );
glyph_begin = glyph_end;
}
current_glyph += std::string( old_pos, pos );
glyph_end = pos;
}
result.push_back( current_glyph );
return result;
result.emplace_back( glyph_begin, std::distance( glyph_begin, glyph_end ) );
}

int center_text_pos( const char *text, int start_pos, int end_pos )
Expand Down
1 change: 1 addition & 0 deletions src/catacharset.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ std::u32string utf8_to_utf32( std::string_view str );
// Split the given string into displayed characters. Each element of the returned vector
// contains one 'regular' codepoint and all subsequent combining characters.
std::vector<std::string> utf8_display_split( const std::string & );
void utf8_display_split_into( const std::string &, std::vector<std::string_view> & );

/**
* UTF8-Wrapper over std::string.
Expand Down
36 changes: 18 additions & 18 deletions src/field.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,8 @@ field_entry *field::find_field( const field_type_id &field_type_to_find, const b
if( !_displayed_field_type ) {
return nullptr;
}
const auto it = _field_type_list.find( field_type_to_find );
if( it != _field_type_list.end() && ( !alive_only || it->second.is_field_alive() ) ) {
const auto it = _field_type_list->find( field_type_to_find );
if( it != _field_type_list->end() && ( !alive_only || it->second.is_field_alive() ) ) {
return &it->second;
}
return nullptr;
Expand All @@ -137,8 +137,8 @@ const field_entry *field::find_field( const field_type_id &field_type_to_find,
if( !_displayed_field_type ) {
return nullptr;
}
const auto it = _field_type_list.find( field_type_to_find );
if( it != _field_type_list.end() && ( !alive_only || it->second.is_field_alive() ) ) {
const auto it = _field_type_list->find( field_type_to_find );
if( it != _field_type_list->end() && ( !alive_only || it->second.is_field_alive() ) ) {
return &it->second;
}
return nullptr;
Expand All @@ -159,8 +159,8 @@ bool field::add_field( const field_type_id &field_type_to_add, const int new_int
if( !field_type_to_add ) {
return false;
}
auto it = _field_type_list.find( field_type_to_add );
if( it != _field_type_list.end() ) {
auto it = _field_type_list->find( field_type_to_add );
if( it != _field_type_list->end() ) {
//Already exists, but lets update it. This is tentative.
int prev_intensity = it->second.get_field_intensity();
if( !it->second.is_field_alive() ) {
Expand All @@ -180,8 +180,8 @@ bool field::add_field( const field_type_id &field_type_to_add, const int new_int

bool field::remove_field( const field_type_id &field_to_remove )
{
const auto it = _field_type_list.find( field_to_remove );
if( it == _field_type_list.end() ) {
const auto it = _field_type_list->find( field_to_remove );
if( it == _field_type_list->end() ) {
return false;
}
remove_field( it );
Expand All @@ -190,9 +190,9 @@ bool field::remove_field( const field_type_id &field_to_remove )

void field::remove_field( std::map<field_type_id, field_entry>::iterator const it )
{
_field_type_list.erase( it );
_field_type_list->erase( it );
_displayed_field_type = fd_null;
for( auto &fld : _field_type_list ) {
for( auto &fld : *_field_type_list ) {
if( !_displayed_field_type || fld.first.obj().priority >= _displayed_field_type.obj().priority ) {
_displayed_field_type = fld.first;
}
Expand All @@ -201,7 +201,7 @@ void field::remove_field( std::map<field_type_id, field_entry>::iterator const i

void field::clear()
{
_field_type_list.clear();
_field_type_list->clear();
_displayed_field_type = fd_null;
}

Expand All @@ -211,27 +211,27 @@ Returns the number of fields existing on the current tile.
*/
unsigned int field::field_count() const
{
return _field_type_list.size();
return _field_type_list->size();
}

std::map<field_type_id, field_entry>::iterator field::begin()
{
return _field_type_list.begin();
return _field_type_list->begin();
}

std::map<field_type_id, field_entry>::const_iterator field::begin() const
{
return _field_type_list.begin();
return _field_type_list->begin();
}

std::map<field_type_id, field_entry>::iterator field::end()
{
return _field_type_list.end();
return _field_type_list->end();
}

std::map<field_type_id, field_entry>::const_iterator field::end() const
{
return _field_type_list.end();
return _field_type_list->end();
}

/*
Expand All @@ -250,14 +250,14 @@ description_affix field::displayed_description_affix() const

int field::displayed_intensity() const
{
auto it = _field_type_list.find( _displayed_field_type );
auto it = _field_type_list->find( _displayed_field_type );
return it->second.get_field_intensity();
}

int field::total_move_cost() const
{
int current_cost = 0;
for( const auto &fld : _field_type_list ) {
for( const auto &fld : *_field_type_list ) {
current_cost += fld.second.get_intensity_level().move_cost;
}
return current_cost;
Expand Down
3 changes: 2 additions & 1 deletion src/field.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <vector>

#include "calendar.h"
#include "cata_lazy.h"
#include "color.h"
#include "enums.h"
#include "field_type.h"
Expand Down Expand Up @@ -180,7 +181,7 @@ class field

private:
// A pointer lookup table of all field effects on the current tile.
std::map<field_type_id, field_entry> _field_type_list;
lazy<std::map<field_type_id, field_entry>> _field_type_list;
//_displayed_field_type currently is equal to the last field added to the square. You can modify this behavior in the class functions if you wish.
field_type_id _displayed_field_type;
};
Expand Down
31 changes: 21 additions & 10 deletions src/item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ item::item( const itype_id &id, time_point turn, solitary_tag tag )

safe_reference<item> item::get_safe_reference()
{
return anchor.reference_to( this );
return anchor->reference_to( this );
}

static const item *get_most_rotten_component( const item &craft )
Expand Down Expand Up @@ -587,10 +587,10 @@ item::item( const recipe *rec, int qty, item &component )
}

item::item( const item & ) = default;
item::item( item && ) noexcept( map_is_noexcept ) = default;
item::item( item && ) noexcept = default;
item::~item() = default;
item &item::operator=( const item & ) = default;
item &item::operator=( item && ) noexcept( list_is_noexcept ) = default;
item &item::operator=( item && ) noexcept = default;

item item::make_corpse( const mtype_id &mt, time_point turn, const std::string &name,
const int upgrade_time )
Expand Down Expand Up @@ -1436,7 +1436,8 @@ bool item::stacks_with( const item &rhs, bool check_components, bool combine_liq
itype_variant().id != rhs.itype_variant().id ) ) {
return false;
}
if( ammo_remaining() != 0 && rhs.ammo_remaining() != 0 && is_money() ) {
const std::set<ammotype> ammo = ammo_types();
if( is_money( ammo ) && ammo_remaining( ammo ) != 0 && rhs.ammo_remaining() != 0 ) {
// Dealing with nonempty cash cards
// TODO: Fix cash cards not showing total value. Until that is fixed do not stack cash cards.
// When that is fixed just change this to true.
Expand Down Expand Up @@ -1488,11 +1489,11 @@ bool item::stacks_with( const item &rhs, bool check_components, bool combine_liq
}
// Guns that differ only by dirt/shot_counter can still stack,
// but other item_vars such as label/note will prevent stacking
const std::vector<std::string> ignore_keys = { "dirt", "shot_counter", "spawn_location_omt" };
if( map_without_keys( item_vars, ignore_keys ) != map_without_keys( rhs.item_vars, ignore_keys ) ) {
static const std::set<std::string> ignore_keys = { "dirt", "shot_counter", "spawn_location_omt" };
if( !map_equal_ignoring_keys( item_vars, rhs.item_vars, ignore_keys ) ) {
return false;
}
const std::string omt_loc_var = "spawn_location_omt";
static const std::string omt_loc_var = "spawn_location_omt";
const bool this_has_location = has_var( omt_loc_var );
const bool that_has_location = has_var( omt_loc_var );
if( this_has_location != that_has_location ) {
Expand Down Expand Up @@ -8601,7 +8602,12 @@ bool item::ready_to_revive( map &here, const tripoint &pos ) const

bool item::is_money() const
{
return ammo_types().count( ammo_money );
return is_money( ammo_types() );
}

bool item::is_money( const std::set<ammotype> &ammo ) const
{
return ammo.count( ammo_money );
}

bool item::is_cash_card() const
Expand Down Expand Up @@ -10670,7 +10676,8 @@ int item::shots_remaining( const Character *carrier ) const
return ret;
}

int item::ammo_remaining( const Character *carrier, const bool include_linked ) const
int item::ammo_remaining( const std::set<ammotype> &ammo, const Character *carrier,
const bool include_linked ) const
{
int ret = 0;

Expand All @@ -10692,7 +10699,6 @@ int item::ammo_remaining( const Character *carrier, const bool include_linked )
}
}

std::set<ammotype> ammo = ammo_types();
// Non ammo using item that uses charges
if( ammo.empty() ) {
ret += charges;
Expand Down Expand Up @@ -10729,6 +10735,11 @@ int item::ammo_remaining( const Character *carrier, const bool include_linked )

return ret;
}
int item::ammo_remaining( const Character *carrier, const bool include_linked ) const
{
std::set<ammotype> ammo = ammo_types();
return ammo_remaining( ammo, carrier, include_linked );
}

int item::ammo_remaining( const bool include_linked ) const
{
Expand Down
25 changes: 17 additions & 8 deletions src/item.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <vector>

#include "calendar.h"
#include "cata_lazy.h"
#include "cata_utility.h"
#include "compatibility.h"
#include "enums.h"
Expand Down Expand Up @@ -192,9 +193,9 @@ class item : public visitable

item();

item( item && ) noexcept( map_is_noexcept );
item( item && ) noexcept;
item( const item & );
item &operator=( item && ) noexcept( list_is_noexcept );
item &operator=( item && ) noexcept;
item &operator=( const item & );

explicit item( const itype_id &id, time_point turn = calendar::turn, int qty = -1 );
Expand Down Expand Up @@ -352,6 +353,10 @@ class item : public visitable
bool ready_to_revive( map &here, const tripoint &pos ) const;

bool is_money() const;
private:
bool is_money( const std::set<ammotype> &ammo ) const;
public:

bool is_cash_card() const;
bool is_software() const;
bool is_software_storage() const;
Expand Down Expand Up @@ -2375,6 +2380,10 @@ class item : public visitable
*/
int ammo_remaining( const Character *carrier = nullptr, bool include_linked = false ) const;
int ammo_remaining( bool include_linked ) const;
private:
int ammo_remaining( const std::set<ammotype> &ammo, const Character *carrier = nullptr,
bool include_linked = false ) const;
public:

/**
* ammo capacity for a specific ammo
Expand Down Expand Up @@ -2995,21 +3004,21 @@ class item : public visitable
const itype *type;
item_components components;
/** What faults (if any) currently apply to this item */
std::set<fault_id> faults;
cata::heap<std::set<fault_id>> faults;

private:
item_contents contents;
/** `true` if item has any of the flags that require processing in item::process_internal.
* This flag is reset to `true` if item tags are changed.
*/
bool requires_tags_processing = true;
FlagsSetType item_tags; // generic item specific flags
FlagsSetType inherited_tags_cache;
safe_reference_anchor anchor;
std::map<std::string, std::string> item_vars;
cata::heap<FlagsSetType> item_tags; // generic item specific flags
cata::heap<FlagsSetType> inherited_tags_cache;
lazy<safe_reference_anchor> anchor;
cata::heap<std::map<std::string, std::string>> item_vars;
const mtype *corpse = nullptr;
std::string corpse_name; // Name of the late lamented
std::set<matec_id> techniques; // item specific techniques
cata::heap<std::set<matec_id>> techniques; // item specific techniques

// Select a random variant from the possibilities
// Intended to be called when no explicit variant is set
Expand Down
Loading
Loading