Skip to content

Commit f089b65

Browse files
GuardianDllehughsbairdmqrause
authored
Initial implementation of wounds (#82525)
* Initial implementation of wounds * Apply suggestions from code review Co-authored-by: ehughsbaird <[email protected]> * Apply suggestions from code review Co-authored-by: mqrause <[email protected]> * Update doc/JSON/WOUNDS.md * Apply suggestions from code review Co-authored-by: ehughsbaird <[email protected]> * try to sort finalization a bit --------- Co-authored-by: ehughsbaird <[email protected]> Co-authored-by: mqrause <[email protected]>
1 parent 6d28f7d commit f089b65

File tree

17 files changed

+486
-3
lines changed

17 files changed

+486
-3
lines changed

data/core/external_options.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,13 @@
104104
"stype": "bool",
105105
"value": false
106106
},
107+
{
108+
"type": "EXTERNAL_OPTION",
109+
"name": "WOUND_CHANCE",
110+
"//": "Probability to get a wound from taking damage. 0.25 means 25% to get a wound.",
111+
"stype": "float",
112+
"value": 0.25
113+
},
107114
{
108115
"type": "EXTERNAL_OPTION",
109116
"name": "PAIN_PENALTY_MOD_STR",

data/mods/Isolation-Protocol/game_balance.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,11 @@
1616
"name": "WEARY_RECOVERY_MULT",
1717
"stype": "float",
1818
"value": 2
19+
},
20+
{
21+
"type": "EXTERNAL_OPTION",
22+
"name": "WOUND_CHANCE",
23+
"stype": "float",
24+
"value": 0.0
1925
}
2026
]

data/mods/aftershock_exoplanet/options.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@
1111
"//": "Anesthetic cost removed in this mod, as autodocs do all the heavy lifting.",
1212
"tools": [ [ ] ]
1313
},
14+
{
15+
"type": "EXTERNAL_OPTION",
16+
"name": "WOUND_CHANCE",
17+
"stype": "float",
18+
"value": 0.0
19+
},
1420
{
1521
"type": "EXTERNAL_OPTION",
1622
"name": "CBM_SLOTS_ENABLED",

doc/JSON/WOUNDS.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Wounds
2+
3+
Wound is a type, that affects specific bodyparts. It's similar in this to effects, but it can be applied only on specific bodyparts, and their ability to be aided/repaired using (at the moment of writing, yet to be added) wound_fix
4+
5+
### Wound
6+
7+
```jsonc
8+
{
9+
"id": "scratch",
10+
"type": "wound",
11+
"name": { "str": "scratch", "str_pl": "scratches" },
12+
"description": "Foobar.",
13+
"weight": 10, // weight of a wound, determines the chance of this specific wound to be picked when limb takes damage. Default 1
14+
"damage_types": [ "cut", "bash" ], // only taking these type of damage can apply wound. Mandatory
15+
"damage_required": [ 1, 1000 ], // smallest and highest damage that is required for this wound to be applied. Mandatory
16+
"pain": [ 1, 10 ], // when wound is applied, it would give character this random amount of pain rolled between this two numbers. Default 0
17+
"healing_time": [ "2 hours", "25 days" ], // how long this wound need time to be fully healed. Rolled randomly when applied, supposed to be adjusted by wound_fix.
18+
// Default infinite duration
19+
"whitelist_bp_with_flag": "LIMB_UPPER", // only body parts with this flag can receive the wound.
20+
"blacklist_bp_with_flag": "CYBERNETIC_OR_WHATEVER_IT_DOESNT_EXIST_YET", // Bodyparts with this flag cannot receive this wound.
21+
}
22+
```
23+

src/bodypart.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <vector>
1010

1111
#include "body_part_set.h"
12+
#include "calendar.h"
1213
#include "creature.h"
1314
#include "debug.h"
1415
#include "enum_conversions.h"
@@ -20,6 +21,8 @@
2021
#include "pimpl.h"
2122
#include "rng.h"
2223
#include "subbodypart.h"
24+
#include "type_id.h"
25+
#include "wound.h"
2326

2427
const bodypart_str_id body_part_arm_l( "arm_l" );
2528
const bodypart_str_id body_part_arm_r( "arm_r" );
@@ -514,6 +517,14 @@ void body_part_type::finalize()
514517
if( !damage.empty() ) {
515518
unarmed_bonus = true;
516519
}
520+
521+
for( const wound_type &wd : wound_type::get_all() ) {
522+
if( wd.allowed_on_bodypart( id ) ) {
523+
const bp_wounds bpw = { wd.id, wd.damage_types, wd.damage_required };
524+
potential_wounds.add( bpw, wd.weight );
525+
}
526+
}
527+
517528
finalize_damage_map( armor.resist_vals );
518529
}
519530

@@ -1031,6 +1042,28 @@ float bodypart::get_limb_score_max( const limb_score_id &score ) const
10311042
return id->get_limb_score_max( score );
10321043
}
10331044

1045+
std::vector<wound> bodypart::get_wounds() const
1046+
{
1047+
return wounds;
1048+
}
1049+
1050+
void bodypart::add_wound( wound &wd )
1051+
{
1052+
wounds.push_back( wd );
1053+
}
1054+
1055+
void bodypart::add_wound( wound_type_id wd )
1056+
{
1057+
wounds.emplace_back( wd );
1058+
}
1059+
1060+
void bodypart::update_wounds( time_duration time_passed )
1061+
{
1062+
wounds.erase( std::remove_if( wounds.begin(), wounds.end(), [time_passed]( wound & wd ) {
1063+
return wd.update_wound( time_passed );
1064+
} ), wounds.end() );
1065+
}
1066+
10341067
int bodypart::get_hp_cur() const
10351068
{
10361069
return hp_cur;
@@ -1224,6 +1257,8 @@ void bodypart::serialize( JsonOut &json ) const
12241257
json.member( "temp_conv", units::to_legacy_bodypart_temp( temp_conv ) );
12251258
json.member( "frostbite_timer", frostbite_timer );
12261259

1260+
json.member( "wounds", wounds );
1261+
12271262
json.end_object();
12281263
}
12291264

@@ -1245,6 +1280,8 @@ void bodypart::deserialize( const JsonObject &jo )
12451280
}
12461281
jo.read( "frostbite_timer", frostbite_timer, true );
12471282

1283+
jo.read( "wounds", wounds );
1284+
12481285
}
12491286

12501287
void stat_hp_mods::load( const JsonObject &jsobj )

src/bodypart.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,13 @@
2020
#include "type_id.h"
2121
#include "units.h"
2222
#include "weather.h"
23+
#include "weighted_list.h"
24+
#include "wound.h"
2325

2426
class Creature;
2527
class JsonObject;
2628
class JsonOut;
29+
class time_duration;
2730
struct body_part_type;
2831
struct localized_comparator;
2932
template <typename E> struct enum_traits;
@@ -135,6 +138,14 @@ struct bp_limb_score {
135138
float max = 0.0f;
136139
};
137140

141+
struct bp_wounds {
142+
wound_type_id id;
143+
// damage type that can apply this specific wound
144+
std::vector<damage_type_id> damage_type;
145+
// how much damage one need to deal to apply this wound, min and max
146+
std::pair<int, int> damage_required;
147+
};
148+
138149
struct bp_onhit_effect {
139150
// ID of the effect to apply
140151
efftype_id id;
@@ -317,6 +328,8 @@ struct body_part_type {
317328
// TODO: Coverage/Encumbrance multiplier
318329
std::vector<bodypart_str_id> similar_bodyparts;
319330

331+
weighted_int_list<bp_wounds> potential_wounds;
332+
320333
private:
321334
int bionic_slots_ = 0;
322335
body_part_type::type _primary_limb_type = body_part_type::type::num_types;
@@ -456,6 +469,8 @@ class bodypart
456469

457470
std::array<int, NUM_WATER_TOLERANCE> mut_drench; // NOLINT(cata-serialize)
458471

472+
std::vector<wound> wounds;
473+
459474
// adjust any limb "value" based on how wounded the limb is. scaled to 0-75%
460475
float wound_adjusted_limb_value( float val ) const;
461476
// Same idea as for wounds, though not all scores get this applied. Should be applied after wounds.
@@ -500,6 +515,12 @@ class bodypart
500515
int override_wounds = -1 ) const;
501516
float get_limb_score_max( const limb_score_id &score ) const;
502517

518+
std::vector<wound> get_wounds() const;
519+
520+
void add_wound( wound &wd );
521+
void add_wound( wound_type_id wd );
522+
void update_wounds( time_duration time_passed );
523+
503524
int get_hp_cur() const;
504525
int get_hp_max() const;
505526
float get_hit_size() const;

src/character.cpp

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,8 @@
113113
#include "translation.h"
114114
#include "translations.h"
115115
#include "trap.h"
116-
#include "uilist.h"
117116
#include "ui_manager.h"
117+
#include "uilist.h"
118118
#include "uistate.h"
119119
#include "units.h"
120120
#include "value_ptr.h"
@@ -127,6 +127,7 @@
127127
#include "vpart_range.h"
128128
#include "weather.h"
129129
#include "weather_type.h"
130+
#include "wound.h"
130131

131132
class activity_actor;
132133

@@ -2546,6 +2547,8 @@ void Character::process_turn()
25462547
}
25472548
}
25482549

2550+
update_wounds( 1_turns );
2551+
25492552
// We can dodge again! Assuming we can actually move...
25502553
if( in_sleep_state() ) {
25512554
blocks_left = 0;
@@ -7957,6 +7960,21 @@ void Character::update_cached_mutations()
79577960
trait_flag_cache.clear();
79587961
}
79597962

7963+
void Character::apply_wound( bodypart_id bp, wound_type_id wd )
7964+
{
7965+
bodypart &body_bp = body.at( bp.id() );
7966+
body_bp.add_wound( wd );
7967+
morale->on_stat_change( "perceived_pain", get_perceived_pain() );
7968+
}
7969+
7970+
void Character::update_wounds( time_duration time_passed )
7971+
{
7972+
for( auto &bp : body ) {
7973+
bp.second.update_wounds( time_passed );
7974+
}
7975+
morale->on_stat_change( "perceived_pain", get_perceived_pain() );
7976+
}
7977+
79607978
void Character::passive_absorb_hit( const bodypart_id &bp, damage_unit &du ) const
79617979
{
79627980
// >0 check because some mutations provide negative armor
@@ -8377,6 +8395,30 @@ void Character::apply_damage( Creature *source, bodypart_id hurt, int dam,
83778395
}
83788396
}
83798397

8398+
void Character::apply_random_wound( bodypart_id bp, const damage_instance &d )
8399+
{
8400+
if( x_in_y( 1.0f - get_option<float>( "WOUND_CHANCE" ), 1.0f ) ) {
8401+
return;
8402+
}
8403+
8404+
weighted_int_list<wound_type_id> possible_wounds;
8405+
for( const weighted_object<int, bp_wounds> &wd : bp->potential_wounds ) {
8406+
for( const damage_unit &du : d.damage_units ) {
8407+
const bool damage_within_limits =
8408+
du.amount >= wd.obj.damage_required.first &&
8409+
du.amount <= wd.obj.damage_required.second;
8410+
const bool damage_type_matches = std::find( wd.obj.damage_type.begin(), wd.obj.damage_type.end(),
8411+
du.type ) != wd.obj.damage_type.end();
8412+
if( damage_within_limits && damage_type_matches ) {
8413+
possible_wounds.add( wd.obj.id, wd.weight );
8414+
}
8415+
}
8416+
}
8417+
if( !possible_wounds.empty() ) {
8418+
apply_wound( bp, *possible_wounds.pick() );
8419+
}
8420+
}
8421+
83808422
dealt_damage_instance Character::deal_damage( Creature *source, bodypart_id bp,
83818423
const damage_instance &d, const weakpoint_attack &attack, const weakpoint & )
83828424
{
@@ -8493,6 +8535,8 @@ dealt_damage_instance Character::deal_damage( Creature *source, bodypart_id bp,
84938535
}
84948536
}
84958537

8538+
apply_random_wound( bp, d );
8539+
84968540
on_hurt( source );
84978541
return dealt_dams;
84988542
}
@@ -12798,6 +12842,18 @@ bool Character::immune_to( const bodypart_id &bp, damage_unit dam ) const
1279812842
return dam.amount <= 0;
1279912843
}
1280012844

12845+
int Character::get_pain() const
12846+
{
12847+
int p = 0;
12848+
for( const std::pair<const bodypart_str_id, bodypart> &bp : get_body() ) {
12849+
for( const wound &wd : bp.second.get_wounds() ) {
12850+
p += wd.get_pain();
12851+
}
12852+
}
12853+
12854+
return p + Creature::get_pain();
12855+
}
12856+
1280112857
int Character::mod_pain( int npain )
1280212858
{
1280312859
if( npain > 0 ) {

src/character.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -952,6 +952,10 @@ class Character : public Creature, public visitable
952952
void update_stomach( const time_point &from, const time_point &to );
953953
/** Updates the mutations from enchantments */
954954
void update_cached_mutations();
955+
/** Adds this specific wound to bodypart */
956+
void apply_wound( bodypart_id bp, wound_type_id wd );
957+
/** Updates the status of wounds character has */
958+
void update_wounds( time_duration time_passed );
955959
/** Returns true if character needs food, false if character is an NPC with NO_NPC_FOOD set */
956960
bool needs_food() const;
957961
/** Increases hunger, thirst, sleepiness and stimulants wearing off. `rate_multiplier` is for retroactive updates. */
@@ -1289,6 +1293,7 @@ class Character : public Creature, public visitable
12891293
/** Actually hurt the player, hurts a body_part directly, no armor reduction */
12901294
void apply_damage( Creature *source, bodypart_id hurt, int dam,
12911295
bool bypass_med = false ) override;
1296+
void apply_random_wound( bodypart_id bp, const damage_instance &d );
12921297
/** Calls Creature::deal_damage and handles damaged effects (waking up, etc.) */
12931298
dealt_damage_instance deal_damage( Creature *source, bodypart_id bp,
12941299
const damage_instance &d,
@@ -2677,6 +2682,8 @@ class Character : public Creature, public visitable
26772682
*/
26782683
bool immune_to( const bodypart_id &bp, damage_unit dam ) const;
26792684

2685+
/** Modifies a pain value by wounds before passing it to Creature::mod_pain() */
2686+
int get_pain() const override;
26802687
/** Modifies a pain value by player traits before passing it to Creature::mod_pain() */
26812688
int mod_pain( int npain ) override;
26822689
/** Sets new intensity of pain an reacts to it */

src/character_morale.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,11 @@ void Character::check_and_recover_morale()
208208
apply_persistent_morale();
209209

210210
if( !morale->consistent_with( test_morale ) ) {
211+
212+
add_msg_debug( debugmode::DF_CHARACTER, "Test morale:\n%s\n", test_morale.to_string_writable() );
213+
add_msg_debug( debugmode::DF_CHARACTER, "Actual %s morale:\n%s\n", disp_name( true ),
214+
morale->to_string_writable() );
215+
211216
*morale = player_morale( test_morale ); // Recover consistency
212217
add_msg_debug( debugmode::DF_CHARACTER, "%s morale was recovered.", disp_name( true ) );
213218
}

src/creature.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -824,7 +824,6 @@ class Creature : public viewer
824824
bodypart_id get_random_body_part( bool main = false ) const;
825825
/**
826826
* Returns body parts this creature have.
827-
* @param only_main If true, only displays parts that can have hit points
828827
*/
829828
std::vector<bodypart_id> get_all_body_parts(
830829
get_body_part_flags = get_body_part_flags::none ) const;

0 commit comments

Comments
 (0)