Skip to content

Commit

Permalink
Merge pull request #35910 from DaviBones/monotony-penalty
Browse files Browse the repository at this point in the history
Eating same food repeatedly gives less fun
  • Loading branch information
kevingranade authored Dec 15, 2019
2 parents 488d353 + fccacad commit fbf8bce
Show file tree
Hide file tree
Showing 23 changed files with 243 additions and 55 deletions.
2 changes: 1 addition & 1 deletion data/json/furniture_and_terrain/furniture-graves.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
"description": "A worn-out gravestone.",
"symbol": "^",
"color": "dark_gray",
"move_cost_mod": 1.5,
"move_cost_mod": 2,
"coverage": 50,
"required_str": -1,
"flags": [ "MINEABLE", "NOITEM", "TRANSPARENT", "MOUNTABLE", "ROUGH", "PLACE_ITEM", "UNSTABLE" ],
Expand Down
9 changes: 6 additions & 3 deletions data/json/items/chemicals_and_resources.json
Original file line number Diff line number Diff line change
Expand Up @@ -1310,7 +1310,8 @@
"charges": 50,
"description": "This is a pre-mixed salty solution of protein and sugar. It's meant for bacteria to eat, but if you were desperate you could eat it too; it's not much different from cup noodle stock.",
"material": "powder_nonflam",
"volume": 0.1,
"volume": "100 ml",
"//": "Bulk density is around 0.5 g/mL",
"weight": "1 g"
},
{
Expand All @@ -1329,7 +1330,8 @@
"charges": 50,
"description": "These clear flakes of processed seaweed can be dissolved in boiling water to create a very sturdy, temperature resistant gel. Not only is it good for making gels to separate molecules by size, but it's a great cheat ingredient to make sure your jellies set properly.",
"material": "powder_nonflam",
"volume": 0.1,
"volume": "90 ml",
"//": "Bulk density is around 0.55 g/mL",
"weight": "1 g"
},
{
Expand All @@ -1349,7 +1351,8 @@
"charges": 50,
"description": "This highly carcinogenic white powder can be readily polymerized into a whole bunch of useful water-soluble gels.",
"material": "powder_nonflam",
"volume": 0.1,
"volume": "100 ml",
"//": "Bulk density is around 0.5 g/mL (because it's a powder, the actual density of 1.12 g/mL is not useful)",
"weight": "1 g"
},
{
Expand Down
2 changes: 1 addition & 1 deletion data/json/items/comestibles/bread.json
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@
"stack_size": 20,
"flags": [ "EATEN_HOT" ],
"fun": -1,
"vitamins": [ [ "calcium", 12.5 ] ],
"vitamins": [ [ "calcium", 12 ] ],
"//": "this item will generally inherit its values, so it is essentially a copy of bread"
}
]
5 changes: 3 additions & 2 deletions data/json/items/tool/science.json
Original file line number Diff line number Diff line change
Expand Up @@ -265,8 +265,9 @@
"category": "spare_parts",
"name": "small glass tube",
"description": "This is a small glass tube. What more could you possibly want to know about it?",
"weight": "1 g",
"volume": 0.1,
"weight": "9 g",
"volume": "30ml",
"//": "Volume here is displacement volume of a corked 25mL test tube.",
"price": 10,
"to_hit": -5,
"bashing": 1,
Expand Down
6 changes: 3 additions & 3 deletions data/mods/CRT_EXPANSION/items/crt_gunmods.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"name": "butt hook stock",
"description": ", A military-grade stock which folds reducing the guns volume. The weight and the pivoting hook which latches onto your forearm allows for greater stability. ",
"weight": 500,
"volume": 0.5,
"volume": "125 ml",
"integral_volume": 0,
"price": 42000,
"material": [ "plastic", "steel" ],
Expand Down Expand Up @@ -71,7 +71,7 @@
"name": "tactical flashlight",
"description": "A compact flashlight which is mounted to the side of your weapon, not powerful, but good enough for tight hallways.",
"weight": 250,
"volume": 0.5,
"volume": "125 ml",
"integral_volume": 0,
"price": 42000,
"material": [ "plastic", "steel" ],
Expand Down Expand Up @@ -99,7 +99,7 @@
"name": "tactical flashlight (on)",
"description": "A compact flashlight which is attatched to the side of your weapon, not powerful, but good enough for tight hallways.",
"weight": 250,
"volume": 0.5,
"volume": "125 ml",
"integral_volume": 0,
"price": 42000,
"material": [ "plastic", "steel" ],
Expand Down
2 changes: 1 addition & 1 deletion data/mods/CRT_EXPANSION/items/crt_toolarmor.json
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@
"environmental_protection": 16,
"max_charges": 300,
"initial_charges": 0,
"turns_per_charge": 0.5,
"turns_per_charge": 1,
"revert_to": "crt_em_vest",
"use_action": { "type": "transform", "menu_text": "Turn off armor", "msg": "C.R.I.T E.M powering off...", "target": "crt_em_vest" },
"artifact_data": {
Expand Down
2 changes: 1 addition & 1 deletion data/mods/CRT_EXPANSION/martial/generaltechn.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
"unarmed_allowed": true,
"melee_allowed": true,
"crit_tec": true,
"stun_dur": 1.5,
"stun_dur": 1,
"mult_bonuses": [ [ "movecost", 1.2 ], [ "damage", "bash", 1.25 ], [ "damage", "cut", 1.25 ], [ "damage", "stab", 1.25 ] ],
"flat_bonuses": [ [ "movecost", "str", -0.8 ] ],
"messages": [
Expand Down
2 changes: 1 addition & 1 deletion data/mods/Fuji_Structures/items/items_games.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"name_plural": "decks of cards",
"description": "A collection of 52 cards made to play poker.",
"weight": 96,
"volume": 0.5,
"volume": "125 ml",
"price": 599,
"material": [ "paper" ],
"symbol": "?",
Expand Down
4 changes: 2 additions & 2 deletions data/mods/Magiclysm/items/ethereal_items.json
Original file line number Diff line number Diff line change
Expand Up @@ -335,8 +335,8 @@
"type": "TOOL",
"name": "faintly glowing dust",
"description": "This fine dust glows with a growing intensity",
"weight": 0.25,
"volume": 0.1,
"weight": "1 g",
"volume": "30 ml",
"price": 0,
"symbol": "!",
"color": "white",
Expand Down
2 changes: 1 addition & 1 deletion data/mods/Magiclysm/items/spellbooks.json
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@
"name": "The Tome of the Hollow Earth",
"description": "This large dusty spellbook seems perpetually, well, dusty. It contains the power of the earth.",
"weight": 483,
"volume": 3.3,
"volume": "825 ml",
"symbol": "?",
"color": "brown",
"use_action": {
Expand Down
6 changes: 3 additions & 3 deletions data/mods/Magiclysm/monstergroups.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"default": "mon_null",
"is_animal": true,
"monsters": [
{ "monster": "mon_black_pudding", "freq": 14, "cost_multiplier": 0.8, "pack_size": [ 4, 6 ] },
{ "monster": "mon_black_pudding", "freq": 14, "cost_multiplier": 1, "pack_size": [ 4, 6 ] },
{ "monster": "mon_stirge", "freq": 7, "cost_multiplier": 1, "pack_size": [ 2, 8 ] },
{ "monster": "mon_wisp", "freq": 5, "cost_multiplier": 10, "conditions": [ "NIGHT" ] },
{ "monster": "mon_wisp", "freq": 1, "cost_multiplier": 10, "conditions": [ "NIGHT" ], "pack_size": [ 1, 3 ] },
Expand Down Expand Up @@ -55,7 +55,7 @@
"default": "mon_null",
"is_animal": true,
"monsters": [
{ "monster": "mon_black_pudding", "freq": 14, "cost_multiplier": 0.8, "pack_size": [ 4, 6 ] },
{ "monster": "mon_black_pudding", "freq": 14, "cost_multiplier": 1, "pack_size": [ 4, 6 ] },
{ "monster": "mon_wisp", "freq": 5, "cost_multiplier": 10, "conditions": [ "NIGHT" ] },
{ "monster": "mon_wisp", "freq": 1, "cost_multiplier": 10, "conditions": [ "NIGHT" ], "pack_size": [ 1, 3 ] },
{ "monster": "mon_bat", "freq": 5, "cost_multiplier": 5, "pack_size": [ 6, 10 ] },
Expand All @@ -76,7 +76,7 @@
"default": "mon_lizardfolk_warrior",
"is_animal": true,
"monsters": [
{ "monster": "mon_black_pudding", "freq": 14, "cost_multiplier": 0.8, "pack_size": [ 4, 6 ] },
{ "monster": "mon_black_pudding", "freq": 14, "cost_multiplier": 1, "pack_size": [ 4, 6 ] },
{ "monster": "mon_dragon_black_wyrmling", "freq": 2, "cost_multiplier": 50 },
{ "monster": "mon_dragon_black_young", "freq": 1, "cost_multiplier": 65 },
{ "monster": "mon_lizardfolk_warrior", "freq": 20, "cost_multiplier": 1, "pack_size": [ 2, 4 ] },
Expand Down
2 changes: 1 addition & 1 deletion data/mods/My_Sweet_Cataclysm/sweet_materials.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"type": "material",
"ident": "sugar",
"name": "Sugar",
"density": 1.54,
"density": 1,
"specific_heat_liquid": 2.03,
"specific_heat_solid": 2.03,
"latent_heat": 57,
Expand Down
1 change: 1 addition & 0 deletions doc/JSON_FLAGS.md
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,7 @@ Some armor flags, such as `WATCH` and `ALARMCLOCK` are compatible with other ite
- ```MELTS``` Provides half fun unless frozen. Edible when frozen.
- ```MILLABLE``` Can be placed inside a mill, to turn into flour.
- ```MYCUS_OK``` Can be eaten by post-threshold Mycus characters. Only applies to mycus fruits by default.
- ```NEGATIVE_MONOTONY_OK``` Allows ```negative_monotony``` property to lower comestible fun to negative values.
- ```NO_INGEST``` Administered by some means other than oral intake.
- ```PKILL_1``` Minor painkiller.
- ```PKILL_2``` Moderate painkiller.
Expand Down
2 changes: 2 additions & 0 deletions doc/JSON_INFO.md
Original file line number Diff line number Diff line change
Expand Up @@ -1408,6 +1408,8 @@ CBMs can be defined like this:
"quench" : 0, // Thirst quenched
"heal" : -2, // Health effects (used for sickness chances)
"addiction_potential" : 80, // Ability to cause addictions
"monotony_penalty" : 0, // (Optional, default: 2) Fun is reduced by this number for each one you've consumed in the last 48 hours.
// Can't drop fun below 0, unless the comestible also has the "NEGATIVE_MONOTONY_OK" flag.
"calories" : 0, // Hunger satisfied (in kcal)
"nutrition" : 0, // Hunger satisfied (OBSOLETE)
"tool" : "apparatus", // Tool required to be eaten/drank
Expand Down
19 changes: 19 additions & 0 deletions src/character.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,21 @@ struct social_modifiers {
return *this;
}
};

struct consumption_event {
time_point time;
itype_id type_id;
uint64_t component_hash;

consumption_event() = default;
consumption_event( const item &food ) : time( calendar::turn ) {
type_id = food.typeId();
component_hash = food.make_component_hash();
}
void serialize( JsonOut &json ) const;
void deserialize( JsonIn &jsin );
};

inline social_modifiers operator+( social_modifiers lhs, const social_modifiers &rhs )
{
lhs += rhs;
Expand Down Expand Up @@ -668,6 +683,9 @@ class Character : public Creature, public visitable<Character>
const item &new_item ) const;

std::array<std::array<int, NUM_WATER_TOLERANCE>, num_bp> mut_drench;

void serialize_consumption_history( JsonOut jsout ) const;
void deserialize_consumption_history( JsonArray jarr );
public:
// recalculates enchantment cache by iterating through all held, worn, and wielded items
void recalculate_enchantment_cache();
Expand Down Expand Up @@ -1242,6 +1260,7 @@ class Character : public Creature, public visitable<Character>

stomach_contents stomach;
stomach_contents guts;
std::list<consumption_event> consumption_history;

int oxygen;
int radiation;
Expand Down
22 changes: 22 additions & 0 deletions src/consumption.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,21 @@ std::pair<int, int> Character::fun_for( const item &comest ) const
}
}

// Food is less enjoyable when eaten too often.
if( fun > 0 || comest.has_flag( "NEGATIVE_MONOTONY_OK" ) ) {
for( const consumption_event &event : consumption_history ) {
if( event.time > calendar::turn - 2_days && event.type_id == comest.typeId() &&
event.component_hash == comest.make_component_hash() ) {
fun -= comest.get_comestible()->monotony_penalty;
// This effect can't drop fun below 0, unless the food has the right flag.
if( fun <= 0 && !comest.has_flag( "NEGATIVE_MONOTONY_OK" ) ) {
fun = 0;
break; // 0 is the lowest we'll go, no need to keep looping.
}
}
}
}

float fun_max = fun < 0 ? fun * 6 : fun * 3;
if( comest.has_flag( flag_EATEN_COLD ) && comest.has_flag( flag_COLD ) ) {
if( fun > 0 ) {
Expand Down Expand Up @@ -920,6 +935,13 @@ bool player::eat( item &food, bool force )
if( will_vomit ) {
vomit();
}

consumption_history.emplace_back( food );
// Clean out consumption_history so it doesn't get bigger than needed.
while( consumption_history.front().time < calendar::turn - 2_days ) {
consumption_history.pop_front();
}

return true;
}

Expand Down
17 changes: 17 additions & 0 deletions src/item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8062,6 +8062,23 @@ std::string item::components_to_string() const
}, enumeration_conjunction::none );
}

uint64_t item::make_component_hash() const
{
// First we need to sort the IDs so that identical ingredients give identical hashes.
std::multiset<std::string> id_set;
for( const item &it : components ) {
id_set.insert( it.typeId() );
}

std::string concatenated_ids;
for( std::string id : id_set ) {
concatenated_ids += id;
}

std::hash<std::string> hasher;
return hasher( concatenated_ids );
}

bool item::needs_processing() const
{
return active || has_flag( "RADIO_ACTIVATION" ) || has_flag( "ETHEREAL_ITEM" ) ||
Expand Down
3 changes: 3 additions & 0 deletions src/item.h
Original file line number Diff line number Diff line change
Expand Up @@ -1205,6 +1205,9 @@ class item : public visitable<item>
* no components */
std::string components_to_string() const;

/** Creates a hash from the itype_ids of this item's @ref components. */
uint64_t make_component_hash() const;

/** return the unique identifier of the items underlying type */
itype_id typeId() const;

Expand Down
17 changes: 12 additions & 5 deletions src/item_factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1691,12 +1691,13 @@ void Item_factory::load( islot_comestible &slot, const JsonObject &jo, const std
assign( jo, "cooks_like", slot.cooks_like, strict );
assign( jo, "smoking_result", slot.smoking_result, strict );

bool is_junkfood = false;
if( jo.has_member( "primary_material" ) ) {
slot.specific_heat_solid = material_id(
jo.get_string( "primary_material" ) )->specific_heat_solid();
slot.specific_heat_liquid = material_id(
jo.get_string( "primary_material" ) )->specific_heat_liquid();
slot.latent_heat = material_id( jo.get_string( "primary_material" ) )->latent_heat();
std::string mat = jo.get_string( "primary_material" );
slot.specific_heat_solid = material_id( mat )->specific_heat_solid();
slot.specific_heat_liquid = material_id( mat )->specific_heat_liquid();
slot.latent_heat = material_id( mat )->latent_heat();
is_junkfood = is_junkfood || mat == "junk";
} else if( jo.has_member( "material" ) ) {
float specific_heat_solid = 0;
float specific_heat_liquid = 0;
Expand All @@ -1706,13 +1707,19 @@ void Item_factory::load( islot_comestible &slot, const JsonObject &jo, const std
specific_heat_solid += material_id( m )->specific_heat_solid();
specific_heat_liquid += material_id( m )->specific_heat_liquid();
latent_heat += material_id( m )->latent_heat();
is_junkfood = is_junkfood || m == "junk";
}
// Average based on number of materials.
slot.specific_heat_liquid = specific_heat_liquid / jo.get_tags( "material" ).size();
slot.specific_heat_solid = specific_heat_solid / jo.get_tags( "material" ).size();
slot.latent_heat = latent_heat / jo.get_tags( "material" ).size();
}

if( is_junkfood ) { // Junk food never gets old by default, but this can still be overriden.
slot.monotony_penalty = 0;
}
assign( jo, "monotony_penalty", slot.monotony_penalty, strict );

if( jo.has_string( "addiction_type" ) ) {
slot.add = addiction_type( jo.get_string( "addiction_type" ) );
}
Expand Down
3 changes: 3 additions & 0 deletions src/itype.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,9 @@ struct islot_comestible {
float specific_heat_solid = 2.108;
float latent_heat = 333;

/** A penalty applied to fun for every time this food has been eaten in the last 48 hours */
int monotony_penalty = 2;

/** 1 nutr ~= 8.7kcal (1 nutr/5min = 288 nutr/day at 2500kcal/day) */
static constexpr float kcal_per_nutr = 2500.0f / ( 12 * 24 );

Expand Down
Loading

0 comments on commit fbf8bce

Please sign in to comment.