From acba7d3679683e16f4a10a6c9e4101fdc7b16501 Mon Sep 17 00:00:00 2001 From: Reisen Usagi Date: Wed, 15 Oct 2025 10:03:27 -0300 Subject: [PATCH 01/31] split off item_vars into its own class --- src/data_vars.h | 124 ++++++++++++++++++++++++++++++++++++++++++++++++ src/item.cpp | 90 ----------------------------------- src/item.h | 35 +++++++------- 3 files changed, 141 insertions(+), 108 deletions(-) create mode 100644 src/data_vars.h diff --git a/src/data_vars.h b/src/data_vars.h new file mode 100644 index 000000000000..dbe6551aef89 --- /dev/null +++ b/src/data_vars.h @@ -0,0 +1,124 @@ +#pragma once + +#include +#include +#include + +#include "point.h" +#include "string_formatter.h" +#include "string_utils.h" + +namespace detail +{ + +template +struct data_var_sstream_converter { + std::string operator()( const T &value ) const { + std::stringstream ss; + ss.imbue( std::locale::classic() ); + ss << value; + if( ss.fail() ) { + return ""; + } + return ss.str(); + }; + + T operator()( const std::string& key, const T &def ) const { + T value = def; + std::stringstream ss( key ); + ss.imbue( std::locale::classic() ); + ss >> value; + if( ss.fail() ) { + return def; + } + return value; + } +}; + +struct data_var_tripoint_converter { + std::string operator()( const tripoint &value ) const { + return string_format( "%d,%d,%d", value.x, value.y, value.z ); + } + tripoint operator()( const std::string &value, const tripoint & ) const { + const std::vector values = string_split( value, ',' ); + return tripoint( std::stoi( values[0] ), + std::stoi( values[1] ), + std::stoi( values[2] ) ); + } +}; + +template +struct data_var_converter { + using type = void; +}; + +template +using data_var_converter_t = typename data_var_converter::type; + +template +requires std::is_integral_v or std::is_floating_point_v +struct data_var_converter { + using type = data_var_sstream_converter; +}; + +template<> +struct data_var_converter { + using type = data_var_tripoint_converter; +}; + +} // namespace detail + +class data_vars +{ + public: + using storage = std::map; + using key_type = storage::key_type; + using value_type = storage::value_type; + using mapped_type = storage::mapped_type; + using iterator = storage::iterator; + using const_iterator = storage::const_iterator; + + storage data; + + template > + void set( const key_type& name, const T& value, const Cvt& cvt = {} ) { + data[name] = cvt( value ); + } + + template > + T get( const key_type& name, const T& default_value = T{}, const Cvt& cvt = {} ) const { + const auto it = find( name ); + if( it == end() ) { + return default_value; + } + return static_cast( cvt( it->second, default_value ) ); + } + + void set( const key_type& name, const mapped_type& value ) { + data[name] = value; + } + + mapped_type get( const key_type& name, const mapped_type& default_value = mapped_type{} ) const { + const auto it = find( name ); + if( it == end() ) { + return default_value; + } + return it->second; + } + + bool operator==( const data_vars & other ) const { return ( data ) == other.data; } + mapped_type &operator[]( const key_type &name ) { return data[name]; } + + bool empty() const { return data.empty(); } + bool contains( const key_type &key ) const { return data.contains( key ); } + iterator begin() { return data.begin(); } + const_iterator begin() const { return data.begin(); } + iterator end() { return data.end(); } + const_iterator end() const { return data.end(); } + iterator find( const key_type &key ) { return data.find( key ); } + const_iterator find( const key_type &key ) const { return data.find( key ); } + + void clear() { data.clear(); } + void erase( const key_type &name ) { data.erase( name ); } + void erase( iterator it ) { data.erase( it ); } +}; \ No newline at end of file diff --git a/src/item.cpp b/src/item.cpp index 6d28d68214a4..da6e6a56964a 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -1194,96 +1194,6 @@ void item::put_in( detached_ptr &&payload ) contents.insert_item( std::move( payload ) ); } -void item::set_var( const std::string &name, const int value ) -{ - std::ostringstream tmpstream; - tmpstream.imbue( std::locale::classic() ); - tmpstream << value; - item_vars[name] = tmpstream.str(); -} - -void item::set_var( const std::string &name, const long long value ) -{ - std::ostringstream tmpstream; - tmpstream.imbue( std::locale::classic() ); - tmpstream << value; - item_vars[name] = tmpstream.str(); -} - -// NOLINTNEXTLINE(cata-no-long) -void item::set_var( const std::string &name, const long value ) -{ - std::ostringstream tmpstream; - tmpstream.imbue( std::locale::classic() ); - tmpstream << value; - item_vars[name] = tmpstream.str(); -} - -void item::set_var( const std::string &name, const double value ) -{ - item_vars[name] = string_format( "%f", value ); -} - -double item::get_var( const std::string &name, const double default_value ) const -{ - const auto it = item_vars.find( name ); - if( it == item_vars.end() ) { - return default_value; - } - return atof( it->second.c_str() ); -} - -void item::set_var( const std::string &name, const tripoint &value ) -{ - item_vars[name] = string_format( "%d,%d,%d", value.x, value.y, value.z ); -} - -tripoint item::get_var( const std::string &name, const tripoint &default_value ) const -{ - const auto it = item_vars.find( name ); - if( it == item_vars.end() ) { - return default_value; - } - std::vector values = string_split( it->second, ',' ); - return tripoint( atoi( values[0].c_str() ), - atoi( values[1].c_str() ), - atoi( values[2].c_str() ) ); -} - -void item::set_var( const std::string &name, const std::string &value ) -{ - item_vars[name] = value; -} - -std::string item::get_var( const std::string &name, const std::string &default_value ) const -{ - const auto it = item_vars.find( name ); - if( it == item_vars.end() ) { - return default_value; - } - return it->second; -} - -std::string item::get_var( const std::string &name ) const -{ - return get_var( name, "" ); -} - -bool item::has_var( const std::string &name ) const -{ - return item_vars.contains( name ); -} - -void item::erase_var( const std::string &name ) -{ - item_vars.erase( name ); -} - -void item::clear_vars() -{ - item_vars.clear(); -} - void item::add_item_with_id( const itype_id &itype, int count ) { detached_ptr new_item = item::spawn( itype, calendar::turn, count ); diff --git a/src/item.h b/src/item.h index 21d90ae169b3..2386c6189bf0 100644 --- a/src/item.h +++ b/src/item.h @@ -28,6 +28,7 @@ #include "units.h" #include "value_ptr.h" #include "visitable.h" +#include "data_vars.h" class Character; class JsonIn; @@ -1495,25 +1496,23 @@ class item : public location_visitable, public game_object * */ /*@{*/ - void set_var( const std::string &name, int value ); - void set_var( const std::string &name, long long value ); + void set_var( const std::string &name, int value ) { item_vars.set( name, value ); }; + void set_var( const std::string &name, long long value ) { item_vars.set( name, value ); }; // Acceptable to use long as part of overload set // NOLINTNEXTLINE(cata-no-long) - void set_var( const std::string &name, long value ); - void set_var( const std::string &name, double value ); - double get_var( const std::string &name, double default_value ) const; - void set_var( const std::string &name, const tripoint &value ); - tripoint get_var( const std::string &name, const tripoint &default_value ) const; - void set_var( const std::string &name, const std::string &value ); - std::string get_var( const std::string &name, const std::string &default_value ) const; - /** Get the variable, if it does not exists, returns an empty string. */ - std::string get_var( const std::string &name ) const; - /** Whether the variable is defined at all. */ - bool has_var( const std::string &name ) const; - /** Erase the value of the given variable. */ - void erase_var( const std::string &name ); - /** Removes all item variables. */ - void clear_vars(); + void set_var( const std::string &name, long value ) { item_vars.set( name, value ); }; + void set_var( const std::string &name, double value ) { item_vars.set( name, value ); }; + void set_var( const std::string &name, const tripoint &value ) { item_vars.set( name, value ); }; + void set_var( const std::string &name, const std::string &value ) { item_vars.set( name, value ); }; + + double get_var( const std::string &name, double default_value ) const { return item_vars.get( name, default_value ); } + tripoint get_var( const std::string &name, const tripoint &default_value ) const { return item_vars.get( name, default_value ); } + std::string get_var( const std::string &name, const std::string &default_value ) const { return item_vars.get( name, default_value ); } + std::string get_var( const std::string &name ) const { return item_vars.get( name, "" ); } + bool has_var( const std::string &name ) const { return item_vars.contains( name ); } + void erase_var( const std::string &name ) { item_vars.erase( name ); } + void clear_vars() { item_vars.clear(); } + /** Adds child items to the contents of this one. */ void add_item_with_id( const itype_id &itype, int count = 1 ); /** Checks if this item contains an item with itype. */ @@ -2388,7 +2387,7 @@ class item : public location_visitable, public game_object private: location_vector components; const itype *curammo = nullptr; - std::map item_vars; + data_vars item_vars = {}; const mtype *corpse = nullptr; std::string corpse_name; // Name of the late lamented std::set techniques; // item specific techniques From 3ac7baed9022fe3c09c5a11bf98ffec728a365ea Mon Sep 17 00:00:00 2001 From: Reisen Usagi Date: Thu, 16 Oct 2025 05:39:37 -0300 Subject: [PATCH 02/31] Add array2d class --- src/array2d.h | 251 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 251 insertions(+) create mode 100644 src/array2d.h diff --git a/src/array2d.h b/src/array2d.h new file mode 100644 index 000000000000..3fcf38b3d3cd --- /dev/null +++ b/src/array2d.h @@ -0,0 +1,251 @@ +#pragma once + +#include + +namespace detail +{ + +template +struct array2d_info { + using RowType = std::array; + using ArrType = std::array; +}; + +template, size_t ... Xs> +constexpr static auto init_array_2d_xs( size_t py, F && fn, std::index_sequence ) +{ + static_assert( sizeof...( Xs ) == SizeX ); + using RowType = typename array2d_info::RowType; + return RowType { + fn( Xs, py ) ..., + }; +} + +template, size_t ... Ys> +constexpr static auto init_array_2d_ys( F && fn, std::integer_sequence ) +{ + static_assert( sizeof...( Ys ) == SizeY ); + using ArrType = typename array2d_info::ArrType; + return ArrType { + detail::init_array_2d_xs( + Ys, + std::forward( fn ), + std::make_index_sequence() + ) ..., + }; +} + +template +constexpr static auto init_array_2d( F&& fn ) +{ + return detail::init_array_2d_ys( + std::forward( fn ), + std::make_index_sequence() + ); +} + +} // namespace detail + +/// 2D Array Class +/// +/// std::array, SizeY> backed +/// +/// Elements in are stored as rows of columns \p SizeY rows of \p SizeX +/// elements: +/// +/// For values without a default constructor, will initialize the elements via copy-constructor from: +/// - A constant value +/// - A callable that takes the \p x and \p y coordinates, and returns \p T +/// +/// @tparam T Element Type +/// @tparam SizeX X Dimensions +/// @tparam SizeY Y Dimensions +template +struct array2d { + struct sentinel; + struct iterator; + struct const_iterator; + struct index; + + using storage = std::array; + + using index_type = index; + using size_type = size_t; + using value_type = T; + + using pointer = value_type*; + using const_pointer = const value_type*; + using reference = value_type&; + using const_reference = const value_type&; + + struct index { + size_t px; + size_t py; + + constexpr size_t to_offset() const { + return ( py * SizeX ) + px; + } + constexpr static index from_offset( const size_t pos ) { + return index{pos / SizeX, pos % SizeX}; + } + + constexpr index &operator++() { + ++px; + if( px >= SizeX ) { + px = 0; + py++; + } + return *this; + } + + constexpr index &operator--() { + if( px == 0 ) { + px = SizeX - 1; + py--; + } else { + --px; + } + return *this; + } + + constexpr index operator++( int ) { + index temp = *this; + this->operator++(); + return temp; + } + + constexpr index operator--( int ) { + index temp = *this; + this->operator--(); + return temp; + } + }; + + struct iterator { + friend array2d; + + private: + array2d &arr; + index_type index; + + iterator( array2d &arr, index_type i ) : arr( arr ), index( i ) {} + + public: + reference operator*() const { + return arr[index]; + } + pointer operator->() { + return &*arr[index]; + } + + iterator &operator++() { + ++index; + return *this; + } + + iterator &operator--() { + --index; + return *this; + } + + iterator operator++( int ) { + iterator tmp = *this; + ++index; + return tmp; + } + iterator operator--( int ) { + iterator tmp = *this; + --index; + return tmp; + } + }; + + struct const_iterator { + friend array2d; + + private: + const array2d &arr; + index_type index; + + const_iterator( const array2d &arr, index_type i ) : arr( arr ), index( i ) {} + + public: + const_reference operator*() const { + return arr[index]; + } + const_pointer operator->() { + return &*arr[index]; + } + + const_iterator &operator++() { + ++index; + return *this; + } + + const_iterator &operator--() { + --index; + return *this; + } + + const_iterator operator++( int ) { + const_iterator tmp = *this; + ++index; + return tmp; + } + const_iterator operator--( int ) { + const_iterator tmp = *this; + --index; + return tmp; + } + }; + + struct sentinel {}; + + array2d() = default; + array2d( const array2d & ) = default; + array2d( array2d && ) = default; + + template + requires std::is_same_v> + explicit array2d( F&& fn ) + : _data{detail::init_array_2d( fn ) } { } + + explicit array2d( const T& value ) + : _data{detail::init_array_2d( [&]( size_t, size_t ) { return value; } )} { } + + pointer data() { return &_data[0][0]; } + const_pointer data() const { return &_data[0][0]; } + + iterator begin() { return iterator( *this, {0, 0} ); } + sentinel end() const { return sentinel{}; } + const_iterator begin() const { return const_iterator( *this, {0, 0} ); } + + // _data is an Y sized array, of X sized arrays, of T elements + // we access Y first then X to reach T + + reference operator[]( index_type pos ) { return _data[pos.py][pos.px]; } + const_reference operator[]( index_type pos ) const { return _data[pos.py][pos.px]; } + reference operator[]( size_type px, size_type py ) { return operator[]( index_type{px, py} ); } + const_reference operator[]( size_type px, size_type py ) const { return operator[]( index_type{px, py} ); } + reference operator[]( size_type offset ) { return operator[]( index_type::from_offset( offset ) ); } + const_reference operator[]( size_type offset ) const { return operator[]( index_type::from_offset( offset ) ); } + + static constexpr size_type size() { return SizeX * SizeY; } + static constexpr index_type size_2d() { return index_type{SizeX, SizeY}; } + static constexpr index_type index_2d( size_type x, size_type y ) { return index_type{x, y}; } + + friend void swap( array2d &lhs, array2d &rhs ) noexcept { std::swap( lhs._data, rhs._data ); } + + friend bool operator== ( const array2d &lhs, const array2d &rhs ) { return lhs.data() == rhs.data() && lhs.size() == rhs.size(); } + + friend bool operator== ( const sentinel &, const sentinel & ) { return true; } + friend bool operator== ( const iterator &a, const sentinel & ) { return a.index.px >= SizeX || a.index.py >= SizeY; }; + friend bool operator== ( const const_iterator &a, const sentinel & ) { return a.index.px >= SizeX || a.index.py >= SizeY; }; + + friend bool operator== ( const iterator &a, const iterator &b ) { return a.index == b.index && a.arr == b.arr; }; + friend bool operator== ( const const_iterator &a, const const_iterator &b ) { return a.index == b.index && a.arr == b.arr; }; + friend bool operator== ( const const_iterator &a, const iterator &b ) { return a.index == b.index && a.arr == b.arr; }; + private: + std::array, SizeY> _data; +}; + From 7d3d9afca6a727116838b451d49f4fdbaf870c99 Mon Sep 17 00:00:00 2001 From: Reisen Usagi Date: Thu, 16 Oct 2025 06:09:04 -0300 Subject: [PATCH 03/31] Make submap use array2d instead of c arrays --- src/savegame_json.cpp | 34 +++--- src/submap.cpp | 265 +++++++++--------------------------------- src/submap.h | 53 ++++----- 3 files changed, 101 insertions(+), 251 deletions(-) diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index c6f74c4eae43..e537cfde165b 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -3722,7 +3722,7 @@ void submap::store( JsonOut &jsout ) const for( int j = 0; j < SEEY; j++ ) { // NOLINTNEXTLINE(modernize-loop-convert) for( int i = 0; i < SEEX; i++ ) { - const std::string this_id = ter[i][j].obj().id.str(); + const std::string this_id = ter[i, j].obj().id.str(); if( !last_id.empty() ) { if( this_id == last_id ) { num_same++; @@ -3802,12 +3802,12 @@ void submap::store( JsonOut &jsout ) const jsout.start_array(); for( int j = 0; j < SEEY; j++ ) { for( int i = 0; i < SEEX; i++ ) { - if( itm[i][j].empty() ) { + if( itm[i, j].empty() ) { continue; } jsout.write( i ); jsout.write( j ); - jsout.write( itm[i][j] ); + jsout.write( itm[i, j] ); } } jsout.end_array(); @@ -3835,11 +3835,11 @@ void submap::store( JsonOut &jsout ) const for( int j = 0; j < SEEY; j++ ) { for( int i = 0; i < SEEX; i++ ) { // Save fields - if( fld[i][j].field_count() > 0 ) { + if( fld[i, j].field_count() > 0 ) { jsout.write( i ); jsout.write( j ); jsout.start_array(); - for( auto &elem : fld[i][j] ) { + for( auto &elem : fld[i, j] ) { const field_entry &cur = elem.second; jsout.write( cur.get_field_type().id() ); jsout.write( cur.get_field_intensity() ); @@ -3960,7 +3960,7 @@ void submap::load( JsonIn &jsin, const std::string &member_name, int version, } else { --remaining; } - ter[i][j] = iid; + ter[i, j] = iid; } } if( remaining ) { @@ -3986,7 +3986,7 @@ void submap::load( JsonIn &jsin, const std::string &member_name, int version, jsin.start_array(); int i = jsin.get_int(); int j = jsin.get_int(); - frn[i][j] = furn_id( jsin.get_string() ); + frn[i, j] = furn_id( jsin.get_string() ); jsin.end_array(); } } else if( member_name == "items" ) { @@ -4008,19 +4008,17 @@ void submap::load( JsonIn &jsin, const std::string &member_name, int version, tmp->legacy_fast_forward_time(); } item &obj = *tmp; - itm[p.x][p.y].push_back( std::move( tmp ) ); + itm[p.x, p.y].push_back( std::move( tmp ) ); if( obj.needs_processing() ) { active_items.add( obj ); } } } - for( auto &it1 : itm ) { - for( auto &it2 : it1 ) { - std::vector> cleared = it2.clear(); - to_cbc_migration::migrate( cleared ); - for( detached_ptr &item : cleared ) { - it2.push_back( std::move( item ) ); - } + for( auto &it : itm ) { + std::vector> cleared = it.clear(); + to_cbc_migration::migrate( cleared ); + for( detached_ptr &item : cleared ) { + it.push_back( std::move( item ) ); } } } else if( member_name == "traps" ) { @@ -4031,7 +4029,7 @@ void submap::load( JsonIn &jsin, const std::string &member_name, int version, int j = jsin.get_int(); const point p( i, j ); // TODO: jsin should support returning an id like jsin.get_id() - trp[p.x][p.y] = trap_str_id( jsin.get_string() ).id(); + trp[p.x, p.y] = trap_str_id( jsin.get_string() ).id(); jsin.end_array(); } } else if( member_name == "fields" ) { @@ -4058,10 +4056,10 @@ void submap::load( JsonIn &jsin, const std::string &member_name, int version, } else { ft = field_types::get_field_type_by_legacy_enum( type_int ).id; } - if( fld[i][j].find_field( ft ) == nullptr ) { + if( fld[i, j].find_field( ft ) == nullptr ) { field_count++; } - fld[i][j].add_field( ft, intensity, time_duration::from_turns( age ) ); + fld[i, j].add_field( ft, intensity, time_duration::from_turns( age ) ); } } } else if( member_name == "graffiti" ) { diff --git a/src/submap.cpp b/src/submap.cpp index a8b8e696d377..999c598738d2 100644 --- a/src/submap.cpp +++ b/src/submap.cpp @@ -17,220 +17,71 @@ template void maptile_soa::swap_soa_tile( point p1, point p2 ) { - - std::swap( ter[p1.x][p1.y], ter[p2.x][p2.y] ); - std::swap( frn[p1.x][p1.y], frn[p2.x][p2.y] ); - std::swap( lum[p1.x][p1.y], lum[p2.x][p2.y] ); - std::swap( itm[p1.x][p1.y], itm[p2.x][p2.y] ); - std::swap( fld[p1.x][p1.y], fld[p2.x][p2.y] ); - std::swap( trp[p1.x][p1.y], trp[p2.x][p2.y] ); - std::swap( rad[p1.x][p1.y], rad[p2.x][p2.y] ); + using std::swap; + + swap( ter[p1.x, p1.y], ter[p2.x, p2.y] ); + swap( frn[p1.x, p1.y], frn[p2.x, p2.y] ); + swap( lum[p1.x, p1.y], lum[p2.x, p2.y] ); + swap( itm[p1.x, p1.y], itm[p2.x, p2.y] ); + swap( fld[p1.x, p1.y], fld[p2.x, p2.y] ); + swap( trp[p1.x, p1.y], trp[p2.x, p2.y] ); + swap( rad[p1.x, p1.y], rad[p2.x, p2.y] ); } void submap::swap( submap &first, submap &second ) { - std::swap( first.ter, second.ter ); - std::swap( first.frn, second.frn ); - std::swap( first.lum, second.lum ); - std::swap( first.fld, second.fld ); - std::swap( first.trp, second.trp ); - std::swap( first.rad, second.rad ); - std::swap( first.is_uniform, second.is_uniform ); - std::swap( first.active_items, second.active_items ); - std::swap( first.field_count, second.field_count ); - std::swap( first.last_touched, second.last_touched ); - std::swap( first.spawns, second.spawns ); - std::swap( first.vehicles, second.vehicles ); - std::swap( first.partial_constructions, second.partial_constructions ); - std::swap( first.active_furniture, second.active_furniture ); - std::swap( first.is_uniform, second.is_uniform ); - std::swap( first.computers, second.computers ); - std::swap( first.legacy_computer, second.legacy_computer ); - std::swap( first.temperature, second.temperature ); - std::swap( first.cosmetics, second.cosmetics ); - + using std::swap; + + swap( first.ter, second.ter ); + swap( first.frn, second.frn ); + swap( first.lum, second.lum ); + swap( first.fld, second.fld ); + swap( first.trp, second.trp ); + swap( first.rad, second.rad ); + swap( first.is_uniform, second.is_uniform ); + swap( first.active_items, second.active_items ); + swap( first.field_count, second.field_count ); + swap( first.last_touched, second.last_touched ); + swap( first.spawns, second.spawns ); + swap( first.vehicles, second.vehicles ); + swap( first.partial_constructions, second.partial_constructions ); + swap( first.active_furniture, second.active_furniture ); + swap( first.is_uniform, second.is_uniform ); + swap( first.computers, second.computers ); + swap( first.legacy_computer, second.legacy_computer ); + swap( first.temperature, second.temperature ); + swap( first.cosmetics, second.cosmetics ); + + // TODO: Check if its effect is the same as + // swap( first.itm, second.itm ); for( int x = 0; x < SEEX; x++ ) { for( int y = 0; y < SEEY; y++ ) { - std::swap( first.itm[x][y], second.itm[x][y] ); + std::swap( first.itm[x, y], second.itm[x, y] ); } } } -//There's not a briefer way to write this I don't think +struct LocationVectorInitializer { + tripoint offset; + constexpr auto operator()(const size_t x, const size_t y ) const { + return location_vector{ new tile_item_location( offset + point( 0, 0 ) )}; + } +}; + template -maptile_soa::maptile_soa( tripoint offset ) : itm{{ - // NOLINTNEXTLINE(cata-use-named-point-constants) - location_vector{ new tile_item_location( offset + point( 0, 0 ) )}, - // NOLINTNEXTLINE(cata-use-named-point-constants) - location_vector{ new tile_item_location( offset + point( 0, 1 ) )}, - location_vector{ new tile_item_location( offset + point( 0, 2 ) )}, - location_vector{ new tile_item_location( offset + point( 0, 3 ) )}, - location_vector{ new tile_item_location( offset + point( 0, 4 ) )}, - location_vector{ new tile_item_location( offset + point( 0, 5 ) )}, - location_vector{ new tile_item_location( offset + point( 0, 6 ) )}, - location_vector{ new tile_item_location( offset + point( 0, 7 ) )}, - location_vector{ new tile_item_location( offset + point( 0, 8 ) )}, - location_vector{ new tile_item_location( offset + point( 0, 9 ) )}, - location_vector{ new tile_item_location( offset + point( 0, 10 ) )}, - location_vector{ new tile_item_location( offset + point( 0, 11 ) )}, - }, - { - // NOLINTNEXTLINE(cata-use-named-point-constants) - location_vector{ new tile_item_location( offset + point( 1, 0 ) )}, - // NOLINTNEXTLINE(cata-use-named-point-constants) - location_vector{ new tile_item_location( offset + point( 1, 1 ) )}, - location_vector{ new tile_item_location( offset + point( 1, 2 ) )}, - location_vector{ new tile_item_location( offset + point( 1, 3 ) )}, - location_vector{ new tile_item_location( offset + point( 1, 4 ) )}, - location_vector{ new tile_item_location( offset + point( 1, 5 ) )}, - location_vector{ new tile_item_location( offset + point( 1, 6 ) )}, - location_vector{ new tile_item_location( offset + point( 1, 7 ) )}, - location_vector{ new tile_item_location( offset + point( 1, 8 ) )}, - location_vector{ new tile_item_location( offset + point( 1, 9 ) )}, - location_vector{ new tile_item_location( offset + point( 1, 10 ) )}, - location_vector{ new tile_item_location( offset + point( 1, 11 ) )}, - }, { - location_vector{ new tile_item_location( offset + point( 2, 0 ) )}, - location_vector{ new tile_item_location( offset + point( 2, 1 ) )}, - location_vector{ new tile_item_location( offset + point( 2, 2 ) )}, - location_vector{ new tile_item_location( offset + point( 2, 3 ) )}, - location_vector{ new tile_item_location( offset + point( 2, 4 ) )}, - location_vector{ new tile_item_location( offset + point( 2, 5 ) )}, - location_vector{ new tile_item_location( offset + point( 2, 6 ) )}, - location_vector{ new tile_item_location( offset + point( 2, 7 ) )}, - location_vector{ new tile_item_location( offset + point( 2, 8 ) )}, - location_vector{ new tile_item_location( offset + point( 2, 9 ) )}, - location_vector{ new tile_item_location( offset + point( 2, 10 ) )}, - location_vector{ new tile_item_location( offset + point( 2, 11 ) )}, - }, { - location_vector{ new tile_item_location( offset + point( 3, 0 ) )}, - location_vector{ new tile_item_location( offset + point( 3, 1 ) )}, - location_vector{ new tile_item_location( offset + point( 3, 2 ) )}, - location_vector{ new tile_item_location( offset + point( 3, 3 ) )}, - location_vector{ new tile_item_location( offset + point( 3, 4 ) )}, - location_vector{ new tile_item_location( offset + point( 3, 5 ) )}, - location_vector{ new tile_item_location( offset + point( 3, 6 ) )}, - location_vector{ new tile_item_location( offset + point( 3, 7 ) )}, - location_vector{ new tile_item_location( offset + point( 3, 8 ) )}, - location_vector{ new tile_item_location( offset + point( 3, 9 ) )}, - location_vector{ new tile_item_location( offset + point( 3, 10 ) )}, - location_vector{ new tile_item_location( offset + point( 3, 11 ) )}, - }, { - location_vector{ new tile_item_location( offset + point( 4, 0 ) )}, - location_vector{ new tile_item_location( offset + point( 4, 1 ) )}, - location_vector{ new tile_item_location( offset + point( 4, 2 ) )}, - location_vector{ new tile_item_location( offset + point( 4, 3 ) )}, - location_vector{ new tile_item_location( offset + point( 4, 4 ) )}, - location_vector{ new tile_item_location( offset + point( 4, 5 ) )}, - location_vector{ new tile_item_location( offset + point( 4, 6 ) )}, - location_vector{ new tile_item_location( offset + point( 4, 7 ) )}, - location_vector{ new tile_item_location( offset + point( 4, 8 ) )}, - location_vector{ new tile_item_location( offset + point( 4, 9 ) )}, - location_vector{ new tile_item_location( offset + point( 4, 10 ) )}, - location_vector{ new tile_item_location( offset + point( 4, 11 ) )}, - }, { - location_vector{ new tile_item_location( offset + point( 5, 0 ) )}, - location_vector{ new tile_item_location( offset + point( 5, 1 ) )}, - location_vector{ new tile_item_location( offset + point( 5, 2 ) )}, - location_vector{ new tile_item_location( offset + point( 5, 3 ) )}, - location_vector{ new tile_item_location( offset + point( 5, 4 ) )}, - location_vector{ new tile_item_location( offset + point( 5, 5 ) )}, - location_vector{ new tile_item_location( offset + point( 5, 6 ) )}, - location_vector{ new tile_item_location( offset + point( 5, 7 ) )}, - location_vector{ new tile_item_location( offset + point( 5, 8 ) )}, - location_vector{ new tile_item_location( offset + point( 5, 9 ) )}, - location_vector{ new tile_item_location( offset + point( 5, 10 ) )}, - location_vector{ new tile_item_location( offset + point( 5, 11 ) )}, - }, { - location_vector{ new tile_item_location( offset + point( 6, 0 ) )}, - location_vector{ new tile_item_location( offset + point( 6, 1 ) )}, - location_vector{ new tile_item_location( offset + point( 6, 2 ) )}, - location_vector{ new tile_item_location( offset + point( 6, 3 ) )}, - location_vector{ new tile_item_location( offset + point( 6, 4 ) )}, - location_vector{ new tile_item_location( offset + point( 6, 5 ) )}, - location_vector{ new tile_item_location( offset + point( 6, 6 ) )}, - location_vector{ new tile_item_location( offset + point( 6, 7 ) )}, - location_vector{ new tile_item_location( offset + point( 6, 8 ) )}, - location_vector{ new tile_item_location( offset + point( 6, 9 ) )}, - location_vector{ new tile_item_location( offset + point( 6, 10 ) )}, - location_vector{ new tile_item_location( offset + point( 6, 11 ) )}, - }, { - location_vector{ new tile_item_location( offset + point( 7, 0 ) )}, - location_vector{ new tile_item_location( offset + point( 7, 1 ) )}, - location_vector{ new tile_item_location( offset + point( 7, 2 ) )}, - location_vector{ new tile_item_location( offset + point( 7, 3 ) )}, - location_vector{ new tile_item_location( offset + point( 7, 4 ) )}, - location_vector{ new tile_item_location( offset + point( 7, 5 ) )}, - location_vector{ new tile_item_location( offset + point( 7, 6 ) )}, - location_vector{ new tile_item_location( offset + point( 7, 7 ) )}, - location_vector{ new tile_item_location( offset + point( 7, 8 ) )}, - location_vector{ new tile_item_location( offset + point( 7, 9 ) )}, - location_vector{ new tile_item_location( offset + point( 7, 10 ) )}, - location_vector{ new tile_item_location( offset + point( 7, 11 ) )}, - }, { - location_vector{ new tile_item_location( offset + point( 8, 0 ) )}, - location_vector{ new tile_item_location( offset + point( 8, 1 ) )}, - location_vector{ new tile_item_location( offset + point( 8, 2 ) )}, - location_vector{ new tile_item_location( offset + point( 8, 3 ) )}, - location_vector{ new tile_item_location( offset + point( 8, 4 ) )}, - location_vector{ new tile_item_location( offset + point( 8, 5 ) )}, - location_vector{ new tile_item_location( offset + point( 8, 6 ) )}, - location_vector{ new tile_item_location( offset + point( 8, 7 ) )}, - location_vector{ new tile_item_location( offset + point( 8, 8 ) )}, - location_vector{ new tile_item_location( offset + point( 8, 9 ) )}, - location_vector{ new tile_item_location( offset + point( 8, 10 ) )}, - location_vector{ new tile_item_location( offset + point( 8, 11 ) )}, - }, { - location_vector{ new tile_item_location( offset + point( 9, 0 ) )}, - location_vector{ new tile_item_location( offset + point( 9, 1 ) )}, - location_vector{ new tile_item_location( offset + point( 9, 2 ) )}, - location_vector{ new tile_item_location( offset + point( 9, 3 ) )}, - location_vector{ new tile_item_location( offset + point( 9, 4 ) )}, - location_vector{ new tile_item_location( offset + point( 9, 5 ) )}, - location_vector{ new tile_item_location( offset + point( 9, 6 ) )}, - location_vector{ new tile_item_location( offset + point( 9, 7 ) )}, - location_vector{ new tile_item_location( offset + point( 9, 8 ) )}, - location_vector{ new tile_item_location( offset + point( 9, 9 ) )}, - location_vector{ new tile_item_location( offset + point( 9, 10 ) )}, - location_vector{ new tile_item_location( offset + point( 9, 11 ) )}, - }, { - location_vector{ new tile_item_location( offset + point( 10, 0 ) )}, - location_vector{ new tile_item_location( offset + point( 10, 1 ) )}, - location_vector{ new tile_item_location( offset + point( 10, 2 ) )}, - location_vector{ new tile_item_location( offset + point( 10, 3 ) )}, - location_vector{ new tile_item_location( offset + point( 10, 4 ) )}, - location_vector{ new tile_item_location( offset + point( 10, 5 ) )}, - location_vector{ new tile_item_location( offset + point( 10, 6 ) )}, - location_vector{ new tile_item_location( offset + point( 10, 7 ) )}, - location_vector{ new tile_item_location( offset + point( 10, 8 ) )}, - location_vector{ new tile_item_location( offset + point( 10, 9 ) )}, - location_vector{ new tile_item_location( offset + point( 10, 10 ) )}, - location_vector{ new tile_item_location( offset + point( 10, 11 ) )}, - }, { - location_vector{ new tile_item_location( offset + point( 11, 0 ) )}, - location_vector{ new tile_item_location( offset + point( 11, 1 ) )}, - location_vector{ new tile_item_location( offset + point( 11, 2 ) )}, - location_vector{ new tile_item_location( offset + point( 11, 3 ) )}, - location_vector{ new tile_item_location( offset + point( 11, 4 ) )}, - location_vector{ new tile_item_location( offset + point( 11, 5 ) )}, - location_vector{ new tile_item_location( offset + point( 11, 6 ) )}, - location_vector{ new tile_item_location( offset + point( 11, 7 ) )}, - location_vector{ new tile_item_location( offset + point( 11, 8 ) )}, - location_vector{ new tile_item_location( offset + point( 11, 9 ) )}, - location_vector{ new tile_item_location( offset + point( 11, 10 ) )}, - location_vector{ new tile_item_location( offset + point( 11, 11 ) )}, - }} +maptile_soa::maptile_soa( tripoint offset ) + : ter(t_null) + , frn(f_null) + , lum(0) + , itm(LocationVectorInitializer{offset}) + , fld(field{}) + , trp(tr_null) + , rad(0) { } submap::submap( tripoint offset ) : maptile_soa( offset ) { - std::uninitialized_fill_n( &ter[0][0], elements, t_null ); - std::uninitialized_fill_n( &frn[0][0], elements, f_null ); - std::uninitialized_fill_n( &lum[0][0], elements, 0 ); - std::uninitialized_fill_n( &trp[0][0], elements, tr_null ); - std::uninitialized_fill_n( &rad[0][0], elements, 0 ); - is_uniform = false; } @@ -241,22 +92,22 @@ void submap::update_lum_rem( point p, const item &i ) is_uniform = false; if( !i.is_emissive() ) { return; - } else if( lum[p.x][p.y] && lum[p.x][p.y] < 255 ) { - lum[p.x][p.y]--; + } else if( lum[p.x, p.y] && lum[p.x, p.y] < 255 ) { + lum[p.x, p.y]--; return; } // Have to scan through all items to be sure removing i will actually lower // the count below 255. int count = 0; - for( const auto &it : itm[p.x][p.y] ) { + for( const auto &it : itm[p.x, p.y] ) { if( it->is_emissive() ) { count++; } } if( count <= 256 ) { - lum[p.x][p.y] = static_cast( count - 1 ); + lum[p.x, p.y] = static_cast( count - 1 ); } } @@ -335,7 +186,7 @@ void submap::delete_graffiti( point p ) } bool submap::has_signage( point p ) const { - if( frn[p.x][p.y].obj().has_flag( "SIGN" ) ) { + if( frn[p.x, p.y].obj().has_flag( "SIGN" ) ) { return find_cosmetic( cosmetics, p, COSMETICS_SIGNAGE ).result; } @@ -343,7 +194,7 @@ bool submap::has_signage( point p ) const } std::string submap::get_signage( point p ) const { - if( frn[p.x][p.y].obj().has_flag( "SIGN" ) ) { + if( frn[p.x, p.y].obj().has_flag( "SIGN" ) ) { const auto fresult = find_cosmetic( cosmetics, p, COSMETICS_SIGNAGE ); if( fresult.result ) { return cosmetics[ fresult.ndx ].str; @@ -378,7 +229,7 @@ void submap::update_legacy_computer() if( legacy_computer ) { for( int x = 0; x < SEEX; ++x ) { for( int y = 0; y < SEEY; ++y ) { - if( ter[x][y] == t_console ) { + if( ter[x, y] == t_console ) { computers.emplace( point( x, y ), *legacy_computer ); } } @@ -389,7 +240,7 @@ void submap::update_legacy_computer() bool submap::has_computer( point p ) const { - return computers.contains( p ) || ( legacy_computer && ter[p.x][p.y] == t_console ); + return computers.contains( p ) || ( legacy_computer && ter[p.x, p.y] == t_console ); } const computer *submap::get_computer( point p ) const @@ -400,7 +251,7 @@ const computer *submap::get_computer( point p ) const if( it != computers.end() ) { return &it->second; } - if( legacy_computer && ter[p.x][p.y] == t_console ) { + if( legacy_computer && ter[p.x, p.y] == t_console ) { return legacy_computer.get(); } return nullptr; diff --git a/src/submap.h b/src/submap.h index 94ae33681f51..03ebd5d7a21b 100644 --- a/src/submap.h +++ b/src/submap.h @@ -20,6 +20,7 @@ #include "monster.h" #include "point.h" #include "poly_serialized.h" +#include "array2d.h" class JsonIn; class JsonOut; @@ -71,13 +72,13 @@ struct maptile_soa { protected: maptile_soa( tripoint offset ); public: - ter_id ter[sx][sy]; // Terrain on each square - furn_id frn[sx][sy]; // Furniture on each square - std::uint8_t lum[sx][sy]; // Number of items emitting light on each square - location_vector itm[sx][sy]; // Items on each square - field fld[sx][sy]; // Field on each square - trap_id trp[sx][sy]; // Trap on each square - int rad[sx][sy]; // Irradiation of each square + array2d ter; // Terrain on each square + array2d frn; // Furniture on each square + array2d lum; // Number of items emitting light on each square + array2d,sx,sy> itm; // Items on each square + array2d fld; // Field on each square + array2d trp; // Trap on each square + array2d rad; // Irradiation of each square void swap_soa_tile( point p1, point p2 ); }; @@ -89,66 +90,66 @@ class submap : maptile_soa ~submap(); trap_id get_trap( point p ) const { - return trp[p.x][p.y]; + return trp[p.x, p.y]; } void set_trap( point p, trap_id trap ) { is_uniform = false; - trp[p.x][p.y] = trap; + trp[p.x, p.y] = trap; } void set_all_traps( const trap_id &trap ) { - std::uninitialized_fill_n( &trp[0][0], elements, trap ); + std::uninitialized_fill_n( &trp[0, 0], elements, trap ); } furn_id get_furn( point p ) const { - return frn[p.x][p.y]; + return frn[p.x, p.y]; } void set_furn( point p, furn_id furn ) { is_uniform = false; - frn[p.x][p.y] = furn; + frn[p.x, p.y] = furn; } void set_all_furn( const furn_id &furn ) { - std::uninitialized_fill_n( &frn[0][0], elements, furn ); + std::uninitialized_fill_n( &frn[0, 0], elements, furn ); } ter_id get_ter( point p ) const { - return ter[p.x][p.y]; + return ter[p.x, p.y]; } void set_ter( point p, ter_id terr ) { is_uniform = false; - ter[p.x][p.y] = terr; + ter[p.x, p.y] = terr; } void set_all_ter( const ter_id &terr ) { - std::uninitialized_fill_n( &ter[0][0], elements, terr ); + std::uninitialized_fill_n( &ter[0, 0], elements, terr ); } int get_radiation( point p ) const { - return rad[p.x][p.y]; + return rad[p.x, p.y]; } void set_radiation( point p, const int radiation ) { is_uniform = false; - rad[p.x][p.y] = radiation; + rad[p.x, p.y] = radiation; } uint8_t get_lum( point p ) const { - return lum[p.x][p.y]; + return lum[p.x, p.y]; } void set_lum( point p, uint8_t luminance ) { is_uniform = false; - lum[p.x][p.y] = luminance; + lum[p.x, p.y] = luminance; } void update_lum_add( point p, const item &i ) { is_uniform = false; - if( i.is_emissive() && lum[p.x][p.y] < 255 ) { - lum[p.x][p.y]++; + if( i.is_emissive() && lum[p.x, p.y] < 255 ) { + lum[p.x, p.y]++; } } @@ -156,20 +157,20 @@ class submap : maptile_soa // TODO: Replace this as it essentially makes itm public location_vector &get_items( const point &p ) { - return itm[p.x][p.y]; + return itm[p.x, p.y]; } const location_vector &get_items( const point &p ) const { - return itm[p.x][p.y]; + return itm[p.x, p.y]; } // TODO: Replace this as it essentially makes fld public field &get_field( point p ) { - return fld[p.x][p.y]; + return fld[p.x, p.y]; } const field &get_field( point p ) const { - return fld[p.x][p.y]; + return fld[p.x, p.y]; } struct cosmetic_t { From 70e4f0f159f698ed6dad79c7e1a3d12284bd29ff Mon Sep 17 00:00:00 2001 From: Reisen Usagi Date: Thu, 16 Oct 2025 09:19:42 -0300 Subject: [PATCH 04/31] Update data_vars --- src/data_vars.cpp | 26 ++++++++++++++++ src/data_vars.h | 76 +++++++++++++++++++++++++++-------------------- 2 files changed, 69 insertions(+), 33 deletions(-) create mode 100644 src/data_vars.cpp diff --git a/src/data_vars.cpp b/src/data_vars.cpp new file mode 100644 index 000000000000..b98f04cf5375 --- /dev/null +++ b/src/data_vars.cpp @@ -0,0 +1,26 @@ +#include "data_vars.h" + +#include "creature_tracker.h" +#include "json.h" + +void data_vars::set(const key_type &name, const mapped_type &value) { + data[name] = value; +} + +data_vars::mapped_type data_vars::get(const key_type &name, + const mapped_type &default_value) const { + const auto it = find(name); + if (it == end()) { + return default_value; + } + return it->second; +} + +bool data_vars::try_get(const key_type &name, mapped_type &value) const { + const auto it = find(name); + if (it == end()) { + return false; + } + value = it->second; + return true; +} \ No newline at end of file diff --git a/src/data_vars.h b/src/data_vars.h index dbe6551aef89..783b3be5f521 100644 --- a/src/data_vars.h +++ b/src/data_vars.h @@ -13,37 +13,42 @@ namespace detail template struct data_var_sstream_converter { - std::string operator()( const T &value ) const { + bool operator()( const T &in_val, std::string& out_val ) const { std::stringstream ss; ss.imbue( std::locale::classic() ); - ss << value; + ss << in_val; if( ss.fail() ) { - return ""; + return false; } - return ss.str(); + out_val = ss.str(); + return true; }; - T operator()( const std::string& key, const T &def ) const { - T value = def; - std::stringstream ss( key ); + bool operator()( const std::string& in_val, T &out_val ) const { + std::stringstream ss( in_val ); ss.imbue( std::locale::classic() ); - ss >> value; + ss >> out_val; if( ss.fail() ) { - return def; + return false; } - return value; + return true; } }; struct data_var_tripoint_converter { - std::string operator()( const tripoint &value ) const { - return string_format( "%d,%d,%d", value.x, value.y, value.z ); + bool operator()( const tripoint &in_val, std::string& out_val ) const { + out_val = string_format( "%d,%d,%d", in_val.x, in_val.y, in_val.z ); + return true; } - tripoint operator()( const std::string &value, const tripoint & ) const { - const std::vector values = string_split( value, ',' ); - return tripoint( std::stoi( values[0] ), - std::stoi( values[1] ), - std::stoi( values[2] ) ); + bool operator()( const std::string &in_val, tripoint& out_val) const { + const std::vector values = string_split( in_val, ',' ); + constexpr data_var_sstream_converter cvt; + tripoint p; + if (cvt(values[0], p.x) && cvt(values[1], p.y) && cvt(values[2], p.z)) { + out_val = p; + return true; + }; + return false; } }; @@ -82,29 +87,34 @@ class data_vars template > void set( const key_type& name, const T& value, const Cvt& cvt = {} ) { - data[name] = cvt( value ); + std::string strval; + if (cvt(value, strval)) { + data_vars::set(name, strval); + } + else { + throw std::runtime_error("failed to convert value to string"); + } } template > - T get( const key_type& name, const T& default_value = T{}, const Cvt& cvt = {} ) const { - const auto it = find( name ); - if( it == end() ) { - return default_value; - } - return static_cast( cvt( it->second, default_value ) ); + bool try_get( const key_type& name, T& value, const Cvt& cvt = {} ) const { + std::string strval; + if (!data_vars::try_get(name, strval)) + return false; + return cvt(strval, value); } - void set( const key_type& name, const mapped_type& value ) { - data[name] = value; + template > + T get( const key_type& name, const T& default_value = T{}, const Cvt& cvt = {} ) const { + T value; + return try_get(name, value, cvt) + ? value + : default_value; } - mapped_type get( const key_type& name, const mapped_type& default_value = mapped_type{} ) const { - const auto it = find( name ); - if( it == end() ) { - return default_value; - } - return it->second; - } + void set(const key_type &name, const mapped_type &value); + mapped_type get(const key_type &name, const mapped_type &default_value = mapped_type{}) const; + bool try_get( const key_type &name, mapped_type &value ) const; bool operator==( const data_vars & other ) const { return ( data ) == other.data; } mapped_type &operator[]( const key_type &name ) { return data[name]; } From 000a708c01f82bd07489e93384605a28037d85b7 Mon Sep 17 00:00:00 2001 From: Reisen Usagi Date: Thu, 16 Oct 2025 09:59:03 -0300 Subject: [PATCH 05/31] Add reset method to array2d --- src/array2d.h | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/array2d.h b/src/array2d.h index 3fcf38b3d3cd..32d39d034395 100644 --- a/src/array2d.h +++ b/src/array2d.h @@ -53,9 +53,9 @@ constexpr static auto init_array_2d( F&& fn ) /// Elements in are stored as rows of columns \p SizeY rows of \p SizeX /// elements: /// -/// For values without a default constructor, will initialize the elements via copy-constructor from: -/// - A constant value -/// - A callable that takes the \p x and \p y coordinates, and returns \p T +/// For values without a default constructor, will initialize the elements from: +/// - A constant value (if copy-constructible) +/// - A functor or function that takes the \p x and \p y coordinates, and constructs \p T /// /// @tparam T Element Type /// @tparam SizeX X Dimensions @@ -213,6 +213,20 @@ struct array2d { explicit array2d( const T& value ) : _data{detail::init_array_2d( [&]( size_t, size_t ) { return value; } )} { } + void reset() { + _data = {}; + } + + void reset(const T& value) { + _data = detail::init_array_2d( [&]( size_t, size_t ) { return value; } ); + } + + template + requires std::is_same_v> + void reset( F&& fn ) { + _data = detail::init_array_2d( fn ); + } + pointer data() { return &_data[0][0]; } const_pointer data() const { return &_data[0][0]; } From 380d27ede5666675175a2aae804b35329eae5fab Mon Sep 17 00:00:00 2001 From: Reisen Usagi Date: Thu, 16 Oct 2025 11:25:23 -0300 Subject: [PATCH 06/31] Add data_vars support to furniture and terrain --- src/distribution_grid.cpp | 15 +++++++----- src/game.cpp | 7 ++++++ src/map.cpp | 21 ++++++++++++++++ src/map.h | 7 ++++++ src/savegame_json.cpp | 46 +++++++++++++++++++++++++++++++++++ src/submap.cpp | 31 ++++++++++++++---------- src/submap.h | 50 ++++++++++++++++++++++++++++++--------- 7 files changed, 148 insertions(+), 29 deletions(-) diff --git a/src/distribution_grid.cpp b/src/distribution_grid.cpp index b358f6ebe3f6..89e648e6b9ba 100644 --- a/src/distribution_grid.cpp +++ b/src/distribution_grid.cpp @@ -301,6 +301,7 @@ void grid_furn_transform_queue::apply( mapbuffer &mb, distribution_grid_tracker submap *sm = mb.lookup_submap( p_abs_sm ); if( sm == nullptr ) { + // Something transforming on a non-existant map...? return; } @@ -308,19 +309,21 @@ void grid_furn_transform_queue::apply( mapbuffer &mb, distribution_grid_tracker const furn_t &new_t = qt.id.obj(); const tripoint pos_local = m.getlocal( qt.p.raw() ); - if( m.inbounds( pos_local ) ) { - m.furn_set( pos_local, qt.id ); - } else { - sm->set_furn( p_within_sm.raw(), qt.id ); - } - if( !qt.msg.empty() ) { if( u.sees( pos_local ) ) { add_msg( "%s", _( qt.msg ) ); } } + if( m.inbounds( pos_local ) ) { + m.furn_set( pos_local, qt.id ); + return; + } + + // Something is transforming from an unloaded map...? + // TODO: this is copy-pasted from map.cpp + sm->set_furn( p_within_sm.raw(), qt.id ); if( old_t.active ) { sm->active_furniture.erase( p_within_sm ); // TODO: Only for g->m? Observer pattern? diff --git a/src/game.cpp b/src/game.cpp index 6ffa00d48a09..0d590c32d835 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -9844,6 +9844,13 @@ bool game::grabbed_furn_move( const tripoint &dp ) active_tile_data *atd = active_tiles::furn_at ( tripoint_abs_ms( m.getabs( fpos ) ) ); + // Swap furniture vars between tiles beforehand + // because the furn_set call will clear the vars + // when furniture is set to f_null + const auto dstVars = m.furn_vars(fdest); + const auto srcVars = m.furn_vars(fpos); + std::swap(*srcVars, *dstVars); + // Actually move the furniture. m.furn_set( fdest, m.furn( fpos ), atd ? atd->clone() : nullptr ); m.furn_set( fpos, f_null ); diff --git a/src/map.cpp b/src/map.cpp index df772bd21c46..6f0a0ca8649f 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1617,6 +1617,27 @@ ter_id map::ter( const tripoint &p ) const return current_submap->get_ter( l ); } +data_vars* map::ter_vars(const tripoint &p) const { + if( !inbounds( p ) ) { + return nullptr; + } + + point l; + const auto sm = get_submap_at(p, l); + return &sm->get_ter_vars(l); +} + + +data_vars* map::furn_vars(const tripoint &p) const { + if( !inbounds( p ) ) { + return nullptr; + } + + point l; + const auto sm = get_submap_at(p, l); + return &sm->get_furn_vars(l); +} + uint8_t map::get_known_connections( const tripoint &p, int connect_group, const std::map &override ) const { diff --git a/src/map.h b/src/map.h index afe26dcbe65f..eacbfb4bf3dc 100644 --- a/src/map.h +++ b/src/map.h @@ -820,6 +820,13 @@ class map return ter( tripoint( p, abs_sub.z ) ); } + // Data Vars + + // requires inbounds(p), may return nullptr otherwise + data_vars* ter_vars(const tripoint &p) const; + // requires inbounds(p), may return nullptr otherwise + data_vars* furn_vars(const tripoint &p) const; + // Return a bitfield of the adjacent tiles which connect to the given // connect_group. From least-significant bit the order is south, east, // west, north (because that's what cata_tiles expects). diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index e537cfde165b..2dd696ba9008 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -3812,6 +3812,34 @@ void submap::store( JsonOut &jsout ) const } jsout.end_array(); + jsout.member( "furniture_vars" ); + jsout.start_array(); + for( int j = 0; j < SEEY; j++ ) { + for( int i = 0; i < SEEX; i++ ) { + if( frn_vars[i, j].empty() ) { + continue; + } + jsout.write( i ); + jsout.write( j ); + jsout.write( frn_vars[i, j] ); + } + } + jsout.end_array(); + + jsout.member( "terrain_vars" ); + jsout.start_array(); + for( int j = 0; j < SEEY; j++ ) { + for( int i = 0; i < SEEX; i++ ) { + if( ter_vars[i, j].empty() ) { + continue; + } + jsout.write( i ); + jsout.write( j ); + jsout.write( ter_vars[i, j] ); + } + } + jsout.end_array(); + jsout.member( "traps" ); jsout.start_array(); for( int j = 0; j < SEEY; j++ ) { @@ -4021,6 +4049,24 @@ void submap::load( JsonIn &jsin, const std::string &member_name, int version, it.push_back( std::move( item ) ); } } + } else if( member_name == "furniture_vars" ) { + jsin.start_array(); + while( !jsin.end_array() ) { + int i = jsin.get_int(); + int j = jsin.get_int(); + data_vars vars; + jsin.read( vars ); + frn_vars[i, j] = std::move( vars ); + } + } else if( member_name == "terrain_vars" ) { + jsin.start_array(); + while( !jsin.end_array() ) { + int i = jsin.get_int(); + int j = jsin.get_int(); + data_vars vars; + jsin.read( vars ); + ter_vars[i, j] = std::move( vars ); + } } else if( member_name == "traps" ) { jsin.start_array(); while( !jsin.end_array() ) { diff --git a/src/submap.cpp b/src/submap.cpp index 999c598738d2..2490eff07f62 100644 --- a/src/submap.cpp +++ b/src/submap.cpp @@ -26,9 +26,11 @@ void maptile_soa::swap_soa_tile( point p1, point p2 ) swap( fld[p1.x, p1.y], fld[p2.x, p2.y] ); swap( trp[p1.x, p1.y], trp[p2.x, p2.y] ); swap( rad[p1.x, p1.y], rad[p2.x, p2.y] ); + swap( frn_vars[p1.x, p1.y], frn_vars[p2.x, p2.y] ); + swap( ter_vars[p1.x, p1.y], ter_vars[p2.x, p2.y] ); } -void submap::swap( submap &first, submap &second ) +void submap::swap( submap &first, submap &second ) noexcept { using std::swap; @@ -38,6 +40,8 @@ void submap::swap( submap &first, submap &second ) swap( first.fld, second.fld ); swap( first.trp, second.trp ); swap( first.rad, second.rad ); + swap( first.frn_vars, second.frn_vars ); + swap( first.ter_vars, second.ter_vars ); swap( first.is_uniform, second.is_uniform ); swap( first.active_items, second.active_items ); swap( first.field_count, second.field_count ); @@ -56,31 +60,34 @@ void submap::swap( submap &first, submap &second ) // swap( first.itm, second.itm ); for( int x = 0; x < SEEX; x++ ) { for( int y = 0; y < SEEY; y++ ) { - std::swap( first.itm[x, y], second.itm[x, y] ); + swap( first.itm[x, y], second.itm[x, y] ); } } } struct LocationVectorInitializer { tripoint offset; - constexpr auto operator()(const size_t x, const size_t y ) const { - return location_vector{ new tile_item_location( offset + point( 0, 0 ) )}; + explicit LocationVectorInitializer( tripoint offset = {} ) : offset( offset ) {} + auto operator()( size_t x, size_t y ) const { + return location_vector{ new tile_item_location( offset + point( x, y ) )}; } }; template maptile_soa::maptile_soa( tripoint offset ) - : ter(t_null) - , frn(f_null) - , lum(0) - , itm(LocationVectorInitializer{offset}) - , fld(field{}) - , trp(tr_null) - , rad(0) + : ter( t_null ) + , frn( f_null ) + , lum( 0 ) + , itm( LocationVectorInitializer( offset ) ) + , fld() + , trp( tr_null ) + , rad( 0 ) + , ter_vars() + , frn_vars() { } -submap::submap( tripoint offset ) : maptile_soa( offset ) +submap::submap( tripoint offset ) : maptile_soa( offset ) { is_uniform = false; } diff --git a/src/submap.h b/src/submap.h index 03ebd5d7a21b..7108ab871156 100644 --- a/src/submap.h +++ b/src/submap.h @@ -72,13 +72,15 @@ struct maptile_soa { protected: maptile_soa( tripoint offset ); public: - array2d ter; // Terrain on each square - array2d frn; // Furniture on each square - array2d lum; // Number of items emitting light on each square - array2d,sx,sy> itm; // Items on each square - array2d fld; // Field on each square - array2d trp; // Trap on each square - array2d rad; // Irradiation of each square + array2d< ter_id, sx, sy > ter; // Terrain on each square + array2d< furn_id, sx, sy > frn; // Furniture on each square + array2d< std::uint8_t, sx, sy > lum; // Number of items emitting light on each square + array2d< location_vector, sx, sy > itm; // Items on each square + array2d< field, sx, sy > fld; // Field on each square + array2d< trap_id, sx, sy > trp; // Trap on each square + array2d< int, sx, sy > rad; // Irradiation of each square + array2d< data_vars, sx, sy > ter_vars; // Terrain vars + array2d< data_vars, sx, sy > frn_vars; // Furniture vars void swap_soa_tile( point p1, point p2 ); }; @@ -99,7 +101,7 @@ class submap : maptile_soa } void set_all_traps( const trap_id &trap ) { - std::uninitialized_fill_n( &trp[0, 0], elements, trap ); + trp.reset(trap); } furn_id get_furn( point p ) const { @@ -109,10 +111,20 @@ class submap : maptile_soa void set_furn( point p, furn_id furn ) { is_uniform = false; frn[p.x, p.y] = furn; + // Reset furniture vars on clear + if (furn == f_null) { + frn_vars[p.x, p.y].clear(); + } } void set_all_furn( const furn_id &furn ) { - std::uninitialized_fill_n( &frn[0, 0], elements, furn ); + frn.reset(furn); + // Reset furniture vars on clear + if (furn == f_null) { + for (auto &v : frn_vars) { + v.clear(); + } + } } ter_id get_ter( point p ) const { @@ -173,6 +185,22 @@ class submap : maptile_soa return fld[p.x, p.y]; } + const data_vars &get_ter_vars( point p ) const { + return ter_vars[p.x, p.y]; + }; + + data_vars &get_ter_vars( point p ) { + return ter_vars[p.x, p.y]; + }; + + const data_vars &get_furn_vars( point p ) const { + return frn_vars[p.x, p.y]; + }; + + data_vars &get_furn_vars( point p ) { + return frn_vars[p.x, p.y]; + }; + struct cosmetic_t { point pos; std::string type; @@ -238,7 +266,7 @@ class submap : maptile_soa std::map> partial_constructions; std::map> active_furniture; - static void swap( submap &first, submap &second ); + static void swap( submap &first, submap &second ) noexcept; private: std::map computers; @@ -296,7 +324,7 @@ struct maptile { return sm->get_field( pos() ); } - field_entry *find_field( const field_type_id &field_to_find ) { + field_entry *find_field( const field_type_id &field_to_find ) const { return sm->get_field( pos() ).find_field( field_to_find ); } From 74162964bdb8fbc6ef362d5262dbdf0fdca36167 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Thu, 16 Oct 2025 15:07:47 +0000 Subject: [PATCH 07/31] style(autofix.ci): automated formatting --- src/array2d.h | 16 ++++++++-------- src/data_vars.cpp | 35 +++++++++++++++++++---------------- src/data_vars.h | 42 +++++++++++++++++++++--------------------- src/game.cpp | 6 +++--- src/map.cpp | 14 ++++++++------ src/map.h | 4 ++-- src/submap.h | 10 +++++----- 7 files changed, 66 insertions(+), 61 deletions(-) diff --git a/src/array2d.h b/src/array2d.h index 32d39d034395..0d02f6d1a6ec 100644 --- a/src/array2d.h +++ b/src/array2d.h @@ -36,7 +36,7 @@ constexpr static auto init_array_2d_ys( F && fn, std::integer_sequence -constexpr static auto init_array_2d( F&& fn ) +constexpr static auto init_array_2d( F &&fn ) { return detail::init_array_2d_ys( std::forward( fn ), @@ -67,7 +67,7 @@ struct array2d { struct const_iterator; struct index; - using storage = std::array; + using storage = std::array; using index_type = index; using size_type = size_t; @@ -207,23 +207,23 @@ struct array2d { template requires std::is_same_v> - explicit array2d( F&& fn ) - : _data{detail::init_array_2d( fn ) } { } + explicit array2d( F &&fn ) + : _data{detail::init_array_2d( fn ) } { } - explicit array2d( const T& value ) - : _data{detail::init_array_2d( [&]( size_t, size_t ) { return value; } )} { } + explicit array2d( const T &value ) + : _data{detail::init_array_2d( [ & ]( size_t, size_t ) { return value; } )} { } void reset() { _data = {}; } - void reset(const T& value) { + void reset( const T &value ) { _data = detail::init_array_2d( [&]( size_t, size_t ) { return value; } ); } template requires std::is_same_v> - void reset( F&& fn ) { + void reset( F &&fn ) { _data = detail::init_array_2d( fn ); } diff --git a/src/data_vars.cpp b/src/data_vars.cpp index b98f04cf5375..ae3070e72242 100644 --- a/src/data_vars.cpp +++ b/src/data_vars.cpp @@ -3,24 +3,27 @@ #include "creature_tracker.h" #include "json.h" -void data_vars::set(const key_type &name, const mapped_type &value) { - data[name] = value; +void data_vars::set( const key_type &name, const mapped_type &value ) +{ + data[name] = value; } -data_vars::mapped_type data_vars::get(const key_type &name, - const mapped_type &default_value) const { - const auto it = find(name); - if (it == end()) { - return default_value; - } - return it->second; +data_vars::mapped_type data_vars::get( const key_type &name, + const mapped_type &default_value ) const +{ + const auto it = find( name ); + if( it == end() ) { + return default_value; + } + return it->second; } -bool data_vars::try_get(const key_type &name, mapped_type &value) const { - const auto it = find(name); - if (it == end()) { - return false; - } - value = it->second; - return true; +bool data_vars::try_get( const key_type &name, mapped_type &value ) const +{ + const auto it = find( name ); + if( it == end() ) { + return false; + } + value = it->second; + return true; } \ No newline at end of file diff --git a/src/data_vars.h b/src/data_vars.h index 783b3be5f521..cf9608bb3776 100644 --- a/src/data_vars.h +++ b/src/data_vars.h @@ -13,7 +13,7 @@ namespace detail template struct data_var_sstream_converter { - bool operator()( const T &in_val, std::string& out_val ) const { + bool operator()( const T &in_val, std::string &out_val ) const { std::stringstream ss; ss.imbue( std::locale::classic() ); ss << in_val; @@ -24,7 +24,7 @@ struct data_var_sstream_converter { return true; }; - bool operator()( const std::string& in_val, T &out_val ) const { + bool operator()( const std::string &in_val, T &out_val ) const { std::stringstream ss( in_val ); ss.imbue( std::locale::classic() ); ss >> out_val; @@ -36,15 +36,15 @@ struct data_var_sstream_converter { }; struct data_var_tripoint_converter { - bool operator()( const tripoint &in_val, std::string& out_val ) const { + bool operator()( const tripoint &in_val, std::string &out_val ) const { out_val = string_format( "%d,%d,%d", in_val.x, in_val.y, in_val.z ); return true; } - bool operator()( const std::string &in_val, tripoint& out_val) const { + bool operator()( const std::string &in_val, tripoint &out_val ) const { const std::vector values = string_split( in_val, ',' ); constexpr data_var_sstream_converter cvt; tripoint p; - if (cvt(values[0], p.x) && cvt(values[1], p.y) && cvt(values[2], p.z)) { + if( cvt( values[0], p.x ) && cvt( values[1], p.y ) && cvt( values[2], p.z ) ) { out_val = p; return true; }; @@ -86,37 +86,37 @@ class data_vars storage data; template > - void set( const key_type& name, const T& value, const Cvt& cvt = {} ) { + void set( const key_type &name, const T &value, const Cvt &cvt = {} ) { std::string strval; - if (cvt(value, strval)) { - data_vars::set(name, strval); - } - else { - throw std::runtime_error("failed to convert value to string"); + if( cvt( value, strval ) ) { + data_vars::set( name, strval ); + } else { + throw std::runtime_error( "failed to convert value to string" ); } } template > - bool try_get( const key_type& name, T& value, const Cvt& cvt = {} ) const { + bool try_get( const key_type &name, T &value, const Cvt &cvt = {} ) const { std::string strval; - if (!data_vars::try_get(name, strval)) + if( !data_vars::try_get( name, strval ) ) { return false; - return cvt(strval, value); + } + return cvt( strval, value ); } template > - T get( const key_type& name, const T& default_value = T{}, const Cvt& cvt = {} ) const { + T get( const key_type &name, const T &default_value = T{}, const Cvt &cvt = {} ) const { T value; - return try_get(name, value, cvt) - ? value - : default_value; + return try_get( name, value, cvt ) + ? value + : default_value; } - void set(const key_type &name, const mapped_type &value); - mapped_type get(const key_type &name, const mapped_type &default_value = mapped_type{}) const; + void set( const key_type &name, const mapped_type &value ); + mapped_type get( const key_type &name, const mapped_type &default_value = mapped_type{} ) const; bool try_get( const key_type &name, mapped_type &value ) const; - bool operator==( const data_vars & other ) const { return ( data ) == other.data; } + bool operator==( const data_vars &other ) const { return ( data ) == other.data; } mapped_type &operator[]( const key_type &name ) { return data[name]; } bool empty() const { return data.empty(); } diff --git a/src/game.cpp b/src/game.cpp index 0d590c32d835..ada318949195 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -9847,9 +9847,9 @@ bool game::grabbed_furn_move( const tripoint &dp ) // Swap furniture vars between tiles beforehand // because the furn_set call will clear the vars // when furniture is set to f_null - const auto dstVars = m.furn_vars(fdest); - const auto srcVars = m.furn_vars(fpos); - std::swap(*srcVars, *dstVars); + const auto dstVars = m.furn_vars( fdest ); + const auto srcVars = m.furn_vars( fpos ); + std::swap( *srcVars, *dstVars ); // Actually move the furniture. m.furn_set( fdest, m.furn( fpos ), atd ? atd->clone() : nullptr ); diff --git a/src/map.cpp b/src/map.cpp index 6f0a0ca8649f..ae1c246e4e76 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1617,25 +1617,27 @@ ter_id map::ter( const tripoint &p ) const return current_submap->get_ter( l ); } -data_vars* map::ter_vars(const tripoint &p) const { +data_vars *map::ter_vars( const tripoint &p ) const +{ if( !inbounds( p ) ) { return nullptr; } point l; - const auto sm = get_submap_at(p, l); - return &sm->get_ter_vars(l); + const auto sm = get_submap_at( p, l ); + return &sm->get_ter_vars( l ); } -data_vars* map::furn_vars(const tripoint &p) const { +data_vars *map::furn_vars( const tripoint &p ) const +{ if( !inbounds( p ) ) { return nullptr; } point l; - const auto sm = get_submap_at(p, l); - return &sm->get_furn_vars(l); + const auto sm = get_submap_at( p, l ); + return &sm->get_furn_vars( l ); } uint8_t map::get_known_connections( const tripoint &p, int connect_group, diff --git a/src/map.h b/src/map.h index eacbfb4bf3dc..d14d82913cd3 100644 --- a/src/map.h +++ b/src/map.h @@ -823,9 +823,9 @@ class map // Data Vars // requires inbounds(p), may return nullptr otherwise - data_vars* ter_vars(const tripoint &p) const; + data_vars *ter_vars( const tripoint &p ) const; // requires inbounds(p), may return nullptr otherwise - data_vars* furn_vars(const tripoint &p) const; + data_vars *furn_vars( const tripoint &p ) const; // Return a bitfield of the adjacent tiles which connect to the given // connect_group. From least-significant bit the order is south, east, diff --git a/src/submap.h b/src/submap.h index 7108ab871156..7e4f721400d6 100644 --- a/src/submap.h +++ b/src/submap.h @@ -101,7 +101,7 @@ class submap : maptile_soa } void set_all_traps( const trap_id &trap ) { - trp.reset(trap); + trp.reset( trap ); } furn_id get_furn( point p ) const { @@ -112,16 +112,16 @@ class submap : maptile_soa is_uniform = false; frn[p.x, p.y] = furn; // Reset furniture vars on clear - if (furn == f_null) { + if( furn == f_null ) { frn_vars[p.x, p.y].clear(); } } void set_all_furn( const furn_id &furn ) { - frn.reset(furn); + frn.reset( furn ); // Reset furniture vars on clear - if (furn == f_null) { - for (auto &v : frn_vars) { + if( furn == f_null ) { + for( auto &v : frn_vars ) { v.clear(); } } From 73605b4caf58ea618c569f9a28623219e72ec4fb Mon Sep 17 00:00:00 2001 From: Reisen Usagi Date: Fri, 17 Oct 2025 09:06:34 -0300 Subject: [PATCH 08/31] Move data_vars conversion stuff to separate file --- src/data_vars.cpp | 29 ------- src/data_vars.h | 159 ++++++++++++---------------------- src/data_vars_cvt.h | 204 ++++++++++++++++++++++++++++++++++++++++++++ src/item.cpp | 8 +- 4 files changed, 261 insertions(+), 139 deletions(-) delete mode 100644 src/data_vars.cpp create mode 100644 src/data_vars_cvt.h diff --git a/src/data_vars.cpp b/src/data_vars.cpp deleted file mode 100644 index ae3070e72242..000000000000 --- a/src/data_vars.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "data_vars.h" - -#include "creature_tracker.h" -#include "json.h" - -void data_vars::set( const key_type &name, const mapped_type &value ) -{ - data[name] = value; -} - -data_vars::mapped_type data_vars::get( const key_type &name, - const mapped_type &default_value ) const -{ - const auto it = find( name ); - if( it == end() ) { - return default_value; - } - return it->second; -} - -bool data_vars::try_get( const key_type &name, mapped_type &value ) const -{ - const auto it = find( name ); - if( it == end() ) { - return false; - } - value = it->second; - return true; -} \ No newline at end of file diff --git a/src/data_vars.h b/src/data_vars.h index cf9608bb3776..c48ff8fafa88 100644 --- a/src/data_vars.h +++ b/src/data_vars.h @@ -1,77 +1,8 @@ #pragma once -#include #include -#include - -#include "point.h" -#include "string_formatter.h" -#include "string_utils.h" - -namespace detail -{ - -template -struct data_var_sstream_converter { - bool operator()( const T &in_val, std::string &out_val ) const { - std::stringstream ss; - ss.imbue( std::locale::classic() ); - ss << in_val; - if( ss.fail() ) { - return false; - } - out_val = ss.str(); - return true; - }; - - bool operator()( const std::string &in_val, T &out_val ) const { - std::stringstream ss( in_val ); - ss.imbue( std::locale::classic() ); - ss >> out_val; - if( ss.fail() ) { - return false; - } - return true; - } -}; - -struct data_var_tripoint_converter { - bool operator()( const tripoint &in_val, std::string &out_val ) const { - out_val = string_format( "%d,%d,%d", in_val.x, in_val.y, in_val.z ); - return true; - } - bool operator()( const std::string &in_val, tripoint &out_val ) const { - const std::vector values = string_split( in_val, ',' ); - constexpr data_var_sstream_converter cvt; - tripoint p; - if( cvt( values[0], p.x ) && cvt( values[1], p.y ) && cvt( values[2], p.z ) ) { - out_val = p; - return true; - }; - return false; - } -}; - -template -struct data_var_converter { - using type = void; -}; - -template -using data_var_converter_t = typename data_var_converter::type; - -template -requires std::is_integral_v or std::is_floating_point_v -struct data_var_converter { - using type = data_var_sstream_converter; -}; - -template<> -struct data_var_converter { - using type = data_var_tripoint_converter; -}; - -} // namespace detail +#include +#include "data_vars_cvt.h" class data_vars { @@ -83,52 +14,70 @@ class data_vars using iterator = storage::iterator; using const_iterator = storage::const_iterator; - storage data; + template> + bool try_get( const std::string &key, Value &out_val, const Conv &converter = {} ) const { + const auto it = _data.find( key ); + if( it == _data.end() ) { + return false; + } + return converter( it->second, out_val ); + } - template > - void set( const key_type &name, const T &value, const Cvt &cvt = {} ) { - std::string strval; - if( cvt( value, strval ) ) { - data_vars::set( name, strval ); - } else { + template > + Value get( const std::string &key, const Value &default_value, const Conv &converter = {} ) const { + Value val; + if( !data_vars::try_get( key, val, converter ) ) { + return default_value; + } + return val; + } + + template > + void set( const key_type &name, const Value &value, const Conv &converter = {} ) { + std::string str; + if( !converter( value, str ) ) { throw std::runtime_error( "failed to convert value to string" ); } + _data[name] = str; } - template > - bool try_get( const key_type &name, T &value, const Cvt &cvt = {} ) const { - std::string strval; - if( !data_vars::try_get( name, strval ) ) { + bool try_get( const std::string &key, mapped_type &out_val ) const { + const auto it = _data.find( key ); + if( it == _data.end() ) { return false; } - return cvt( strval, value ); + out_val = it->second; + return true; + } + + mapped_type get( const std::string &key, const mapped_type &default_value ) const { + std::string val; + if( !try_get( key, val ) ) { + return default_value; + } + return val; } - template > - T get( const key_type &name, const T &default_value = T{}, const Cvt &cvt = {} ) const { - T value; - return try_get( name, value, cvt ) - ? value - : default_value; + void set( const key_type &name, const mapped_type &value ) { + _data[name] = value; } - void set( const key_type &name, const mapped_type &value ); - mapped_type get( const key_type &name, const mapped_type &default_value = mapped_type{} ) const; - bool try_get( const key_type &name, mapped_type &value ) const; + bool operator==( const data_vars &other ) const { return ( _data ) == other._data; } + mapped_type &operator[]( const key_type &name ) { return _data[name]; } - bool operator==( const data_vars &other ) const { return ( data ) == other.data; } - mapped_type &operator[]( const key_type &name ) { return data[name]; } + bool empty() const { return _data.empty(); } + bool contains( const key_type &key ) const { return _data.contains( key ); } + iterator begin() { return _data.begin(); } + const_iterator begin() const { return _data.begin(); } + iterator end() { return _data.end(); } + const_iterator end() const { return _data.end(); } + iterator find( const key_type &key ) { return _data.find( key ); } + const_iterator find( const key_type &key ) const { return _data.find( key ); } - bool empty() const { return data.empty(); } - bool contains( const key_type &key ) const { return data.contains( key ); } - iterator begin() { return data.begin(); } - const_iterator begin() const { return data.begin(); } - iterator end() { return data.end(); } - const_iterator end() const { return data.end(); } - iterator find( const key_type &key ) { return data.find( key ); } - const_iterator find( const key_type &key ) const { return data.find( key ); } + void clear() { _data.clear(); } + void erase( const key_type &name ) { _data.erase( name ); } + void erase( const iterator it ) { _data.erase( it ); } - void clear() { data.clear(); } - void erase( const key_type &name ) { data.erase( name ); } - void erase( iterator it ) { data.erase( it ); } + private: + storage _data; }; \ No newline at end of file diff --git a/src/data_vars_cvt.h b/src/data_vars_cvt.h new file mode 100644 index 000000000000..64acd3bd0d9d --- /dev/null +++ b/src/data_vars_cvt.h @@ -0,0 +1,204 @@ +#pragma once + +#include +#include + +#include "point.h" + +namespace detail +{ + +// Concepts + +template +concept has_istream_read = requires( T &in_val, std::ostream &os ) +{ + { os << in_val } + -> std::convertible_to; +}; + +template +concept has_ostream_write = requires( T &in_val, std::istream &is ) +{ + { is >> in_val } + -> std::convertible_to; +}; + +template +concept has_iostream_read_write = has_istream_read &&has_ostream_write; + +template +concept is_reference = std::is_reference_v; + +template +concept is_numeric = std::disjunction_v, std::is_integral>; + +template +concept has_get_ref = requires( T &t ) { { std::get( t ) } -> is_reference; }; + +template +struct has_get_ref_n : std::integral_constant < bool, has_get_ref &&has_get_ref_n < T, + N - 1 >::value > {}; + +template +struct has_get_ref_n : std::integral_constant> {}; + +template +concept is_tuple_like = requires { typename std::tuple_size::type; } &&has_get_ref_n < T, + std::tuple_size_v - 1 >::value; + +// Helpers + +template +requires( has_ostream_write &&( has_ostream_write &&... ) ) +static bool ostream_write_values( std::ostream &ss, const char delim, const First &val, + const Rest &... rest ) +{ + ss << val; + if( ss.fail() ) { + return false; + } + if constexpr( sizeof...( rest ) > 0 ) { + ss << delim; + return detail::ostream_write_values( ss, delim, rest... ); + } else { + return true; + } +} + +template +requires( has_istream_read ) +static bool istream_read_values( std::istream &ss, const char, Value &val ) +{ + ss >> std::ws >> val; + if( ss.fail() ) { + return false; + } + ss >> std::ws; + return ss.eof(); +} + +template +requires( has_istream_read &&( has_istream_read &&... ) ) +static bool istream_read_values( std::istream &ss, const char delim, First &val, Rest &... rest ) +{ + ss >> std::ws >> val; + if( ss.fail() ) { + return false; + } + + char c; + ss >> std::ws >> c; + if( c != delim ) { + return false; + } + + return detail::istream_read_values( ss, delim, rest... ); +} + +template +static bool ostream_write_seq_get( std::ostream &ss, const char delim, const T &val, + std::index_sequence ) +{ + return detail::ostream_write_values( ss, delim, std::get( val )... ); +} + +template +static bool istream_read_seq_get( std::istream &ss, const char delim, T &val, + std::index_sequence ) +{ + return detail::istream_read_values( ss, delim, std::get( val )... ); +} + +// Converters + +template +struct type_converter; + +template +requires( has_iostream_read_write ) +struct basic_converter { + + bool operator()( const T &in_val, std::string &out_val ) const { + std::ostringstream ss; + if( !ostream_write_values( ss, '\0', in_val ) ) { + return false; + } + out_val = ss.str(); + return true; + }; + + bool operator()( const std::string &in_val, T &out_val ) const { + std::istringstream ss( in_val ); + T tmp; + if( !istream_read_values( ss, '\0', tmp ) ) { + return false; + } + out_val = tmp; + return true; + } +}; + +template> +requires( has_get_ref_n < T, N - 1 >::value ) +struct tuple_converter { + + bool operator()( const T &in_val, std::string &out_val ) const { + std::ostringstream ss; + if( !ostream_write_seq_get( ss, ',', in_val, std::make_index_sequence() ) ) { + return false; + } + out_val = ss.str(); + return true; + } + + bool operator()( const std::string &in_val, T &out_val ) const { + std::istringstream ss( in_val ); + T tmp; + if( !istream_read_seq_get( ss, ',', tmp, std::make_index_sequence() ) ) { + return false; + } + out_val = tmp; + return true; + } +}; + +struct tripoint_converter { + using RealType = tripoint; + using ProxyType = std::tuple; + using ProxyConverter = tuple_converter; + + static ProxyType to_proxy( const RealType &v ) { + auto& [x, y, z] = v; + return ProxyType{x, y, z}; + } + static RealType from_proxy( const ProxyType &v ) { + auto& [x, y, z] = v; + return RealType( x, y, z ); + } + + bool operator()( const tripoint &in_val, std::string &out_val ) const { + constexpr auto cvt = ProxyConverter{}; + return cvt( to_proxy( in_val ), out_val ); + } + bool operator()( const std::string &in_val, tripoint &out_val ) const { + constexpr auto cvt = ProxyConverter{}; + ProxyType tmp; + if( !cvt( in_val, tmp ) ) { + return false; + } + out_val = from_proxy( tmp ); + return true; + } +}; + +template +struct type_converter : tuple_converter {}; + +template +struct type_converter : basic_converter {}; + +template<> +struct type_converter : tripoint_converter {}; + +} // namespace detail \ No newline at end of file diff --git a/src/item.cpp b/src/item.cpp index da6e6a56964a..b2ba072bf36c 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -1657,8 +1657,7 @@ void item::basic_info( std::vector &info, const iteminfo_query *parts, if( parts->test( iteminfo_parts::DESCRIPTION ) ) { insert_separation_line( info ); - const std::map::const_iterator idescription = - item_vars.find( "description" ); + const auto idescription = item_vars.find( "description" ); const std::optional snippet = SNIPPET.get_snippet_by_id( snip_id ); if( snippet.has_value() && ( !get_avatar().has_trait( trait_ILLITERATE ) || !has_flag( flag_SNIPPET_NEEDS_LITERACY ) ) ) { @@ -1683,9 +1682,8 @@ void item::basic_info( std::vector &info, const iteminfo_query *parts, info.emplace_back( "DESCRIPTION", type->description.translated() ); } } - std::map::const_iterator item_note = item_vars.find( "item_note" ); - std::map::const_iterator item_note_tool = - item_vars.find( "item_note_tool" ); + const auto item_note = item_vars.find( "item_note" ); + const auto item_note_tool = item_vars.find( "item_note_tool" ); if( item_note != item_vars.end() && parts->test( iteminfo_parts::DESCRIPTION_NOTES ) ) { std::string ntext; From dd085baa593ba0bd105213238ede4000fea17913 Mon Sep 17 00:00:00 2001 From: Reisen Usagi Date: Sat, 18 Oct 2025 15:41:16 -0300 Subject: [PATCH 09/31] add const char* overloads for data_vars to fetch strings directly without calling converter --- src/data_vars.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/data_vars.h b/src/data_vars.h index c48ff8fafa88..39bbec9b2dc5 100644 --- a/src/data_vars.h +++ b/src/data_vars.h @@ -62,6 +62,18 @@ class data_vars _data[name] = value; } + std::string get( const std::string &key, const char* default_value ) const { + std::string val; + if( !try_get( key, val ) ) { + return default_value; + } + return val; + } + + void set( const key_type &name, const char* value ) { + _data[name] = std::string{value}; + } + bool operator==( const data_vars &other ) const { return ( _data ) == other._data; } mapped_type &operator[]( const key_type &name ) { return _data[name]; } From 00a37a1f17c7c9c2549ee07f53869b3c8326f518 Mon Sep 17 00:00:00 2001 From: Reisen Usagi Date: Sat, 18 Oct 2025 19:03:54 -0300 Subject: [PATCH 10/31] add basic_converter / json_converter reorganize namespace --- src/cata_utility.h | 25 ++++ src/data_vars.h | 29 ++-- src/data_vars_cvt.h | 306 +++++++++++++++++++++++++++--------------- src/item.cpp | 34 ++--- src/item.h | 38 +++--- src/map.cpp | 4 +- src/map.h | 4 +- src/savegame_json.cpp | 14 +- src/submap.h | 12 +- tests/data_vars.cpp | 195 +++++++++++++++++++++++++++ 10 files changed, 498 insertions(+), 163 deletions(-) create mode 100644 tests/data_vars.cpp diff --git a/src/cata_utility.h b/src/cata_utility.h index e9cbd98660e2..ce7e1096bbf9 100644 --- a/src/cata_utility.h +++ b/src/cata_utility.h @@ -341,4 +341,29 @@ class restore_on_out_of_scope */ holiday get_holiday_from_time( std::time_t time = 0, bool force_refresh = false ); +template +constexpr T _pow10p( std::index_sequence const & ) +{ + auto apply = []( size_t, T & v ) { v *= 10; }; + T ret { 1 }; + ( apply( Is, ret ), ... ); + return ret; +} + +template +constexpr T _pow10n( std::index_sequence const & ) +{ + auto apply = []( size_t, T & v ) { v /= 10; }; + T ret { 1 }; + ( apply( Is, ret ), ... ); + return ret; +} + +template < typename T, int E, std::size_t N = ( E < 0 ? -E : E ) > + constexpr T pow10() +{ + return E < 0 + ? _pow10n( std::make_index_sequence {} ) + : _pow10p( std::make_index_sequence {} ); +} diff --git a/src/data_vars.h b/src/data_vars.h index 39bbec9b2dc5..113026b1a721 100644 --- a/src/data_vars.h +++ b/src/data_vars.h @@ -2,9 +2,18 @@ #include #include + #include "data_vars_cvt.h" -class data_vars +namespace data_vars { + +template +struct type_converter; + +template +using type_converter_t = typename type_converter::type; + +class data_set { public: using storage = std::map; @@ -14,7 +23,7 @@ class data_vars using iterator = storage::iterator; using const_iterator = storage::const_iterator; - template> + template> bool try_get( const std::string &key, Value &out_val, const Conv &converter = {} ) const { const auto it = _data.find( key ); if( it == _data.end() ) { @@ -23,16 +32,16 @@ class data_vars return converter( it->second, out_val ); } - template > - Value get( const std::string &key, const Value &default_value, const Conv &converter = {} ) const { + template > + Value get( const std::string &key, const Value &default_value = {}, const Conv &converter = {} ) const { Value val; - if( !data_vars::try_get( key, val, converter ) ) { + if( !data_set::try_get( key, val, converter ) ) { return default_value; } return val; } - template > + template > void set( const key_type &name, const Value &value, const Conv &converter = {} ) { std::string str; if( !converter( value, str ) ) { @@ -50,7 +59,7 @@ class data_vars return true; } - mapped_type get( const std::string &key, const mapped_type &default_value ) const { + mapped_type get( const std::string &key, const mapped_type &default_value = {} ) const { std::string val; if( !try_get( key, val ) ) { return default_value; @@ -74,7 +83,7 @@ class data_vars _data[name] = std::string{value}; } - bool operator==( const data_vars &other ) const { return ( _data ) == other._data; } + bool operator==( const data_set &other ) const { return ( _data ) == other._data; } mapped_type &operator[]( const key_type &name ) { return _data[name]; } bool empty() const { return _data.empty(); } @@ -92,4 +101,6 @@ class data_vars private: storage _data; -}; \ No newline at end of file +}; + +} // namespace data_vars \ No newline at end of file diff --git a/src/data_vars_cvt.h b/src/data_vars_cvt.h index 64acd3bd0d9d..116460ebc8bc 100644 --- a/src/data_vars_cvt.h +++ b/src/data_vars_cvt.h @@ -1,13 +1,17 @@ #pragma once -#include #include +#include #include "point.h" +#include "json.h" +#include "cata_utility.h" -namespace detail +namespace data_vars { +namespace detail +{ // Concepts template @@ -24,181 +28,273 @@ concept has_ostream_write = requires( T &in_val, std::istream &is ) -> std::convertible_to; }; -template -concept has_iostream_read_write = has_istream_read &&has_ostream_write; - template concept is_reference = std::is_reference_v; template concept is_numeric = std::disjunction_v, std::is_integral>; -template -concept has_get_ref = requires( T &t ) { { std::get( t ) } -> is_reference; }; - -template -struct has_get_ref_n : std::integral_constant < bool, has_get_ref &&has_get_ref_n < T, - N - 1 >::value > {}; - template -struct has_get_ref_n : std::integral_constant> {}; - -template -concept is_tuple_like = requires { typename std::tuple_size::type; } &&has_get_ref_n < T, - std::tuple_size_v - 1 >::value; +concept is_tuple_like = requires { typename std::tuple_size::type; }; // Helpers +template +struct stream_value_io; + template -requires( has_ostream_write &&( has_ostream_write &&... ) ) -static bool ostream_write_values( std::ostream &ss, const char delim, const First &val, - const Rest &... rest ) +static bool stream_write_values( std::ostream &os, const char delim, const First &val, const Rest &... rest ) { - ss << val; - if( ss.fail() ) { + constexpr auto io = detail::stream_value_io{}; + io(os, delim, val); + + if( os.fail() ) { return false; } - if constexpr( sizeof...( rest ) > 0 ) { - ss << delim; - return detail::ostream_write_values( ss, delim, rest... ); - } else { - return true; - } -} + os.clear(); -template -requires( has_istream_read ) -static bool istream_read_values( std::istream &ss, const char, Value &val ) -{ - ss >> std::ws >> val; - if( ss.fail() ) { - return false; + if constexpr( sizeof...( rest ) == 0 ) { + return true; + } else { + os << delim; + return detail::stream_write_values( os, delim, rest... ); } - ss >> std::ws; - return ss.eof(); } template -requires( has_istream_read &&( has_istream_read &&... ) ) -static bool istream_read_values( std::istream &ss, const char delim, First &val, Rest &... rest ) +static bool stream_read_values( std::istream &is, const char delim, First &val, Rest &... rest ) { - ss >> std::ws >> val; - if( ss.fail() ) { - return false; - } + constexpr auto io = detail::stream_value_io{}; + io(is, delim, val); - char c; - ss >> std::ws >> c; - if( c != delim ) { + if( is.fail() ) { return false; } + is.clear(); - return detail::istream_read_values( ss, delim, rest... ); + if constexpr( sizeof...( rest ) == 0 ) { + is >> std::ws; + return true; + } else { + char c; + is >> std::ws >> c; + if( c != delim ) { + return false; + } + return detail::stream_read_values( is, delim, rest... ); + } } -template -static bool ostream_write_seq_get( std::ostream &ss, const char delim, const T &val, - std::index_sequence ) +template +static bool stream_write_indexed( std::ostream &os, const char delim, const T &val, Fn && getter, std::index_sequence ) { - return detail::ostream_write_values( ss, delim, std::get( val )... ); + return detail::stream_write_values( os, delim, getter.template operator()(val)... ); } -template -static bool istream_read_seq_get( std::istream &ss, const char delim, T &val, - std::index_sequence ) +template +static bool stream_read_indexed( std::istream &is, const char delim, T &val, Fn && getter, std::index_sequence ) { - return detail::istream_read_values( ss, delim, std::get( val )... ); + return detail::stream_read_values( is, delim, getter.template operator()(val)... ); } -// Converters +} // namespace detail -template -struct type_converter; +// Converters +namespace converters +{ template -requires( has_iostream_read_write ) struct basic_converter { + using value_type = T; + + static void configure_stream(std::ios_base& ss) { + ss.imbue( std::locale::classic() ); + ss.setf( std::ios_base::showpoint ); + ss.setf( std::ios_base::dec, std::ostream::basefield ); + } + + static void configure_stream(std::ios_base& ss, const T& in_val) { + configure_stream( ss ); + if constexpr( std::is_floating_point_v ) { + constexpr auto max_digits = std::numeric_limits::digits10; + constexpr auto max_repr = pow10(); + const auto float_flags = in_val >= max_repr ? std::ios_base::scientific : std::ios_base::fixed; + ss.precision( max_digits ); + ss.setf(float_flags, std::ios_base::floatfield); + } + } + bool operator()( const T &in_val, std::string &out_val ) const { - std::ostringstream ss; - if( !ostream_write_values( ss, '\0', in_val ) ) { + std::ostringstream os; + basic_converter::configure_stream(os, in_val); + + if( !detail::stream_write_values( os, ',', in_val ) ) { return false; } - out_val = ss.str(); + + out_val = os.str(); return true; }; bool operator()( const std::string &in_val, T &out_val ) const { - std::istringstream ss( in_val ); + std::istringstream is( in_val ); + basic_converter::configure_stream(is); + T tmp; - if( !istream_read_values( ss, '\0', tmp ) ) { + if( !detail::stream_read_values( is, ',', tmp ) ) { return false; } + out_val = tmp; return true; } }; -template> -requires( has_get_ref_n < T, N - 1 >::value ) -struct tuple_converter { +template +struct json_converter { + using value_type = T; - bool operator()( const T &in_val, std::string &out_val ) const { - std::ostringstream ss; - if( !ostream_write_seq_get( ss, ',', in_val, std::make_index_sequence() ) ) { - return false; - } - out_val = ss.str(); + // Json parser shits itself with 17 digits of precision + + bool operator()( const T &in_val, std::string &out_val ) const + { + std::ostringstream os; + JsonOut jsout{os}; + jsout.write( in_val ); + out_val = os.str(); return true; } bool operator()( const std::string &in_val, T &out_val ) const { - std::istringstream ss( in_val ); - T tmp; - if( !istream_read_seq_get( ss, ',', tmp, std::make_index_sequence() ) ) { + std::istringstream is{in_val}; + JsonIn jsin{is}; + return jsin.read( out_val, false ); + } +}; + +} // namespace converters + +namespace detail +{ + +// Fallback +template +struct stream_value_io { + bool operator()(std::ostream &ss, const char, const T &val) const { + if constexpr (has_ostream_write) { + ss << val; + return true; + } else { + [](){ static_assert(f, "no matching ostream operator for type"); }(); + return false; + } + } + + bool operator()(std::istream& ss, const char, T& val) { + if constexpr (has_istream_read) { + ss >> val; + return true; + } else { + [](){ static_assert(f, "no matching istream operator for type"); }(); return false; } - out_val = tmp; - return true; } }; -struct tripoint_converter { - using RealType = tripoint; - using ProxyType = std::tuple; - using ProxyConverter = tuple_converter; +// String - static ProxyType to_proxy( const RealType &v ) { - auto& [x, y, z] = v; - return ProxyType{x, y, z}; +template<> +struct stream_value_io { + using T = std::string; + + static constexpr std::string RESERVED_CHARS = "[],\""; + + bool operator()(std::ostream &os, const char, const T &val) const { + os << std::quoted(val); + return true; } - static RealType from_proxy( const ProxyType &v ) { - auto& [x, y, z] = v; - return RealType( x, y, z ); + + bool operator()(std::istream &is, const char, T &val) const { + is >> std::quoted(val); + return true; } +}; - bool operator()( const tripoint &in_val, std::string &out_val ) const { - constexpr auto cvt = ProxyConverter{}; - return cvt( to_proxy( in_val ), out_val ); +// Numeric + +template +struct stream_value_io { + bool operator()(std::ostream &ss, const char, const T &val) const { + ss << val; + return true; } - bool operator()( const std::string &in_val, tripoint &out_val ) const { - constexpr auto cvt = ProxyConverter{}; - ProxyType tmp; - if( !cvt( in_val, tmp ) ) { - return false; - } - out_val = from_proxy( tmp ); + + bool operator()(std::istream &ss, const char, T &val) const { + ss >> val; return true; } }; -template -struct type_converter : tuple_converter {}; +// Tripoint -template -struct type_converter : basic_converter {}; +template<> +struct stream_value_io { + bool operator()(std::ostream &ss, const char delim, const tripoint &val) const { + return stream_write_values(ss, delim, val.x, val.y, val.z); + } + bool operator()(std::istream &ss, const char delim, tripoint &val) const { + return stream_read_values(ss, delim, val.x, val.y, val.z); + } +}; + +// Point template<> -struct type_converter : tripoint_converter {}; +struct stream_value_io { + bool operator()(std::ostream &ss, const char delim, const point &val) const { + return stream_write_values(ss, delim, val.x, val.y); + } + bool operator()(std::istream &ss, const char delim, point &val) const { + return stream_read_values(ss, delim, val.x, val.y); + } +}; + +// Tuple-Like +template +struct stream_value_io { + struct tuple_get { + template + auto& operator()(T& val) { return std::get(val); } + + template + const auto& operator()(const T& val) { return std::get(val); } + }; + + bool operator()(std::ostream &os, const char delim, const T &val) const { + os << '['; + detail::stream_write_indexed(os, delim, val, tuple_get{} , std::make_index_sequence>()); + os << ']'; + return true; + } + bool operator()(std::istream &is, const char delim, T &val) const { + char c; + is >> c >> std::ws; + if (c != '[') + return false; + detail::stream_read_indexed(is, delim, val, tuple_get{}, std::make_index_sequence>()); + is >> c >> std::ws; + if (c != ']') + return false; + return true; + } +}; + +} // namespace detail + +template +struct type_converter { + using type = converters::json_converter; +}; -} // namespace detail \ No newline at end of file +} // namespace data_vars diff --git a/src/item.cpp b/src/item.cpp index b2ba072bf36c..86ebbd87ff67 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -444,7 +444,7 @@ item::item( const item &source ) : game_object( source ), contents( this ) faults = source.faults; item_tags = source.item_tags; curammo = source.curammo; - item_vars = source.item_vars; + item_vars_ = source.item_vars_; corpse = source.corpse; corpse_name = source.corpse_name; techniques = source.techniques; @@ -489,7 +489,7 @@ item &item::operator=( const item &source ) faults = source.faults; item_tags = source.item_tags; curammo = source.curammo; - item_vars = source.item_vars; + item_vars_ = source.item_vars_; corpse = source.corpse; corpse_name = source.corpse_name; techniques = source.techniques; @@ -1075,7 +1075,7 @@ bool item::stacks_with( const item &rhs, bool check_components, bool skip_type_c if( techniques != rhs.techniques ) { return false; } - if( item_vars != rhs.item_vars ) { + if( item_vars_ != rhs.item_vars_ ) { return false; } @@ -1657,7 +1657,7 @@ void item::basic_info( std::vector &info, const iteminfo_query *parts, if( parts->test( iteminfo_parts::DESCRIPTION ) ) { insert_separation_line( info ); - const auto idescription = item_vars.find( "description" ); + const auto idescription = item_vars_.find( "description" ); const std::optional snippet = SNIPPET.get_snippet_by_id( snip_id ); if( snippet.has_value() && ( !get_avatar().has_trait( trait_ILLITERATE ) || !has_flag( flag_SNIPPET_NEEDS_LITERACY ) ) ) { @@ -1668,7 +1668,7 @@ void item::basic_info( std::vector &info, const iteminfo_query *parts, //note that you have seen the snippet get_avatar().add_snippet( snip_id ); } - } else if( idescription != item_vars.end() ) { + } else if( idescription != item_vars_.end() ) { info.emplace_back( "DESCRIPTION", idescription->second ); } else { if( is_craft() ) { @@ -1682,13 +1682,13 @@ void item::basic_info( std::vector &info, const iteminfo_query *parts, info.emplace_back( "DESCRIPTION", type->description.translated() ); } } - const auto item_note = item_vars.find( "item_note" ); - const auto item_note_tool = item_vars.find( "item_note_tool" ); + const auto item_note = item_vars_.find( "item_note" ); + const auto item_note_tool = item_vars_.find( "item_note_tool" ); - if( item_note != item_vars.end() && parts->test( iteminfo_parts::DESCRIPTION_NOTES ) ) { + if( item_note != item_vars_.end() && parts->test( iteminfo_parts::DESCRIPTION_NOTES ) ) { std::string ntext; const inscribe_actor *use_actor = nullptr; - if( item_note_tool != item_vars.end() ) { + if( item_note_tool != item_vars_.end() ) { const use_function *use_func = itype_id( item_note_tool->second )->get_use( "inscribe" ); use_actor = dynamic_cast( use_func->get_actor_ptr() ); } @@ -1778,7 +1778,7 @@ void item::basic_info( std::vector &info, const iteminfo_query *parts, const std::string tags_listed = enumerate_as_string( item_tags, f, enumeration_conjunction::none ); info.emplace_back( "BASE", string_format( _( "tags: %s" ), tags_listed ) ); - for( auto const &imap : item_vars ) { + for( auto const &imap : item_vars_ ) { info.emplace_back( "BASE", string_format( _( "item var: %s, %s" ), imap.first, imap.second ) ); @@ -4809,7 +4809,7 @@ std::string item::tname( unsigned int quantity, bool with_prefix, unsigned int t } std::string maintext; - if( is_corpse() || item_vars.contains( "name" ) ) { + if( is_corpse() || item_vars_.contains( "name" ) ) { maintext = type_name( quantity ); } else if( is_craft() ) { maintext = string_format( _( "in progress %s" ), craft_data_->making->result_name() ); @@ -5004,7 +5004,7 @@ std::string item::tname( unsigned int quantity, bool with_prefix, unsigned int t ret = utf8_truncate( ret, truncate + truncate_override ); } - if( item_vars.contains( "item_note" ) ) { + if( item_vars_.contains( "item_note" ) ) { //~ %s is an item name. This style is used to denote items with notes. return string_format( _( "*%s*" ), ret ); } else { @@ -9498,8 +9498,8 @@ bool item_compare_by_charges( const item &left, const item &right ) static const std::string USED_BY_IDS( "USED_BY_IDS" ); bool item::already_used_by_player( const player &p ) const { - const auto it = item_vars.find( USED_BY_IDS ); - if( it == item_vars.end() ) { + const auto it = item_vars_.find( USED_BY_IDS ); + if( it == item_vars_.end() ) { return false; } // USED_BY_IDS always starts *and* ends with a ';', the search string @@ -9511,7 +9511,7 @@ bool item::already_used_by_player( const player &p ) const void item::mark_as_used_by_player( const player &p ) { - std::string &used_by_ids = item_vars[ USED_BY_IDS ]; + std::string &used_by_ids = item_vars_[ USED_BY_IDS ]; if( used_by_ids.empty() ) { // *always* start with a ';' used_by_ids = ";"; @@ -10635,9 +10635,9 @@ bool item::is_reloadable() const std::string item::type_name( unsigned int quantity ) const { - const auto iter = item_vars.find( "name" ); + const auto iter = item_vars_.find( "name" ); std::string ret_name; - if( iter != item_vars.end() ) { + if( iter != item_vars_.end() ) { return iter->second; } else { ret_name = type->nname( quantity ); diff --git a/src/item.h b/src/item.h index 2386c6189bf0..6f6748e33c23 100644 --- a/src/item.h +++ b/src/item.h @@ -1496,22 +1496,30 @@ class item : public location_visitable, public game_object * */ /*@{*/ - void set_var( const std::string &name, int value ) { item_vars.set( name, value ); }; - void set_var( const std::string &name, long long value ) { item_vars.set( name, value ); }; + + void set_var( const std::string &name, int value ) { item_vars_.set( name, value ); }; + void set_var( const std::string &name, long long value ) { item_vars_.set( name, value ); }; // Acceptable to use long as part of overload set // NOLINTNEXTLINE(cata-no-long) - void set_var( const std::string &name, long value ) { item_vars.set( name, value ); }; - void set_var( const std::string &name, double value ) { item_vars.set( name, value ); }; - void set_var( const std::string &name, const tripoint &value ) { item_vars.set( name, value ); }; - void set_var( const std::string &name, const std::string &value ) { item_vars.set( name, value ); }; - - double get_var( const std::string &name, double default_value ) const { return item_vars.get( name, default_value ); } - tripoint get_var( const std::string &name, const tripoint &default_value ) const { return item_vars.get( name, default_value ); } - std::string get_var( const std::string &name, const std::string &default_value ) const { return item_vars.get( name, default_value ); } - std::string get_var( const std::string &name ) const { return item_vars.get( name, "" ); } - bool has_var( const std::string &name ) const { return item_vars.contains( name ); } - void erase_var( const std::string &name ) { item_vars.erase( name ); } - void clear_vars() { item_vars.clear(); } + void set_var( const std::string &name, long value ) { item_vars_.set( name, value ); }; + void set_var( const std::string &name, double value ) { item_vars_.set( name, value ); }; + void set_var( const std::string &name, const tripoint &value ) { item_vars_.set( name, value ); }; + void set_var( const std::string &name, const std::string &value ) { item_vars_.set( name, value ); }; + + double get_var( const std::string &name, double default_value ) const { return item_vars_.get( name, default_value ); } + tripoint get_var( const std::string &name, const tripoint &default_value ) const { return item_vars_.get( name, default_value ); } + std::string get_var( const std::string &name, const std::string &default_value ) const { return item_vars_.get( name, default_value ); } + std::string get_var( const std::string &name ) const { return item_vars_.get( name, "" ); } + bool has_var( const std::string &name ) const { return item_vars_.contains( name ); } + void erase_var( const std::string &name ) { item_vars_.erase( name ); } + void clear_vars() { item_vars_.clear(); } + + // TODO in follow-up PR: + // remove internal usage of get_var / set_var internally + // mark as [[deprecated]] + // completely remove in a future update + + data_vars::data_set& item_vars() { return item_vars_; } /** Adds child items to the contents of this one. */ void add_item_with_id( const itype_id &itype, int count = 1 ); @@ -2387,7 +2395,7 @@ class item : public location_visitable, public game_object private: location_vector components; const itype *curammo = nullptr; - data_vars item_vars = {}; + data_vars::data_set item_vars_ = {}; const mtype *corpse = nullptr; std::string corpse_name; // Name of the late lamented std::set techniques; // item specific techniques diff --git a/src/map.cpp b/src/map.cpp index ae1c246e4e76..8ac84ca98a2e 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1617,7 +1617,7 @@ ter_id map::ter( const tripoint &p ) const return current_submap->get_ter( l ); } -data_vars *map::ter_vars( const tripoint &p ) const +data_vars::data_set *map::ter_vars( const tripoint &p ) const { if( !inbounds( p ) ) { return nullptr; @@ -1629,7 +1629,7 @@ data_vars *map::ter_vars( const tripoint &p ) const } -data_vars *map::furn_vars( const tripoint &p ) const +data_vars::data_set *map::furn_vars( const tripoint &p ) const { if( !inbounds( p ) ) { return nullptr; diff --git a/src/map.h b/src/map.h index d14d82913cd3..ba673e06f02a 100644 --- a/src/map.h +++ b/src/map.h @@ -823,9 +823,9 @@ class map // Data Vars // requires inbounds(p), may return nullptr otherwise - data_vars *ter_vars( const tripoint &p ) const; + data_vars::data_set *ter_vars( const tripoint &p ) const; // requires inbounds(p), may return nullptr otherwise - data_vars *furn_vars( const tripoint &p ) const; + data_vars::data_set *furn_vars( const tripoint &p ) const; // Return a bitfield of the adjacent tiles which connect to the given // connect_group. From least-significant bit the order is south, east, diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index 2dd696ba9008..94fde98e4970 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -2255,7 +2255,7 @@ void item::io( Archive &archive ) archive.io( "bday", bday, calendar::start_of_cataclysm ); archive.io( "mission_id", mission_id, -1 ); archive.io( "player_id", player_id, -1 ); - archive.io( "item_vars", item_vars, io::empty_default_tag() ); + archive.io( "item_vars", item_vars_, io::empty_default_tag() ); // TODO: change default to empty string archive.io( "name", corpse_name, std::string() ); archive.io( "owner", owner, faction_id::NULL_ID() ); @@ -2361,9 +2361,9 @@ void item::io( Archive &archive ) // Books without any chapters don't need to store a remaining-chapters // counter, it will always be 0 and it prevents proper stacking. if( get_chapters() == 0 ) { - for( auto it = item_vars.begin(); it != item_vars.end(); ) { + for( auto it = item_vars_.begin(); it != item_vars_.end(); ) { if( it->first.starts_with( "remaining-chapters-" ) ) { - item_vars.erase( it++ ); + item_vars_.erase( it++ ); } else { ++it; } @@ -2371,8 +2371,8 @@ void item::io( Archive &archive ) } // Remove stored translated gerund in favor of storing the inscription tool type - item_vars.erase( "item_label_type" ); - item_vars.erase( "item_note_type" ); + item_vars_.erase( "item_label_type" ); + item_vars_.erase( "item_note_type" ); // Activate corpses from old saves if( is_corpse() && !is_active() ) { @@ -4054,7 +4054,7 @@ void submap::load( JsonIn &jsin, const std::string &member_name, int version, while( !jsin.end_array() ) { int i = jsin.get_int(); int j = jsin.get_int(); - data_vars vars; + data_vars::data_set vars; jsin.read( vars ); frn_vars[i, j] = std::move( vars ); } @@ -4063,7 +4063,7 @@ void submap::load( JsonIn &jsin, const std::string &member_name, int version, while( !jsin.end_array() ) { int i = jsin.get_int(); int j = jsin.get_int(); - data_vars vars; + data_vars::data_set vars; jsin.read( vars ); ter_vars[i, j] = std::move( vars ); } diff --git a/src/submap.h b/src/submap.h index 7e4f721400d6..a6947547f3f8 100644 --- a/src/submap.h +++ b/src/submap.h @@ -79,8 +79,8 @@ struct maptile_soa { array2d< field, sx, sy > fld; // Field on each square array2d< trap_id, sx, sy > trp; // Trap on each square array2d< int, sx, sy > rad; // Irradiation of each square - array2d< data_vars, sx, sy > ter_vars; // Terrain vars - array2d< data_vars, sx, sy > frn_vars; // Furniture vars + array2d< data_vars::data_set, sx, sy > ter_vars; // Terrain vars + array2d< data_vars::data_set, sx, sy > frn_vars; // Furniture vars void swap_soa_tile( point p1, point p2 ); }; @@ -185,19 +185,19 @@ class submap : maptile_soa return fld[p.x, p.y]; } - const data_vars &get_ter_vars( point p ) const { + const data_vars::data_set &get_ter_vars( point p ) const { return ter_vars[p.x, p.y]; }; - data_vars &get_ter_vars( point p ) { + data_vars::data_set &get_ter_vars( point p ) { return ter_vars[p.x, p.y]; }; - const data_vars &get_furn_vars( point p ) const { + const data_vars::data_set &get_furn_vars( point p ) const { return frn_vars[p.x, p.y]; }; - data_vars &get_furn_vars( point p ) { + data_vars::data_set &get_furn_vars( point p ) { return frn_vars[p.x, p.y]; }; diff --git a/tests/data_vars.cpp b/tests/data_vars.cpp new file mode 100644 index 000000000000..75e341c57df8 --- /dev/null +++ b/tests/data_vars.cpp @@ -0,0 +1,195 @@ +#include "catch/catch.hpp" + +#include "data_vars.h" + +#include + +TEST_CASE("load_store_string", "[data_vars]") { + data_vars::data_set dv; + CHECK( dv.empty() ); + + dv.set("string1", "1234"); + CHECK(dv["string1"] == "1234"); + + CHECK(dv.get("string2", "default") == "default"); + + CHECK(dv.get("string3", "") == ""); + + dv.erase("string1"); + CHECK_FALSE(dv.contains("string1")); + + dv.clear(); + CHECK(dv.empty()); + + dv.set("string", ",[]\""); + CHECK(dv["string"] == ",[]\""); + +} + +TEST_CASE( "load_store_numbers", "[data_vars]" ) +{ + data_vars::data_set dv; + CHECK( dv.empty() ); + + for (int i = 0; i < 1000; i++) { + + auto x = rand(); + + dv.set("value", x); + CHECK(dv.contains("value")); + + auto i4 = dv.get("value"); + CHECK(x == i4); + + auto i8 = dv.get("value"); + CHECK(x == i8); + + auto f4 = dv.get("value"); + CHECK(x == f4); + + auto f8 = dv.get("value"); + CHECK(x == f8); + } +} + +TEST_CASE( "load_store_points", "[data_vars]" ) +{ + data_vars::data_set dv; + CHECK( dv.empty() ); + + for (int i = 0; i < 1000; i++) { + auto key = std::to_string(rand() % 10); + point p = { rand(), rand() }; + + dv.set(key, p); + + point q = dv.get(key); + + CHECK(p == q); + } +} + +TEST_CASE( "load_store_tripoints", "[data_vars]" ) +{ + data_vars::data_set dv; + CHECK( dv.empty() ); + + for (int i = 0; i < 1000; i++) { + auto key = std::to_string(rand() % 10); + tripoint p = { rand(), rand(), rand() }; + + dv.set(key, p); + + tripoint q = dv.get(key); + + CHECK(p == q); + } +} + +template +static auto gen_array(F&& fun, std::index_sequence) { + auto rand = [&](size_t i) { return fun(i); }; + return std::array{ rand(Is) ... }; +} + +TEST_CASE( "load_store_numeric_arrays", "[data_vars]" ) +{ + data_vars::data_set dv; + CHECK( dv.empty() ); + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution distrib(0, 100000); + + for (int i = 0; i < 1000; i++) { + auto key = std::to_string(rand() % 10); + auto fn = [&](size_t) { return distrib(gen); }; + auto p = gen_array(fn, std::make_index_sequence<10>()); + dv.set(key, p); + auto q = dv.get(key); + CHECK(std::ranges::equal(p,q, [](auto& lhs, auto& rhs) { return std::fabs(lhs - rhs) == 0; } )); + } + + std::uniform_int_distribution distrib2(0, 10); + + for (int i = 0; i < 1000; i++) { + auto key = std::to_string(rand() % 10); + auto p = std::vector(); + for (int j = 0; j < distrib2(gen); j++) { + p.push_back(distrib(gen)); + } + dv.set(key, p); + auto q = dv.get(key); + CHECK(std::ranges::equal(p,q, [](auto& lhs, auto& rhs) { return std::fabs(lhs - rhs) == 0; } )); + } +} + +TEST_CASE( "load_store_string_arrays", "[data_vars]" ) +{ + data_vars::data_set dv; + CHECK( dv.empty() ); + + { + auto key = std::to_string(rand() % 10); + auto p = gen_array([](size_t i) { return "item" + std::to_string(i); }, std::make_index_sequence<10>()); + dv.set(key, p); + auto q = dv.get(key); + CHECK(p == q); + } + + { + auto key = std::to_string(rand() % 10); + auto p = std::vector {",", "[", "]", ",", "\"", "[]\",", "{\"key\":1213}" }; + dv.set(key, p); + auto q = dv.get(key); + CHECK(p == q); + } +} + +TEST_CASE( "load_store_map", "[data_vars]" ) +{ + data_vars::data_set dv; + CHECK( dv.empty() ); + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution distrib(0, 100000); + + { + std::map p; + for (int i = 0; i < 10; i++) { + auto key = std::to_string(i); + auto value = distrib(gen); + p[key] = value; + } + dv.set("test", p); + auto q = dv.get("test"); + CHECK(p == q); + } +} + +TEST_CASE( "load_store_string_ids", "[data_vars]" ) +{ + data_vars::data_set dv; + CHECK( dv.empty() ); + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution distrib(0, 100000); + + struct test_obj {}; + + constexpr int num_ids = 1000000; + + std::vector> ids; + // lots of ids to make sure that "interning" map gets expanded + for( int i = 0; i < num_ids; ++i ) { + ids.push_back( string_id( "test_id" + std::to_string( i ) ) ); + } + + { + dv.set("test", ids); + auto q = dv.get("test"); + CHECK(ids == q); + } +} \ No newline at end of file From c5de6e156fb38c387e029a0c175106cb64a7b8f0 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sat, 18 Oct 2025 22:41:30 +0000 Subject: [PATCH 11/31] style(autofix.ci): automated formatting --- src/data_vars.h | 10 +-- src/data_vars_cvt.h | 96 +++++++++++++++------------- src/item.h | 2 +- tests/data_vars.cpp | 151 ++++++++++++++++++++++---------------------- 4 files changed, 136 insertions(+), 123 deletions(-) diff --git a/src/data_vars.h b/src/data_vars.h index 113026b1a721..e4654908ad50 100644 --- a/src/data_vars.h +++ b/src/data_vars.h @@ -5,7 +5,8 @@ #include "data_vars_cvt.h" -namespace data_vars { +namespace data_vars +{ template struct type_converter; @@ -33,7 +34,8 @@ class data_set } template > - Value get( const std::string &key, const Value &default_value = {}, const Conv &converter = {} ) const { + Value get( const std::string &key, const Value &default_value = {}, const Conv &converter = {} ) + const { Value val; if( !data_set::try_get( key, val, converter ) ) { return default_value; @@ -71,7 +73,7 @@ class data_set _data[name] = value; } - std::string get( const std::string &key, const char* default_value ) const { + std::string get( const std::string &key, const char *default_value ) const { std::string val; if( !try_get( key, val ) ) { return default_value; @@ -79,7 +81,7 @@ class data_set return val; } - void set( const key_type &name, const char* value ) { + void set( const key_type &name, const char *value ) { _data[name] = std::string{value}; } diff --git a/src/data_vars_cvt.h b/src/data_vars_cvt.h index 116460ebc8bc..a1b4241e8c24 100644 --- a/src/data_vars_cvt.h +++ b/src/data_vars_cvt.h @@ -43,10 +43,11 @@ template struct stream_value_io; template -static bool stream_write_values( std::ostream &os, const char delim, const First &val, const Rest &... rest ) +static bool stream_write_values( std::ostream &os, const char delim, const First &val, + const Rest &... rest ) { - constexpr auto io = detail::stream_value_io{}; - io(os, delim, val); + constexpr auto io = detail::stream_value_io {}; + io( os, delim, val ); if( os.fail() ) { return false; @@ -64,8 +65,8 @@ static bool stream_write_values( std::ostream &os, const char delim, const First template static bool stream_read_values( std::istream &is, const char delim, First &val, Rest &... rest ) { - constexpr auto io = detail::stream_value_io{}; - io(is, delim, val); + constexpr auto io = detail::stream_value_io {}; + io( is, delim, val ); if( is.fail() ) { return false; @@ -86,15 +87,17 @@ static bool stream_read_values( std::istream &is, const char delim, First &val, } template -static bool stream_write_indexed( std::ostream &os, const char delim, const T &val, Fn && getter, std::index_sequence ) +static bool stream_write_indexed( std::ostream &os, const char delim, const T &val, Fn &&getter, + std::index_sequence ) { - return detail::stream_write_values( os, delim, getter.template operator()(val)... ); + return detail::stream_write_values( os, delim, getter.template operator()( val )... ); } template -static bool stream_read_indexed( std::istream &is, const char delim, T &val, Fn && getter, std::index_sequence ) +static bool stream_read_indexed( std::istream &is, const char delim, T &val, Fn &&getter, + std::index_sequence ) { - return detail::stream_read_values( is, delim, getter.template operator()(val)... ); + return detail::stream_read_values( is, delim, getter.template operator()( val )... ); } } // namespace detail @@ -108,26 +111,26 @@ struct basic_converter { using value_type = T; - static void configure_stream(std::ios_base& ss) { + static void configure_stream( std::ios_base &ss ) { ss.imbue( std::locale::classic() ); ss.setf( std::ios_base::showpoint ); ss.setf( std::ios_base::dec, std::ostream::basefield ); } - static void configure_stream(std::ios_base& ss, const T& in_val) { + static void configure_stream( std::ios_base &ss, const T &in_val ) { configure_stream( ss ); if constexpr( std::is_floating_point_v ) { constexpr auto max_digits = std::numeric_limits::digits10; constexpr auto max_repr = pow10(); const auto float_flags = in_val >= max_repr ? std::ios_base::scientific : std::ios_base::fixed; ss.precision( max_digits ); - ss.setf(float_flags, std::ios_base::floatfield); + ss.setf( float_flags, std::ios_base::floatfield ); } } bool operator()( const T &in_val, std::string &out_val ) const { std::ostringstream os; - basic_converter::configure_stream(os, in_val); + basic_converter::configure_stream( os, in_val ); if( !detail::stream_write_values( os, ',', in_val ) ) { return false; @@ -139,7 +142,7 @@ struct basic_converter { bool operator()( const std::string &in_val, T &out_val ) const { std::istringstream is( in_val ); - basic_converter::configure_stream(is); + basic_converter::configure_stream( is ); T tmp; if( !detail::stream_read_values( is, ',', tmp ) ) { @@ -157,8 +160,7 @@ struct json_converter { // Json parser shits itself with 17 digits of precision - bool operator()( const T &in_val, std::string &out_val ) const - { + bool operator()( const T &in_val, std::string &out_val ) const { std::ostringstream os; JsonOut jsout{os}; jsout.write( in_val ); @@ -181,22 +183,24 @@ namespace detail // Fallback template struct stream_value_io { - bool operator()(std::ostream &ss, const char, const T &val) const { - if constexpr (has_ostream_write) { + bool operator()( std::ostream &ss, const char, const T &val ) const { + if constexpr( has_ostream_write ) { ss << val; return true; } else { - [](){ static_assert(f, "no matching ostream operator for type"); }(); + []() { static_assert( f, "no matching ostream operator for type" ); } + (); return false; } } - bool operator()(std::istream& ss, const char, T& val) { - if constexpr (has_istream_read) { + bool operator()( std::istream &ss, const char, T &val ) { + if constexpr( has_istream_read ) { ss >> val; return true; } else { - [](){ static_assert(f, "no matching istream operator for type"); }(); + []() { static_assert( f, "no matching istream operator for type" ); } + (); return false; } } @@ -210,13 +214,13 @@ struct stream_value_io { static constexpr std::string RESERVED_CHARS = "[],\""; - bool operator()(std::ostream &os, const char, const T &val) const { - os << std::quoted(val); + bool operator()( std::ostream &os, const char, const T &val ) const { + os << std::quoted( val ); return true; } - bool operator()(std::istream &is, const char, T &val) const { - is >> std::quoted(val); + bool operator()( std::istream &is, const char, T &val ) const { + is >> std::quoted( val ); return true; } }; @@ -225,12 +229,12 @@ struct stream_value_io { template struct stream_value_io { - bool operator()(std::ostream &ss, const char, const T &val) const { + bool operator()( std::ostream &ss, const char, const T &val ) const { ss << val; return true; } - bool operator()(std::istream &ss, const char, T &val) const { + bool operator()( std::istream &ss, const char, T &val ) const { ss >> val; return true; } @@ -240,11 +244,11 @@ struct stream_value_io { template<> struct stream_value_io { - bool operator()(std::ostream &ss, const char delim, const tripoint &val) const { - return stream_write_values(ss, delim, val.x, val.y, val.z); + bool operator()( std::ostream &ss, const char delim, const tripoint &val ) const { + return stream_write_values( ss, delim, val.x, val.y, val.z ); } - bool operator()(std::istream &ss, const char delim, tripoint &val) const { - return stream_read_values(ss, delim, val.x, val.y, val.z); + bool operator()( std::istream &ss, const char delim, tripoint &val ) const { + return stream_read_values( ss, delim, val.x, val.y, val.z ); } }; @@ -252,11 +256,11 @@ struct stream_value_io { template<> struct stream_value_io { - bool operator()(std::ostream &ss, const char delim, const point &val) const { - return stream_write_values(ss, delim, val.x, val.y); + bool operator()( std::ostream &ss, const char delim, const point &val ) const { + return stream_write_values( ss, delim, val.x, val.y ); } - bool operator()(std::istream &ss, const char delim, point &val) const { - return stream_read_values(ss, delim, val.x, val.y); + bool operator()( std::istream &ss, const char delim, point &val ) const { + return stream_read_values( ss, delim, val.x, val.y ); } }; @@ -265,27 +269,31 @@ template struct stream_value_io { struct tuple_get { template - auto& operator()(T& val) { return std::get(val); } + auto &operator()( T &val ) { return std::get( val ); } template - const auto& operator()(const T& val) { return std::get(val); } + const auto &operator()( const T &val ) { return std::get( val ); } }; - bool operator()(std::ostream &os, const char delim, const T &val) const { + bool operator()( std::ostream &os, const char delim, const T &val ) const { os << '['; - detail::stream_write_indexed(os, delim, val, tuple_get{} , std::make_index_sequence>()); + detail::stream_write_indexed( os, delim, val, tuple_get{}, + std::make_index_sequence>() ); os << ']'; return true; } - bool operator()(std::istream &is, const char delim, T &val) const { + bool operator()( std::istream &is, const char delim, T &val ) const { char c; is >> c >> std::ws; - if (c != '[') + if( c != '[' ) { return false; - detail::stream_read_indexed(is, delim, val, tuple_get{}, std::make_index_sequence>()); + } + detail::stream_read_indexed( is, delim, val, tuple_get{}, + std::make_index_sequence>() ); is >> c >> std::ws; - if (c != ']') + if( c != ']' ) { return false; + } return true; } }; diff --git a/src/item.h b/src/item.h index 6f6748e33c23..c458a8180bf8 100644 --- a/src/item.h +++ b/src/item.h @@ -1519,7 +1519,7 @@ class item : public location_visitable, public game_object // mark as [[deprecated]] // completely remove in a future update - data_vars::data_set& item_vars() { return item_vars_; } + data_vars::data_set &item_vars() { return item_vars_; } /** Adds child items to the contents of this one. */ void add_item_with_id( const itype_id &itype, int count = 1 ); diff --git a/tests/data_vars.cpp b/tests/data_vars.cpp index 75e341c57df8..49ec48aa353d 100644 --- a/tests/data_vars.cpp +++ b/tests/data_vars.cpp @@ -4,25 +4,26 @@ #include -TEST_CASE("load_store_string", "[data_vars]") { +TEST_CASE( "load_store_string", "[data_vars]" ) +{ data_vars::data_set dv; CHECK( dv.empty() ); - dv.set("string1", "1234"); - CHECK(dv["string1"] == "1234"); + dv.set( "string1", "1234" ); + CHECK( dv["string1"] == "1234" ); - CHECK(dv.get("string2", "default") == "default"); + CHECK( dv.get( "string2", "default" ) == "default" ); - CHECK(dv.get("string3", "") == ""); + CHECK( dv.get( "string3", "" ) == "" ); - dv.erase("string1"); - CHECK_FALSE(dv.contains("string1")); + dv.erase( "string1" ); + CHECK_FALSE( dv.contains( "string1" ) ); dv.clear(); - CHECK(dv.empty()); + CHECK( dv.empty() ); - dv.set("string", ",[]\""); - CHECK(dv["string"] == ",[]\""); + dv.set( "string", ",[]\"" ); + CHECK( dv["string"] == ",[]\"" ); } @@ -31,24 +32,24 @@ TEST_CASE( "load_store_numbers", "[data_vars]" ) data_vars::data_set dv; CHECK( dv.empty() ); - for (int i = 0; i < 1000; i++) { + for( int i = 0; i < 1000; i++ ) { auto x = rand(); - dv.set("value", x); - CHECK(dv.contains("value")); + dv.set( "value", x ); + CHECK( dv.contains( "value" ) ); - auto i4 = dv.get("value"); - CHECK(x == i4); + auto i4 = dv.get( "value" ); + CHECK( x == i4 ); - auto i8 = dv.get("value"); - CHECK(x == i8); + auto i8 = dv.get( "value" ); + CHECK( x == i8 ); - auto f4 = dv.get("value"); - CHECK(x == f4); + auto f4 = dv.get( "value" ); + CHECK( x == f4 ); - auto f8 = dv.get("value"); - CHECK(x == f8); + auto f8 = dv.get( "value" ); + CHECK( x == f8 ); } } @@ -57,15 +58,15 @@ TEST_CASE( "load_store_points", "[data_vars]" ) data_vars::data_set dv; CHECK( dv.empty() ); - for (int i = 0; i < 1000; i++) { - auto key = std::to_string(rand() % 10); + for( int i = 0; i < 1000; i++ ) { + auto key = std::to_string( rand() % 10 ); point p = { rand(), rand() }; - dv.set(key, p); + dv.set( key, p ); - point q = dv.get(key); + point q = dv.get( key ); - CHECK(p == q); + CHECK( p == q ); } } @@ -74,22 +75,23 @@ TEST_CASE( "load_store_tripoints", "[data_vars]" ) data_vars::data_set dv; CHECK( dv.empty() ); - for (int i = 0; i < 1000; i++) { - auto key = std::to_string(rand() % 10); + for( int i = 0; i < 1000; i++ ) { + auto key = std::to_string( rand() % 10 ); tripoint p = { rand(), rand(), rand() }; - dv.set(key, p); + dv.set( key, p ); - tripoint q = dv.get(key); + tripoint q = dv.get( key ); - CHECK(p == q); + CHECK( p == q ); } } template -static auto gen_array(F&& fun, std::index_sequence) { - auto rand = [&](size_t i) { return fun(i); }; - return std::array{ rand(Is) ... }; +static auto gen_array( F &&fun, std::index_sequence ) +{ + auto rand = [&]( size_t i ) { return fun( i ); }; + return std::array{ rand( Is ) ... }; } TEST_CASE( "load_store_numeric_arrays", "[data_vars]" ) @@ -98,29 +100,29 @@ TEST_CASE( "load_store_numeric_arrays", "[data_vars]" ) CHECK( dv.empty() ); std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_int_distribution distrib(0, 100000); - - for (int i = 0; i < 1000; i++) { - auto key = std::to_string(rand() % 10); - auto fn = [&](size_t) { return distrib(gen); }; - auto p = gen_array(fn, std::make_index_sequence<10>()); - dv.set(key, p); - auto q = dv.get(key); - CHECK(std::ranges::equal(p,q, [](auto& lhs, auto& rhs) { return std::fabs(lhs - rhs) == 0; } )); + std::mt19937 gen( rd() ); + std::uniform_int_distribution distrib( 0, 100000 ); + + for( int i = 0; i < 1000; i++ ) { + auto key = std::to_string( rand() % 10 ); + auto fn = [&]( size_t ) { return distrib( gen ); }; + auto p = gen_array( fn, std::make_index_sequence<10>() ); + dv.set( key, p ); + auto q = dv.get( key ); + CHECK( std::ranges::equal( p, q, []( auto & lhs, auto & rhs ) { return std::fabs( lhs - rhs ) == 0; } ) ); } - std::uniform_int_distribution distrib2(0, 10); + std::uniform_int_distribution distrib2( 0, 10 ); - for (int i = 0; i < 1000; i++) { - auto key = std::to_string(rand() % 10); + for( int i = 0; i < 1000; i++ ) { + auto key = std::to_string( rand() % 10 ); auto p = std::vector(); - for (int j = 0; j < distrib2(gen); j++) { - p.push_back(distrib(gen)); + for( int j = 0; j < distrib2( gen ); j++ ) { + p.push_back( distrib( gen ) ); } - dv.set(key, p); - auto q = dv.get(key); - CHECK(std::ranges::equal(p,q, [](auto& lhs, auto& rhs) { return std::fabs(lhs - rhs) == 0; } )); + dv.set( key, p ); + auto q = dv.get( key ); + CHECK( std::ranges::equal( p, q, []( auto & lhs, auto & rhs ) { return std::fabs( lhs - rhs ) == 0; } ) ); } } @@ -130,19 +132,20 @@ TEST_CASE( "load_store_string_arrays", "[data_vars]" ) CHECK( dv.empty() ); { - auto key = std::to_string(rand() % 10); - auto p = gen_array([](size_t i) { return "item" + std::to_string(i); }, std::make_index_sequence<10>()); - dv.set(key, p); - auto q = dv.get(key); - CHECK(p == q); + auto key = std::to_string( rand() % 10 ); + auto p = gen_array( []( size_t i ) { return "item" + std::to_string( i ); }, + std::make_index_sequence<10>() ); + dv.set( key, p ); + auto q = dv.get( key ); + CHECK( p == q ); } { - auto key = std::to_string(rand() % 10); + auto key = std::to_string( rand() % 10 ); auto p = std::vector {",", "[", "]", ",", "\"", "[]\",", "{\"key\":1213}" }; - dv.set(key, p); - auto q = dv.get(key); - CHECK(p == q); + dv.set( key, p ); + auto q = dv.get( key ); + CHECK( p == q ); } } @@ -152,19 +155,19 @@ TEST_CASE( "load_store_map", "[data_vars]" ) CHECK( dv.empty() ); std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_int_distribution distrib(0, 100000); + std::mt19937 gen( rd() ); + std::uniform_int_distribution distrib( 0, 100000 ); { std::map p; - for (int i = 0; i < 10; i++) { - auto key = std::to_string(i); - auto value = distrib(gen); + for( int i = 0; i < 10; i++ ) { + auto key = std::to_string( i ); + auto value = distrib( gen ); p[key] = value; } - dv.set("test", p); - auto q = dv.get("test"); - CHECK(p == q); + dv.set( "test", p ); + auto q = dv.get( "test" ); + CHECK( p == q ); } } @@ -174,8 +177,8 @@ TEST_CASE( "load_store_string_ids", "[data_vars]" ) CHECK( dv.empty() ); std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_int_distribution distrib(0, 100000); + std::mt19937 gen( rd() ); + std::uniform_int_distribution distrib( 0, 100000 ); struct test_obj {}; @@ -188,8 +191,8 @@ TEST_CASE( "load_store_string_ids", "[data_vars]" ) } { - dv.set("test", ids); - auto q = dv.get("test"); - CHECK(ids == q); + dv.set( "test", ids ); + auto q = dv.get( "test" ); + CHECK( ids == q ); } } \ No newline at end of file From ae6241789609a6a18894595b4e69cc83b89b90b0 Mon Sep 17 00:00:00 2001 From: Reisen Usagi Date: Sat, 18 Oct 2025 20:41:03 -0300 Subject: [PATCH 12/31] add merge function to data_set merge item vars to furniture on deploy_furn action marge furniture vars to item on deployed_furniture take down --- src/data_vars.h | 19 ++++-- src/data_vars_cvt.h | 96 ++++++++++++++------------- src/iexamine.cpp | 3 +- src/item.h | 2 +- src/iuse_actor.cpp | 1 + tests/data_vars.cpp | 153 ++++++++++++++++++++++---------------------- 6 files changed, 149 insertions(+), 125 deletions(-) diff --git a/src/data_vars.h b/src/data_vars.h index 113026b1a721..bae84c1a6f6f 100644 --- a/src/data_vars.h +++ b/src/data_vars.h @@ -5,7 +5,8 @@ #include "data_vars_cvt.h" -namespace data_vars { +namespace data_vars +{ template struct type_converter; @@ -33,7 +34,8 @@ class data_set } template > - Value get( const std::string &key, const Value &default_value = {}, const Conv &converter = {} ) const { + Value get( const std::string &key, const Value &default_value = {}, const Conv &converter = {} ) + const { Value val; if( !data_set::try_get( key, val, converter ) ) { return default_value; @@ -71,7 +73,7 @@ class data_set _data[name] = value; } - std::string get( const std::string &key, const char* default_value ) const { + std::string get( const std::string &key, const char *default_value ) const { std::string val; if( !try_get( key, val ) ) { return default_value; @@ -79,10 +81,19 @@ class data_set return val; } - void set( const key_type &name, const char* value ) { + void set( const key_type &name, const char *value ) { _data[name] = std::string{value}; } + void merge( const data_set &other, bool skip_existing = false ) { + for( const auto &[key, value] : other ) { + if( skip_existing && contains( key ) ) { + continue; + } + this[key] = value; + } + } + bool operator==( const data_set &other ) const { return ( _data ) == other._data; } mapped_type &operator[]( const key_type &name ) { return _data[name]; } diff --git a/src/data_vars_cvt.h b/src/data_vars_cvt.h index 116460ebc8bc..a1b4241e8c24 100644 --- a/src/data_vars_cvt.h +++ b/src/data_vars_cvt.h @@ -43,10 +43,11 @@ template struct stream_value_io; template -static bool stream_write_values( std::ostream &os, const char delim, const First &val, const Rest &... rest ) +static bool stream_write_values( std::ostream &os, const char delim, const First &val, + const Rest &... rest ) { - constexpr auto io = detail::stream_value_io{}; - io(os, delim, val); + constexpr auto io = detail::stream_value_io {}; + io( os, delim, val ); if( os.fail() ) { return false; @@ -64,8 +65,8 @@ static bool stream_write_values( std::ostream &os, const char delim, const First template static bool stream_read_values( std::istream &is, const char delim, First &val, Rest &... rest ) { - constexpr auto io = detail::stream_value_io{}; - io(is, delim, val); + constexpr auto io = detail::stream_value_io {}; + io( is, delim, val ); if( is.fail() ) { return false; @@ -86,15 +87,17 @@ static bool stream_read_values( std::istream &is, const char delim, First &val, } template -static bool stream_write_indexed( std::ostream &os, const char delim, const T &val, Fn && getter, std::index_sequence ) +static bool stream_write_indexed( std::ostream &os, const char delim, const T &val, Fn &&getter, + std::index_sequence ) { - return detail::stream_write_values( os, delim, getter.template operator()(val)... ); + return detail::stream_write_values( os, delim, getter.template operator()( val )... ); } template -static bool stream_read_indexed( std::istream &is, const char delim, T &val, Fn && getter, std::index_sequence ) +static bool stream_read_indexed( std::istream &is, const char delim, T &val, Fn &&getter, + std::index_sequence ) { - return detail::stream_read_values( is, delim, getter.template operator()(val)... ); + return detail::stream_read_values( is, delim, getter.template operator()( val )... ); } } // namespace detail @@ -108,26 +111,26 @@ struct basic_converter { using value_type = T; - static void configure_stream(std::ios_base& ss) { + static void configure_stream( std::ios_base &ss ) { ss.imbue( std::locale::classic() ); ss.setf( std::ios_base::showpoint ); ss.setf( std::ios_base::dec, std::ostream::basefield ); } - static void configure_stream(std::ios_base& ss, const T& in_val) { + static void configure_stream( std::ios_base &ss, const T &in_val ) { configure_stream( ss ); if constexpr( std::is_floating_point_v ) { constexpr auto max_digits = std::numeric_limits::digits10; constexpr auto max_repr = pow10(); const auto float_flags = in_val >= max_repr ? std::ios_base::scientific : std::ios_base::fixed; ss.precision( max_digits ); - ss.setf(float_flags, std::ios_base::floatfield); + ss.setf( float_flags, std::ios_base::floatfield ); } } bool operator()( const T &in_val, std::string &out_val ) const { std::ostringstream os; - basic_converter::configure_stream(os, in_val); + basic_converter::configure_stream( os, in_val ); if( !detail::stream_write_values( os, ',', in_val ) ) { return false; @@ -139,7 +142,7 @@ struct basic_converter { bool operator()( const std::string &in_val, T &out_val ) const { std::istringstream is( in_val ); - basic_converter::configure_stream(is); + basic_converter::configure_stream( is ); T tmp; if( !detail::stream_read_values( is, ',', tmp ) ) { @@ -157,8 +160,7 @@ struct json_converter { // Json parser shits itself with 17 digits of precision - bool operator()( const T &in_val, std::string &out_val ) const - { + bool operator()( const T &in_val, std::string &out_val ) const { std::ostringstream os; JsonOut jsout{os}; jsout.write( in_val ); @@ -181,22 +183,24 @@ namespace detail // Fallback template struct stream_value_io { - bool operator()(std::ostream &ss, const char, const T &val) const { - if constexpr (has_ostream_write) { + bool operator()( std::ostream &ss, const char, const T &val ) const { + if constexpr( has_ostream_write ) { ss << val; return true; } else { - [](){ static_assert(f, "no matching ostream operator for type"); }(); + []() { static_assert( f, "no matching ostream operator for type" ); } + (); return false; } } - bool operator()(std::istream& ss, const char, T& val) { - if constexpr (has_istream_read) { + bool operator()( std::istream &ss, const char, T &val ) { + if constexpr( has_istream_read ) { ss >> val; return true; } else { - [](){ static_assert(f, "no matching istream operator for type"); }(); + []() { static_assert( f, "no matching istream operator for type" ); } + (); return false; } } @@ -210,13 +214,13 @@ struct stream_value_io { static constexpr std::string RESERVED_CHARS = "[],\""; - bool operator()(std::ostream &os, const char, const T &val) const { - os << std::quoted(val); + bool operator()( std::ostream &os, const char, const T &val ) const { + os << std::quoted( val ); return true; } - bool operator()(std::istream &is, const char, T &val) const { - is >> std::quoted(val); + bool operator()( std::istream &is, const char, T &val ) const { + is >> std::quoted( val ); return true; } }; @@ -225,12 +229,12 @@ struct stream_value_io { template struct stream_value_io { - bool operator()(std::ostream &ss, const char, const T &val) const { + bool operator()( std::ostream &ss, const char, const T &val ) const { ss << val; return true; } - bool operator()(std::istream &ss, const char, T &val) const { + bool operator()( std::istream &ss, const char, T &val ) const { ss >> val; return true; } @@ -240,11 +244,11 @@ struct stream_value_io { template<> struct stream_value_io { - bool operator()(std::ostream &ss, const char delim, const tripoint &val) const { - return stream_write_values(ss, delim, val.x, val.y, val.z); + bool operator()( std::ostream &ss, const char delim, const tripoint &val ) const { + return stream_write_values( ss, delim, val.x, val.y, val.z ); } - bool operator()(std::istream &ss, const char delim, tripoint &val) const { - return stream_read_values(ss, delim, val.x, val.y, val.z); + bool operator()( std::istream &ss, const char delim, tripoint &val ) const { + return stream_read_values( ss, delim, val.x, val.y, val.z ); } }; @@ -252,11 +256,11 @@ struct stream_value_io { template<> struct stream_value_io { - bool operator()(std::ostream &ss, const char delim, const point &val) const { - return stream_write_values(ss, delim, val.x, val.y); + bool operator()( std::ostream &ss, const char delim, const point &val ) const { + return stream_write_values( ss, delim, val.x, val.y ); } - bool operator()(std::istream &ss, const char delim, point &val) const { - return stream_read_values(ss, delim, val.x, val.y); + bool operator()( std::istream &ss, const char delim, point &val ) const { + return stream_read_values( ss, delim, val.x, val.y ); } }; @@ -265,27 +269,31 @@ template struct stream_value_io { struct tuple_get { template - auto& operator()(T& val) { return std::get(val); } + auto &operator()( T &val ) { return std::get( val ); } template - const auto& operator()(const T& val) { return std::get(val); } + const auto &operator()( const T &val ) { return std::get( val ); } }; - bool operator()(std::ostream &os, const char delim, const T &val) const { + bool operator()( std::ostream &os, const char delim, const T &val ) const { os << '['; - detail::stream_write_indexed(os, delim, val, tuple_get{} , std::make_index_sequence>()); + detail::stream_write_indexed( os, delim, val, tuple_get{}, + std::make_index_sequence>() ); os << ']'; return true; } - bool operator()(std::istream &is, const char delim, T &val) const { + bool operator()( std::istream &is, const char delim, T &val ) const { char c; is >> c >> std::ws; - if (c != '[') + if( c != '[' ) { return false; - detail::stream_read_indexed(is, delim, val, tuple_get{}, std::make_index_sequence>()); + } + detail::stream_read_indexed( is, delim, val, tuple_get{}, + std::make_index_sequence>() ); is >> c >> std::ws; - if (c != ']') + if( c != ']' ) { return false; + } return true; } }; diff --git a/src/iexamine.cpp b/src/iexamine.cpp index 53c17cdebaa9..9ba59faf12f0 100644 --- a/src/iexamine.cpp +++ b/src/iexamine.cpp @@ -1303,7 +1303,8 @@ void iexamine::deployed_furniture( player &p, const tripoint &pos ) p.add_msg_if_player( m_info, _( "You take down the %s." ), here.furn( pos ).obj().name() ); const auto furn_item = here.furn( pos ).obj().deployed_item; - here.add_item_or_charges( pos, item::spawn( furn_item, calendar::turn ) ); + const auto item = here.add_item_or_charges( pos, item::spawn( furn_item, calendar::turn ) ); + item->item_vars().merge( *here.furn_vars( pos ) ); here.furn_set( pos, f_null ); } diff --git a/src/item.h b/src/item.h index 6f6748e33c23..c458a8180bf8 100644 --- a/src/item.h +++ b/src/item.h @@ -1519,7 +1519,7 @@ class item : public location_visitable, public game_object // mark as [[deprecated]] // completely remove in a future update - data_vars::data_set& item_vars() { return item_vars_; } + data_vars::data_set &item_vars() { return item_vars_; } /** Adds child items to the contents of this one. */ void add_item_with_id( const itype_id &itype, int count = 1 ); diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index 540b50e8d08d..d9faa3e0990d 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -1412,6 +1412,7 @@ int deploy_furn_actor::use( player &p, item &it, bool t, const tripoint &pos ) c } here.furn_set( pnt, furn_type ); + here.furn_vars( pnt )->merge( it.item_vars() ); p.mod_moves( to_turns( 2_seconds ) ); return 1; } diff --git a/tests/data_vars.cpp b/tests/data_vars.cpp index 75e341c57df8..618008f86320 100644 --- a/tests/data_vars.cpp +++ b/tests/data_vars.cpp @@ -4,25 +4,26 @@ #include -TEST_CASE("load_store_string", "[data_vars]") { +TEST_CASE( "load_store_string", "[data_vars]" ) +{ data_vars::data_set dv; CHECK( dv.empty() ); - dv.set("string1", "1234"); - CHECK(dv["string1"] == "1234"); + dv.set( "string1", "1234" ); + CHECK( dv["string1"] == "1234" ); - CHECK(dv.get("string2", "default") == "default"); + CHECK( dv.get( "string2", "default" ) == "default" ); - CHECK(dv.get("string3", "") == ""); + CHECK( dv.get( "string3", "" ) == "" ); - dv.erase("string1"); - CHECK_FALSE(dv.contains("string1")); + dv.erase( "string1" ); + CHECK_FALSE( dv.contains( "string1" ) ); dv.clear(); - CHECK(dv.empty()); + CHECK( dv.empty() ); - dv.set("string", ",[]\""); - CHECK(dv["string"] == ",[]\""); + dv.set( "string", ",[]\"" ); + CHECK( dv["string"] == ",[]\"" ); } @@ -31,24 +32,24 @@ TEST_CASE( "load_store_numbers", "[data_vars]" ) data_vars::data_set dv; CHECK( dv.empty() ); - for (int i = 0; i < 1000; i++) { + for( int i = 0; i < 1000; i++ ) { auto x = rand(); - dv.set("value", x); - CHECK(dv.contains("value")); + dv.set( "value", x ); + CHECK( dv.contains( "value" ) ); - auto i4 = dv.get("value"); - CHECK(x == i4); + auto i4 = dv.get( "value" ); + CHECK( x == i4 ); - auto i8 = dv.get("value"); - CHECK(x == i8); + auto i8 = dv.get( "value" ); + CHECK( x == i8 ); - auto f4 = dv.get("value"); - CHECK(x == f4); + auto f4 = dv.get( "value" ); + CHECK( x == f4 ); - auto f8 = dv.get("value"); - CHECK(x == f8); + auto f8 = dv.get( "value" ); + CHECK( x == f8 ); } } @@ -57,15 +58,15 @@ TEST_CASE( "load_store_points", "[data_vars]" ) data_vars::data_set dv; CHECK( dv.empty() ); - for (int i = 0; i < 1000; i++) { - auto key = std::to_string(rand() % 10); + for( int i = 0; i < 1000; i++ ) { + auto key = std::to_string( rand() % 10 ); point p = { rand(), rand() }; - dv.set(key, p); + dv.set( key, p ); - point q = dv.get(key); + point q = dv.get( key ); - CHECK(p == q); + CHECK( p == q ); } } @@ -74,22 +75,23 @@ TEST_CASE( "load_store_tripoints", "[data_vars]" ) data_vars::data_set dv; CHECK( dv.empty() ); - for (int i = 0; i < 1000; i++) { - auto key = std::to_string(rand() % 10); + for( int i = 0; i < 1000; i++ ) { + auto key = std::to_string( rand() % 10 ); tripoint p = { rand(), rand(), rand() }; - dv.set(key, p); + dv.set( key, p ); - tripoint q = dv.get(key); + tripoint q = dv.get( key ); - CHECK(p == q); + CHECK( p == q ); } } template -static auto gen_array(F&& fun, std::index_sequence) { - auto rand = [&](size_t i) { return fun(i); }; - return std::array{ rand(Is) ... }; +static auto gen_array( F &&fun, std::index_sequence ) +{ + auto rand = [&]( size_t i ) { return fun( i ); }; + return std::array{ rand( Is ) ... }; } TEST_CASE( "load_store_numeric_arrays", "[data_vars]" ) @@ -98,29 +100,29 @@ TEST_CASE( "load_store_numeric_arrays", "[data_vars]" ) CHECK( dv.empty() ); std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_int_distribution distrib(0, 100000); - - for (int i = 0; i < 1000; i++) { - auto key = std::to_string(rand() % 10); - auto fn = [&](size_t) { return distrib(gen); }; - auto p = gen_array(fn, std::make_index_sequence<10>()); - dv.set(key, p); - auto q = dv.get(key); - CHECK(std::ranges::equal(p,q, [](auto& lhs, auto& rhs) { return std::fabs(lhs - rhs) == 0; } )); + std::mt19937 gen( rd() ); + std::uniform_int_distribution distrib( 0, 100000 ); + + for( int i = 0; i < 1000; i++ ) { + auto key = std::to_string( rand() % 10 ); + auto fn = [&]( size_t ) { return distrib( gen ); }; + auto p = gen_array( fn, std::make_index_sequence<10>() ); + dv.set( key, p ); + auto q = dv.get( key ); + CHECK( std::ranges::equal( p, q, []( auto & lhs, auto & rhs ) { return std::fabs( lhs - rhs ) == 0; } ) ); } - std::uniform_int_distribution distrib2(0, 10); + std::uniform_int_distribution distrib2( 0, 10 ); - for (int i = 0; i < 1000; i++) { - auto key = std::to_string(rand() % 10); + for( int i = 0; i < 1000; i++ ) { + auto key = std::to_string( rand() % 10 ); auto p = std::vector(); - for (int j = 0; j < distrib2(gen); j++) { - p.push_back(distrib(gen)); + for( int j = 0; j < distrib2( gen ); j++ ) { + p.push_back( distrib( gen ) ); } - dv.set(key, p); - auto q = dv.get(key); - CHECK(std::ranges::equal(p,q, [](auto& lhs, auto& rhs) { return std::fabs(lhs - rhs) == 0; } )); + dv.set( key, p ); + auto q = dv.get( key ); + CHECK( std::ranges::equal( p, q, []( auto & lhs, auto & rhs ) { return std::fabs( lhs - rhs ) == 0; } ) ); } } @@ -130,19 +132,20 @@ TEST_CASE( "load_store_string_arrays", "[data_vars]" ) CHECK( dv.empty() ); { - auto key = std::to_string(rand() % 10); - auto p = gen_array([](size_t i) { return "item" + std::to_string(i); }, std::make_index_sequence<10>()); - dv.set(key, p); - auto q = dv.get(key); - CHECK(p == q); + auto key = std::to_string( rand() % 10 ); + auto p = gen_array( []( size_t i ) { return "item" + std::to_string( i ); }, + std::make_index_sequence<10>() ); + dv.set( key, p ); + auto q = dv.get( key ); + CHECK( p == q ); } { - auto key = std::to_string(rand() % 10); + auto key = std::to_string( rand() % 10 ); auto p = std::vector {",", "[", "]", ",", "\"", "[]\",", "{\"key\":1213}" }; - dv.set(key, p); - auto q = dv.get(key); - CHECK(p == q); + dv.set( key, p ); + auto q = dv.get( key ); + CHECK( p == q ); } } @@ -152,19 +155,19 @@ TEST_CASE( "load_store_map", "[data_vars]" ) CHECK( dv.empty() ); std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_int_distribution distrib(0, 100000); + std::mt19937 gen( rd() ); + std::uniform_int_distribution distrib( 0, 100000 ); { std::map p; - for (int i = 0; i < 10; i++) { - auto key = std::to_string(i); - auto value = distrib(gen); + for( int i = 0; i < 10; i++ ) { + auto key = std::to_string( i ); + auto value = distrib( gen ); p[key] = value; } - dv.set("test", p); - auto q = dv.get("test"); - CHECK(p == q); + dv.set( "test", p ); + auto q = dv.get( "test" ); + CHECK( p == q ); } } @@ -174,12 +177,12 @@ TEST_CASE( "load_store_string_ids", "[data_vars]" ) CHECK( dv.empty() ); std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_int_distribution distrib(0, 100000); + std::mt19937 gen( rd() ); + std::uniform_int_distribution distrib( 0, 100000 ); struct test_obj {}; - constexpr int num_ids = 1000000; + constexpr int num_ids = 1000000; std::vector> ids; // lots of ids to make sure that "interning" map gets expanded @@ -188,8 +191,8 @@ TEST_CASE( "load_store_string_ids", "[data_vars]" ) } { - dv.set("test", ids); - auto q = dv.get("test"); - CHECK(ids == q); + dv.set( "test", ids ); + auto q = dv.get( "test" ); + CHECK( ids == q ); } } \ No newline at end of file From 934a9d829587b530c32e4fffaeb218b7e22d5b2b Mon Sep 17 00:00:00 2001 From: Reisen Usagi Date: Wed, 22 Oct 2025 09:57:05 -0300 Subject: [PATCH 13/31] fix merge function --- src/data_vars.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data_vars.h b/src/data_vars.h index bae84c1a6f6f..7827852665d7 100644 --- a/src/data_vars.h +++ b/src/data_vars.h @@ -90,7 +90,7 @@ class data_set if( skip_existing && contains( key ) ) { continue; } - this[key] = value; + set(key, value); } } From 5ce4f42166f7e8bbe04d2ae2472ea204b8ffc635 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Wed, 22 Oct 2025 13:01:42 +0000 Subject: [PATCH 14/31] style(autofix.ci): automated formatting --- src/data_vars.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data_vars.h b/src/data_vars.h index 7827852665d7..df6f3f1a4bb1 100644 --- a/src/data_vars.h +++ b/src/data_vars.h @@ -90,7 +90,7 @@ class data_set if( skip_existing && contains( key ) ) { continue; } - set(key, value); + set( key, value ); } } From 6d18f1075e8324fbbe3113a3d2c499713b3873d4 Mon Sep 17 00:00:00 2001 From: Reisen Usagi Date: Wed, 22 Oct 2025 10:37:54 -0300 Subject: [PATCH 15/31] remove data_vars basic_converter in favor of json_converter --- src/data_vars_cvt.h | 273 +------------------------------------------- 1 file changed, 1 insertion(+), 272 deletions(-) diff --git a/src/data_vars_cvt.h b/src/data_vars_cvt.h index a1b4241e8c24..c80c46fe313c 100644 --- a/src/data_vars_cvt.h +++ b/src/data_vars_cvt.h @@ -10,156 +10,10 @@ namespace data_vars { -namespace detail -{ -// Concepts - -template -concept has_istream_read = requires( T &in_val, std::ostream &os ) -{ - { os << in_val } - -> std::convertible_to; -}; - -template -concept has_ostream_write = requires( T &in_val, std::istream &is ) -{ - { is >> in_val } - -> std::convertible_to; -}; - -template -concept is_reference = std::is_reference_v; - -template -concept is_numeric = std::disjunction_v, std::is_integral>; - -template -concept is_tuple_like = requires { typename std::tuple_size::type; }; - -// Helpers - -template -struct stream_value_io; - -template -static bool stream_write_values( std::ostream &os, const char delim, const First &val, - const Rest &... rest ) -{ - constexpr auto io = detail::stream_value_io {}; - io( os, delim, val ); - - if( os.fail() ) { - return false; - } - os.clear(); - - if constexpr( sizeof...( rest ) == 0 ) { - return true; - } else { - os << delim; - return detail::stream_write_values( os, delim, rest... ); - } -} - -template -static bool stream_read_values( std::istream &is, const char delim, First &val, Rest &... rest ) -{ - constexpr auto io = detail::stream_value_io {}; - io( is, delim, val ); - - if( is.fail() ) { - return false; - } - is.clear(); - - if constexpr( sizeof...( rest ) == 0 ) { - is >> std::ws; - return true; - } else { - char c; - is >> std::ws >> c; - if( c != delim ) { - return false; - } - return detail::stream_read_values( is, delim, rest... ); - } -} - -template -static bool stream_write_indexed( std::ostream &os, const char delim, const T &val, Fn &&getter, - std::index_sequence ) -{ - return detail::stream_write_values( os, delim, getter.template operator()( val )... ); -} - -template -static bool stream_read_indexed( std::istream &is, const char delim, T &val, Fn &&getter, - std::index_sequence ) -{ - return detail::stream_read_values( is, delim, getter.template operator()( val )... ); -} - -} // namespace detail - -// Converters - -namespace converters -{ -template -struct basic_converter { - - using value_type = T; - - static void configure_stream( std::ios_base &ss ) { - ss.imbue( std::locale::classic() ); - ss.setf( std::ios_base::showpoint ); - ss.setf( std::ios_base::dec, std::ostream::basefield ); - } - - static void configure_stream( std::ios_base &ss, const T &in_val ) { - configure_stream( ss ); - if constexpr( std::is_floating_point_v ) { - constexpr auto max_digits = std::numeric_limits::digits10; - constexpr auto max_repr = pow10(); - const auto float_flags = in_val >= max_repr ? std::ios_base::scientific : std::ios_base::fixed; - ss.precision( max_digits ); - ss.setf( float_flags, std::ios_base::floatfield ); - } - } - - bool operator()( const T &in_val, std::string &out_val ) const { - std::ostringstream os; - basic_converter::configure_stream( os, in_val ); - - if( !detail::stream_write_values( os, ',', in_val ) ) { - return false; - } - - out_val = os.str(); - return true; - }; - - bool operator()( const std::string &in_val, T &out_val ) const { - std::istringstream is( in_val ); - basic_converter::configure_stream( is ); - - T tmp; - if( !detail::stream_read_values( is, ',', tmp ) ) { - return false; - } - - out_val = tmp; - return true; - } -}; - template struct json_converter { using value_type = T; - // Json parser shits itself with 17 digits of precision - bool operator()( const T &in_val, std::string &out_val ) const { std::ostringstream os; JsonOut jsout{os}; @@ -175,134 +29,9 @@ struct json_converter { } }; -} // namespace converters - -namespace detail -{ - -// Fallback -template -struct stream_value_io { - bool operator()( std::ostream &ss, const char, const T &val ) const { - if constexpr( has_ostream_write ) { - ss << val; - return true; - } else { - []() { static_assert( f, "no matching ostream operator for type" ); } - (); - return false; - } - } - - bool operator()( std::istream &ss, const char, T &val ) { - if constexpr( has_istream_read ) { - ss >> val; - return true; - } else { - []() { static_assert( f, "no matching istream operator for type" ); } - (); - return false; - } - } -}; - -// String - -template<> -struct stream_value_io { - using T = std::string; - - static constexpr std::string RESERVED_CHARS = "[],\""; - - bool operator()( std::ostream &os, const char, const T &val ) const { - os << std::quoted( val ); - return true; - } - - bool operator()( std::istream &is, const char, T &val ) const { - is >> std::quoted( val ); - return true; - } -}; - -// Numeric - -template -struct stream_value_io { - bool operator()( std::ostream &ss, const char, const T &val ) const { - ss << val; - return true; - } - - bool operator()( std::istream &ss, const char, T &val ) const { - ss >> val; - return true; - } -}; - -// Tripoint - -template<> -struct stream_value_io { - bool operator()( std::ostream &ss, const char delim, const tripoint &val ) const { - return stream_write_values( ss, delim, val.x, val.y, val.z ); - } - bool operator()( std::istream &ss, const char delim, tripoint &val ) const { - return stream_read_values( ss, delim, val.x, val.y, val.z ); - } -}; - -// Point - -template<> -struct stream_value_io { - bool operator()( std::ostream &ss, const char delim, const point &val ) const { - return stream_write_values( ss, delim, val.x, val.y ); - } - bool operator()( std::istream &ss, const char delim, point &val ) const { - return stream_read_values( ss, delim, val.x, val.y ); - } -}; - -// Tuple-Like -template -struct stream_value_io { - struct tuple_get { - template - auto &operator()( T &val ) { return std::get( val ); } - - template - const auto &operator()( const T &val ) { return std::get( val ); } - }; - - bool operator()( std::ostream &os, const char delim, const T &val ) const { - os << '['; - detail::stream_write_indexed( os, delim, val, tuple_get{}, - std::make_index_sequence>() ); - os << ']'; - return true; - } - bool operator()( std::istream &is, const char delim, T &val ) const { - char c; - is >> c >> std::ws; - if( c != '[' ) { - return false; - } - detail::stream_read_indexed( is, delim, val, tuple_get{}, - std::make_index_sequence>() ); - is >> c >> std::ws; - if( c != ']' ) { - return false; - } - return true; - } -}; - -} // namespace detail - template struct type_converter { - using type = converters::json_converter; + using type = json_converter; }; } // namespace data_vars From 79f2c0da9e08464eb90a9d3389dc3120c02b2830 Mon Sep 17 00:00:00 2001 From: Reisen Usagi Date: Wed, 22 Oct 2025 10:53:32 -0300 Subject: [PATCH 16/31] remove unused using type from array2d --- src/array2d.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/array2d.h b/src/array2d.h index 0d02f6d1a6ec..6ee149e25808 100644 --- a/src/array2d.h +++ b/src/array2d.h @@ -67,8 +67,6 @@ struct array2d { struct const_iterator; struct index; - using storage = std::array; - using index_type = index; using size_type = size_t; using value_type = T; From fc023d247ef8a6e6df78deb57c288a349fb669d9 Mon Sep 17 00:00:00 2001 From: Reisen Usagi Date: Wed, 22 Oct 2025 13:03:50 -0300 Subject: [PATCH 17/31] make msvc happy? --- src/array2d.h | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/array2d.h b/src/array2d.h index 6ee149e25808..4ca7c8744e51 100644 --- a/src/array2d.h +++ b/src/array2d.h @@ -156,6 +156,9 @@ struct array2d { --index; return tmp; } + + bool operator== ( const sentinel & ) const { return index.px >= SizeX || index.py >= SizeY; }; + bool operator== ( const iterator &other ) const { return index == other.index && arr == other.arr; }; }; struct const_iterator { @@ -195,9 +198,16 @@ struct array2d { --index; return tmp; } + + bool operator== ( const sentinel & ) const { return index.px >= SizeX || index.py >= SizeY; }; + bool operator== ( const iterator &other ) const { return index == other.index && arr == other.arr; }; + bool operator== ( const const_iterator &other ) const { return index == other.index && arr == other.arr; }; }; - struct sentinel {}; + struct sentinel { + bool operator== ( const sentinel & ) { return true; } + bool operator!= ( const sentinel & ) { return false; } + }; array2d() = default; array2d( const array2d & ) = default; @@ -248,15 +258,7 @@ struct array2d { friend void swap( array2d &lhs, array2d &rhs ) noexcept { std::swap( lhs._data, rhs._data ); } - friend bool operator== ( const array2d &lhs, const array2d &rhs ) { return lhs.data() == rhs.data() && lhs.size() == rhs.size(); } - - friend bool operator== ( const sentinel &, const sentinel & ) { return true; } - friend bool operator== ( const iterator &a, const sentinel & ) { return a.index.px >= SizeX || a.index.py >= SizeY; }; - friend bool operator== ( const const_iterator &a, const sentinel & ) { return a.index.px >= SizeX || a.index.py >= SizeY; }; - - friend bool operator== ( const iterator &a, const iterator &b ) { return a.index == b.index && a.arr == b.arr; }; - friend bool operator== ( const const_iterator &a, const const_iterator &b ) { return a.index == b.index && a.arr == b.arr; }; - friend bool operator== ( const const_iterator &a, const iterator &b ) { return a.index == b.index && a.arr == b.arr; }; + bool operator== ( const array2d &other ) const { return data() == other.data() && size() == other.size(); } private: std::array, SizeY> _data; }; From 6490e356f568e1f66a55b6bab167e824afec5737 Mon Sep 17 00:00:00 2001 From: Reisen Usagi Date: Wed, 22 Oct 2025 16:53:44 -0300 Subject: [PATCH 18/31] change integer types on tests so msvc doesn't complain --- tests/data_vars.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/data_vars.cpp b/tests/data_vars.cpp index 618008f86320..dfc05a1f6982 100644 --- a/tests/data_vars.cpp +++ b/tests/data_vars.cpp @@ -39,10 +39,10 @@ TEST_CASE( "load_store_numbers", "[data_vars]" ) dv.set( "value", x ); CHECK( dv.contains( "value" ) ); - auto i4 = dv.get( "value" ); + auto i4 = dv.get( "value" ); CHECK( x == i4 ); - auto i8 = dv.get( "value" ); + auto i8 = dv.get( "value" ); CHECK( x == i8 ); auto f4 = dv.get( "value" ); From 0354aabd4264b56d17e73e697f360ec2ff252262 Mon Sep 17 00:00:00 2001 From: Reisen Usagi Date: Wed, 22 Oct 2025 19:03:41 -0300 Subject: [PATCH 19/31] store furniture / terrain vars on-demand on an unordered map instead of inside maptile_soa to save memory. --- src/savegame_json.cpp | 84 ++++++++++++++++++++----------------------- src/submap.cpp | 21 +++++++---- src/submap.h | 46 ++++++++++++++---------- 3 files changed, 81 insertions(+), 70 deletions(-) diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index 94fde98e4970..7ee261154ad7 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -3812,34 +3812,6 @@ void submap::store( JsonOut &jsout ) const } jsout.end_array(); - jsout.member( "furniture_vars" ); - jsout.start_array(); - for( int j = 0; j < SEEY; j++ ) { - for( int i = 0; i < SEEX; i++ ) { - if( frn_vars[i, j].empty() ) { - continue; - } - jsout.write( i ); - jsout.write( j ); - jsout.write( frn_vars[i, j] ); - } - } - jsout.end_array(); - - jsout.member( "terrain_vars" ); - jsout.start_array(); - for( int j = 0; j < SEEY; j++ ) { - for( int i = 0; i < SEEX; i++ ) { - if( ter_vars[i, j].empty() ) { - continue; - } - jsout.write( i ); - jsout.write( j ); - jsout.write( ter_vars[i, j] ); - } - } - jsout.end_array(); - jsout.member( "traps" ); jsout.start_array(); for( int j = 0; j < SEEY; j++ ) { @@ -3956,6 +3928,28 @@ void submap::store( JsonOut &jsout ) const pr.second.serialize( jsout ); } jsout.end_array(); + + jsout.member( "furniture_vars" ); + jsout.start_array(); + for( const auto &[key, value] : frn_vars ) { + if( value.empty() ) { + continue; + } + jsout.write( key ); + jsout.write( value ); + } + jsout.end_array(); + + jsout.member( "terrain_vars" ); + jsout.start_array(); + for( const auto &[key, value] : ter_vars ) { + if( value.empty() ) { + continue; + } + jsout.write( key ); + jsout.write( value ); + } + jsout.end_array(); } void submap::load( JsonIn &jsin, const std::string &member_name, int version, @@ -4049,24 +4043,6 @@ void submap::load( JsonIn &jsin, const std::string &member_name, int version, it.push_back( std::move( item ) ); } } - } else if( member_name == "furniture_vars" ) { - jsin.start_array(); - while( !jsin.end_array() ) { - int i = jsin.get_int(); - int j = jsin.get_int(); - data_vars::data_set vars; - jsin.read( vars ); - frn_vars[i, j] = std::move( vars ); - } - } else if( member_name == "terrain_vars" ) { - jsin.start_array(); - while( !jsin.end_array() ) { - int i = jsin.get_int(); - int j = jsin.get_int(); - data_vars::data_set vars; - jsin.read( vars ); - ter_vars[i, j] = std::move( vars ); - } } else if( member_name == "traps" ) { jsin.start_array(); while( !jsin.end_array() ) { @@ -4215,6 +4191,22 @@ void submap::load( JsonIn &jsin, const std::string &member_name, int version, jsin.read( p ); active_furniture[p].deserialize( jsin ); } + } else if( member_name == "furniture_vars" ) { + jsin.start_array(); + while( !jsin.end_array() ) { + point loc; + jsin.read( loc ); + auto &vars = frn_vars[loc]; + jsin.read( vars ); + } + } else if( member_name == "terrain_vars" ) { + jsin.start_array(); + while( !jsin.end_array() ) { + point loc; + jsin.read( loc ); + auto &vars = ter_vars[loc]; + jsin.read( vars ); + } } else { jsin.skip_value(); } diff --git a/src/submap.cpp b/src/submap.cpp index 2490eff07f62..54267a08a7c3 100644 --- a/src/submap.cpp +++ b/src/submap.cpp @@ -13,6 +13,7 @@ #include "vehicle.h" #include "vehicle_part.h" +const data_vars::data_set submap::EMPTY_VARS{}; template void maptile_soa::swap_soa_tile( point p1, point p2 ) @@ -26,8 +27,6 @@ void maptile_soa::swap_soa_tile( point p1, point p2 ) swap( fld[p1.x, p1.y], fld[p2.x, p2.y] ); swap( trp[p1.x, p1.y], trp[p2.x, p2.y] ); swap( rad[p1.x, p1.y], rad[p2.x, p2.y] ); - swap( frn_vars[p1.x, p1.y], frn_vars[p2.x, p2.y] ); - swap( ter_vars[p1.x, p1.y], ter_vars[p2.x, p2.y] ); } void submap::swap( submap &first, submap &second ) noexcept @@ -40,8 +39,6 @@ void submap::swap( submap &first, submap &second ) noexcept swap( first.fld, second.fld ); swap( first.trp, second.trp ); swap( first.rad, second.rad ); - swap( first.frn_vars, second.frn_vars ); - swap( first.ter_vars, second.ter_vars ); swap( first.is_uniform, second.is_uniform ); swap( first.active_items, second.active_items ); swap( first.field_count, second.field_count ); @@ -55,6 +52,8 @@ void submap::swap( submap &first, submap &second ) noexcept swap( first.legacy_computer, second.legacy_computer ); swap( first.temperature, second.temperature ); swap( first.cosmetics, second.cosmetics ); + swap( first.frn_vars, second.frn_vars ); + swap( first.ter_vars, second.ter_vars ); // TODO: Check if its effect is the same as // swap( first.itm, second.itm ); @@ -82,8 +81,6 @@ maptile_soa::maptile_soa( tripoint offset ) , fld() , trp( tr_null ) , rad( 0 ) - , ter_vars() - , frn_vars() { } @@ -391,4 +388,16 @@ void submap::rotate( int turns ) rot_active_furn.emplace( point_sm_ms( rotate_point( elem.first.raw() ) ), elem.second ); } active_furniture = rot_active_furn; + + std::unordered_map rot_frn_vars; + for( auto &elem : frn_vars ) { + rot_frn_vars.emplace( rotate_point( elem.first ), elem.second ); + } + frn_vars = rot_frn_vars; + + std::unordered_map rot_ter_vars; + for( auto &elem : ter_vars ) { + rot_ter_vars.emplace( rotate_point( elem.first ), elem.second ); + } + ter_vars = rot_ter_vars; } diff --git a/src/submap.h b/src/submap.h index a6947547f3f8..ad09971255b9 100644 --- a/src/submap.h +++ b/src/submap.h @@ -79,8 +79,6 @@ struct maptile_soa { array2d< field, sx, sy > fld; // Field on each square array2d< trap_id, sx, sy > trp; // Trap on each square array2d< int, sx, sy > rad; // Irradiation of each square - array2d< data_vars::data_set, sx, sy > ter_vars; // Terrain vars - array2d< data_vars::data_set, sx, sy > frn_vars; // Furniture vars void swap_soa_tile( point p1, point p2 ); }; @@ -111,20 +109,20 @@ class submap : maptile_soa void set_furn( point p, furn_id furn ) { is_uniform = false; frn[p.x, p.y] = furn; - // Reset furniture vars on clear - if( furn == f_null ) { - frn_vars[p.x, p.y].clear(); + if( furn != f_null ) { + return; } + // Reset furniture vars on clear + frn_vars.erase( p ); } void set_all_furn( const furn_id &furn ) { frn.reset( furn ); - // Reset furniture vars on clear - if( furn == f_null ) { - for( auto &v : frn_vars ) { - v.clear(); - } + if( furn != f_null ) { + return; } + // Reset furniture vars on clear + frn_vars.clear(); } ter_id get_ter( point p ) const { @@ -185,20 +183,28 @@ class submap : maptile_soa return fld[p.x, p.y]; } - const data_vars::data_set &get_ter_vars( point p ) const { - return ter_vars[p.x, p.y]; + data_vars::data_set &get_ter_vars( point p ) { + return ter_vars[p]; }; - data_vars::data_set &get_ter_vars( point p ) { - return ter_vars[p.x, p.y]; + data_vars::data_set &get_furn_vars( point p ) { + return frn_vars[p]; }; - const data_vars::data_set &get_furn_vars( point p ) const { - return frn_vars[p.x, p.y]; + const data_vars::data_set &get_ter_vars( point p ) const { + const auto it = ter_vars.find( p ); + if( it == ter_vars.end() ) { + return EMPTY_VARS; + } + return it->second; }; - data_vars::data_set &get_furn_vars( point p ) { - return frn_vars[p.x, p.y]; + const data_vars::data_set &get_furn_vars( point p ) const { + const auto it = ter_vars.find( p ); + if( it == ter_vars.end() ) { + return EMPTY_VARS; + } + return it->second; }; struct cosmetic_t { @@ -269,6 +275,10 @@ class submap : maptile_soa static void swap( submap &first, submap &second ) noexcept; private: + static const data_vars::data_set EMPTY_VARS; + std::unordered_map ter_vars; + std::unordered_map frn_vars; + std::map computers; std::unique_ptr legacy_computer; int temperature = 0; From bf5b78ad4dc0caf0b7db347c080584586d782668 Mon Sep 17 00:00:00 2001 From: Reisen Usagi Date: Wed, 22 Oct 2025 20:04:16 -0300 Subject: [PATCH 20/31] split off array2d changes into separate branch --- src/array2d.h | 265 ----------------------------------------- src/savegame_json.cpp | 34 +++--- src/submap.cpp | 270 ++++++++++++++++++++++++++++++++---------- src/submap.h | 57 +++++---- 4 files changed, 256 insertions(+), 370 deletions(-) delete mode 100644 src/array2d.h diff --git a/src/array2d.h b/src/array2d.h deleted file mode 100644 index 4ca7c8744e51..000000000000 --- a/src/array2d.h +++ /dev/null @@ -1,265 +0,0 @@ -#pragma once - -#include - -namespace detail -{ - -template -struct array2d_info { - using RowType = std::array; - using ArrType = std::array; -}; - -template, size_t ... Xs> -constexpr static auto init_array_2d_xs( size_t py, F && fn, std::index_sequence ) -{ - static_assert( sizeof...( Xs ) == SizeX ); - using RowType = typename array2d_info::RowType; - return RowType { - fn( Xs, py ) ..., - }; -} - -template, size_t ... Ys> -constexpr static auto init_array_2d_ys( F && fn, std::integer_sequence ) -{ - static_assert( sizeof...( Ys ) == SizeY ); - using ArrType = typename array2d_info::ArrType; - return ArrType { - detail::init_array_2d_xs( - Ys, - std::forward( fn ), - std::make_index_sequence() - ) ..., - }; -} - -template -constexpr static auto init_array_2d( F &&fn ) -{ - return detail::init_array_2d_ys( - std::forward( fn ), - std::make_index_sequence() - ); -} - -} // namespace detail - -/// 2D Array Class -/// -/// std::array, SizeY> backed -/// -/// Elements in are stored as rows of columns \p SizeY rows of \p SizeX -/// elements: -/// -/// For values without a default constructor, will initialize the elements from: -/// - A constant value (if copy-constructible) -/// - A functor or function that takes the \p x and \p y coordinates, and constructs \p T -/// -/// @tparam T Element Type -/// @tparam SizeX X Dimensions -/// @tparam SizeY Y Dimensions -template -struct array2d { - struct sentinel; - struct iterator; - struct const_iterator; - struct index; - - using index_type = index; - using size_type = size_t; - using value_type = T; - - using pointer = value_type*; - using const_pointer = const value_type*; - using reference = value_type&; - using const_reference = const value_type&; - - struct index { - size_t px; - size_t py; - - constexpr size_t to_offset() const { - return ( py * SizeX ) + px; - } - constexpr static index from_offset( const size_t pos ) { - return index{pos / SizeX, pos % SizeX}; - } - - constexpr index &operator++() { - ++px; - if( px >= SizeX ) { - px = 0; - py++; - } - return *this; - } - - constexpr index &operator--() { - if( px == 0 ) { - px = SizeX - 1; - py--; - } else { - --px; - } - return *this; - } - - constexpr index operator++( int ) { - index temp = *this; - this->operator++(); - return temp; - } - - constexpr index operator--( int ) { - index temp = *this; - this->operator--(); - return temp; - } - }; - - struct iterator { - friend array2d; - - private: - array2d &arr; - index_type index; - - iterator( array2d &arr, index_type i ) : arr( arr ), index( i ) {} - - public: - reference operator*() const { - return arr[index]; - } - pointer operator->() { - return &*arr[index]; - } - - iterator &operator++() { - ++index; - return *this; - } - - iterator &operator--() { - --index; - return *this; - } - - iterator operator++( int ) { - iterator tmp = *this; - ++index; - return tmp; - } - iterator operator--( int ) { - iterator tmp = *this; - --index; - return tmp; - } - - bool operator== ( const sentinel & ) const { return index.px >= SizeX || index.py >= SizeY; }; - bool operator== ( const iterator &other ) const { return index == other.index && arr == other.arr; }; - }; - - struct const_iterator { - friend array2d; - - private: - const array2d &arr; - index_type index; - - const_iterator( const array2d &arr, index_type i ) : arr( arr ), index( i ) {} - - public: - const_reference operator*() const { - return arr[index]; - } - const_pointer operator->() { - return &*arr[index]; - } - - const_iterator &operator++() { - ++index; - return *this; - } - - const_iterator &operator--() { - --index; - return *this; - } - - const_iterator operator++( int ) { - const_iterator tmp = *this; - ++index; - return tmp; - } - const_iterator operator--( int ) { - const_iterator tmp = *this; - --index; - return tmp; - } - - bool operator== ( const sentinel & ) const { return index.px >= SizeX || index.py >= SizeY; }; - bool operator== ( const iterator &other ) const { return index == other.index && arr == other.arr; }; - bool operator== ( const const_iterator &other ) const { return index == other.index && arr == other.arr; }; - }; - - struct sentinel { - bool operator== ( const sentinel & ) { return true; } - bool operator!= ( const sentinel & ) { return false; } - }; - - array2d() = default; - array2d( const array2d & ) = default; - array2d( array2d && ) = default; - - template - requires std::is_same_v> - explicit array2d( F &&fn ) - : _data{detail::init_array_2d( fn ) } { } - - explicit array2d( const T &value ) - : _data{detail::init_array_2d( [ & ]( size_t, size_t ) { return value; } )} { } - - void reset() { - _data = {}; - } - - void reset( const T &value ) { - _data = detail::init_array_2d( [&]( size_t, size_t ) { return value; } ); - } - - template - requires std::is_same_v> - void reset( F &&fn ) { - _data = detail::init_array_2d( fn ); - } - - pointer data() { return &_data[0][0]; } - const_pointer data() const { return &_data[0][0]; } - - iterator begin() { return iterator( *this, {0, 0} ); } - sentinel end() const { return sentinel{}; } - const_iterator begin() const { return const_iterator( *this, {0, 0} ); } - - // _data is an Y sized array, of X sized arrays, of T elements - // we access Y first then X to reach T - - reference operator[]( index_type pos ) { return _data[pos.py][pos.px]; } - const_reference operator[]( index_type pos ) const { return _data[pos.py][pos.px]; } - reference operator[]( size_type px, size_type py ) { return operator[]( index_type{px, py} ); } - const_reference operator[]( size_type px, size_type py ) const { return operator[]( index_type{px, py} ); } - reference operator[]( size_type offset ) { return operator[]( index_type::from_offset( offset ) ); } - const_reference operator[]( size_type offset ) const { return operator[]( index_type::from_offset( offset ) ); } - - static constexpr size_type size() { return SizeX * SizeY; } - static constexpr index_type size_2d() { return index_type{SizeX, SizeY}; } - static constexpr index_type index_2d( size_type x, size_type y ) { return index_type{x, y}; } - - friend void swap( array2d &lhs, array2d &rhs ) noexcept { std::swap( lhs._data, rhs._data ); } - - bool operator== ( const array2d &other ) const { return data() == other.data() && size() == other.size(); } - private: - std::array, SizeY> _data; -}; - diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index 7ee261154ad7..c66cf5cf4a66 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -3722,7 +3722,7 @@ void submap::store( JsonOut &jsout ) const for( int j = 0; j < SEEY; j++ ) { // NOLINTNEXTLINE(modernize-loop-convert) for( int i = 0; i < SEEX; i++ ) { - const std::string this_id = ter[i, j].obj().id.str(); + const std::string this_id = ter[i][j].obj().id.str(); if( !last_id.empty() ) { if( this_id == last_id ) { num_same++; @@ -3802,12 +3802,12 @@ void submap::store( JsonOut &jsout ) const jsout.start_array(); for( int j = 0; j < SEEY; j++ ) { for( int i = 0; i < SEEX; i++ ) { - if( itm[i, j].empty() ) { + if( itm[i][j].empty() ) { continue; } jsout.write( i ); jsout.write( j ); - jsout.write( itm[i, j] ); + jsout.write( itm[i][j] ); } } jsout.end_array(); @@ -3835,11 +3835,11 @@ void submap::store( JsonOut &jsout ) const for( int j = 0; j < SEEY; j++ ) { for( int i = 0; i < SEEX; i++ ) { // Save fields - if( fld[i, j].field_count() > 0 ) { + if( fld[i][j].field_count() > 0 ) { jsout.write( i ); jsout.write( j ); jsout.start_array(); - for( auto &elem : fld[i, j] ) { + for( auto &elem : fld[i][j] ) { const field_entry &cur = elem.second; jsout.write( cur.get_field_type().id() ); jsout.write( cur.get_field_intensity() ); @@ -3982,7 +3982,7 @@ void submap::load( JsonIn &jsin, const std::string &member_name, int version, } else { --remaining; } - ter[i, j] = iid; + ter[i][j] = iid; } } if( remaining ) { @@ -4008,7 +4008,7 @@ void submap::load( JsonIn &jsin, const std::string &member_name, int version, jsin.start_array(); int i = jsin.get_int(); int j = jsin.get_int(); - frn[i, j] = furn_id( jsin.get_string() ); + frn[i][j] = furn_id( jsin.get_string() ); jsin.end_array(); } } else if( member_name == "items" ) { @@ -4030,17 +4030,19 @@ void submap::load( JsonIn &jsin, const std::string &member_name, int version, tmp->legacy_fast_forward_time(); } item &obj = *tmp; - itm[p.x, p.y].push_back( std::move( tmp ) ); + itm[p.x][p.y].push_back( std::move( tmp ) ); if( obj.needs_processing() ) { active_items.add( obj ); } } } - for( auto &it : itm ) { - std::vector> cleared = it.clear(); - to_cbc_migration::migrate( cleared ); - for( detached_ptr &item : cleared ) { - it.push_back( std::move( item ) ); + for( auto &it1 : itm ) { + for( auto &it2 : it1 ) { + std::vector> cleared = it2.clear(); + to_cbc_migration::migrate( cleared ); + for( detached_ptr &item : cleared ) { + it2.push_back( std::move( item ) ); + } } } } else if( member_name == "traps" ) { @@ -4051,7 +4053,7 @@ void submap::load( JsonIn &jsin, const std::string &member_name, int version, int j = jsin.get_int(); const point p( i, j ); // TODO: jsin should support returning an id like jsin.get_id() - trp[p.x, p.y] = trap_str_id( jsin.get_string() ).id(); + trp[p.x][p.y] = trap_str_id( jsin.get_string() ).id(); jsin.end_array(); } } else if( member_name == "fields" ) { @@ -4078,10 +4080,10 @@ void submap::load( JsonIn &jsin, const std::string &member_name, int version, } else { ft = field_types::get_field_type_by_legacy_enum( type_int ).id; } - if( fld[i, j].find_field( ft ) == nullptr ) { + if( fld[i][j].find_field( ft ) == nullptr ) { field_count++; } - fld[i, j].add_field( ft, intensity, time_duration::from_turns( age ) ); + fld[i][j].add_field( ft, intensity, time_duration::from_turns( age ) ); } } } else if( member_name == "graffiti" ) { diff --git a/src/submap.cpp b/src/submap.cpp index 54267a08a7c3..b3f5543f9f49 100644 --- a/src/submap.cpp +++ b/src/submap.cpp @@ -18,74 +18,224 @@ const data_vars::data_set submap::EMPTY_VARS{}; template void maptile_soa::swap_soa_tile( point p1, point p2 ) { - using std::swap; - - swap( ter[p1.x, p1.y], ter[p2.x, p2.y] ); - swap( frn[p1.x, p1.y], frn[p2.x, p2.y] ); - swap( lum[p1.x, p1.y], lum[p2.x, p2.y] ); - swap( itm[p1.x, p1.y], itm[p2.x, p2.y] ); - swap( fld[p1.x, p1.y], fld[p2.x, p2.y] ); - swap( trp[p1.x, p1.y], trp[p2.x, p2.y] ); - swap( rad[p1.x, p1.y], rad[p2.x, p2.y] ); + + std::swap( ter[p1.x][p1.y], ter[p2.x][p2.y] ); + std::swap( frn[p1.x][p1.y], frn[p2.x][p2.y] ); + std::swap( lum[p1.x][p1.y], lum[p2.x][p2.y] ); + std::swap( itm[p1.x][p1.y], itm[p2.x][p2.y] ); + std::swap( fld[p1.x][p1.y], fld[p2.x][p2.y] ); + std::swap( trp[p1.x][p1.y], trp[p2.x][p2.y] ); + std::swap( rad[p1.x][p1.y], rad[p2.x][p2.y] ); } -void submap::swap( submap &first, submap &second ) noexcept +void submap::swap( submap &first, submap &second ) { - using std::swap; - - swap( first.ter, second.ter ); - swap( first.frn, second.frn ); - swap( first.lum, second.lum ); - swap( first.fld, second.fld ); - swap( first.trp, second.trp ); - swap( first.rad, second.rad ); - swap( first.is_uniform, second.is_uniform ); - swap( first.active_items, second.active_items ); - swap( first.field_count, second.field_count ); - swap( first.last_touched, second.last_touched ); - swap( first.spawns, second.spawns ); - swap( first.vehicles, second.vehicles ); - swap( first.partial_constructions, second.partial_constructions ); - swap( first.active_furniture, second.active_furniture ); - swap( first.is_uniform, second.is_uniform ); - swap( first.computers, second.computers ); - swap( first.legacy_computer, second.legacy_computer ); - swap( first.temperature, second.temperature ); - swap( first.cosmetics, second.cosmetics ); - swap( first.frn_vars, second.frn_vars ); - swap( first.ter_vars, second.ter_vars ); + std::swap( first.ter, second.ter ); + std::swap( first.frn, second.frn ); + std::swap( first.lum, second.lum ); + std::swap( first.fld, second.fld ); + std::swap( first.trp, second.trp ); + std::swap( first.rad, second.rad ); + std::swap( first.is_uniform, second.is_uniform ); + std::swap( first.active_items, second.active_items ); + std::swap( first.field_count, second.field_count ); + std::swap( first.last_touched, second.last_touched ); + std::swap( first.spawns, second.spawns ); + std::swap( first.vehicles, second.vehicles ); + std::swap( first.partial_constructions, second.partial_constructions ); + std::swap( first.active_furniture, second.active_furniture ); + std::swap( first.is_uniform, second.is_uniform ); + std::swap( first.computers, second.computers ); + std::swap( first.legacy_computer, second.legacy_computer ); + std::swap( first.temperature, second.temperature ); + std::swap( first.cosmetics, second.cosmetics ); + std::swap( first.frn_vars, second.frn_vars ); + std::swap( first.ter_vars, second.ter_vars ); // TODO: Check if its effect is the same as // swap( first.itm, second.itm ); for( int x = 0; x < SEEX; x++ ) { for( int y = 0; y < SEEY; y++ ) { - swap( first.itm[x, y], second.itm[x, y] ); + std::swap( first.itm[x][y], second.itm[x][y] ); } } } -struct LocationVectorInitializer { - tripoint offset; - explicit LocationVectorInitializer( tripoint offset = {} ) : offset( offset ) {} - auto operator()( size_t x, size_t y ) const { - return location_vector{ new tile_item_location( offset + point( x, y ) )}; - } -}; - +//There's not a briefer way to write this I don't think template -maptile_soa::maptile_soa( tripoint offset ) - : ter( t_null ) - , frn( f_null ) - , lum( 0 ) - , itm( LocationVectorInitializer( offset ) ) - , fld() - , trp( tr_null ) - , rad( 0 ) +maptile_soa::maptile_soa( tripoint offset ) : itm{{ + // NOLINTNEXTLINE(cata-use-named-point-constants) + location_vector{ new tile_item_location( offset + point( 0, 0 ) )}, + // NOLINTNEXTLINE(cata-use-named-point-constants) + location_vector{ new tile_item_location( offset + point( 0, 1 ) )}, + location_vector{ new tile_item_location( offset + point( 0, 2 ) )}, + location_vector{ new tile_item_location( offset + point( 0, 3 ) )}, + location_vector{ new tile_item_location( offset + point( 0, 4 ) )}, + location_vector{ new tile_item_location( offset + point( 0, 5 ) )}, + location_vector{ new tile_item_location( offset + point( 0, 6 ) )}, + location_vector{ new tile_item_location( offset + point( 0, 7 ) )}, + location_vector{ new tile_item_location( offset + point( 0, 8 ) )}, + location_vector{ new tile_item_location( offset + point( 0, 9 ) )}, + location_vector{ new tile_item_location( offset + point( 0, 10 ) )}, + location_vector{ new tile_item_location( offset + point( 0, 11 ) )}, + }, + { + // NOLINTNEXTLINE(cata-use-named-point-constants) + location_vector{ new tile_item_location( offset + point( 1, 0 ) )}, + // NOLINTNEXTLINE(cata-use-named-point-constants) + location_vector{ new tile_item_location( offset + point( 1, 1 ) )}, + location_vector{ new tile_item_location( offset + point( 1, 2 ) )}, + location_vector{ new tile_item_location( offset + point( 1, 3 ) )}, + location_vector{ new tile_item_location( offset + point( 1, 4 ) )}, + location_vector{ new tile_item_location( offset + point( 1, 5 ) )}, + location_vector{ new tile_item_location( offset + point( 1, 6 ) )}, + location_vector{ new tile_item_location( offset + point( 1, 7 ) )}, + location_vector{ new tile_item_location( offset + point( 1, 8 ) )}, + location_vector{ new tile_item_location( offset + point( 1, 9 ) )}, + location_vector{ new tile_item_location( offset + point( 1, 10 ) )}, + location_vector{ new tile_item_location( offset + point( 1, 11 ) )}, + }, { + location_vector{ new tile_item_location( offset + point( 2, 0 ) )}, + location_vector{ new tile_item_location( offset + point( 2, 1 ) )}, + location_vector{ new tile_item_location( offset + point( 2, 2 ) )}, + location_vector{ new tile_item_location( offset + point( 2, 3 ) )}, + location_vector{ new tile_item_location( offset + point( 2, 4 ) )}, + location_vector{ new tile_item_location( offset + point( 2, 5 ) )}, + location_vector{ new tile_item_location( offset + point( 2, 6 ) )}, + location_vector{ new tile_item_location( offset + point( 2, 7 ) )}, + location_vector{ new tile_item_location( offset + point( 2, 8 ) )}, + location_vector{ new tile_item_location( offset + point( 2, 9 ) )}, + location_vector{ new tile_item_location( offset + point( 2, 10 ) )}, + location_vector{ new tile_item_location( offset + point( 2, 11 ) )}, + }, { + location_vector{ new tile_item_location( offset + point( 3, 0 ) )}, + location_vector{ new tile_item_location( offset + point( 3, 1 ) )}, + location_vector{ new tile_item_location( offset + point( 3, 2 ) )}, + location_vector{ new tile_item_location( offset + point( 3, 3 ) )}, + location_vector{ new tile_item_location( offset + point( 3, 4 ) )}, + location_vector{ new tile_item_location( offset + point( 3, 5 ) )}, + location_vector{ new tile_item_location( offset + point( 3, 6 ) )}, + location_vector{ new tile_item_location( offset + point( 3, 7 ) )}, + location_vector{ new tile_item_location( offset + point( 3, 8 ) )}, + location_vector{ new tile_item_location( offset + point( 3, 9 ) )}, + location_vector{ new tile_item_location( offset + point( 3, 10 ) )}, + location_vector{ new tile_item_location( offset + point( 3, 11 ) )}, + }, { + location_vector{ new tile_item_location( offset + point( 4, 0 ) )}, + location_vector{ new tile_item_location( offset + point( 4, 1 ) )}, + location_vector{ new tile_item_location( offset + point( 4, 2 ) )}, + location_vector{ new tile_item_location( offset + point( 4, 3 ) )}, + location_vector{ new tile_item_location( offset + point( 4, 4 ) )}, + location_vector{ new tile_item_location( offset + point( 4, 5 ) )}, + location_vector{ new tile_item_location( offset + point( 4, 6 ) )}, + location_vector{ new tile_item_location( offset + point( 4, 7 ) )}, + location_vector{ new tile_item_location( offset + point( 4, 8 ) )}, + location_vector{ new tile_item_location( offset + point( 4, 9 ) )}, + location_vector{ new tile_item_location( offset + point( 4, 10 ) )}, + location_vector{ new tile_item_location( offset + point( 4, 11 ) )}, + }, { + location_vector{ new tile_item_location( offset + point( 5, 0 ) )}, + location_vector{ new tile_item_location( offset + point( 5, 1 ) )}, + location_vector{ new tile_item_location( offset + point( 5, 2 ) )}, + location_vector{ new tile_item_location( offset + point( 5, 3 ) )}, + location_vector{ new tile_item_location( offset + point( 5, 4 ) )}, + location_vector{ new tile_item_location( offset + point( 5, 5 ) )}, + location_vector{ new tile_item_location( offset + point( 5, 6 ) )}, + location_vector{ new tile_item_location( offset + point( 5, 7 ) )}, + location_vector{ new tile_item_location( offset + point( 5, 8 ) )}, + location_vector{ new tile_item_location( offset + point( 5, 9 ) )}, + location_vector{ new tile_item_location( offset + point( 5, 10 ) )}, + location_vector{ new tile_item_location( offset + point( 5, 11 ) )}, + }, { + location_vector{ new tile_item_location( offset + point( 6, 0 ) )}, + location_vector{ new tile_item_location( offset + point( 6, 1 ) )}, + location_vector{ new tile_item_location( offset + point( 6, 2 ) )}, + location_vector{ new tile_item_location( offset + point( 6, 3 ) )}, + location_vector{ new tile_item_location( offset + point( 6, 4 ) )}, + location_vector{ new tile_item_location( offset + point( 6, 5 ) )}, + location_vector{ new tile_item_location( offset + point( 6, 6 ) )}, + location_vector{ new tile_item_location( offset + point( 6, 7 ) )}, + location_vector{ new tile_item_location( offset + point( 6, 8 ) )}, + location_vector{ new tile_item_location( offset + point( 6, 9 ) )}, + location_vector{ new tile_item_location( offset + point( 6, 10 ) )}, + location_vector{ new tile_item_location( offset + point( 6, 11 ) )}, + }, { + location_vector{ new tile_item_location( offset + point( 7, 0 ) )}, + location_vector{ new tile_item_location( offset + point( 7, 1 ) )}, + location_vector{ new tile_item_location( offset + point( 7, 2 ) )}, + location_vector{ new tile_item_location( offset + point( 7, 3 ) )}, + location_vector{ new tile_item_location( offset + point( 7, 4 ) )}, + location_vector{ new tile_item_location( offset + point( 7, 5 ) )}, + location_vector{ new tile_item_location( offset + point( 7, 6 ) )}, + location_vector{ new tile_item_location( offset + point( 7, 7 ) )}, + location_vector{ new tile_item_location( offset + point( 7, 8 ) )}, + location_vector{ new tile_item_location( offset + point( 7, 9 ) )}, + location_vector{ new tile_item_location( offset + point( 7, 10 ) )}, + location_vector{ new tile_item_location( offset + point( 7, 11 ) )}, + }, { + location_vector{ new tile_item_location( offset + point( 8, 0 ) )}, + location_vector{ new tile_item_location( offset + point( 8, 1 ) )}, + location_vector{ new tile_item_location( offset + point( 8, 2 ) )}, + location_vector{ new tile_item_location( offset + point( 8, 3 ) )}, + location_vector{ new tile_item_location( offset + point( 8, 4 ) )}, + location_vector{ new tile_item_location( offset + point( 8, 5 ) )}, + location_vector{ new tile_item_location( offset + point( 8, 6 ) )}, + location_vector{ new tile_item_location( offset + point( 8, 7 ) )}, + location_vector{ new tile_item_location( offset + point( 8, 8 ) )}, + location_vector{ new tile_item_location( offset + point( 8, 9 ) )}, + location_vector{ new tile_item_location( offset + point( 8, 10 ) )}, + location_vector{ new tile_item_location( offset + point( 8, 11 ) )}, + }, { + location_vector{ new tile_item_location( offset + point( 9, 0 ) )}, + location_vector{ new tile_item_location( offset + point( 9, 1 ) )}, + location_vector{ new tile_item_location( offset + point( 9, 2 ) )}, + location_vector{ new tile_item_location( offset + point( 9, 3 ) )}, + location_vector{ new tile_item_location( offset + point( 9, 4 ) )}, + location_vector{ new tile_item_location( offset + point( 9, 5 ) )}, + location_vector{ new tile_item_location( offset + point( 9, 6 ) )}, + location_vector{ new tile_item_location( offset + point( 9, 7 ) )}, + location_vector{ new tile_item_location( offset + point( 9, 8 ) )}, + location_vector{ new tile_item_location( offset + point( 9, 9 ) )}, + location_vector{ new tile_item_location( offset + point( 9, 10 ) )}, + location_vector{ new tile_item_location( offset + point( 9, 11 ) )}, + }, { + location_vector{ new tile_item_location( offset + point( 10, 0 ) )}, + location_vector{ new tile_item_location( offset + point( 10, 1 ) )}, + location_vector{ new tile_item_location( offset + point( 10, 2 ) )}, + location_vector{ new tile_item_location( offset + point( 10, 3 ) )}, + location_vector{ new tile_item_location( offset + point( 10, 4 ) )}, + location_vector{ new tile_item_location( offset + point( 10, 5 ) )}, + location_vector{ new tile_item_location( offset + point( 10, 6 ) )}, + location_vector{ new tile_item_location( offset + point( 10, 7 ) )}, + location_vector{ new tile_item_location( offset + point( 10, 8 ) )}, + location_vector{ new tile_item_location( offset + point( 10, 9 ) )}, + location_vector{ new tile_item_location( offset + point( 10, 10 ) )}, + location_vector{ new tile_item_location( offset + point( 10, 11 ) )}, + }, { + location_vector{ new tile_item_location( offset + point( 11, 0 ) )}, + location_vector{ new tile_item_location( offset + point( 11, 1 ) )}, + location_vector{ new tile_item_location( offset + point( 11, 2 ) )}, + location_vector{ new tile_item_location( offset + point( 11, 3 ) )}, + location_vector{ new tile_item_location( offset + point( 11, 4 ) )}, + location_vector{ new tile_item_location( offset + point( 11, 5 ) )}, + location_vector{ new tile_item_location( offset + point( 11, 6 ) )}, + location_vector{ new tile_item_location( offset + point( 11, 7 ) )}, + location_vector{ new tile_item_location( offset + point( 11, 8 ) )}, + location_vector{ new tile_item_location( offset + point( 11, 9 ) )}, + location_vector{ new tile_item_location( offset + point( 11, 10 ) )}, + location_vector{ new tile_item_location( offset + point( 11, 11 ) )}, + }} { } -submap::submap( tripoint offset ) : maptile_soa( offset ) +submap::submap( tripoint offset ) : maptile_soa( offset ) { + std::uninitialized_fill_n( &ter[0][0], elements, t_null ); + std::uninitialized_fill_n( &frn[0][0], elements, f_null ); + std::uninitialized_fill_n( &lum[0][0], elements, 0 ); + std::uninitialized_fill_n( &trp[0][0], elements, tr_null ); + std::uninitialized_fill_n( &rad[0][0], elements, 0 ); + is_uniform = false; } @@ -96,22 +246,22 @@ void submap::update_lum_rem( point p, const item &i ) is_uniform = false; if( !i.is_emissive() ) { return; - } else if( lum[p.x, p.y] && lum[p.x, p.y] < 255 ) { - lum[p.x, p.y]--; + } else if( lum[p.x][p.y] && lum[p.x][p.y] < 255 ) { + lum[p.x][p.y]--; return; } // Have to scan through all items to be sure removing i will actually lower // the count below 255. int count = 0; - for( const auto &it : itm[p.x, p.y] ) { + for( const auto &it : itm[p.x][p.y] ) { if( it->is_emissive() ) { count++; } } if( count <= 256 ) { - lum[p.x, p.y] = static_cast( count - 1 ); + lum[p.x][p.y] = static_cast( count - 1 ); } } @@ -190,7 +340,7 @@ void submap::delete_graffiti( point p ) } bool submap::has_signage( point p ) const { - if( frn[p.x, p.y].obj().has_flag( "SIGN" ) ) { + if( frn[p.x][p.y].obj().has_flag( "SIGN" ) ) { return find_cosmetic( cosmetics, p, COSMETICS_SIGNAGE ).result; } @@ -198,7 +348,7 @@ bool submap::has_signage( point p ) const } std::string submap::get_signage( point p ) const { - if( frn[p.x, p.y].obj().has_flag( "SIGN" ) ) { + if( frn[p.x][p.y].obj().has_flag( "SIGN" ) ) { const auto fresult = find_cosmetic( cosmetics, p, COSMETICS_SIGNAGE ); if( fresult.result ) { return cosmetics[ fresult.ndx ].str; @@ -233,7 +383,7 @@ void submap::update_legacy_computer() if( legacy_computer ) { for( int x = 0; x < SEEX; ++x ) { for( int y = 0; y < SEEY; ++y ) { - if( ter[x, y] == t_console ) { + if( ter[x][y] == t_console ) { computers.emplace( point( x, y ), *legacy_computer ); } } @@ -244,7 +394,7 @@ void submap::update_legacy_computer() bool submap::has_computer( point p ) const { - return computers.contains( p ) || ( legacy_computer && ter[p.x, p.y] == t_console ); + return computers.contains( p ) || ( legacy_computer && ter[p.x][p.y] == t_console ); } const computer *submap::get_computer( point p ) const @@ -255,7 +405,7 @@ const computer *submap::get_computer( point p ) const if( it != computers.end() ) { return &it->second; } - if( legacy_computer && ter[p.x, p.y] == t_console ) { + if( legacy_computer && ter[p.x][p.y] == t_console ) { return legacy_computer.get(); } return nullptr; diff --git a/src/submap.h b/src/submap.h index ad09971255b9..7113c26d9645 100644 --- a/src/submap.h +++ b/src/submap.h @@ -20,7 +20,6 @@ #include "monster.h" #include "point.h" #include "poly_serialized.h" -#include "array2d.h" class JsonIn; class JsonOut; @@ -72,13 +71,13 @@ struct maptile_soa { protected: maptile_soa( tripoint offset ); public: - array2d< ter_id, sx, sy > ter; // Terrain on each square - array2d< furn_id, sx, sy > frn; // Furniture on each square - array2d< std::uint8_t, sx, sy > lum; // Number of items emitting light on each square - array2d< location_vector, sx, sy > itm; // Items on each square - array2d< field, sx, sy > fld; // Field on each square - array2d< trap_id, sx, sy > trp; // Trap on each square - array2d< int, sx, sy > rad; // Irradiation of each square + ter_id ter[sx][sy]; // Terrain on each square + furn_id frn[sx][sy]; // Furniture on each square + std::uint8_t lum[sx][sy]; // Number of items emitting light on each square + location_vector itm[sx][sy]; // Items on each square + field fld[sx][sy]; // Field on each square + trap_id trp[sx][sy]; // Trap on each square + int rad[sx][sy]; // Irradiation of each square void swap_soa_tile( point p1, point p2 ); }; @@ -90,25 +89,25 @@ class submap : maptile_soa ~submap(); trap_id get_trap( point p ) const { - return trp[p.x, p.y]; + return trp[p.x][p.y]; } void set_trap( point p, trap_id trap ) { is_uniform = false; - trp[p.x, p.y] = trap; + trp[p.x][p.y] = trap; } void set_all_traps( const trap_id &trap ) { - trp.reset( trap ); + std::uninitialized_fill_n( &trp[0][0], elements, trap ); } furn_id get_furn( point p ) const { - return frn[p.x, p.y]; + return frn[p.x][p.y]; } void set_furn( point p, furn_id furn ) { is_uniform = false; - frn[p.x, p.y] = furn; + frn[p.x][p.y] = furn; if( furn != f_null ) { return; } @@ -117,7 +116,7 @@ class submap : maptile_soa } void set_all_furn( const furn_id &furn ) { - frn.reset( furn ); + std::uninitialized_fill_n( &frn[0][0], elements, furn ); if( furn != f_null ) { return; } @@ -126,40 +125,40 @@ class submap : maptile_soa } ter_id get_ter( point p ) const { - return ter[p.x, p.y]; + return ter[p.x][p.y]; } void set_ter( point p, ter_id terr ) { is_uniform = false; - ter[p.x, p.y] = terr; + ter[p.x][p.y] = terr; } void set_all_ter( const ter_id &terr ) { - std::uninitialized_fill_n( &ter[0, 0], elements, terr ); + std::uninitialized_fill_n( &ter[0][0], elements, terr ); } int get_radiation( point p ) const { - return rad[p.x, p.y]; + return rad[p.x][p.y]; } void set_radiation( point p, const int radiation ) { is_uniform = false; - rad[p.x, p.y] = radiation; + rad[p.x][p.y] = radiation; } uint8_t get_lum( point p ) const { - return lum[p.x, p.y]; + return lum[p.x][p.y]; } void set_lum( point p, uint8_t luminance ) { is_uniform = false; - lum[p.x, p.y] = luminance; + lum[p.x][p.y] = luminance; } void update_lum_add( point p, const item &i ) { is_uniform = false; - if( i.is_emissive() && lum[p.x, p.y] < 255 ) { - lum[p.x, p.y]++; + if( i.is_emissive() && lum[p.x][p.y] < 255 ) { + lum[p.x][p.y]++; } } @@ -167,20 +166,20 @@ class submap : maptile_soa // TODO: Replace this as it essentially makes itm public location_vector &get_items( const point &p ) { - return itm[p.x, p.y]; + return itm[p.x][p.y]; } const location_vector &get_items( const point &p ) const { - return itm[p.x, p.y]; + return itm[p.x][p.y]; } // TODO: Replace this as it essentially makes fld public field &get_field( point p ) { - return fld[p.x, p.y]; + return fld[p.x][p.y]; } const field &get_field( point p ) const { - return fld[p.x, p.y]; + return fld[p.x][p.y]; } data_vars::data_set &get_ter_vars( point p ) { @@ -272,7 +271,7 @@ class submap : maptile_soa std::map> partial_constructions; std::map> active_furniture; - static void swap( submap &first, submap &second ) noexcept; + static void swap( submap &first, submap &second ); private: static const data_vars::data_set EMPTY_VARS; @@ -334,7 +333,7 @@ struct maptile { return sm->get_field( pos() ); } - field_entry *find_field( const field_type_id &field_to_find ) const { + field_entry *find_field( const field_type_id &field_to_find ) { return sm->get_field( pos() ).find_field( field_to_find ); } From 218aad267d2535fd832b51ac64f6ec570feddf5c Mon Sep 17 00:00:00 2001 From: Reisen Usagi Date: Wed, 22 Oct 2025 21:02:40 -0300 Subject: [PATCH 21/31] * use templated get_var / set_var for item * enable long read/write if under MSVC --- src/catalua_bindings.cpp | 40 +++++++++++++++++++++++++--------------- src/item.h | 27 ++++++++++++++++++++++++++- src/json.cpp | 20 ++++++++++++++++++++ src/json.h | 7 +++++++ src/submap.cpp | 2 -- 5 files changed, 78 insertions(+), 18 deletions(-) diff --git a/src/catalua_bindings.cpp b/src/catalua_bindings.cpp index 5a88df0e750d..739829ef9f82 100644 --- a/src/catalua_bindings.cpp +++ b/src/catalua_bindings.cpp @@ -487,23 +487,33 @@ void cata::detail::reg_item( sol::state &lua ) luna::set_fx( ut, "convert", &item::convert ); DOC( "Get variable as string" ); - luna::set_fx( ut, "get_var_str", - sol::resolve - ( &item::get_var ) ); + luna::set_fx( ut, "get_var_str", []( const UT_CLASS & c, const std::string & name, const std::string & def_val ) + { + return c.get_var( name, def_val ); + } ); DOC( "Get variable as float number" ); - luna::set_fx( ut, "get_var_num", - sol::resolve( &item::get_var ) ); + luna::set_fx( ut, "get_var_num", []( const UT_CLASS & c, const std::string & name, const double & def_val ) + { + return c.get_var( name, def_val ); + } ); DOC( "Get variable as tripoint" ); - luna::set_fx( ut, "get_var_tri", - sol::resolve - ( &item::get_var ) ); - - luna::set_fx( ut, "set_var_str", sol::resolve - ( &item::set_var ) ); - luna::set_fx( ut, "set_var_num", - sol::resolve( &item::set_var ) ); - luna::set_fx( ut, "set_var_tri", - sol::resolve( &item::set_var ) ); + luna::set_fx( ut, "get_var_tri", []( const UT_CLASS & c, const std::string & name, const tripoint & def_val ) + { + return c.get_var( name, def_val ); + } ); + + luna::set_fx( ut, "set_var_str", []( UT_CLASS & c, const std::string & name, const std::string & val ) + { + c.set_var( name, val ); + } ); + luna::set_fx( ut, "set_var_num", []( UT_CLASS & c, const std::string & name, const double & val ) + { + c.set_var( name, val ); + } ); + luna::set_fx( ut, "set_var_tri", []( UT_CLASS & c, const std::string & name, const tripoint & val ) + { + c.set_var( name, val ); + } ); SET_FX( attack_cost ); SET_FX( stamina_cost ); diff --git a/src/item.h b/src/item.h index c458a8180bf8..4940ea7e2151 100644 --- a/src/item.h +++ b/src/item.h @@ -1497,11 +1497,34 @@ class item : public location_visitable, public game_object */ /*@{*/ + template + T get_var( const std::string &name, const T &default_value ) const { + return item_vars_.get( name, default_value ); + } + + template + void set_var( const std::string &name, const T &value ) { + item_vars_.set( name, value ); + } + + std::string get_var( const std::string &name, const std::string &default_value ) const { + return item_vars_.get( name, default_value ); + } + + std::string get_var( const std::string &name ) const { + return item_vars_.get( name, "" ); + } + + void set_var( const std::string &name, const std::string &value ) { + item_vars_.set( name, value ); + }; + + /* void set_var( const std::string &name, int value ) { item_vars_.set( name, value ); }; void set_var( const std::string &name, long long value ) { item_vars_.set( name, value ); }; // Acceptable to use long as part of overload set // NOLINTNEXTLINE(cata-no-long) - void set_var( const std::string &name, long value ) { item_vars_.set( name, value ); }; + //void set_var( const std::string &name, long value ) { item_vars_.set( name, value ); }; void set_var( const std::string &name, double value ) { item_vars_.set( name, value ); }; void set_var( const std::string &name, const tripoint &value ) { item_vars_.set( name, value ); }; void set_var( const std::string &name, const std::string &value ) { item_vars_.set( name, value ); }; @@ -1510,6 +1533,8 @@ class item : public location_visitable, public game_object tripoint get_var( const std::string &name, const tripoint &default_value ) const { return item_vars_.get( name, default_value ); } std::string get_var( const std::string &name, const std::string &default_value ) const { return item_vars_.get( name, default_value ); } std::string get_var( const std::string &name ) const { return item_vars_.get( name, "" ); } + */ + bool has_var( const std::string &name ) const { return item_vars_.contains( name ); } void erase_var( const std::string &name ) { item_vars_.erase( name ); } void clear_vars() { item_vars_.clear(); } diff --git a/src/json.cpp b/src/json.cpp index b986654e23da..68db3cb05e9e 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -1736,6 +1736,26 @@ bool JsonIn::read( JsonDeserializer &j, bool throw_on_error ) } } +#if defined(_MSC_VER) +bool JsonIn::read( long &i, bool throw_on_error ) +{ + if( !test_number() ) { + return error_or_false( throw_on_error, "Expected number" ); + } + i = static_cast( get_int64() ); + return true; +} + +bool JsonIn::read( unsigned long &u, bool throw_on_error ) +{ + if( !test_number() ) { + return error_or_false( throw_on_error, "Expected number" ); + } + u = static_cast( get_uint64() ); + return true; +} +#endif + /** * Get the normal form of a relative path. Does not work on absolute paths. * Slash and backslash are both treated as path separators and replaced with diff --git a/src/json.h b/src/json.h index b5a2fd87a084..f70e99215350 100644 --- a/src/json.h +++ b/src/json.h @@ -281,6 +281,13 @@ class JsonIn template bool read( std::bitset &b, bool throw_on_error = false ); bool read( JsonDeserializer &j, bool throw_on_error = false ); + +#if defined(_MSC_VER) + // in MSVC, long is neither int32_t, nor int64_t... + bool read( long &i, bool throw_on_error = false ); + bool read( unsigned long &u, bool throw_on_error = false ); +#endif + // This is for the string_id type template auto read( T &thing, bool throw_on_error = false ) -> decltype( thing.str(), true ) { diff --git a/src/submap.cpp b/src/submap.cpp index b3f5543f9f49..c2271d0e2810 100644 --- a/src/submap.cpp +++ b/src/submap.cpp @@ -52,8 +52,6 @@ void submap::swap( submap &first, submap &second ) std::swap( first.frn_vars, second.frn_vars ); std::swap( first.ter_vars, second.ter_vars ); - // TODO: Check if its effect is the same as - // swap( first.itm, second.itm ); for( int x = 0; x < SEEX; x++ ) { for( int y = 0; y < SEEY; y++ ) { std::swap( first.itm[x][y], second.itm[x][y] ); From 3b3e2bc5d0c6b68a9aa8ebd631a369564eae0440 Mon Sep 17 00:00:00 2001 From: Reisen Usagi Date: Wed, 22 Oct 2025 21:05:25 -0300 Subject: [PATCH 22/31] add const char* overload to get_var --- src/item.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/item.h b/src/item.h index 4940ea7e2151..e57fe6ef243c 100644 --- a/src/item.h +++ b/src/item.h @@ -1507,6 +1507,10 @@ class item : public location_visitable, public game_object item_vars_.set( name, value ); } + std::string get_var( const std::string &name, const char *default_value ) const { + return item_vars_.get( name, default_value ); + } + std::string get_var( const std::string &name, const std::string &default_value ) const { return item_vars_.get( name, default_value ); } From 71a5f39f8371312cf295d037bd7c7cc06900d368 Mon Sep 17 00:00:00 2001 From: Reisen Usagi Date: Wed, 22 Oct 2025 21:10:25 -0300 Subject: [PATCH 23/31] add const char* overload to set_var --- src/item.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/item.h b/src/item.h index e57fe6ef243c..b5dd0ef7439f 100644 --- a/src/item.h +++ b/src/item.h @@ -1519,6 +1519,10 @@ class item : public location_visitable, public game_object return item_vars_.get( name, "" ); } + void set_var( const std::string &name, const char *value ) { + item_vars_.set( name, value ); + }; + void set_var( const std::string &name, const std::string &value ) { item_vars_.set( name, value ); }; From 8977e6e97f859831fa293a6b28a4904ba9632d9f Mon Sep 17 00:00:00 2001 From: Reisen Usagi Date: Thu, 23 Oct 2025 13:39:50 -0300 Subject: [PATCH 24/31] wrap json_converter read/write in a try-catch block because throw_on_error = false is a suggestion --- src/data_vars_cvt.h | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/data_vars_cvt.h b/src/data_vars_cvt.h index c80c46fe313c..4989bbe78bd7 100644 --- a/src/data_vars_cvt.h +++ b/src/data_vars_cvt.h @@ -14,18 +14,32 @@ template struct json_converter { using value_type = T; + // Need a try-catch blocks because JsonIn/Out + // Doesn't care if you ask it to not throw on error + // And may just do so anyway + bool operator()( const T &in_val, std::string &out_val ) const { - std::ostringstream os; - JsonOut jsout{os}; - jsout.write( in_val ); - out_val = os.str(); - return true; + try { + std::ostringstream os; + JsonOut jsout{os}; + jsout.write( in_val ); + out_val = os.str(); + return true; + } catch( JsonError &e ) { + debugmsg( "Error writing value: %s", e.what() ); + return false; + } } bool operator()( const std::string &in_val, T &out_val ) const { - std::istringstream is{in_val}; - JsonIn jsin{is}; - return jsin.read( out_val, false ); + try { + std::istringstream is{in_val}; + JsonIn jsin{is}; + return jsin.read( out_val, false ); + } catch( JsonError &e ) { + debugmsg( "Error reading value '%s': %s", in_val, e.what() ); + return false; + } } }; From 75600bab639ccd8261bdf9bc361d93b8623900a1 Mon Sep 17 00:00:00 2001 From: WishDuck Date: Mon, 2 Feb 2026 15:02:47 -0800 Subject: [PATCH 25/31] fix json item vars & autoloader - issues with long long - issues with dict vs var storage --- src/assign.cpp | 18 +++++++++++++++++- src/assign.h | 7 ++++++- src/catalua_bindings_item.cpp | 12 ++++++------ src/item.cpp | 2 +- src/itype.h | 3 ++- src/vehicle_functions.cpp | 21 +++++++++++---------- 6 files changed, 43 insertions(+), 20 deletions(-) diff --git a/src/assign.cpp b/src/assign.cpp index 983002276f9b..a8e1eb9f2d9a 100644 --- a/src/assign.cpp +++ b/src/assign.cpp @@ -1,5 +1,5 @@ #include "assign.h" - +#include "data_vars.h" #include void report_strict_violation( const JsonObject &jo, const std::string &message, @@ -695,3 +695,19 @@ void assign_dmg_relative( damage_instance &out, } } } + +bool assign( const JsonObject &jo, + const std::string &name, + data_vars::data_set &val, + const bool strict ) +{ + if( jo.has_array( name ) ) { + for( JsonArray arr : jo.get_array( name ) ) { + val[arr.get_string( 0 )] = arr.get_string( 1 ); + } + } else { + return false; + } + return true; +} + diff --git a/src/assign.h b/src/assign.h index 86a1bc11e91a..95dcb346acc1 100644 --- a/src/assign.h +++ b/src/assign.h @@ -17,7 +17,7 @@ #include "units.h" #include "units_serde.h" #include "concepts_utility.h" - +#include "data_vars.h" namespace detail { template @@ -424,6 +424,11 @@ bool assign( const JsonObject &jo, nc_color &val, const bool strict = false ); +bool assign( const JsonObject &jo, + const std::string &name, + data_vars::data_set &val, + const bool strict = false ); + class time_duration; template diff --git a/src/catalua_bindings_item.cpp b/src/catalua_bindings_item.cpp index 562ffae173b6..ea35e54e6b19 100644 --- a/src/catalua_bindings_item.cpp +++ b/src/catalua_bindings_item.cpp @@ -270,14 +270,14 @@ void reg_item( sol::state &lua ) DOC( "Get variable as string" ); luna::set_fx( ut, "get_var_str", []( const UT_CLASS & c, const std::string & name, const std::string & def_val ) - { - return c.get_var( name, def_val ); - } ); + { + return c.get_var( name, def_val ); + } ); DOC( "Get variable as float number" ); luna::set_fx( ut, "get_var_num", []( const UT_CLASS & c, const std::string & name, const double & def_val ) - { - return c.get_var( name, def_val ); - } ); + { + return c.get_var( name, def_val ); + } ); DOC( "Get variable as tripoint" ); luna::set_fx( ut, "get_var_tri", []( const UT_CLASS & c, const std::string & name, const tripoint & def_val ) { diff --git a/src/item.cpp b/src/item.cpp index 099ead342cfe..408231a5fffd 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -285,7 +285,7 @@ item::item( const itype *type, time_point turn, int qty ) : type( type ), contents( this ), components( new component_item_location( this ) ), bday( turn ) { - item_vars = type->item_vars; + item_vars_ = type->item_vars; corpse = has_flag( flag_CORPSE ) ? &mtype_id::NULL_ID().obj() : nullptr; item_counter = type->countdown_interval; diff --git a/src/itype.h b/src/itype.h index 6312ac1771af..5261faa43728 100644 --- a/src/itype.h +++ b/src/itype.h @@ -15,6 +15,7 @@ #include "catalua_type_operators.h" #include "color.h" // nc_color #include "damage.h" +#include "data_vars.h" #include "enums.h" // point #include "explosion.h" #include "game_constants.h" @@ -1028,7 +1029,7 @@ struct itype { bool rigid = true; // Default item vars for the resulting item - std::map item_vars; + data_vars::data_set item_vars; /** Damage output in melee for zero or more damage types */ std::array melee; diff --git a/src/vehicle_functions.cpp b/src/vehicle_functions.cpp index 65290371dabd..d54e67fab17a 100644 --- a/src/vehicle_functions.cpp +++ b/src/vehicle_functions.cpp @@ -53,12 +53,12 @@ auto check_reload_timing( item &gun, const item &ammo_item, bool is_ammo_refill, // 1. Handling empty gun startup delay (Cycle logic) if( gun_was_empty ) { - const int64_t cycle_start = gun.get_var( "autoloader_cycle_start", 0LL ); + const int64_t cycle_start = gun.item_vars().get( "autoloader_cycle_start", 0 ); // State: First detection of ammo if( cycle_start == 0LL ) { const int64_t current_turn = to_turns( calendar::turn - calendar::turn_zero ); - gun.set_var( "autoloader_cycle_start", current_turn ); + gun.item_vars().set( "autoloader_cycle_start", current_turn ); add_msg( m_debug, "Autoload: Empty gun found ammo, starting cycle at turn %lld", current_turn ); @@ -84,7 +84,7 @@ auto check_reload_timing( item &gun, const item &ammo_item, bool is_ammo_refill, // 2. Handling standard cooldown (Inter-shot/Inter-mag delay) const time_point last_reload = time_point::from_turn( - static_cast( gun.get_var( "last_autoload_turn", 0LL ) ) + static_cast( gun.item_vars().get( "last_autoload_turn", 0 ) ) ); return calendar::turn >= last_reload + reload_interval; @@ -126,8 +126,9 @@ void perform_reload( vehicle &veh, vehicle_part &cargo_part, item &gun, item &so // State update veh.drain( fuel_type_battery, power_cost ); - gun.set_var( "last_autoload_turn", to_turns( calendar::turn - calendar::turn_zero ) ); - gun.set_var( "autoloader_cycle_start", 0LL ); // Reset cycle + gun.item_vars().set( "last_autoload_turn", + to_turns( calendar::turn - calendar::turn_zero ) ); + gun.item_vars().set( "autoloader_cycle_start", 0 ); // Reset cycle // Feedback if( get_avatar().sees( veh.global_pos3() ) ) { @@ -156,7 +157,7 @@ auto try_autoload_turret( vehicle &veh, vehicle_part &pt ) -> bool ? ( ammo_capacity > 0 && gun.ammo_remaining() < ammo_capacity ) : !gun.ammo_sufficient(); if( !needs_reload ) { - gun.set_var( "autoloader_cycle_start", 0LL ); + gun.item_vars().set( "autoloader_cycle_start", 0 ); return false; } @@ -180,14 +181,14 @@ auto try_autoload_turret( vehicle &veh, vehicle_part &pt ) -> bool // Maintain "Last Ammo" state to detect empty transitions const bool gun_is_empty = gun.ammo_remaining() == 0; - const int64_t last_ammo_check = gun.get_var( "autoloader_last_ammo", -1LL ); + const int64_t last_ammo_check = gun.item_vars().get( "autoloader_last_ammo", -1 ); if( gun_is_empty && last_ammo_check != 0LL ) { - gun.set_var( "autoloader_last_ammo", 0LL ); - gun.set_var( "autoloader_cycle_start", 0LL ); + gun.item_vars().set( "autoloader_last_ammo", 0LL ); + gun.item_vars().set( "autoloader_cycle_start", 0LL ); add_msg( m_debug, "try_autoload_turret: Gun became empty, reset cycle" ); } else if( !gun_is_empty ) { - gun.set_var( "autoloader_last_ammo", gun.ammo_remaining() ); + gun.item_vars().set( "autoloader_last_ammo", gun.ammo_remaining() ); } // Search cargo for compatible items From dd3652dffb20eafb877a368126f1613f5f2b1e93 Mon Sep 17 00:00:00 2001 From: WishDuck Date: Sun, 8 Feb 2026 14:17:49 -0800 Subject: [PATCH 26/31] hopefullyfix --- src/assign.cpp | 8 ++------ src/cata_tiles_color.cpp | 4 ---- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/assign.cpp b/src/assign.cpp index a8e1eb9f2d9a..0f1864dc5179 100644 --- a/src/assign.cpp +++ b/src/assign.cpp @@ -701,12 +701,8 @@ bool assign( const JsonObject &jo, data_vars::data_set &val, const bool strict ) { - if( jo.has_array( name ) ) { - for( JsonArray arr : jo.get_array( name ) ) { - val[arr.get_string( 0 )] = arr.get_string( 1 ); - } - } else { - return false; + for( const JsonMember member : jo.get_object( name ) ) { + val[member.name()] = member.get_string(); } return true; } diff --git a/src/cata_tiles_color.cpp b/src/cata_tiles_color.cpp index 5c1a254b8ba6..3c86fe02988e 100644 --- a/src/cata_tiles_color.cpp +++ b/src/cata_tiles_color.cpp @@ -93,10 +93,6 @@ auto cata_tiles::get_effect_color( auto cata_tiles::get_effect_color( const effect &eff, const Character &c ) -> color_tint_pair { - const color_tint_pair *tint = tileset_ptr->get_tint( eff.get_id().str() ); - if( tint != nullptr ) { - return *tint; - } return { std::nullopt, std::nullopt }; } From 62c0beceef630ac6fdb2d78ffa82d54c894af171 Mon Sep 17 00:00:00 2001 From: WishDuck Date: Sun, 8 Feb 2026 18:03:04 -0800 Subject: [PATCH 27/31] didn't mean to remove that --- src/cata_tiles_color.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/cata_tiles_color.cpp b/src/cata_tiles_color.cpp index 3c86fe02988e..5c1a254b8ba6 100644 --- a/src/cata_tiles_color.cpp +++ b/src/cata_tiles_color.cpp @@ -93,6 +93,10 @@ auto cata_tiles::get_effect_color( auto cata_tiles::get_effect_color( const effect &eff, const Character &c ) -> color_tint_pair { + const color_tint_pair *tint = tileset_ptr->get_tint( eff.get_id().str() ); + if( tint != nullptr ) { + return *tint; + } return { std::nullopt, std::nullopt }; } From b3935d9aa06f770754af2c11d3a5bc75ca6e1645 Mon Sep 17 00:00:00 2001 From: WishDuck Date: Sun, 15 Feb 2026 18:50:59 -0800 Subject: [PATCH 28/31] emergency fix --- src/savegame_json.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index 0ad3d65bc4ec..6b826f94da1b 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -4152,6 +4152,7 @@ void submap::store( JsonOut &jsout ) const } jsout.write( key ); jsout.write( value ); + } jsout.member( "transformer_last_run" ); jsout.start_array(); for( const auto &pr : transformer_last_run ) { From 7e6101f2042413edaf0494fa5efc940d4bce4351 Mon Sep 17 00:00:00 2001 From: WishDuck Date: Sun, 15 Feb 2026 18:54:08 -0800 Subject: [PATCH 29/31] another fix for merge --- src/savegame_json.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index 6b826f94da1b..c4c05a7746c6 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -4418,6 +4418,7 @@ void submap::load( JsonIn &jsin, const std::string &member_name, int version, jsin.read( loc ); auto &vars = ter_vars[loc]; jsin.read( vars ); + } } else if( member_name == "transformer_last_run" ) { jsin.start_array(); while( !jsin.end_array() ) { From c1382b259f1b32c8ef03a4a50645bc90d49e9e1a Mon Sep 17 00:00:00 2001 From: WishDuck Date: Sun, 15 Mar 2026 14:28:23 -0700 Subject: [PATCH 30/31] fix json serilize issue --- src/savegame_json.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index c4c05a7746c6..74ee47cd6413 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -4153,6 +4153,7 @@ void submap::store( JsonOut &jsout ) const jsout.write( key ); jsout.write( value ); } + jsout.end_array(); jsout.member( "transformer_last_run" ); jsout.start_array(); for( const auto &pr : transformer_last_run ) { From c9c0e4fb5bcd738d9fb5f2ee9cb13bc77f53029a Mon Sep 17 00:00:00 2001 From: WishDuck Date: Sun, 15 Mar 2026 15:38:22 -0700 Subject: [PATCH 31/31] feat: add furniture multicooker --- .../json/construction/construct_workshop.json | 13 ++ data/json/construction_group.json | 5 + .../furniture-appliances.json | 39 ++++ .../json/reference/tiles/furn_and_terrain.md | 17 ++ src/iexamine.cpp | 186 ++++++++++++++++++ src/iexamine.h | 1 + src/mapdata.cpp | 2 + src/mapdata.h | 3 + src/submap.h | 2 +- 9 files changed, 267 insertions(+), 1 deletion(-) diff --git a/data/json/construction/construct_workshop.json b/data/json/construction/construct_workshop.json index c0aa9f403f20..bc9d3d552b7b 100644 --- a/data/json/construction/construct_workshop.json +++ b/data/json/construction/construct_workshop.json @@ -259,6 +259,19 @@ "pre_special": "check_empty", "post_furniture": "f_oven" }, + { + "type": "construction", + "id": "constr_multicooker", + "group": "install_multicooker", + "category": "WORKSHOP", + "required_skills": [ ], + "time": "10 m", + "qualities": [ ], + "components": [ [ [ "multi_cooker", 1 ] ] ], + "pre_note": "Will only work if constructed in/on a building that has an electric grid with a mounted battery.", + "pre_special": "check_empty", + "post_furniture": "f_multicooker" + }, { "type": "construction", "id": "constr_gridfridge", diff --git a/data/json/construction_group.json b/data/json/construction_group.json index e86435d6c36d..88eb2b38642e 100644 --- a/data/json/construction_group.json +++ b/data/json/construction_group.json @@ -1009,6 +1009,11 @@ "id": "install_oven", "name": "Plug In Oven" }, + { + "type": "construction_group", + "id": "install_multicooker", + "name": "Plug In Multi Cooker" + }, { "type": "construction_group", "id": "install_refrigerator", diff --git a/data/json/furniture_and_terrain/furniture-appliances.json b/data/json/furniture_and_terrain/furniture-appliances.json index 0fcba00f4f95..be52ecdc6a10 100644 --- a/data/json/furniture_and_terrain/furniture-appliances.json +++ b/data/json/furniture_and_terrain/furniture-appliances.json @@ -682,6 +682,45 @@ "ranged": { "reduction": [ 4, 8 ], "destroy_threshold": 30, "block_unaimed_chance": "50%" } } }, + { + "type": "furniture", + "id": "f_multicooker", + "name": "multi cooker", + "looks_like": "f_oven", + "symbol": "#", + "description": "Used for automatic cooking food with electricity.", + "color": "dark_gray", + "move_cost_mod": 2, + "coverage": 60, + "required_str": 10, + "crafting_pseudo_item": "fake_oven", + "flags": [ "PLACE_ITEM", "TRANSPARENT", "FIRE_CONTAINER", "CONTAINER", "BLOCKSDOOR", "MOUNTABLE", "EASY_DECONSTRUCT" ], + "examine_action": "multicooker", + "deconstruct": { "items": [ { "item": "multi_cooker", "count": 1 } ] }, + "max_volume": "40 L", + "bash": { + "str_min": 8, + "str_max": 30, + "sound": "metal screeching!", + "sound_fail": "clang!", + "items": [ + { "item": "sheet_metal", "count": [ 1, 4 ] }, + { "item": "sheet_metal_small", "count": [ 8, 12 ] }, + { "item": "steel_chunk", "count": [ 0, 3 ] }, + { "item": "scrap", "count": [ 0, 6 ] }, + { "item": "element", "count": [ 1, 3 ] }, + { "item": "cable", "charges": [ 1, 3 ] }, + { "item": "pilot_light", "count": 1 } + ], + "//": "Variable reduction since might hit more or less material.", + "ranged": { "reduction": [ 4, 8 ], "destroy_threshold": 30, "block_unaimed_chance": "50%" } + }, + "default_vars": { + "CATEGORYIDS": "[ \"CSC_FOOD_MEAT\", \"CSC_FOOD_VEGGI\", \"CSC_FOOD_PASTA\" ]", + "CHARGE_PER_MIN": "10", + "CHARGE_START": "10" + } + }, { "type": "furniture", "id": "f_rvoven", diff --git a/docs/en/mod/json/reference/tiles/furn_and_terrain.md b/docs/en/mod/json/reference/tiles/furn_and_terrain.md index 80438e17cb58..91c3f0a9fdfb 100644 --- a/docs/en/mod/json/reference/tiles/furn_and_terrain.md +++ b/docs/en/mod/json/reference/tiles/furn_and_terrain.md @@ -41,6 +41,12 @@ title: Furniture and Terrain "message": "The safe is hacksawed open!", "sound": "Gachunk!", "byproducts": [{ "item": "scrap", "count": 13 }] + }, + "default_vars": { + "CATEGORYIDS": "[ \"CSC_FOOD_MEAT\", \"CSC_FOOD_VEGGI\", \"CSC_FOOD_PASTA\" ]", + "CHARGE_PER_MIN": "5", + "CHARGE_START": "100", + "CRAFTSPEEDMULT": "1.0" } } ``` @@ -171,6 +177,17 @@ it for the purpose of surgery. (Optional) Dispenses infinite amounts of specified liquid item when interacted. Must be used with `"examine_action": "liquid_source"` to work. +#### `default_vars` + +(Optional) Default string variables for objects, always a string string pair; Can be used to store arbitrary data or for some data for iuses such as + +- Multicooker + - "CATEGORYIDS"; String is a json array of categories for applicable recipes + - "RECIPEIDS": String is a json array of valid recipes + - "CHARGE_PER_MIN": Charges consumed per minute + - "CHARGE_START": Charges consumed at start + - "CRAFTSPEEDMULT": Multiplier on craft speed + ### Terrain ```json diff --git a/src/iexamine.cpp b/src/iexamine.cpp index cbde55fa38eb..14ea9f588b98 100644 --- a/src/iexamine.cpp +++ b/src/iexamine.cpp @@ -33,6 +33,8 @@ #include "catacharset.h" #include "character.h" #include "character_functions.h" +#include "data_vars.h" +#include "detached_ptr.h" #include "flag.h" #include "color.h" #include "construction.h" @@ -107,6 +109,7 @@ #include "uistate.h" #include "units.h" #include "units_utility.h" +#include "recipe_dictionary.h" #include "value_ptr.h" #include "vehicle.h" #include "vehicle_part.h" @@ -7469,6 +7472,188 @@ void iexamine::cardreader_plutgen( player &p, const tripoint &examp ) } } +void iexamine::multicooker( player &p, const tripoint &pos ) +{ + map &here = get_map(); + const furn_id furniture = here.furn( pos ); + data_vars::data_set *vars = here.furn_vars( pos ); + const tripoint_abs_ms abspos( here.getabs( pos ) ); + auto grid = get_distribution_grid_tracker().grid_at( abspos ); + int battery = grid.get_resource(); + enum { + mc_start, mc_stop, mc_take, mc_upgrade + }; + + uilist menu; + menu.text = _( "Choose option:" ); + + if( vars->get( "ACTIVE", false ) ) { + if( vars->contains( "STARTTIME" ) && + vars->get( "STARTTIME", 0 ) + vars->get( "COOKTIME", 0 ) > to_turn( calendar::turn ) ) { + menu.addentry( mc_stop, true, 's', _( "Stop crafting" ) ); + } else { + menu.addentry( mc_take, true, 't', _( "Remove Product" ) ); + } + } else { + if( battery < vars->get( "CHARGE_START", 0 ) ) { + p.add_msg_if_player( _( "Batteries are low." ) ); + return; + } + menu.addentry( mc_start, true, 's', _( "Start crafting " ) ); + } + + menu.query(); + int choice = menu.ret; + + if( choice < 0 ) { + return; + } + + if( mc_stop == choice ) { + if( query_yn( _( "Really stop?" ) ) ) { + vars->erase( "RESULT" ); + vars->erase( "ACTIVE" ); + vars->erase( "STARTTIME" ); + vars->erase( "COOKTIME" ); + vars->erase( "BATCHCOUNT" ); + vars->erase( "RECIPE" ); + } + return; + } + + if( mc_take == choice ) { + + detached_ptr dish = item::spawn( vars->get( "RESULT" ), calendar::turn, + vars->get( "BATCHCOUNT", 1 ) ); + + const std::string dish_name = dish->tname( dish->charges, false ); + if( dish->made_of( LIQUID ) ) { + if( !p.check_eligible_containers_for_crafting( *recipe_id( vars->get( "RECIPE" ) ), 1 ) ) { + p.add_msg_if_player( m_info, _( "You don't have a suitable container to store your %s." ), + dish_name ); + return; + } + liquid_handler::handle_all_liquid( std::move( dish ), PICKUP_RANGE ); + } else { + p.i_add( std::move( dish ) ); + } + + grid.mod_resource( vars->get( "COOKTIME", 0 ) * vars->get( "CRAFTSPEEDMULT", + 1.0 ) / 6000 * vars->get( "CHARGE_PER_MIN", 0.0 ) + vars->get( "CHARGE_START", 0.0 ) ); + vars->erase( "RESULT" ); + vars->erase( "ACTIVE" ); + vars->erase( "STARTTIME" ); + vars->erase( "COOKTIME" ); + vars->erase( "BATCHCOUNT" ); + vars->erase( "RECIPE" ); + p.add_msg_if_player( m_good, _( "You got the %s from the %s." ), + dish_name, furniture->name() ); + + return; + } + + if( mc_start == choice ) { + uilist dmenu; + dmenu.text = _( "Choose desired recipe:" ); + + std::vector dishes; + + inventory crafting_inv = g->u.crafting_inventory(); + + for( itype item : furniture->crafting_pseudo_item_types() ) { + crafting_inv.add_item( *item::spawn_temporary( item.get_id(), calendar::start_of_cataclysm ), + false ); + } + crafting_inv.update_quality_cache(); + + int counter = 0; + + for( const auto &r : g->u.get_learned_recipes() ) { + if( vars->get( "CATEGORYIDS", std::set() ).contains( r->subcategory ) || + vars->get( "RECIPEIDS", std::set() ).contains( r->result().str() ) ) { + dishes.push_back( r ); + const bool can_make = r->deduped_requirements().can_make_with_inventory( + crafting_inv, r->get_component_filter() ); + dmenu.addentry( counter++, can_make, -1, string_format( _( "%s (%1.f charges)" ), r->result_name(), + r->time * vars->get( "CRAFTSPEEDMULT", 1.0 ) / 6000 * vars->get( "CHARGE_PER_MIN", + 0.0 ) + vars->get( "CHARGE_START", 0.0 ) ) ); + } + } + + dmenu.query(); + + int choice = dmenu.ret; + + if( choice < 0 ) { + + if( choice == -1024 ) { + p.add_msg_if_player( m_warning, + _( "You don't know of anything you could craft with this." ) ); + } + + return; + } else { + const recipe *meal = dishes[choice]; + + uilist batchmenu; + batchmenu.text = _( "Choose batch count:" ); + int counter = 0; + + for( int i = 1; i < 51; i++ ) { + const bool can_make = meal->deduped_requirements().can_make_with_inventory( + crafting_inv, meal->get_component_filter(), i ); + batchmenu.addentry( counter++, can_make, -1, string_format( _( "%s batches (%1.f charges)" ), i, + meal->batch_time( i, 1, 0 ) * vars->get( "CRAFTSPEEDMULT", + 1.0 ) / 6000 * vars->get( "CHARGE_PER_MIN", 0.0 ) + vars->get( "CHARGE_START", 0.0 ) ) ); + } + + batchmenu.query(); + + int batchcount = batchmenu.ret; + + if( batchcount < 0 ) { + return; + } + batchcount++; + + // Seems to be divided by 100; + // See the CHARGE_PER_MIN calc being 6000 instead of 60 + int mealtime = meal->batch_time( batchcount, 1, 0 ) * vars->get( "CRAFTSPEEDMULT", 1.0 ) / 100; + int all_charges = mealtime / 6000 * vars->get( "CHARGE_PER_MIN", 0.0 ) + vars->get( "CHARGE_START", + 0.0 ); + + if( battery < all_charges ) { + + p.add_msg_if_player( m_warning, + _( "The %s needs %d charges to create this." ), + furniture->name(), all_charges ); + return; + } + + const auto filter = is_crafting_component; + const requirement_data *reqs = + meal->deduped_requirements().select_alternative( p, crafting_inv, filter, batchcount ); + if( !reqs ) { + return; + } + + for( const auto &component : reqs->get_components() ) { + p.consume_items( component, batchcount, filter ); + } + + vars->set( "ACTIVE", true ); + vars->set( "RECIPE", meal->ident().str() ); + vars->set( "RESULT", meal->result().str() ); + vars->set( "STARTTIME", to_turn( calendar::turn ) ); + vars->set( "COOKTIME", mealtime ); + vars->set( "BATCHCOUNT", meal->makes_amount() * batchcount ); + + p.add_msg_if_player( m_good, _( "The %s begins to hum." ), furniture->name() ); + + return; + } + } +} /** * Given then name of one of the above functions, returns the matching function * pointer. If no match is found, defaults to iexamine::none but prints out a @@ -7566,6 +7751,7 @@ iexamine_function iexamine_function_from_string( const std::string &function_nam { "check_power", &iexamine::check_power }, { "migo_nerve_cluster", &iexamine::migo_nerve_cluster }, { "cardreader_plutgen", &iexamine::cardreader_plutgen }, + { "multicooker", &iexamine::multicooker }, } }; diff --git a/src/iexamine.h b/src/iexamine.h index 6d22bc77623c..23b9b3462990 100644 --- a/src/iexamine.h +++ b/src/iexamine.h @@ -117,6 +117,7 @@ void dimensional_portal( player &p, const tripoint &examp ); void check_power( player &p, const tripoint &examp ); void migo_nerve_cluster( player &p, const tripoint &examp ); void cardreader_plutgen( player &p, const tripoint &examp ); +void multicooker( player &p, const tripoint &pos ); detached_ptr pour_into_keg( const tripoint &pos, detached_ptr &&liquid ); std::optional getGasPumpByNumber( const tripoint &p, int number ); diff --git a/src/mapdata.cpp b/src/mapdata.cpp index 82ce360dae12..4aed49cf5f1d 100644 --- a/src/mapdata.cpp +++ b/src/mapdata.cpp @@ -1393,6 +1393,8 @@ void map_data_common_t::load( const JsonObject &jo, const std::string &src ) optional( jo, was_loaded, "prompt", prompt ); assign( jo, "flags", flags ); + assign( jo, "default_vars", default_vars ); + bitflags.reset(); transparent = false; diff --git a/src/mapdata.h b/src/mapdata.h index 6dddb00a9919..86d62163e89b 100644 --- a/src/mapdata.h +++ b/src/mapdata.h @@ -18,6 +18,7 @@ #include "type_id.h" #include "units.h" #include "value_ptr.h" +#include "data_vars.h" struct ter_t; using ter_str_id = string_id; @@ -485,6 +486,8 @@ struct map_data_common_t { iexamine_function examine; // What happens when the terrain/furniture is examined + data_vars::data_set default_vars; + /** * When will this terrain/furniture get harvested and what will drop? * Note: This excludes items that take extra tools to harvest. diff --git a/src/submap.h b/src/submap.h index d0f04288c930..6ed5b2862cbf 100644 --- a/src/submap.h +++ b/src/submap.h @@ -108,10 +108,10 @@ class submap : maptile_soa void set_furn( point p, furn_id furn ) { is_uniform = false; frn[p.x][p.y] = furn; + frn_vars[p].merge( furn->default_vars ); if( furn != f_null ) { return; } - // Reset furniture vars on clear frn_vars.erase( p ); }