Skip to content

Commit

Permalink
Add fitness band and heartrate function for character (#36233)
Browse files Browse the repository at this point in the history
  • Loading branch information
CountAlex authored Apr 12, 2020
1 parent 56ca067 commit 6f4bbd5
Show file tree
Hide file tree
Showing 12 changed files with 197 additions and 8 deletions.
5 changes: 5 additions & 0 deletions data/json/item_actions.json
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,11 @@
"id": "PORTABLE_GAME",
"name": { "ctxt": "PORTABLE_GAME", "str": "Play" }
},
{
"type": "item_action",
"id": "FITNESS_CHECK",
"name": "Check health metrics"
},
{
"type": "item_action",
"id": "deploy_tent",
Expand Down
6 changes: 4 additions & 2 deletions data/json/itemgroups/Clothing_Gear/clothing.json
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,8 @@
{ "item": "sf_watch", "prob": 5 },
{ "item": "pocketwatch", "prob": 30 },
{ "item": "diving_watch", "prob": 20 },
{ "item": "game_watch", "prob": 20 }
{ "item": "game_watch", "prob": 20 },
{ "item": "fitness_band", "prob": 10 }
]
},
{
Expand Down Expand Up @@ -514,7 +515,8 @@
{ "item": "aspirin", "prob": 30 },
{ "item": "water_clean", "prob": 30 },
{ "item": "sports_drink", "prob": 30 },
{ "item": "1st_aid", "prob": 20 }
{ "item": "1st_aid", "prob": 20 },
{ "item": "fitness_band", "prob": 5 }
]
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1824,7 +1824,8 @@
[ "briefs", 30 ],
[ "yoghurt", 12 ],
[ "vitamins", 10 ],
[ "aspirin", 10 ]
[ "aspirin", 10 ],
[ "fitness_band", 10 ]
]
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,8 @@
[ "cell_phone", 60 ],
[ "smart_phone", 60 ],
[ "wristwatch", 60 ],
[ "mobile_memory_card_used", 10 ]
[ "mobile_memory_card_used", 10 ],
[ "fitness_band", 5 ]
]
},
{
Expand Down
6 changes: 4 additions & 2 deletions data/json/itemgroups/Locations_MapExtras/mansion.json
Original file line number Diff line number Diff line change
Expand Up @@ -1022,7 +1022,8 @@
[ "yoghurt", 12 ],
[ "vitamins", 10 ],
[ "aspirin", 15 ],
{ "group": "corpse_mansion", "prob": 1 }
{ "group": "corpse_mansion", "prob": 1 },
[ "fitness_band", 3 ]
]
},
{
Expand Down Expand Up @@ -1103,7 +1104,8 @@
[ "vitamins", 10 ],
[ "aspirin", 10 ],
[ "karate_gi", 10 ],
[ "judo_gi", 5 ]
[ "judo_gi", 5 ],
[ "fitness_band", 5 ]
]
},
{
Expand Down
6 changes: 4 additions & 2 deletions data/json/itemgroups/activities_hobbies.json
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@
[ "survnote", 1 ],
[ "halter_top", 30 ],
[ "iceaxe", 30 ],
[ "tourist_table", 30 ]
[ "tourist_table", 30 ],
[ "fitness_band", 5 ]
]
},
{
Expand Down Expand Up @@ -163,7 +164,8 @@
{ "item": "slingshot", "prob": 10 },
{ "item": "wristrocket", "prob": 5 },
{ "item": "powered_earmuffs", "prob": 80 },
{ "item": "bandolier_wrist", "prob": 100 }
{ "item": "bandolier_wrist", "prob": 100 },
{ "item": "fitness_band", "prob": 5 }
]
},
{
Expand Down
18 changes: 18 additions & 0 deletions data/json/items/tool_armor.json
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,24 @@
"use_action": "PORTABLE_GAME",
"magazines": [ [ "battery", [ "light_minus_battery_cell", "light_minus_atomic_battery_cell", "light_minus_disposable_cell" ] ] ]
},
{
"type": "TOOL_ARMOR",
"id": "fitness_band",
"name": "fitness band",
"category": "clothing",
"volume": "100 ml",
"description": "A fitness band that can track your heartbeat, exercise level and also has a clock. Activate to see your metrics.",
"weight": "30 g",
"to_hit": -1,
"color": "light_gray",
"covers": [ "HAND_EITHER" ],
"price": 5000,
"material": [ "plastic" ],
"flags": [ "WATCH", "WATER_FRIENDLY", "BELTED", "FRAGILE", "ALLOWS_NATURAL_ATTACKS", "OVERSIZE" ],
"coverage": 15,
"symbol": "[",
"use_action": "FITNESS_CHECK"
},
{
"id": "holo_cloak",
"type": "TOOL_ARMOR",
Expand Down
117 changes: 117 additions & 0 deletions src/character.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ static const efftype_id effect_bleed( "bleed" );
static const efftype_id effect_blind( "blind" );
static const efftype_id effect_blisters( "blisters" );
static const efftype_id effect_boomered( "boomered" );
static const efftype_id effect_cig( "cig" );
static const efftype_id effect_cold( "cold" );
static const efftype_id effect_common_cold( "common_cold" );
static const efftype_id effect_contacts( "contacts" );
Expand Down Expand Up @@ -214,11 +215,17 @@ static const bionic_id str_bio_night( "bio_night" );
// Aftershock stuff!
static const bionic_id afs_bio_linguistic_coprocessor( "afs_bio_linguistic_coprocessor" );

static const trait_id trait_BADTEMPER( "BADTEMPER" );
static const trait_id trait_BARK( "BARK" );
static const trait_id trait_BIRD_EYE( "BIRD_EYE" );
static const trait_id trait_CEPH_EYES( "CEPH_EYES" );
static const trait_id trait_CEPH_VISION( "CEPH_VISION" );
static const trait_id trait_CHEMIMBALANCE( "CHEMIMBALANCE" );
static const trait_id trait_CHLOROMORPH( "CHLOROMORPH" );
static const trait_id trait_COLDBLOOD( "COLDBLOOD" );
static const trait_id trait_COLDBLOOD2( "COLDBLOOD2" );
static const trait_id trait_COLDBLOOD3( "COLDBLOOD3" );
static const trait_id trait_COLDBLOOD4( "COLDBLOOD4" );
static const trait_id trait_DEAF( "DEAF" );
static const trait_id trait_DEBUG_CLOAK( "DEBUG_CLOAK" );
static const trait_id trait_DEBUG_LS( "DEBUG_LS" );
Expand Down Expand Up @@ -264,6 +271,7 @@ static const trait_id trait_PAWS_LARGE( "PAWS_LARGE" );
static const trait_id trait_PER_SLIME( "PER_SLIME" );
static const trait_id trait_PER_SLIME_OK( "PER_SLIME_OK" );
static const trait_id trait_PROF_FOODP( "PROF_FOODP" );
static const trait_id trait_QUICK( "QUICK" );
static const trait_id trait_PYROMANIA( "PYROMANIA" );
static const trait_id trait_RADIOGENIC( "RADIOGENIC" );
static const trait_id trait_ROOTS2( "ROOTS2" );
Expand All @@ -274,6 +282,7 @@ static const trait_id trait_SHELL( "SHELL" );
static const trait_id trait_SHELL2( "SHELL2" );
static const trait_id trait_SHOUT2( "SHOUT2" );
static const trait_id trait_SHOUT3( "SHOUT3" );
static const trait_id trait_SLIMESPAWNER( "SLIMESPAWNER" );
static const trait_id trait_SLIMY( "SLIMY" );
static const trait_id trait_STRONGSTOMACH( "STRONGSTOMACH" );
static const trait_id trait_THRESH_CEPHALOPOD( "THRESH_CEPHALOPOD" );
Expand Down Expand Up @@ -9405,3 +9414,111 @@ void Character::use_fire( const int quantity )
return;
}
}

int Character::heartrate_bpm() const
{
//Dead have no heartbeat usually and no heartbeat in omnicell
if( is_dead_state() || has_trait( trait_SLIMESPAWNER ) ) {
return 0;
}
//This function returns heartrate in BPM basing of health, physical state, tiredness,
//moral effects, stimulators and anything that should fit here.
//Some values are picked to make sense from math point of view
//and seem correct but effects may vary in real life.
//This needs more attention from experienced contributors to work more smooth.
//Average healthy bpm is 60-80. That's a simple imitation of normal distribution.
//Must a better way to do that. Possibly this value should be generated with player creation.
int average_heartbeat = 70 + rng( -5, 5 ) + rng( -5, 5 );
//Chemical imbalance makes this less predictable. It's possible this range needs tweaking
if( has_trait( trait_CHEMIMBALANCE ) ) {
average_heartbeat += rng( -15, 15 );
}
//Quick also raises basic BPM
if( has_trait( trait_QUICK ) ) {
average_heartbeat *= 1.1;
}
//Badtemper makes your BPM raise from anger
if( has_trait( trait_BADTEMPER ) ) {
average_heartbeat *= 1.1;
}
//COLDBLOOD dependencies, works almost same way as temperature effect for speed.
const int player_local_temp = g->weather.get_temperature( pos() );
float temperature_modifier = 0;
if( has_trait( trait_COLDBLOOD ) ) {
temperature_modifier = 0.002;
}
if( has_trait( trait_COLDBLOOD2 ) ) {
temperature_modifier = 0.00333;
}
if( has_trait( trait_COLDBLOOD3 ) || has_trait( trait_COLDBLOOD4 ) ) {
temperature_modifier = 0.005;
}
average_heartbeat *= 1 + ( ( player_local_temp - 65 ) * temperature_modifier );
//Limit avg from below with 20, arbitary
average_heartbeat = std::max( 20, average_heartbeat );
const float stamina_level = static_cast<float>( get_stamina() ) / get_stamina_max();
float stamina_effect = 0;
if( stamina_level >= 0.9 ) {
stamina_effect = 0;
} else if( stamina_level >= 0.8 ) {
stamina_effect = 0.2;
} else if( stamina_level >= 0.6 ) {
stamina_effect = 0.5;
} else if( stamina_level >= 0.4 ) {
stamina_effect = 1;
} else if( stamina_level >= 0.2 ) {
stamina_effect = 1.5;
} else {
stamina_effect = 2;
}
//can triple heartrate
int heartbeat = average_heartbeat * ( 1 + stamina_effect );
const int stim_level = get_stim();
int stim_modifer = 0;
if( stim_level > 0 ) {
//that's asymptotical function that is equal to 1 at around 30 stim level
//and slows down all the time almost reaching 2.
//Tweaking x*x multiplier will accordingly change effect accumulation
stim_modifer = 2 - 2 / ( 1 + 0.001 * stim_level * stim_level );
}
heartbeat += average_heartbeat * stim_modifer;
if( get_effect_dur( effect_cig ) > 0_turns ) {
//Nicotine-induced tachycardia
if( get_effect_dur( effect_cig ) > 10_minutes * ( addiction_level( ADD_CIG ) + 1 ) ) {
heartbeat += average_heartbeat * 0.4;
} else {
heartbeat += average_heartbeat * 0.1;
}
}
//health effect that can make things better or worse is applied in the end.
//Based on get_max_healthy that already has bmi factored
const int healthy = get_max_healthy();
//a bit arbitary formula that can use some love
float healthy_modifier = -0.05f * round( healthy / 20.0f );
heartbeat += average_heartbeat * healthy_modifier;
//Pain simply adds 2% per point after it reaches 5 (that's arbitary)
const int cur_pain = get_perceived_pain();
float pain_modifier = 0;
if( cur_pain > 5 ) {
pain_modifier = 0.02 * ( cur_pain - 5 );
}
heartbeat += average_heartbeat * pain_modifier;
//if BPM raised at least by 20% for a player with ADRENALINE, it adds 20% of avg to result
if( has_trait( trait_ADRENALINE ) && heartbeat > average_heartbeat * 1.2 ) {
heartbeat += average_heartbeat * 0.2;
}
//Happy get it bit faster and miserable some more.
//Morale effects might need more consideration
const int morale_level = get_morale_level();
if( morale_level >= 20 ) {
heartbeat += average_heartbeat * 0.1;
}
if( morale_level <= -20 ) {
heartbeat += average_heartbeat * 0.2;
}
//add fear?
//A single clamp in the end should be enough
const int max_heartbeat = average_heartbeat * 3.5;
heartbeat = clamp( heartbeat, average_heartbeat, max_heartbeat );
return heartbeat;
}
1 change: 1 addition & 0 deletions src/character.h
Original file line number Diff line number Diff line change
Expand Up @@ -1897,6 +1897,7 @@ class Character : public Creature, public visitable<Character>
void drench( int saturation, const body_part_set &flags, bool ignore_waterproof );
/** Recalculates morale penalty/bonus from wetness based on mutations, equipment and temperature */
void apply_wetness_morale( int temperature );
int heartrate_bpm() const;

protected:
Character();
Expand Down
1 change: 1 addition & 0 deletions src/item_factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -873,6 +873,7 @@ void Item_factory::init()
add_iuse( "PLANTBLECH", &iuse::plantblech );
add_iuse( "POISON", &iuse::poison );
add_iuse( "PORTABLE_GAME", &iuse::portable_game );
add_iuse( "FITNESS_CHECK", &iuse::fitness_check );
add_iuse( "PORTAL", &iuse::portal );
add_iuse( "PROZAC", &iuse::prozac );
add_iuse( "PURIFIER", &iuse::purifier );
Expand Down
38 changes: 38 additions & 0 deletions src/iuse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4448,6 +4448,44 @@ int iuse::portable_game( player *p, item *it, bool, const tripoint & )
return it->type->charges_to_use();
}

int iuse::fitness_check( player *p, item *it, bool, const tripoint & )
{
if( p->has_trait( trait_ILLITERATE ) ) {
p->add_msg_if_player( m_info, _( "You don't know what you're looking at." ) );
return 0;
} else {
//What else should block using f-band?
const int bpm = p->heartrate_bpm();
p->add_msg_if_player( _( "You check your health metrics on your %s." ), it->tname() );
//Maybe should pick better words
p->add_msg_if_player( _( "Your %s displays your heart's BPM: %i." ), it->tname(), bpm );
if( bpm > 179 ) {
p->add_msg_if_player( _( "Your %s shows warning: 'Slow down! "
"Your pulse is getting too high, champion!'" ), it->tname() );
}
const std::string exercise = p->activity_level_str();
if( exercise == "NO_EXERCISE" ) {
p->add_msg_if_player( _( "Your %s shows your overall activity: "
"'You are not really active today. Try going for a walk!'." ), it->tname() );
} else if( exercise == "LIGHT_EXERCISE" ) {
p->add_msg_if_player( _( "Your %s shows your overall activity: "
"'Good start! Keep it up and move more.'" ), it->tname() );
} else if( exercise == "MODERATE_EXERCISE" ) {
p->add_msg_if_player( _( "Your %s shows your overall activity: "
"'Doing good! Don't stop, push the limit!'" ), it->tname() );
} else if( exercise == "ACTIVE_EXERCISE" ) {
//Ad will most likely need to go
p->add_msg_if_player( _( "Your %s shows your overall activity: 'Great job! "
"Take a break from workout and refresh with a bottle of sport drink!'" ), it->tname() );
} else {
p->add_msg_if_player( _( "Your %s shows your overall activity: "
"'You are too active! Avoid overexertion for your safety and health.'" ), it->tname() );
}
//TODO add whatever else makes sense (sleep quality, health level approximation?)
}
return it->type->charges_to_use();
}

int iuse::hand_crank( player *p, item *it, bool, const tripoint & )
{
if( p->is_npc() ) {
Expand Down
1 change: 1 addition & 0 deletions src/iuse.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ class iuse
int dive_tank( player *, item *, bool, const tripoint & );
int gasmask( player *, item *, bool, const tripoint & );
int portable_game( player *, item *, bool, const tripoint & );
int fitness_check( player *p, item *it, bool, const tripoint & );
int vibe( player *, item *, bool, const tripoint & );
int hand_crank( player *, item *, bool, const tripoint & );
int vortex( player *, item *, bool, const tripoint & );
Expand Down

0 comments on commit 6f4bbd5

Please sign in to comment.