Skip to content

Commit

Permalink
Can use string to specify price (#34176)
Browse files Browse the repository at this point in the history
  • Loading branch information
Fris0uman authored and kevingranade committed Sep 24, 2019
1 parent 955a8e0 commit 5e0f826
Show file tree
Hide file tree
Showing 7 changed files with 171 additions and 16 deletions.
7 changes: 4 additions & 3 deletions doc/JSON_INFO.md
Expand Up @@ -951,7 +951,8 @@ See also VEHICLE_JSON.md
"integral_weight" : 0, // Weight added to base item when item is integrated into another (eg. a gunmod integrated to a gun)
"rigid": false, // For non-rigid items volume (and for worn items encumbrance) increases proportional to contents
"insulation": 1, // (Optional, default = 1) If container or vehicle part, how much insulation should it provide to the contents
"price" : 100, // Used when bartering with NPCs. For stackable items (ammo, comestibles) this is the price for stack_size charges.
"price" : 100, // Used when bartering with NPCs. For stackable items (ammo, comestibles) this is the price for stack_size charges. Can use string "cent" "USD" or "kUSD".
"price_post" : "1 USD", // Same as price but represent value post cataclysm. Can use string "cent" "USD" or "kUSD".
"material" : ["COTTON"], // Material types, can be as many as you want. See materials.json for possible options
"cutting" : 0, // (Optional, default = 0) Cutting damage caused by using it as a melee weapon
"bashing" : -5, // (Optional, default = 0) Bashing damage caused by using it as a melee weapon
Expand Down Expand Up @@ -1190,7 +1191,7 @@ It could also be written as a generic item ("type": "GENERIC") with "armor_data"
"color": "light_gray", // ASCII character color
"name": "hatchet", // In-game name displayed
"description": "A one-handed hatchet. Makes a great melee weapon, and is useful both for cutting wood, and for use as a hammer.", // In-game description
"price": 95, // Used when bartering with NPCs
"price": 95, // Used when bartering with NPCs. Can use string "cent" "USD" or "kUSD".
"material": ["iron", "wood"], // Material types. See materials.json for possible options
"weight": 907, // Weight, measured in grams
"volume": "1500 ml", // Volume, volume in ml and L can be used - "50 ml" or "2 L"
Expand Down Expand Up @@ -1292,7 +1293,7 @@ Alternately, every item (book, tool, armor, even food) can be used as a gunmod i
"color": "brown", // ASCII character color
"name": "torch (lit)", // In-game name displayed
"description": "A large stick, wrapped in gasoline soaked rags. This is burning, producing plenty of light", // In-game description
"price": 0, // Used when bartering with NPCs
"price": 0, // Used when bartering with NPCs. Can use string "cent" "USD" or "kUSD".
"material": "wood", // Material types. See materials.json for possible options
"techniques": "FLAMING", // Combat techniques used by this tool
"flags": "FIRE", // Indicates special effects
Expand Down
13 changes: 7 additions & 6 deletions src/artifact.cpp
Expand Up @@ -10,6 +10,7 @@
#include <unordered_map>
#include <vector>

#include "assign.h"
#include "cata_utility.h"
#include "item_factory.h"
#include "json.h"
Expand Down Expand Up @@ -615,7 +616,7 @@ it_artifact_tool::it_artifact_tool()
tool.emplace();
artifact.emplace();
id = item_controller->create_artifact_id();
price = 0;
price = 0_cent;
tool->charges_per_use = 1;
artifact->charge_type = ARTC_NULL;
artifact->charge_req = ACR_NULL;
Expand All @@ -639,7 +640,7 @@ it_artifact_armor::it_artifact_armor()
armor.emplace();
artifact.emplace();
id = item_controller->create_artifact_id();
price = 0;
price = 0_cent;
}

it_artifact_armor::it_artifact_armor( JsonObject &jo )
Expand Down Expand Up @@ -1140,7 +1141,7 @@ void it_artifact_tool::deserialize( JsonObject &jo )
sym = jo.get_string( "sym" );
}
jo.read( "color", color );
price = jo.get_int( "price" );
assign( jo, "price", price, false, 0_cent );
// LEGACY: Since it seems artifacts get serialized out to disk, and they're
// dynamic, we need to allow for them to be read from disk for, oh, I guess
// quite some time. Loading and saving once will write things out as a JSON
Expand Down Expand Up @@ -1255,7 +1256,7 @@ void it_artifact_armor::deserialize( JsonObject &jo )
sym = jo.get_string( "sym" );
}
jo.read( "color", color );
price = jo.get_int( "price" );
assign( jo, "price", price, false, 0_cent );
// LEGACY: Since it seems artifacts get serialized out to disk, and they're
// dynamic, we need to allow for them to be read from disk for, oh, I guess
// quite some time. Loading and saving once will write things out as a JSON
Expand Down Expand Up @@ -1346,7 +1347,7 @@ void it_artifact_tool::serialize( JsonOut &json ) const
json.member( "description", description.translated() );
json.member( "sym", sym );
json.member( "color", color );
json.member( "price", price );
json.member( "price", units::to_cent( price ) );
json.member( "materials" );
json.start_array();
for( const material_id &mat : materials ) {
Expand Down Expand Up @@ -1402,7 +1403,7 @@ void it_artifact_armor::serialize( JsonOut &json ) const
json.member( "description", description.translated() );
json.member( "sym", sym );
json.member( "color", color );
json.member( "price", price );
json.member( "price", units::to_cent( price ) );
json.member( "materials" );
json.start_array();
for( const material_id &mat : materials ) {
Expand Down
60 changes: 60 additions & 0 deletions src/assign.h
Expand Up @@ -337,6 +337,66 @@ inline bool assign( JsonObject &jo, const std::string &name, units::mass &val,
return true;
}

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

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

units::money out;

// Object via which to report errors which differs for proportional/relative values
JsonObject err = jo;

// 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( jo.get_object( "relative" ).has_member( name ) ) {
units::money tmp;
err = jo.get_object( "relative" );
if( !parse( err, tmp ) ) {
err.throw_error( "invalid relative value specified", name );
}
strict = false;
out = val + tmp;

} else if( jo.get_object( "proportional" ).has_member( name ) ) {
double scalar;
err = jo.get_object( "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( JsonObject &jo, const std::string &name, nc_color &val,
const bool strict = false )
{
Expand Down
2 changes: 1 addition & 1 deletion src/item.cpp
Expand Up @@ -3707,7 +3707,7 @@ int item::price( bool practical ) const
return VisitResponse::NEXT;
}

int child = practical ? e->type->price_post : e->type->price;
int child = units::to_cent( practical ? e->type->price_post : e->type->price );
if( e->damage() > 0 ) {
// maximal damage level is 4, maximal reduction is 40% of the value.
child -= child * static_cast<double>( e->damage_level( 4 ) ) / 10;
Expand Down
8 changes: 4 additions & 4 deletions src/item_factory.cpp
Expand Up @@ -180,7 +180,7 @@ void Item_factory::finalize_pre( itype &obj )
}

// use pre-cataclysm price as default if post-cataclysm price unspecified
if( obj.price_post < 0 ) {
if( obj.price_post < 0_cent ) {
obj.price_post = obj.price;
}
// use base volume if integral volume unspecified
Expand Down Expand Up @@ -864,7 +864,7 @@ void Item_factory::check_definitions() const
msg << "invalid stack_size " << type->stack_size << " on type using charges\n";
}
}
if( type->price < 0 ) {
if( type->price < 0_cent ) {
msg << "negative price" << "\n";
}
if( type->damage_min() > 0 || type->damage_max() < 0 || type->damage_min() > type->damage_max() ) {
Expand Down Expand Up @@ -2041,8 +2041,8 @@ void Item_factory::load_basic_info( JsonObject &jo, itype &def, const std::strin
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, "price", def.price );
assign( jo, "price_postapoc", def.price_post );
assign( jo, "price", def.price, false, 0_cent );
assign( jo, "price_postapoc", def.price_post, false, 0_cent );
assign( jo, "stackable", def.stackable_, strict );
assign( jo, "integral_volume", def.integral_volume );
assign( jo, "bashing", def.melee[DT_BASH], strict, 0 );
Expand Down
4 changes: 2 additions & 2 deletions src/itype.h
Expand Up @@ -901,9 +901,9 @@ struct itype {
int stack_size = 0;

/** Value before cataclysm. Price given is for a default-sized stack. */
int price = 0;
units::money price = 0_cent;
/** Value after cataclysm, dependent upon practical usages. Price given is for a default-sized stack. */
int price_post = -1;
units::money price_post = -1_cent;

/**@}*/
// If non-rigid volume (and if worn encumbrance) increases proportional to contents
Expand Down
93 changes: 93 additions & 0 deletions src/units.h
Expand Up @@ -401,6 +401,55 @@ inline constexpr value_type to_kilojoule( const quantity<value_type, energy_in_m
return to_joule( v ) / 1000.0;
}

class money_in_cent_tag
{
};

using money = quantity<int, money_in_cent_tag>;

const money money_min = units::money( std::numeric_limits<units::money::value_type>::min(),
units::money::unit_type{} );

const money money_max = units::money( std::numeric_limits<units::money::value_type>::max(),
units::money::unit_type{} );

template<typename value_type>
inline constexpr quantity<value_type, money_in_cent_tag> from_cent(
const value_type v )
{
return quantity<value_type, money_in_cent_tag>( v, money_in_cent_tag{} );
}

template<typename value_type>
inline constexpr quantity<value_type, money_in_cent_tag> from_usd( const value_type v )
{
return from_cent<value_type>( v * 100 );
}

template<typename value_type>
inline constexpr quantity<value_type, money_in_cent_tag> from_kusd( const value_type v )
{
return from_usd<value_type>( v * 1000 );
}

template<typename value_type>
inline constexpr value_type to_cent( const quantity<value_type, money_in_cent_tag> &v )
{
return v / from_cent<value_type>( 1 );
}

template<typename value_type>
inline constexpr value_type to_usd( const quantity<value_type, money_in_cent_tag> &v )
{
return to_cent( v ) / 100.0;
}

template<typename value_type>
inline constexpr value_type to_kusd( const quantity<value_type, money_in_cent_tag> &v )
{
return to_usd( v ) / 1000.0;
}

// Streaming operators for debugging and tests
// (for UI output other functions should be used which render in the user's
// chosen units)
Expand All @@ -419,6 +468,11 @@ inline std::ostream &operator<<( std::ostream &o, energy_in_millijoule_tag )
return o << "mJ";
}

inline std::ostream &operator<<( std::ostream &o, money_in_cent_tag )
{
return o << "cent";
}

template<typename value_type, typename tag_type>
inline std::ostream &operator<<( std::ostream &o, const quantity<value_type, tag_type> &v )
{
Expand Down Expand Up @@ -505,6 +559,39 @@ inline constexpr units::quantity<double, units::energy_in_millijoule_tag> operat
return units::from_kilojoule( v );
}

inline constexpr units::money operator"" _cent( const unsigned long long v )
{
return units::from_cent( v );
}

inline constexpr units::quantity<double, units::money_in_cent_tag> operator"" _cent(
const long double v )
{
return units::from_cent( v );
}

inline constexpr units::money operator"" _USD( const unsigned long long v )
{
return units::from_usd( v );
}

inline constexpr units::quantity<double, units::money_in_cent_tag> operator"" _USD(
const long double v )
{
return units::from_usd( v );
}

inline constexpr units::money operator"" _kUSD( const unsigned long long v )
{
return units::from_kusd( v );
}

inline constexpr units::quantity<double, units::money_in_cent_tag> operator"" _kUSD(
const long double v )
{
return units::from_kusd( v );
}

namespace units
{
static const std::vector<std::pair<std::string, energy>> energy_units = { {
Expand All @@ -519,6 +606,12 @@ static const std::vector<std::pair<std::string, mass>> mass_units = { {
{ "kg", 1_kilogram },
}
};
static const std::vector<std::pair<std::string, money>> money_units = { {
{ "cent", 1_cent },
{ "USD", 1_USD },
{ "kUSD", 1_kUSD },
}
};
} // namespace units

template<typename T>
Expand Down

0 comments on commit 5e0f826

Please sign in to comment.