Skip to content

Commit

Permalink
src,data,tests: Add bodypart status widget
Browse files Browse the repository at this point in the history
Indicates bitten, bleeding, and/or infected status by comma-separated
list of words, with colorization by limb_color.

- New display::bodypart_status - bitten, bleeding, and/or infected
- New display::bodypart_status_text_color - colorized status for part
- New bp_status_text widget_var - showing bodypart_status_text_color
- New widget declarations in sidebar.json
- Test cases

TODO:
- Fold limb_color refactor into this
- Remove "top" and "bottom" layouts, replace with more useful layout
  • Loading branch information
wapcaplet committed Jan 2, 2022
1 parent 85b999a commit d3d56bf
Show file tree
Hide file tree
Showing 7 changed files with 241 additions and 0 deletions.
65 changes: 65 additions & 0 deletions data/json/ui/bodyparts.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
[
{
"id": "bodypart_status_text",
"//": "Base widget for showing body part status; needs bodypart field defined in derived widget.",
"type": "widget",
"style": "text",
"var": "bp_status_text"
},
{
"id": "bp_status_head_text",
"type": "widget",
"label": "HEAD",
"bodypart": "head",
"copy-from": "bodypart_status_text"
},
{
"id": "bp_status_torso_text",
"type": "widget",
"label": "TORSO",
"bodypart": "torso",
"copy-from": "bodypart_status_text"
},
{
"id": "bp_status_left_arm_text",
"type": "widget",
"label": "L ARM",
"bodypart": "arm_l",
"copy-from": "bodypart_status_text"
},
{
"id": "bp_status_right_arm_text",
"type": "widget",
"label": "R ARM",
"bodypart": "arm_r",
"copy-from": "bodypart_status_text"
},
{
"id": "bp_status_left_leg_text",
"type": "widget",
"label": "L LEG",
"bodypart": "leg_l",
"copy-from": "bodypart_status_text"
},
{
"id": "bp_status_right_leg_text",
"type": "widget",
"label": "R LEG",
"bodypart": "leg_r",
"copy-from": "bodypart_status_text"
},
{
"id": "bodypart_status_top_layout",
"type": "widget",
"style": "layout",
"arrange": "columns",
"widgets": [ "bp_status_left_arm_text", "bp_status_head_text", "bp_status_right_arm_text" ]
},
{
"id": "bodypart_status_bottom_layout",
"type": "widget",
"style": "layout",
"arrange": "columns",
"widgets": [ "bp_status_left_leg_text", "bp_status_torso_text", "bp_status_right_leg_text" ]
}
]
16 changes: 16 additions & 0 deletions data/mods/TEST_DATA/widgets.json
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,22 @@
"bodypart": "torso",
"style": "number"
},
{
"id": "test_status_torso_text",
"type": "widget",
"label": "TORSO STATUS",
"var": "bp_status_text",
"bodypart": "torso",
"style": "text"
},
{
"id": "test_status_left_arm_text",
"type": "widget",
"label": "LEFT ARM STATUS",
"var": "bp_status_text",
"bodypart": "arm_l",
"style": "text"
},
{
"id": "test_stat_panel",
"type": "widget",
Expand Down
39 changes: 39 additions & 0 deletions src/panels.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1432,6 +1432,45 @@ std::pair<std::string, nc_color> display::rad_badge_text_color( const Character
return std::make_pair( rad_text, rad_color );
}

std::string display::bodypart_status( const Character &u, const bodypart_id &bp )
{
if( bp == bodypart_str_id::NULL_ID() ) {
return "";
}
std::string bp_status;
const int bleed_intensity = u.get_effect_int( effect_bleed, bp );
const bool bleeding = bleed_intensity > 0;
const bool bitten = u.has_effect( effect_bite, bp.id() );
const bool infected = u.has_effect( effect_infected, bp.id() );

std::vector<std::string> ailments;
if( bitten ) {
ailments.emplace_back( "bitten" );
}
if( bleeding ) {
ailments.emplace_back( "bleeding" );
}
if( infected ) {
ailments.emplace_back( "infected" );
}
// TODO: Include bandage/antiseptic quality, broken limbs, splints
if( ailments.empty() ) {
bp_status = "--";
} else {
bp_status = join( ailments, ", " );
}

return bp_status;
}

std::pair<std::string, nc_color> display::bodypart_status_text_color( const Character &u,
const bodypart_id &bp )
{
std::string bp_stat_text = display::bodypart_status( u, bp );
nc_color bp_stat_color = display::limb_color( u, bp, true, true, true );
return std::make_pair( bp_stat_text, bp_stat_color );
}

static void draw_stats( avatar &u, const catacurses::window &w )
{
werase( w );
Expand Down
6 changes: 6 additions & 0 deletions src/panels.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ std::pair<std::string, nc_color> morale_emotion( const int morale_cur, const moo

// Current movement mode (as single letter) and color
std::pair<std::string, nc_color> move_mode_text_color( const Character &u );
// Current body part status (bleeding, bitten, infected) phrase and color
std::pair<std::string, nc_color> bodypart_status_text_color( const Character &u,
const bodypart_id &bp );

// TODO: Swap text/string order to match previous functions
std::pair<std::string, nc_color> temp_text_color( const Character &u );
Expand All @@ -101,6 +104,9 @@ nc_color bodytemp_color( const Character &u, const bodypart_id &bp );
// Returns color which this limb would have in healing menus
nc_color limb_color( const Character &u, const bodypart_id &bp, bool bleed, bool bite,
bool infect );
// Returns status phrase for status of body part (bleeding, bitten, and/or infected)
std::string bodypart_status( const Character &u, const bodypart_id &bp );

// Color for displaying the given encumbrance level
nc_color encumb_color( const int level );

Expand Down
6 changes: 6 additions & 0 deletions src/widget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ std::string enum_to_string<widget_var>( widget_var data )
return "activity_text";
case widget_var::body_temp_text:
return "body_temp_text";
case widget_var::bp_status_text:
return "bp_status_text";
case widget_var::date_text:
return "date_text";
case widget_var::env_temp_text:
Expand Down Expand Up @@ -334,6 +336,7 @@ bool widget::uses_text_function()
switch( _var ) {
case widget_var::activity_text:
case widget_var::body_temp_text:
case widget_var::bp_status_text:
case widget_var::date_text:
case widget_var::env_temp_text:
case widget_var::fatigue_text:
Expand Down Expand Up @@ -374,6 +377,9 @@ std::string widget::color_text_function_string( const avatar &ava )
case widget_var::body_temp_text:
desc = display::temp_text_color( ava );
break;
case widget_var::bp_status_text:
desc = display::bodypart_status_text_color( ava, _bp_id );
break;
case widget_var::date_text:
desc.first = display::date_string();
break;
Expand Down
1 change: 1 addition & 0 deletions src/widget.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ enum class widget_var : int {
// Text vars
activity_text, // Activity level text, color string
body_temp_text, // Felt body temperature, color string
bp_status_text, // Status of bodypart (bleeding, bitten, and/or infected)
date_text, // Current date, in terms of day within season
env_temp_text, // Environment temperature, if character has thermometer
fatigue_text, // Fagitue description text, color string
Expand Down
108 changes: 108 additions & 0 deletions tests/widget_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
#include "morale.h"
#include "widget.h"

static const efftype_id effect_bite( "bite" );
static const efftype_id effect_bleed( "bleed" );
static const efftype_id effect_infected( "infected" );

static const itype_id itype_rad_badge( "rad_badge" );

// test widgets defined in data/json/sidebar.json and data/mods/TEST_DATA/widgets.json
Expand All @@ -28,6 +32,8 @@ static const widget_id widget_test_speed_num( "test_speed_num" );
static const widget_id widget_test_stamina_graph( "test_stamina_graph" );
static const widget_id widget_test_stamina_num( "test_stamina_num" );
static const widget_id widget_test_stat_panel( "test_stat_panel" );
static const widget_id widget_test_status_left_arm_text( "test_status_left_arm_text" );
static const widget_id widget_test_status_torso_text( "test_status_torso_text" );
static const widget_id widget_test_str_num( "test_str_num" );
static const widget_id widget_test_text_widget( "test_text_widget" );
static const widget_id widget_test_weariness_num( "test_weariness_num" );
Expand Down Expand Up @@ -342,6 +348,108 @@ TEST_CASE( "widgets showing avatar attributes", "[widget][avatar]" )
CHECK( head_graph_w.layout( ava ) == "HEAD: ,,,,," );
}

SECTION( "bodypart status" ) {
bodypart_id arm( "arm_l" );
bodypart_id torso( "torso" );
widget arm_status_w = widget_test_status_left_arm_text.obj();
widget torso_status_w = widget_test_status_torso_text.obj();

// No ailments
CHECK( arm_status_w.layout( ava ) == "LEFT ARM STATUS: <color_c_light_gray>--</color>" );
CHECK( torso_status_w.layout( ava ) == "TORSO STATUS: <color_c_light_gray>--</color>" );

// Add various ailments to the left arm, and ensure status is displayed correctly,
// while torso status display is unaffected

WHEN( "bitten" ) {
ava.add_effect( effect_bite, 1_minutes, arm );
CHECK( arm_status_w.layout( ava ) == "LEFT ARM STATUS: <color_c_blue>bitten</color>" );
CHECK( torso_status_w.layout( ava ) == "TORSO STATUS: <color_c_light_gray>--</color>" );
}

WHEN( "bleeding" ) {
ava.add_effect( effect_bleed, 1_minutes, arm );
CHECK( arm_status_w.layout( ava ) == "LEFT ARM STATUS: <color_c_light_red>bleeding</color>" );
CHECK( torso_status_w.layout( ava ) == "TORSO STATUS: <color_c_light_gray>--</color>" );
}

WHEN( "infected" ) {
ava.add_effect( effect_infected, 1_minutes, arm );
CHECK( arm_status_w.layout( ava ) == "LEFT ARM STATUS: <color_c_green>infected</color>" );
CHECK( torso_status_w.layout( ava ) == "TORSO STATUS: <color_c_light_gray>--</color>" );
}

WHEN( "bitten and bleeding" ) {
ava.add_effect( effect_bite, 1_minutes, arm );
ava.add_effect( effect_bleed, 1_minutes, arm );
CHECK( arm_status_w.layout( ava ) == "LEFT ARM STATUS: <color_c_magenta>bitten, bleeding</color>" );
CHECK( torso_status_w.layout( ava ) == "TORSO STATUS: <color_c_light_gray>--</color>" );
}

WHEN( "bitten and infected" ) {
ava.add_effect( effect_bite, 1_minutes, arm );
ava.add_effect( effect_infected, 1_minutes, arm );
CHECK( arm_status_w.layout( ava ) == "LEFT ARM STATUS: <color_c_green>bitten, infected</color>" );
CHECK( torso_status_w.layout( ava ) == "TORSO STATUS: <color_c_light_gray>--</color>" );
}

WHEN( "bleeding and infected" ) {
ava.add_effect( effect_bleed, 1_minutes, arm );
ava.add_effect( effect_infected, 1_minutes, arm );
CHECK( arm_status_w.layout( ava ) ==
"LEFT ARM STATUS: <color_c_yellow>bleeding, infected</color>" );
CHECK( torso_status_w.layout( ava ) == "TORSO STATUS: <color_c_light_gray>--</color>" );
}

WHEN( "bitten, bleeding, and infected" ) {
ava.add_effect( effect_bite, 1_minutes, arm );
ava.add_effect( effect_bleed, 1_minutes, arm );
ava.add_effect( effect_infected, 1_minutes, arm );
CHECK( arm_status_w.layout( ava ) ==
"LEFT ARM STATUS: <color_c_yellow>bitten, bleeding, infected</color>" );
CHECK( torso_status_w.layout( ava ) == "TORSO STATUS: <color_c_light_gray>--</color>" );
}
}

SECTION( "pain" ) {
widget pain_num_w = widget_test_pain_num.obj();
widget pain_text_w = widget_test_pain_text.obj();

ava.set_pain( 0 );
CHECK( pain_num_w.layout( ava ) == "PAIN: 0" );
CHECK( pain_text_w.layout( ava ) == "PAIN: <color_c_white></color>" );
// FIXME: Is it possible for "No pain" to be displayed?
//CHECK( pain_text_w.layout( ava ) == "PAIN: <color_c_white>No pain</color>" );

ava.set_pain( 1 );
CHECK( pain_num_w.layout( ava ) == "PAIN: 1" );
CHECK( pain_text_w.layout( ava ) == "PAIN: <color_c_yellow>Minimal pain</color>" );
ava.set_pain( 10 );
CHECK( pain_num_w.layout( ava ) == "PAIN: 10" );
CHECK( pain_text_w.layout( ava ) == "PAIN: <color_c_yellow>Minimal pain</color>" );
ava.set_pain( 20 );
CHECK( pain_num_w.layout( ava ) == "PAIN: 20" );
CHECK( pain_text_w.layout( ava ) == "PAIN: <color_c_yellow>Mild pain</color>" );
ava.set_pain( 30 );
CHECK( pain_num_w.layout( ava ) == "PAIN: 30" );
CHECK( pain_text_w.layout( ava ) == "PAIN: <color_c_yellow>Moderate pain</color>" );
ava.set_pain( 40 );
CHECK( pain_num_w.layout( ava ) == "PAIN: 40" );
CHECK( pain_text_w.layout( ava ) == "PAIN: <color_c_light_red>Distracting pain</color>" );
ava.set_pain( 50 );
CHECK( pain_num_w.layout( ava ) == "PAIN: 50" );
CHECK( pain_text_w.layout( ava ) == "PAIN: <color_c_light_red>Distressing pain</color>" );
ava.set_pain( 60 );
CHECK( pain_num_w.layout( ava ) == "PAIN: 60" );
CHECK( pain_text_w.layout( ava ) == "PAIN: <color_c_red>Unmanageable pain</color>" );
ava.set_pain( 70 );
CHECK( pain_num_w.layout( ava ) == "PAIN: 70" );
CHECK( pain_text_w.layout( ava ) == "PAIN: <color_c_red>Intense pain</color>" );
ava.set_pain( 80 );
CHECK( pain_num_w.layout( ava ) == "PAIN: 80" );
CHECK( pain_text_w.layout( ava ) == "PAIN: <color_c_red>Severe pain</color>" );
}

SECTION( "weariness" ) {
widget weariness_w = widget_test_weariness_num.obj();

Expand Down

0 comments on commit d3d56bf

Please sign in to comment.