diff --git a/data/json/flags.json b/data/json/flags.json index d270a0b6e9b41..f7590b315fc3f 100644 --- a/data/json/flags.json +++ b/data/json/flags.json @@ -1218,6 +1218,11 @@ "type": "json_flag", "context": [ ] }, + { + "id": "CRUTCHES", + "type": "json_flag", + "context": [ ] + }, { "id": "CUSTOM_EXPLOSION", "type": "json_flag", diff --git a/data/json/items/melee/bludgeons.json b/data/json/items/melee/bludgeons.json index 7d6f391c00a16..4b10824004ffc 100644 --- a/data/json/items/melee/bludgeons.json +++ b/data/json/items/melee/bludgeons.json @@ -337,13 +337,32 @@ "symbol": "/", "material": [ "wood", "aluminum" ], "techniques": [ "WBLOCK_1" ], - "flags": [ "NONCONDUCTIVE" ], + "flags": [ "NONCONDUCTIVE", "CRUTCHES" ], "volume": "1 L", "longest_side": "80 cm", "bashing": 10, "price": 16000, "price_postapoc": 100 }, + { + "id": "crutches", + "type": "GENERIC", + "symbol": "/", + "color": "dark_gray", + "name": { "str": "crutches", "str_pl": "pairs of crutches" }, + "description": "A pair of crutches.", + "price": 2500, + "material": [ "aluminum" ], + "techniques": [ "WBLOCK_1", "SWEEP" ], + "flags": [ "CRUTCHES" ], + "weight": "2200 g", + "volume": "1500 ml", + "longest_side": "150 cm", + "bashing": 24, + "to_hit": 2, + "price_postapoc": 1250, + "category": "weapons" + }, { "type": "GENERIC", "id": "cudgel", diff --git a/data/json/move_modes.json b/data/json/move_modes.json index e3cfd9bcda5b5..5fdf737e80844 100644 --- a/data/json/move_modes.json +++ b/data/json/move_modes.json @@ -52,5 +52,22 @@ "sound_multiplier": 0.5, "move_speed_multiplier": 0.5, "swim_speed_mod": 50 + }, + { + "type": "movement_mode", + "id": "prone", + "character": "p", + "panel_char": "P", + "name": "prone", + "panel_color": "green", + "symbol_color": "blue", + "exertion_level": "NO_EXERCISE", + "change_good_none": "You lie down.", + "change_good_animal": "You stop your steed.", + "change_good_mech": "You turn off your mech's leg servos.", + "move_type": "prone", + "sound_multiplier": 0.2, + "move_speed_multiplier": 0.2, + "swim_speed_mod": 50 } ] diff --git a/src/action.h b/src/action.h index b8db9c42cd158..9ae737a786658 100644 --- a/src/action.h +++ b/src/action.h @@ -66,6 +66,8 @@ enum action_id : int { ACTION_TOGGLE_RUN, /** Toggle crouch on/off */ ACTION_TOGGLE_CROUCH, + /** Toggle lying down on/off */ + ACTION_TOGGLE_PRONE, /** Open movement mode menu */ ACTION_OPEN_MOVEMENT, /**@}*/ diff --git a/src/activity_actor.cpp b/src/activity_actor.cpp index 318bd48f0c655..cad6a2d57d659 100644 --- a/src/activity_actor.cpp +++ b/src/activity_actor.cpp @@ -1587,10 +1587,12 @@ std::unique_ptr consume_activity_actor::deserialize( JsonIn &jsi return actor.clone(); } -void try_sleep_activity_actor::start( player_activity &act, Character &/*who*/ ) +void try_sleep_activity_actor::start( player_activity &act, Character &who ) { act.moves_total = to_moves( duration ); act.moves_left = act.moves_total; + who.set_movement_mode( move_mode_id( "prone" ) ); + who.add_msg_if_player( _( "You lie down preparing to fall asleep." ) ); } void try_sleep_activity_actor::do_turn( player_activity &act, Character &who ) @@ -1621,6 +1623,7 @@ void try_sleep_activity_actor::finish( player_activity &act, Character &who ) if( !who.has_effect( effect_sleep ) ) { who.add_msg_if_player( _( "You try to sleep, but can't." ) ); } + who.set_movement_mode( move_mode_id( "walk" ) ); } void try_sleep_activity_actor::query_keep_trying( player_activity &act, Character &who ) diff --git a/src/avatar.cpp b/src/avatar.cpp index 4e6fb1144db10..da87a48493382 100644 --- a/src/avatar.cpp +++ b/src/avatar.cpp @@ -1396,6 +1396,14 @@ void avatar::toggle_crouch_mode() } } +void avatar::toggle_prone_mode() +{ + if( is_prone() ) { + set_movement_mode( move_mode_id( "walk" ) ); + } else { + set_movement_mode( move_mode_id( "prone" ) ); + } +} void avatar::activate_crouch_mode() { if( !is_crouching() ) { diff --git a/src/avatar.h b/src/avatar.h index 7524228734f69..8131389dc1bda 100644 --- a/src/avatar.h +++ b/src/avatar.h @@ -226,6 +226,8 @@ class avatar : public player void toggle_run_mode(); // Toggles crouching on/off. void toggle_crouch_mode(); + // Toggles lying down on/off. + void toggle_prone_mode(); // Activate crouch mode if not in crouch mode. void activate_crouch_mode(); diff --git a/src/avatar_action.cpp b/src/avatar_action.cpp index ccc0986c6ffb7..2c8ebc48b6502 100644 --- a/src/avatar_action.cpp +++ b/src/avatar_action.cpp @@ -90,6 +90,15 @@ bool avatar_action::move( avatar &you, map &m, const tripoint &d ) } return false; } + + // If any leg broken without crutches and not already on the ground topple over + if( ( you.get_working_leg_count() < 2 && !you.weapon.has_flag( flag_CRUTCHES ) ) && + !you.is_prone() ) { + you.set_movement_mode( move_mode_id( "prone" ) ); + you.add_msg_if_player( m_bad, + _( "Your broken legs can't hold your weight and you fall down in pain." ) ); + } + const bool is_riding = you.is_mounted(); tripoint dest_loc; if( d.z == 0 && you.has_effect( effect_stunned ) ) { diff --git a/src/character.cpp b/src/character.cpp index 10f974a68b90c..829bc08341fd2 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -1031,7 +1031,8 @@ int Character::swim_speed() const bool Character::is_on_ground() const { - return get_working_leg_count() < 2 || has_effect( effect_downed ); + return ( ( get_working_leg_count() < 2 && !weapon.has_flag( flag_CRUTCHES ) ) ) || + has_effect( effect_downed ) || is_prone(); } bool Character::can_stash( const item &it ) @@ -1898,6 +1899,11 @@ bool Character::is_crouching() const return move_mode->type() == move_mode_type::CROUCHING; } +bool Character::is_prone() const +{ + return move_mode->type() == move_mode_type::PRONE; +} + steed_type Character::get_steed_type() const { steed_type steed; @@ -9966,7 +9972,7 @@ void Character::on_hit( Creature *source, bodypart_id bp_hit, source->add_effect( effect_blind, 2_turns ); } } - if( worn_with_flag( flag_REQUIRES_BALANCE ) && !has_effect( effect_downed ) ) { + if( worn_with_flag( flag_REQUIRES_BALANCE ) && !is_on_ground() ) { int rolls = 4; if( worn_with_flag( flag_ROLLER_ONE ) ) { rolls += 2; diff --git a/src/character.h b/src/character.h index 944f6a0ad8dbd..74155838959fd 100644 --- a/src/character.h +++ b/src/character.h @@ -583,7 +583,7 @@ class Character : public Creature, public visitable bool has_watch() const; /** Called after every action, invalidates player caches */ void action_taken(); - /** Returns true if the player is knocked over or has broken legs */ + /** Returns true if the player is knocked over, has broken legs or is lying down */ bool is_on_ground() const override; /** Returns the player's speed for swimming across water tiles */ int swim_speed() const; @@ -711,6 +711,8 @@ class Character : public Creature, public visitable bool is_running() const; bool is_walking() const; bool is_crouching() const; + bool is_prone() const; + bool can_switch_to( const move_mode_id &mode ) const; steed_type get_steed_type() const; diff --git a/src/creature.cpp b/src/creature.cpp index 569c8aea3bb79..7a99ce9a4c581 100644 --- a/src/creature.cpp +++ b/src/creature.cpp @@ -281,7 +281,7 @@ bool Creature::sees( const Creature &critter ) const return false; } if( ch != nullptr ) { - if( ch->is_crouching() ) { + if( ch->is_crouching() || ch->is_prone() ) { const int coverage = here.obstacle_coverage( pos(), critter.pos() ); if( coverage < 30 ) { return sees( critter.pos(), critter.is_avatar() ) && visible( ch ); @@ -306,7 +306,15 @@ bool Creature::sees( const Creature &critter ) const debugmsg( "ERROR: Creature has invalid size class." ); break; } - const int vision_modifier = 30 - 0.5 * coverage * size_modifier; + + int vision_modifier {0}; + + if( ch->is_crouching() ) { + vision_modifier = 30 - 0.5 * coverage * size_modifier; + } else if( ch->is_prone() ) { + vision_modifier = 30 - 0.9 * coverage * size_modifier; + } + if( vision_modifier > 1 ) { return sees( critter.pos(), critter.is_avatar(), vision_modifier ) && visible( ch ); } diff --git a/src/flag.cpp b/src/flag.cpp index 5b5b68364f9c0..a47cc49de6716 100644 --- a/src/flag.cpp +++ b/src/flag.cpp @@ -63,6 +63,7 @@ const flag_id flag_CONDUCTIVE( "CONDUCTIVE" ); const flag_id flag_CONSUMABLE( "CONSUMABLE" ); const flag_id flag_COOKED( "COOKED" ); const flag_id flag_CORPSE( "CORPSE" ); +const flag_id flag_CRUTCHES( "CRUTCHES" ); const flag_id flag_CUSTOM_EXPLOSION( "CUSTOM_EXPLOSION" ); const flag_id flag_CUT_IMMUNE( "CUT_IMMUNE" ); const flag_id flag_DANGEROUS( "DANGEROUS" ); diff --git a/src/flag.h b/src/flag.h index ce60d0a3f6580..1ea8902ee893c 100644 --- a/src/flag.h +++ b/src/flag.h @@ -70,6 +70,7 @@ extern const flag_id flag_CONDUCTIVE; extern const flag_id flag_CONSUMABLE; extern const flag_id flag_COOKED; extern const flag_id flag_CORPSE; +extern const flag_id flag_CRUTCHES; extern const flag_id flag_CUSTOM_EXPLOSION; extern const flag_id flag_CUT_IMMUNE; extern const flag_id flag_DANGEROUS; diff --git a/src/handle_action.cpp b/src/handle_action.cpp index 91c7d1f8f2e8d..7fe207b6f538b 100644 --- a/src/handle_action.cpp +++ b/src/handle_action.cpp @@ -1783,6 +1783,10 @@ bool game::do_regular_action( action_id &act, avatar &player_character, player_character.toggle_crouch_mode(); break; + case ACTION_TOGGLE_PRONE: + player_character.toggle_prone_mode(); + break; + case ACTION_OPEN_MOVEMENT: open_movement_mode_menu(); break; diff --git a/src/melee.cpp b/src/melee.cpp index bece169ebf6f8..bc74c0280f504 100644 --- a/src/melee.cpp +++ b/src/melee.cpp @@ -487,6 +487,16 @@ bool Character::melee_attack_abstract( Creature &t, bool allow_special, const matec_id &force_technique, bool allow_unarmed ) { + if( get_working_leg_count() < 2 ) { + if( !movement_mode_is( move_mode_id( "prone" ) ) ) { + add_msg_if_player( m_bad, _( "Your broken legs cannot hold you and you fall down." ) ); + set_movement_mode( move_mode_id( "prone" ) ); + } else if( is_on_ground() ) { + add_msg_if_player( m_warning, _( "You cannot fight while on the ground." ) ); + } + return false; + } + melee::melee_stats.attack_count += 1; int hit_spread = t.deal_melee_attack( this, hit_roll() ); if( !t.is_player() ) { diff --git a/src/move_mode.cpp b/src/move_mode.cpp index c2375f65c64ee..b5f3826277e80 100644 --- a/src/move_mode.cpp +++ b/src/move_mode.cpp @@ -36,6 +36,7 @@ bool move_mode_id::is_valid() const } static const std::map move_types { + { "prone", move_mode_type::PRONE }, { "crouching", move_mode_type::CROUCHING }, { "walking", move_mode_type::WALKING }, { "running", move_mode_type::RUNNING } diff --git a/src/move_mode.h b/src/move_mode.h index a105247594038..bc6401eb28801 100644 --- a/src/move_mode.h +++ b/src/move_mode.h @@ -23,6 +23,7 @@ enum class steed_type : int { }; enum class move_mode_type : int { + PRONE, CROUCHING, WALKING, RUNNING diff --git a/src/player.cpp b/src/player.cpp index 9765db5e56c1c..96b479e93a4d7 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -622,7 +622,7 @@ void player::pause() if( has_effect( effect_onfire ) ) { time_duration total_removed = 0_turns; time_duration total_left = 0_turns; - bool on_ground = has_effect( effect_downed ); + bool on_ground = is_prone(); for( const bodypart_id &bp : get_all_body_parts() ) { effect &eff = get_effect( effect_onfire, bp ); if( eff.is_null() ) {