Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Item length #40186

Merged
merged 13 commits into from
May 11, 2020
23 changes: 20 additions & 3 deletions data/json/items/armor/storage.json
Original file line number Diff line number Diff line change
Expand Up @@ -393,9 +393,9 @@
"repairs_like": "backpack_hiking",
"type": "ARMOR",
"name": { "str": "golf bag" },
"description": "A tall canvas and plastic bag with fold-out legs used for golfing. It even has straps to be worn on the back.",
"description": "A tall canvas and plastic bag with fold-out legs used for golfing. It even has straps to be worn on the back and a slot for an umbrella.",
"weight": "1900 g",
"volume": "15 L",
"volume": "45 L",
"price": 800,
"price_postapoc": 750,
"material": [ "cotton", "plastic" ],
Expand All @@ -406,7 +406,24 @@
"coverage": 35,
"encumbrance": 2,
"max_encumbrance": 15,
"pocket_data": [ { "pocket_type": "CONTAINER", "max_contains_volume": "18 L", "max_contains_weight": "30 kg", "moves": 300 } ],
"longest_side": "900 cm",
"//": "The main section of the golf bag actually assumes things will stick out of it, but specifically contains them, so some extra wiggle room was added.",
"pocket_data": [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So new melee meta is going to be golf bag of weapons? :D

{
"pocket_type": "CONTAINER",
"max_contains_volume": "42 L",
"max_contains_weight": "200 kg",
"moves": 300,
"rigid": true,
"max_item_length": "120 cm"
},
{ "max_contains_volume": "90 ml", "max_contains_weight": "750 g", "moves": 200 },
{ "max_contains_volume": "90 ml", "max_contains_weight": "750 g", "moves": 200 },
{ "max_contains_volume": "1350 ml", "max_contains_weight": "4 kg", "moves": 300 },
{ "max_contains_volume": "675 ml", "max_contains_weight": "2 kg", "moves": 300 },
{ "max_contains_volume": "675 ml", "max_contains_weight": "2 kg", "moves": 300 },
{ "max_contains_volume": "500 ml", "max_contains_weight": "300 g", "moves": 100 }
],
"warmth": 5,
"material_thickness": 3,
"flags": [ "BELTED", "OVERSIZE" ],
Expand Down
1 change: 1 addition & 0 deletions data/json/items/melee/bludgeons.json
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,7 @@
"material": [ "wood" ],
"techniques": [ "WBLOCK_1" ],
"volume": "2 L",
"longest_side": "150 cm",
"bashing": 10,
"price": 8000,
"price_postapoc": 50
Expand Down
65 changes: 65 additions & 0 deletions src/assign.h
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,71 @@ inline bool assign( const JsonObject &jo, const std::string &name, units::mass &
return true;
}

inline bool assign( const JsonObject &jo, const std::string &name, units::length &val,
bool strict = false,
const units::length lo = units::length_min,
const units::length hi = units::length_max )
{
const auto parse = [&name]( const JsonObject & obj, units::length & out ) {
if( obj.has_int( name ) ) {
out = units::from_millimeter<std::int64_t>( obj.get_int( name ) );
return true;
}
if( obj.has_string( name ) ) {

out = read_from_json_string<units::length>( *obj.get_raw( name ), units::length_units );
return true;
}
return false;
};

units::length out;

// Object via which to report errors which differs for proportional/relative values
JsonObject err = jo;
err.allow_omitted_members();
JsonObject relative = jo.get_object( "relative" );
relative.allow_omitted_members();
JsonObject proportional = jo.get_object( "proportional" );
proportional.allow_omitted_members();

// Do not require strict parsing for relative and proportional values as rules
// such as +10% are well-formed independent of whether they affect base value
if( relative.has_member( name ) ) {
units::length tmp;
err = relative;
if( !parse( err, tmp ) ) {
err.throw_error( "invalid relative value specified", name );
}
strict = false;
out = val + tmp;

} else if( proportional.has_member( name ) ) {
double scalar;
err = proportional;
if( !err.read( name, scalar ) || scalar <= 0 || scalar == 1 ) {
err.throw_error( "invalid proportional scalar", name );
}
strict = false;
out = val * scalar;

} else if( !parse( jo, out ) ) {
return false;
}

if( out < lo || out > hi ) {
err.throw_error( "value outside supported range", name );
}

if( strict && out == val ) {
report_strict_violation( err, "assignment does not update value", name );
}

val = out;

return true;
}

inline bool assign( const JsonObject &jo, const std::string &name, units::money &val,
bool strict = false,
const units::money lo = units::money_min,
Expand Down
72 changes: 72 additions & 0 deletions src/cata_utility.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,78 @@ double convert_weight( const units::mass &weight )
return ret;
}

int convert_length( const units::length &length )
{
int ret = to_millimeter( length );
const bool metric = get_option<std::string>( "DISTANCE_UNITS" ) == "metric";
if( metric ) {
if( ret % 1'000'000 == 0 ) {
// kilometers
ret /= 1'000'000;
} else if( ret % 1'000 == 0 ) {
// meters
ret /= 1'000;
} else if( ret % 10 == 0 ) {
// centimeters
ret /= 10;
}
} else {
// imperial's a doozy, we can only try to approximate
// so first we convert it to inches which are the smallest unit
ret /= 25.4;
if( ret % 63360 == 0 ) {
ret /= 63360;
} else if( ret % 36 == 0 ) {
ret /= 36;
} else if( ret % 12 == 0 ) {
ret /= 12;
}
}
return ret;
}

std::string length_units( const units::length &length )
{
int length_mm = to_millimeter( length );
const bool metric = get_option<std::string>( "DISTANCE_UNITS" ) == "metric";
if( metric ) {
if( length_mm % 1'000'000 == 0 ) {
//~ kilometers
return _( "km" );
} else if( length_mm % 1'000 == 0 ) {
//~ meters
return _( "m" );
} else if( length_mm % 10 == 0 ) {
//~ centimeters
return _( "cm" );
} else {
//~ millimeters
return _( "mm" );
}
} else {
// imperial's a doozy, we can only try to approximate
// so first we convert it to inches which are the smallest unit
length_mm /= 25.4;
if( length_mm == 0 ) {
//~ inches
return _( "in." );
}
if( length_mm % 63360 == 0 ) {
//~ miles
return _( "mi" );
} else if( length_mm % 36 == 0 ) {
//~ yards (length)
return _( "yd" );
} else if( length_mm % 12 == 0 ) {
//~ feet (length)
return _( "ft" );
} else {
//~ inches
return _( "in." );
}
}
}

std::string weight_to_string( const units::mass &weight )
{
const double converted_weight = convert_weight( weight );
Expand Down
9 changes: 9 additions & 0 deletions src/cata_utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,15 @@ double convert_velocity( int velocity, units_type vel_units );
*/
double convert_weight( const units::mass &weight );

/**
* converts length to largest unit available
* 1000 mm = 1 meter for example
* assumed to be used in conjunction with unit string functions
* also works for imperial units
*/
int convert_length( const units::length &length );
std::string length_units( const units::length &length );

/** convert a mass unit to a string readable by a human */
std::string weight_to_string( const units::mass &weight );

Expand Down
14 changes: 14 additions & 0 deletions src/item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1490,6 +1490,12 @@ void item::basic_info( std::vector<iteminfo> &info, const iteminfo_query *parts,
iteminfo::lower_is_better | iteminfo::is_decimal,
convert_weight( weight() ) * batch ) );
}
if( parts->test( iteminfo_parts::BASE_LENGTH ) && length() > 0_mm ) {
info.push_back( iteminfo( "BASE", _( "Length: " ),
string_format( "<num> %s", length_units( length() ) ),
iteminfo::lower_is_better,
convert_length( length() ) ) );
}
if( !owner.is_null() ) {
info.push_back( iteminfo( "BASE", string_format( _( "Owner: %s" ),
_( get_owner_name() ) ) ) );
Expand Down Expand Up @@ -4650,6 +4656,14 @@ units::mass item::weight( bool, bool integral ) const
return ret;
}

units::length item::length() const
{
if( made_of( LIQUID ) || is_soft() ) {
return 0_mm;
}
return type->longest_side;
}

units::volume item::corpse_volume( const mtype *corpse ) const
{
units::volume corpse_volume = corpse->volume;
Expand Down
2 changes: 2 additions & 0 deletions src/item.h
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,8 @@ class item : public visitable<item>
*/
units::volume volume( bool integral = false ) const;

units::length length() const;

/**
* Simplified, faster volume check for when processing time is important and exact volume is not.
* NOTE: Result is rounded up to next nearest milliliter when working with stackable (@ref count_by_charges) items that have fractional volume per charge.
Expand Down
7 changes: 7 additions & 0 deletions src/item_factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,10 @@ void Item_factory::finalize_pre( itype &obj )
// martial art is derived from the item id
obj.book->martial_art = matype_id( "style_" + obj.get_id().substr( 7 ) );
}

if( obj.longest_side == -1_mm ) {
obj.longest_side = units::cube_to_volume<int>( obj.volume );
}
}

void Item_factory::register_cached_uses( const itype &obj )
Expand Down Expand Up @@ -2148,6 +2152,7 @@ void Item_factory::check_and_create_magazine_pockets( itype &def )
mag_data.fire_protection = def.magazine->protects_contents;
mag_data.max_contains_volume = 200_liter;
mag_data.max_contains_weight = 400_kilogram;
mag_data.max_item_length = 2_km;
mag_data.rigid = true;
mag_data.watertight = true;
def.pockets.push_back( mag_data );
Expand All @@ -2162,6 +2167,7 @@ void Item_factory::check_and_create_magazine_pockets( itype &def )
mag_data.watertight = true;
mag_data.max_contains_volume = 200_liter;
mag_data.max_contains_weight = 400_kilogram;
mag_data.max_item_length = 2_km;
// the magazine pocket does not use can_contain like normal CONTAINER pockets
// so we don't have to worry about having random items be put into the mag
def.pockets.push_back( mag_data );
Expand Down Expand Up @@ -2200,6 +2206,7 @@ void Item_factory::load_basic_info( const JsonObject &jo, itype &def, const std:
assign( jo, "weight", def.weight, strict, 0_gram );
assign( jo, "integral_weight", def.integral_weight, strict, 0_gram );
assign( jo, "volume", def.volume );
assign( jo, "longest_side", def.longest_side );
assign( jo, "price", def.price, false, 0_cent );
assign( jo, "price_postapoc", def.price_post, false, 0_cent );
assign( jo, "stackable", def.stackable_, strict );
Expand Down
14 changes: 14 additions & 0 deletions src/item_pocket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ void pocket_data::load( const JsonObject &jo )
}
mandatory( jo, was_loaded, "max_contains_volume", max_contains_volume, volume_reader() );
mandatory( jo, was_loaded, "max_contains_weight", max_contains_weight, mass_reader() );
optional( jo, was_loaded, "max_item_length", max_item_length,
units::cube_to_volume( max_contains_volume ) * M_SQRT2 );
}
optional( jo, was_loaded, "spoil_multiplier", spoil_multiplier, 1.0f );
optional( jo, was_loaded, "weight_multiplier", weight_multiplier, 1.0f );
Expand Down Expand Up @@ -605,12 +607,20 @@ void item_pocket::general_info( std::vector<iteminfo> &info, int pocket_number,
string_format( _( "Minimum volume of item allowed: <neutral>%s</neutral>" ),
vol_to_string( data->min_item_volume ) ) );
}

if( data->max_item_volume ) {
info.emplace_back( "DESCRIPTION",
string_format( _( "Maximum volume of item allowed: <neutral>%s</neutral>" ),
vol_to_string( *data->max_item_volume ) ) );
}

if( data->max_item_length != 0_mm ) {
info.push_back( iteminfo( "BASE", _( "Max Item Length: " ),
string_format( "<num> %s", length_units( data->max_item_length ) ),
iteminfo::lower_is_better,
convert_length( data->max_item_length ) ) );
}

info.emplace_back( "DESCRIPTION", string_format( _( "Volume Capacity: <neutral>%s</neutral>" ),
vol_to_string( data->max_contains_volume ) ) );

Expand Down Expand Up @@ -811,6 +821,10 @@ ret_val<item_pocket::contain_code> item_pocket::can_contain( const item &it ) co
return ret_val<item_pocket::contain_code>::make_failure(
contain_code::ERR_TOO_BIG, _( "item too big" ) );
}
if( it.length() > data->max_item_length ) {
return ret_val<item_pocket::contain_code>::make_failure(
contain_code::ERR_TOO_BIG, _( "item is too long" ) );
}

if( it.volume() < data->min_item_volume ) {
return ret_val<item_pocket::contain_code>::make_failure(
Expand Down
3 changes: 3 additions & 0 deletions src/item_pocket.h
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,9 @@ class pocket_data
units::volume min_item_volume = 0_ml;
// max weight of stuff the pocket can hold
units::mass max_contains_weight = 0_gram;
// longest item that can fit into the pocket
// if not defined in json, calculated to be cbrt( volume ) * sqrt( 2 )
units::length max_item_length = 0_mm;
// if true, this pocket can can contain one and only one item
bool holster = false;
// multiplier for spoilage rate of contained items
Expand Down
1 change: 1 addition & 0 deletions src/iteminfo_query.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ enum class iteminfo_parts : size_t {
BASE_BARTER,
BASE_VOLUME,
BASE_WEIGHT,
BASE_LENGTH,
BASE_RIGIDITY,
BASE_DAMAGE,
BASE_TOHIT,
Expand Down
2 changes: 2 additions & 0 deletions src/itype.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
#include "ret_val.h"
#include "translations.h"

#include "math_defines.h"

struct tripoint;

std::string gunmod_location::name() const
Expand Down
5 changes: 5 additions & 0 deletions src/itype.h
Original file line number Diff line number Diff line change
Expand Up @@ -903,6 +903,11 @@ struct itype {
*/
units::volume integral_volume = -1_ml;

/**
* How long the longest side of this item is. If undefined, calculated from volume instead.
*/
units::length longest_side = -1_mm;

/** Number of items per above volume for @ref count_by_charges items */
int stack_size = 0;

Expand Down
20 changes: 20 additions & 0 deletions src/units.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,24 @@ void mass::serialize( JsonOut &jsout ) const
jsout.write( string_format( "%d mg", value_ ) );
}
}

template<>
void length::serialize( JsonOut &jsout ) const
{
if( value_ % 1'000'000 ) {
jsout.write( string_format( "%d km", value_ / 1'000'000 ) );
} else if( value_ % 1'000 ) {
jsout.write( string_format( "%d meter", value_ / 1'000'000 ) );
Copy link
Contributor

@olanti-p olanti-p May 19, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be 1000?
And all value_ % 1xxx checks be value_ % 1xxx == 0 to avoid e.g. formatting 1 as 0 km and 1000000 as 1000000 mm

} else if( value_ % 10 ) {
jsout.write( string_format( "%d cm", value_ / 10 ) );
} else {
jsout.write( string_format( "%d mm", value_ ) );
}
}

template<>
void length::deserialize( JsonIn &jsin )
{
*this = read_from_json_string( jsin, units::length_units );
}
} // namespace units
Loading