diff --git a/data/json/effects.json b/data/json/effects.json index db8818b3f829f..5cbb6135cdf05 100644 --- a/data/json/effects.json +++ b/data/json/effects.json @@ -4615,5 +4615,22 @@ ], "max_intensity": 4, "rating": "neutral" + }, + { + "type": "effect_type", + "id": "gliding", + "name": [ "Gliding" ], + "desc": [ "You are gliding through the air." ], + "flags": [ "GLIDING" ], + "rating": "good" + }, + { + "type": "effect_type", + "id": "slow_descent", + "show_in_info": false, + "name": [ "Slow Descent" ], + "desc": [ "You are descending at a safe speed." ], + "flags": [ "FEATHER_FALL" ], + "max_duration": "1 s" } ] diff --git a/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json b/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json index 1eac1012d12ba..f5ada9404890d 100644 --- a/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json +++ b/data/json/effects_on_condition/mutation_eocs/mutation_effect_eocs.json @@ -414,5 +414,17 @@ "//": "will remove ink_gland_ink effect when the mutation is lost", "condition": { "and": [ { "u_has_effect": "ink_gland_ink" }, { "compare_string": [ "INK_GLANDS", { "context_val": "trait" } ] } ] }, "effect": [ { "u_lose_effect": "ink_gland_ink" } ] + }, + { + "type": "effect_on_condition", + "id": "EOC_insect_wings_shutoff", + "recurrence": [ "1 seconds", "1 seconds" ], + "condition": { + "and": [ { "compare_num": [ { "u_val": "stamina" }, "<", { "const": 3000 } ] }, { "u_has_trait": "WINGS_INSECT_active" } ] + }, + "effect": [ + { "u_activate_trait": "WINGS_INSECT_active" }, + { "u_message": "You don't have the stamina to keep buzzing.", "type": "bad" } + ] } ] diff --git a/data/json/enchantments.json b/data/json/enchantments.json index d5ac4ee420162..d084fcbf096d2 100644 --- a/data/json/enchantments.json +++ b/data/json/enchantments.json @@ -105,5 +105,26 @@ "has": "WORN", "condition": "ACTIVE", "values": [ { "value": "FOOTSTEP_NOISE", "multiply": -0.67 } ] + }, + { + "type": "enchantment", + "id": "ench_wings_bird", + "condition": "ALWAYS", + "modified_bodyparts": [ + { "lose": "arm_l" }, + { "lose": "arm_r" }, + { "lose": "hand_r" }, + { "lose": "hand_l" }, + { "gain": "wing_bird_l" }, + { "gain": "wing_bird_r" } + ] + }, + { + "id": "ench_wings_insect_active", + "type": "enchantment", + "name": { "str": "Insect Wings (Buzzing)" }, + "description": "Your rapidly buzzing wings propel you with extra speed.", + "condition": "ALWAYS", + "values": [ { "value": "REGEN_STAMINA", "add": -110 } ] } ] diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index 8c173e7219628..6cc6c84e0eced 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -5115,13 +5115,15 @@ "points": 2, "visibility": 4, "ugliness": 2, - "description": "You have a pair of large, feathered wings. Your body is too heavy to be able to fly, but you can use them to slow your descent during a fall, and will not take falling damage under any circumstances.", - "types": [ "WINGS" ], - "prereqs": [ "WINGS_STUB" ], + "description": "Your arms have mostly transformed into a huge pair of feathered wings. Though you retain a few fingers, your fine manipulation is greatly hampered. On the other hand, you can glide from ledges and will take less damage if you fall, provided you're healthy and unburdened.", + "types": [ "ARMS", "HANDS" ], + "enchantments": [ "ench_wings_bird" ], + "prereqs": [ "ARM_FEATHERS" ], "prereqs2": [ "HOLLOW_BONES" ], "threshreq": [ "THRESH_BIRD" ], "category": [ "BIRD" ], - "flags": [ "WINGS_2" ] + "restricts_gear": [ "arm_l", "arm_r", "hand_l", "hand_r", "arm_stub_r", "arm_stub_l" ], + "flags": [ "WINGS_2", "WING_GLIDE" ] }, { "type": "mutation", @@ -5130,17 +5132,26 @@ "points": 1, "visibility": 4, "ugliness": 4, - "description": "You have a pair of large, translucent wings. They are too small to lift you, but are powerful enough to greatly speed your movement, with some effort and noise.", + "description": "A large pair of translucent wings grow from your back. They are too small to lift you, but if you're not weighed down, they can help a bit if you fall. You can flutter them quickly to improve your move speed, but this is noisy and tires you out.", "types": [ "WINGS" ], "prereqs": [ "WINGS_STUB" ], "category": [ "INSECT" ], + "restricts_gear": [ "torso" ], + "flags": [ "WINGS_1" ], "active": true, - "cost": 3, - "time": "1 s", - "fatigue": true, - "kcal": true, - "thirst": true, - "movecost_modifier": 0.75 + "transform": { "target": "WINGS_INSECT_active", "msg_transform": "You begin buzzing your wings.", "active": true, "moves": 20 } + }, + { + "type": "mutation", + "id": "WINGS_INSECT_active", + "name": { "str": "Insect Wings (Buzzing)" }, + "description": "You're fluttering your wings as fast as you can. This doesn't generate enough lift to move you, but it does increase your movement speed. If you're not burdened, you can fall short distances without harm.", + "copy-from": "WINGS_INSECT", + "valid": false, + "movecost_modifier": 0.75, + "flags": [ "WINGS_1", "MUSCLE_VEH_BOOST" ], + "enchantments": [ "ench_wings_insect_active" ], + "transform": { "target": "WINGS_INSECT", "msg_transform": "Your wings go still.", "active": false, "moves": 0 } }, { "type": "mutation", @@ -6152,8 +6163,8 @@ "ugliness": 2, "description": "You have a pair of stubby little wings projecting from your shoulderblades. They can be wiggled at will, but are useless.", "types": [ "WINGS" ], - "changes_to": [ "WINGS_BIRD", "WINGS_BAT", "WINGS_INSECT", "WINGS_BUTTERFLY" ], - "category": [ "BIRD", "BEAST", "INSECT" ] + "changes_to": [ "WINGS_INSECT", "WINGS_BUTTERFLY", "WINGS_BAT" ], + "category": [ "INSECT", "BEAST" ] }, { "type": "mutation", @@ -7544,19 +7555,22 @@ "type": "mutation", "id": "ARM_FEATHERS", "name": { "str": "Feathered Arms" }, - "points": -2, + "points": 2, "visibility": 8, "ugliness": 2, - "description": "Your arms have grown vibrantly-colored feathers. They effectively waterproof your arms and take the edge off hits, but really get in the way. They're simply too small to help you in the air.", + "mixed_effect": true, + "description": "Your arms have sprouted feathers, and you find you can no longer fully extend your elbows. This gets in the way a bit, but the feathers offer minor protection from damage and the elements.", "category": [ "RAPTOR", "BIRD" ], + "flags": [ "WINGS_1", "ARM_WINGS" ], + "changes_to": [ "WINGS_BIRD" ], "wet_protection": [ { "part": "arm_l", "neutral": 50 }, { "part": "arm_r", "neutral": 50 }, { "part": "hand_l", "neutral": 10 }, { "part": "hand_r", "neutral": 10 } ], - "encumbrance_always": [ [ "arm_l", 20 ], [ "arm_r", 20 ] ], - "armor": [ { "parts": [ "arm_l", "arm_r" ], "bash": 1 } ] + "encumbrance_always": [ [ "arm_l", 8 ], [ "arm_r", 8 ] ], + "armor": [ { "parts": [ "arm_l", "arm_r" ], "bash": 1, "cut": 1 } ] }, { "type": "mutation", diff --git a/data/json/player_activities.json b/data/json/player_activities.json index f49946f6895af..f5062b15db58e 100644 --- a/data/json/player_activities.json +++ b/data/json/player_activities.json @@ -1114,5 +1114,17 @@ "interruptable": false, "can_resume": false, "interruptable_with_kb": false + }, + { + "id": "ACT_GLIDE", + "type": "activity_type", + "activity_level": "NO_EXERCISE", + "verb": "gliding", + "based_on": "neither", + "interruptable": false, + "can_resume": false, + "refuel_fires": false, + "auto_needs": false, + "interruptable_with_kb": false } ] diff --git a/data/mods/Limb_WIP/mutation_eocs/prosthetics_eocs.json b/data/mods/Limb_WIP/mutation_eocs/prosthetics_eocs.json index e6e243e62a0df..3acd6333b35ba 100644 --- a/data/mods/Limb_WIP/mutation_eocs/prosthetics_eocs.json +++ b/data/mods/Limb_WIP/mutation_eocs/prosthetics_eocs.json @@ -385,6 +385,7 @@ { "u_has_trait": "ARACHNID_ARMS" }, { "u_has_trait": "ARACHNID_ARMS_OK" }, { "u_has_trait": "AMORPHOUS" }, + { "u_has_trait": "WINGS_BIRD" }, { "u_has_trait": "ARM_TENTACLES" }, { "u_has_trait": "ARM_TENTACLES_4" }, { "u_has_trait": "ARM_TENTACLES_8" } diff --git a/data/mods/Limb_WIP/mutations/mutation_limbs.json b/data/mods/Limb_WIP/mutations/mutation_limbs.json index 222745df610c1..183f072d58db0 100644 --- a/data/mods/Limb_WIP/mutations/mutation_limbs.json +++ b/data/mods/Limb_WIP/mutations/mutation_limbs.json @@ -1017,5 +1017,81 @@ "sub_parts": [ "hand_wrist_l", "hand_palm_l", "hand_back_l", "hand_fingers_l" ], "bmi_encumbrance_threshold": 0, "bmi_encumbrance_scalar": 0 + }, + { + "id": "wing_bird_l", + "type": "body_part", + "name": "left wing", + "name_multiple": "left wings", + "accusative": { "ctxt": "bodypart_accusative", "str": "left wing" }, + "accusative_multiple": { "ctxt": "bodypart_accusative", "str": "left wings" }, + "heading": "l. wing", + "heading_multiple": "wings", + "encumbrance_text": "Melee combat is hampered.", + "hp_bar_ui_text": "L WING", + "main_part": "wing_bird_l", + "connected_to": "torso", + "opposite_part": "wing_bird_r", + "flags": [ "LIMB_UPPER" ], + "conditional_flags": [ "WING_ARM_RIGHT" ], + "drench_capacity": 250, + "encumbrance_threshold": 0, + "hit_size": 0.9, + "hit_difficulty": 1.2, + "is_limb": true, + "limb_types": [ [ "arm", 0.8 ], [ "hand", 0.3 ] ], + "limb_scores": [ + [ "grip", 0.3 ], + [ "manip", 0.38 ], + [ "lift", 0.42 ], + [ "balance", 0.2 ], + [ "block", 0.8 ], + [ "swim", 0.25 ], + [ "crawl", 0.3 ] + ], + "smash_message": "You buffet the %s with your wing.", + "side": "left", + "base_hp": 45, + "sub_parts": [ "arm_shoulder_l", "arm_upper_l", "arm_elbow_l", "arm_lower_l", "hand_fingers_l", "hand_wrist_l" ], + "armor": { "cut": 3, "bash": 4 }, + "unarmed_damage": [ { "damage_type": "bash", "amount": -2 } ] + }, + { + "id": "wing_bird_r", + "type": "body_part", + "name": "right wing", + "name_multiple": "right wings", + "accusative": { "ctxt": "bodypart_accusative", "str": "right wing" }, + "accusative_multiple": { "ctxt": "bodypart_accusative", "str": "right wings" }, + "heading": "r. wing", + "heading_multiple": "wings", + "encumbrance_text": "Melee combat is hampered.", + "hp_bar_ui_text": "R WING", + "main_part": "wing_bird_r", + "connected_to": "torso", + "opposite_part": "wing_bird_l", + "flags": [ "LIMB_UPPER" ], + "conditional_flags": [ "WING_ARM_LEFT" ], + "drench_capacity": 250, + "encumbrance_threshold": 0, + "hit_size": 0.9, + "hit_difficulty": 1.2, + "is_limb": true, + "limb_types": [ [ "arm", 0.8 ], [ "hand", 0.3 ] ], + "limb_scores": [ + [ "grip", 0.3 ], + [ "manip", 0.38 ], + [ "lift", 0.42 ], + [ "balance", 0.2 ], + [ "block", 0.8 ], + [ "swim", 0.25 ], + [ "crawl", 0.3 ] + ], + "smash_message": "You buffet the %s with your wing.", + "side": "right", + "base_hp": 45, + "sub_parts": [ "arm_shoulder_r", "arm_upper_r", "arm_elbow_r", "arm_lower_r", "hand_fingers_r", "hand_wrist_r" ], + "armor": { "cut": 3, "bash": 4 }, + "unarmed_damage": [ { "damage_type": "bash", "amount": -2 } ] } ] diff --git a/data/mods/Magiclysm/mutations/mutations.json b/data/mods/Magiclysm/mutations/mutations.json index f4d7dbd1bc7f8..e2a79f30ceaac 100644 --- a/data/mods/Magiclysm/mutations/mutations.json +++ b/data/mods/Magiclysm/mutations/mutations.json @@ -439,11 +439,23 @@ "copy-from": "FORKED_TONGUE", "extend": { "category": [ "DRAGON_BLACK", "SPECIES_LIZARDFOLK" ] } }, + { + "type": "mutation", + "id": "HOLLOW_BONES", + "copy-from": "HOLLOW_BONES", + "extend": { "category": [ "SPECIES_RAVENFOLK" ] } + }, + { + "type": "mutation", + "id": "ARM_FEATHERS", + "copy-from": "ARM_FEATHERS", + "extend": { "category": [ "SPECIES_RAVENFOLK" ] } + }, { "type": "mutation", "id": "WINGS_STUB", "copy-from": "WINGS_STUB", - "extend": { "category": [ "DRAGON_BLACK", "SPECIES_RAVENFOLK" ], "changes_to": [ "DRAGON_WINGS_BLACK" ] } + "extend": { "category": [ "DRAGON_BLACK" ], "changes_to": [ "DRAGON_WINGS_BLACK" ] } }, { "type": "mutation", diff --git a/doc/JSON_FLAGS.md b/doc/JSON_FLAGS.md index 4ba03c05367d1..4755a9b2d9acd 100644 --- a/doc/JSON_FLAGS.md +++ b/doc/JSON_FLAGS.md @@ -360,6 +360,7 @@ Some armor flags, such as `WATCH` and `ALARMCLOCK` are compatible with other ite - ```ACID_IMMUNE``` You are immune to acid damage. - ```ALARMCLOCK``` You always can set alarms. - ```ALBINO``` Cause you to have painful sunburns +- ```ARM_WINGS``` You have wings instead of regular arms - ```BASH_IMMUNE``` You are immune to bashing damage. - ```BG_OTHER_SURVIVORS_STORY``` Given to NPC when it has other survival story - ```BG_SURVIVAL_STORY``` Given to NPC when it has a survival story @@ -389,6 +390,8 @@ Some armor flags, such as `WATCH` and `ALARMCLOCK` are compatible with other ite - ```FEATHER_FALL``` You are immune to fall damage. - ```GILLS``` You can breathe underwater. - ```GLARE_RESIST``` Protect your eyes from glare like sunglasses. +- ```GLIDE``` You can glide from ledges without the use of wings, as if by magic. +- ```GLIDING``` You are in the process of gliding. - ```HARDTOHIT``` Whenever something attacks you, RNG gets rolled twice, and you get the better result. - ```HEATSINK``` You are resistant to extreme heat. - ```HEAT_IMMUNE``` Immune to very hot temperatures. @@ -403,6 +406,7 @@ Some armor flags, such as `WATCH` and `ALARMCLOCK` are compatible with other ite - ```INVISIBLE``` You can't be seen. - ```LARGE``` Changes your size to `creature_size::large`. Checked third of the size category flags. - ```MEND_ALL``` You need no splint to heal broken bones. +- ```MUSCLE_VEH_BOOST``` Something, such as buzzing insect wings, is speeding you up when you use a muscle-powered vehicle. - ```MYCUS_IMMUNE``` Critter is immune to fungal hase field (`fd_fungal_haze`) - ```MYOPIC_IN_LIGHT``` You are nearsighted in light, but can see normally in low-light conditions. - ```MYOPIC``` You are nearsighted - vision range is severely reduced without glasses. @@ -448,8 +452,9 @@ Some armor flags, such as `WATCH` and `ALARMCLOCK` are compatible with other ite - ```WEBBED_FEET``` You have webbings on your feet, supporting your swimming speed if not wearing footwear. - ```WEBBED_HANDS``` You have webbings on your hands, supporting your swimming speed. - ```WEB_RAPPEL``` You can rappel down staircases and sheer drops of any height. -- ```WINGS_1``` You have 50% chance to ignore falling traps (including ledges). -- ```WINGS_2``` You have 100% chance to ignore falling traps (including ledges). Requires two flag instances. +- ```WINGS_1``` If you're not immobilized and have <50% burden, you reduce fall damage by 1 Z-level. +- ```WINGS_2``` If you're not immobilized and have <50% burden, you reduce fall damage by 2 Z-levels. +- ```WINGGLIDE``` You can glide using some part of your body and strenuous physical effort. - ```mycus``` TBD diff --git a/src/activity_actor.cpp b/src/activity_actor.cpp index 238f548f5dce2..56ea42ccdce16 100644 --- a/src/activity_actor.cpp +++ b/src/activity_actor.cpp @@ -110,6 +110,7 @@ static const activity_id ACT_EBOOKSAVE( "ACT_EBOOKSAVE" ); static const activity_id ACT_FIRSTAID( "ACT_FIRSTAID" ); static const activity_id ACT_FORAGE( "ACT_FORAGE" ); static const activity_id ACT_FURNITURE_MOVE( "ACT_FURNITURE_MOVE" ); +static const activity_id ACT_GLIDE( "ACT_GLIDE" ); static const activity_id ACT_GUNMOD_ADD( "ACT_GUNMOD_ADD" ); static const activity_id ACT_GUNMOD_REMOVE( "ACT_GUNMOD_REMOVE" ); static const activity_id ACT_HACKING( "ACT_HACKING" ); @@ -157,6 +158,8 @@ static const activity_id ACT_WORKOUT_MODERATE( "ACT_WORKOUT_MODERATE" ); static const ammotype ammo_plutonium( "plutonium" ); static const efftype_id effect_docile( "docile" ); +static const efftype_id effect_downed( "downed" ); +static const efftype_id effect_gliding( "gliding" ); static const efftype_id effect_paid( "paid" ); static const efftype_id effect_pet( "pet" ); static const efftype_id effect_sensor_stun( "sensor_stun" ); @@ -219,6 +222,8 @@ static const skill_id skill_mechanics( "mechanics" ); static const skill_id skill_survival( "survival" ); static const skill_id skill_traps( "traps" ); +static const species_id species_ZOMBIE( "ZOMBIE" ); + static const ter_str_id ter_t_underbrush_harvested_autumn( "t_underbrush_harvested_autumn" ); static const ter_str_id ter_t_underbrush_harvested_spring( "t_underbrush_harvested_spring" ); static const ter_str_id ter_t_underbrush_harvested_summer( "t_underbrush_harvested_summer" ); @@ -1501,6 +1506,167 @@ std::unique_ptr bikerack_racking_activity_actor::deserialize( Js return actor.clone(); } +void glide_activity_actor::do_turn( player_activity &act, Character &you ) +{ + tripoint heading; + if( jump_direction == 0 ) { + heading = tripoint_south; + } + if( jump_direction == 1 ) { + heading = tripoint_south_west; + } + if( jump_direction == 2 ) { + heading = tripoint_west; + } + if( jump_direction == 3 ) { + heading = tripoint_north_west; + } + if( jump_direction == 4 ) { + heading = tripoint_north; + } + if( jump_direction == 5 ) { + heading = tripoint_north_east; + } + if( jump_direction == 6 ) { + heading = tripoint_east; + } + if( jump_direction == 7 ) { + heading = tripoint_south_east; + } + const tripoint_abs_ms newpos = you.get_location() + heading; + const tripoint_bub_ms checknewpos = you.pos_bub() + heading; + if( get_map().tr_at( you.pos() ) != tr_ledge || heading == tripoint_zero ) { + you.add_msg_player_or_npc( m_good, + _( "You come to a gentle landing." ), + _( " comes to a gentle landing." ) ); + you.remove_effect( effect_gliding ); + you.gravity_check(); + act.set_to_null(); + return; + } // Have we crashed into a wall? + if( get_map().impassable( checknewpos ) ) { + you.add_msg_player_or_npc( m_bad, + _( "You collide with %s, bringing an abrupt halt to your glide." ), + _( " collides with %s, bringing an abrupt halt to their glide." ), + get_map().tername( checknewpos.raw() ) ); + you.remove_effect( effect_gliding ); + you.gravity_check(); + act.set_to_null(); + return; + } + if( !you.can_fly() ) { + you.remove_effect( effect_gliding ); + you.gravity_check(); + act.set_to_null(); + return; + } + Creature *creature_ahead = get_creature_tracker().creature_at( newpos ); + if( creature_ahead && creature_ahead->get_size() >= creature_size::medium && + you.get_size() >= creature_size::medium ) { + // Zombies are too stupid to avoid midair collision + if( !you.dodge_check( 15, true ) || ( !creature_ahead->in_species( species_ZOMBIE ) && + !creature_ahead->dodge_check( 15, true ) ) ) { + you.add_msg_player_or_npc( m_bad, + _( "You collide with %s, bringing an abrupt halt to your glide." ), + _( " collides with %s, bringing an abrupt halt to their glide." ), + creature_ahead->disp_name() ); + if( creature_ahead->get_size() < creature_size::huge ) { + creature_ahead->add_effect( effect_downed, 2_turns, false ); + } + you.remove_effect( effect_gliding ); + you.gravity_check(); + act.set_to_null(); + return; + } + you.add_msg_player_or_npc( m_good, + _( "You deftly maneuver around %s." ), + _( " deftly maneuvers around %s." ), creature_ahead->disp_name() ); + } + you.move_to( newpos ); + moved_tiles ++; + if( moved_tiles >= glide_distance ) { + g->vertical_move( -1, false, false ); + moved_tiles = 0; + } + you.moves -= 50; + get_map().update_visibility_cache( you.pos().z ); + get_map().update_visibility_cache( you.pos().x ); + get_map().update_visibility_cache( you.pos().y ); + if( you.is_avatar() ) { + g->update_map( you ); + } +} + +glide_activity_actor::glide_activity_actor( Character *you, int jump_direction, int glide_distance ) + : jump_direction( jump_direction ), glide_distance( glide_distance ) +{ + you->add_effect( effect_gliding, 1_turns, true ); + tripoint heading; + if( jump_direction == 0 ) { + heading = tripoint_south; + } + if( jump_direction == 1 ) { + heading = tripoint_south_west; + } + if( jump_direction == 2 ) { + heading = tripoint_west; + } + if( jump_direction == 3 ) { + heading = tripoint_north_west; + } + if( jump_direction == 4 ) { + heading = tripoint_north; + } + if( jump_direction == 5 ) { + heading = tripoint_north_east; + } + if( jump_direction == 6 ) { + heading = tripoint_east; + } + if( jump_direction == 7 ) { + heading = tripoint_south_west; + } +} + +void glide_activity_actor::serialize( JsonOut &jsout ) const +{ + jsout.start_object(); + + jsout.member( "moved_tiles", moved_tiles ); + jsout.member( "moves_total", moves_total ); + jsout.member( "jump_direction", jump_direction ); + jsout.member( "glide_distance", glide_distance ); + + jsout.end_object(); +} + +std::unique_ptr glide_activity_actor::deserialize( JsonValue &jsin ) +{ + glide_activity_actor actor; + JsonObject data = jsin.get_object(); + data.read( "moved_tiles", actor.moved_tiles ); + data.read( "moves_total", actor.moves_total ); + data.read( "jump_direction", actor.jump_direction ); + data.read( "glide_distance", actor.glide_distance ); + return actor.clone(); +} +void glide_activity_actor::start( player_activity &act, Character & ) +{ + act.moves_total = moves_total; + act.moves_left = moves_total; +} + +void glide_activity_actor::finish( player_activity &act, Character &you ) +{ + g->update_map( you ); + you.add_msg_player_or_npc( m_good, + _( "You come to a gentle landing." ), + _( " comes to a gentle landing." ) ); + you.remove_effect( effect_gliding ); + you.gravity_check(); + act.set_to_null(); +} + bikerack_unracking_activity_actor::bikerack_unracking_activity_actor( const vehicle &parent_vehicle, const std::vector &parts, const std::vector &racks ) : parts( parts ), racks( racks ) @@ -7494,6 +7660,7 @@ deserialize_functions = { { ACT_FIRSTAID, &firstaid_activity_actor::deserialize }, { ACT_FORAGE, &forage_activity_actor::deserialize }, { ACT_FURNITURE_MOVE, &move_furniture_activity_actor::deserialize }, + { ACT_GLIDE, &glide_activity_actor::deserialize }, { ACT_GUNMOD_ADD, &gunmod_add_activity_actor::deserialize }, { ACT_GUNMOD_REMOVE, &gunmod_remove_activity_actor::deserialize }, { ACT_HACKING, &hacking_activity_actor::deserialize }, diff --git a/src/activity_actor_definitions.h b/src/activity_actor_definitions.h index 81b938e952f0f..d164e45a10f5f 100644 --- a/src/activity_actor_definitions.h +++ b/src/activity_actor_definitions.h @@ -344,6 +344,34 @@ class hotwire_car_activity_actor : public activity_actor static std::unique_ptr deserialize( JsonValue &jsin ); }; +class glide_activity_actor : public activity_actor +{ + private: + int jump_direction; + int glide_distance; + int moved_tiles = 0; + int moves_total = to_moves( 1_seconds ); + explicit glide_activity_actor() = default; + + public: + explicit glide_activity_actor( Character *you, int jump_direction, int glide_distance ); + + activity_id get_type() const override { + return activity_id( "ACT_GLIDE" ); + } + + void start( player_activity &act, Character & ) override; + void do_turn( player_activity &act, Character &you ) override; + void finish( player_activity &act, Character &you ) override; + + std::unique_ptr clone() const override { + return std::make_unique( *this ); + } + + void serialize( JsonOut &jsout ) const override; + static std::unique_ptr deserialize( JsonValue &jsin ); +}; + class bikerack_racking_activity_actor : public activity_actor { private: diff --git a/src/character.cpp b/src/character.cpp index edd1610d92e63..eda262d205550 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -236,6 +236,7 @@ static const efftype_id effect_foodpoison( "foodpoison" ); static const efftype_id effect_fungus( "fungus" ); static const efftype_id effect_glowing( "glowing" ); static const efftype_id effect_glowy_led( "glowy_led" ); +static const efftype_id effect_grabbed( "grabbed" ); static const efftype_id effect_harnessed( "harnessed" ); static const efftype_id effect_heavysnare( "heavysnare" ); static const efftype_id effect_in_pit( "in_pit" ); @@ -308,6 +309,8 @@ static const json_character_flag json_flag_ECTOTHERM( "ECTOTHERM" ); static const json_character_flag json_flag_ENHANCED_VISION( "ENHANCED_VISION" ); static const json_character_flag json_flag_EYE_MEMBRANE( "EYE_MEMBRANE" ); static const json_character_flag json_flag_FEATHER_FALL( "FEATHER_FALL" ); +static const json_character_flag json_flag_GLIDE( "GLIDE" ); +static const json_character_flag json_flag_GLIDING( "GLIDING" ); static const json_character_flag json_flag_GRAB( "GRAB" ); static const json_character_flag json_flag_HEAL_OVERRIDE( "HEAL_OVERRIDE" ); static const json_character_flag json_flag_HEATSINK( "HEATSINK" ); @@ -340,6 +343,10 @@ static const json_character_flag json_flag_WALK_UNDERWATER( "WALK_UNDERWATER" ); static const json_character_flag json_flag_WATCH( "WATCH" ); static const json_character_flag json_flag_WEBBED_FEET( "WEBBED_FEET" ); static const json_character_flag json_flag_WEBBED_HANDS( "WEBBED_HANDS" ); +static const json_character_flag json_flag_WINGS_1( "WINGS_1" ); +static const json_character_flag json_flag_WINGS_2( "WINGS_2" ); +static const json_character_flag json_flag_WING_ARMS( "WING_ARMS" ); +static const json_character_flag json_flag_WING_GLIDE( "WING_GLIDE" ); static const limb_score_id limb_score_balance( "balance" ); static const limb_score_id limb_score_breathing( "breathing" ); @@ -1581,7 +1588,6 @@ player_activity Character::get_destination_activity() const void Character::mount_creature( monster &z ) { - avatar &player_avatar = get_avatar(); tripoint pnt = z.pos(); shared_ptr_fast mons = g->shared_from( z ); if( mons == nullptr ) { @@ -1605,6 +1611,7 @@ void Character::mount_creature( monster &z ) } mounted_creature = mons; mons->mounted_player = this; + avatar &player_avatar = get_avatar(); if( is_avatar() && player_avatar.get_grab_type() != object_type::NONE ) { add_msg( m_warning, _( "You let go of the grabbed object." ) ); player_avatar.grab( object_type::NONE ); @@ -10979,12 +10986,59 @@ void Character::process_effects() void Character::gravity_check() { - if( get_map().tr_at( pos() ) == tr_ledge ) { + if( get_map().tr_at( pos() ) == tr_ledge && !has_effect_with_flag( json_flag_GLIDING ) ) { get_map().tr_at( pos() ).trigger( pos(), *this ); get_map().update_visibility_cache( pos().z ); } } +void Character::stagger() +{ + map &here = get_map(); + std::vector valid_stumbles; + valid_stumbles.reserve( 11 ); + for( const tripoint &dest : here.points_in_radius( pos(), 1 ) ) { + if( dest != pos() ) { + if( here.has_flag( ter_furn_flag::TFLAG_RAMP_DOWN, dest ) ) { + valid_stumbles.emplace_back( dest.xy(), dest.z - 1 ); + } else if( here.has_flag( ter_furn_flag::TFLAG_RAMP_UP, dest ) ) { + valid_stumbles.emplace_back( dest.xy(), dest.z + 1 ); + } else { + valid_stumbles.push_back( dest ); + } + } + } + const tripoint below( posx(), posy(), posz() - 1 ); + if( here.valid_move( pos(), below, false, true ) ) { + valid_stumbles.push_back( below ); + } + creature_tracker &creatures = get_creature_tracker(); + while( !valid_stumbles.empty() ) { + bool blocked = false; + const tripoint dest = random_entry_removed( valid_stumbles ); + const optional_vpart_position vp_there = here.veh_at( dest ); + if( vp_there ) { + vehicle &veh = vp_there->vehicle(); + if( veh.enclosed_at( dest ) ) { + blocked = true; + } + } + if( here.passable( dest ) && !blocked && + ( creatures.creature_at( dest, is_hallucination() ) == nullptr ) ) { + avatar &player_avatar = get_avatar(); + if( is_avatar() && player_avatar.get_grab_type() != object_type::NONE ) { + player_avatar.grab( object_type::NONE ); + } + add_msg_player_or_npc( m_warning, + _( "You stumble!" ), + _( " stumbles!" ) ); + setpos( dest ); + mod_moves( -100 ); + break; + } + } +} + double Character::vomit_mod() { double mod = mutation_value( "vomit_multiplier" ); @@ -12425,6 +12479,29 @@ int Character::impact( const int force, const tripoint &p ) return total_dealt; } +bool Character::can_fly() +{ + if( has_effect( effect_stunned ) || has_effect( effect_narcosis ) || + ( has_effect( effect_grabbed ) && !try_remove_grab() ) || movement_mode_is( move_mode_prone ) || + has_effect( effect_downed ) ) { + return false; + } + // GLIDE is for artifacts or things like jetpacks that don't care if you're tired or hurt. + if( has_trait_flag( json_flag_GLIDE ) ) { + return true; + } + if( ( has_trait_flag( json_flag_WINGS_1 ) || has_trait_flag( json_flag_WINGS_2 ) || + has_trait_flag( json_flag_WING_GLIDE ) ) && + ( 100 * weight_carried() / weight_capacity() > 50 || get_str() < 4 || + has_effect( effect_winded ) ) ) { + return false; + } + if( has_trait_flag( json_flag_WING_ARMS ) && get_working_arm_count() < 2 ) { + return false; + } + return true; +} + // FIXME: Relies on hardcoded bash damage type void Character::knock_back_to( const tripoint &to ) { diff --git a/src/character.h b/src/character.h index 8732fea966ae5..edb51bc612ab3 100644 --- a/src/character.h +++ b/src/character.h @@ -701,6 +701,7 @@ class Character : public Creature, public visitable public: void gravity_check(); + void stagger(); void mod_stat( const std::string &stat, float modifier ) override; @@ -2528,6 +2529,8 @@ class Character : public Creature, public visitable float fall_damage_mod() const override; /** Deals falling/collision damage with terrain/creature at pos */ int impact( int force, const tripoint &pos ) override; + /** Checks to see if the character is able to use their wings properly */ + bool can_fly(); /** Knocks the player to a specified tile */ void knock_back_to( const tripoint &to ) override; @@ -4047,4 +4050,4 @@ std::string get_stat_name( character_stat Stat ); extern template bool Character::can_lift( const item &obj ) const; extern template bool Character::can_lift( const vehicle &obj ) const; -#endif // CATA_SRC_CHARACTER_H +#endif // CATA_SRC_CHARACTER_H \ No newline at end of file diff --git a/src/do_turn.cpp b/src/do_turn.cpp index 2455a6f0ded8c..3acf76b573049 100644 --- a/src/do_turn.cpp +++ b/src/do_turn.cpp @@ -9,6 +9,7 @@ #include "bionics.h" #include "cached_options.h" #include "calendar.h" +#include "character.h" #include "creature_tracker.h" #include "event_bus.h" #include "explosion.h" @@ -448,6 +449,11 @@ bool do_turn() // Make sure players cant defy gravity by standing still, Looney tunes style. u.gravity_check(); + // If you're inside a wall or something and haven't been telefragged, let's get you out. + if( m.impassable( u.pos() ) && !m.has_flag( ter_furn_flag::TFLAG_CLIMBABLE, u.pos() ) ) { + u.stagger(); + } + // If riding a horse - chance to spook if( u.is_mounted() ) { u.check_mount_is_spooked(); diff --git a/src/game.cpp b/src/game.cpp index 195bc1e031dd9..f60fe0a06d628 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -236,6 +236,7 @@ static const efftype_id effect_docile( "docile" ); static const efftype_id effect_downed( "downed" ); static const efftype_id effect_fake_common_cold( "fake_common_cold" ); static const efftype_id effect_fake_flu( "fake_flu" ); +static const efftype_id effect_gliding( "gliding" ); static const efftype_id effect_laserlocked( "laserlocked" ); static const efftype_id effect_led_by_leash( "led_by_leash" ); static const efftype_id effect_no_sight( "no_sight" ); @@ -11850,7 +11851,8 @@ void game::vertical_move( int movez, bool force, bool peeking ) } if( !force && movez == -1 && !here.has_flag( ter_furn_flag::TFLAG_GOES_DOWN, u.pos() ) && - !u.is_underwater() && !here.has_flag( ter_furn_flag::TFLAG_NO_FLOOR_WATER, u.pos() ) ) { + !u.is_underwater() && !here.has_flag( ter_furn_flag::TFLAG_NO_FLOOR_WATER, u.pos() ) && + ( u.has_effect( effect_gliding ) && get_map().tr_at( u.pos() ) != tr_ledge ) ) { if( wall_cling && !here.has_floor_or_support( u.pos() ) ) { climbing = true; climbing_aid = climbing_aid_ability_WALL_CLING; @@ -12113,7 +12115,8 @@ void game::vertical_move( int movez, bool force, bool peeking ) here.invalidate_map_cache( here.get_abs_sub().z() ); // Upon force movement, traps can not be avoided. - if( !wall_cling ) { + if( !wall_cling && ( get_map().tr_at( u.pos() ) == tr_ledge && + !u.has_effect( effect_gliding ) ) ) { here.creature_on_trap( u, !force ); } @@ -12192,7 +12195,8 @@ std::optional game::find_or_make_stairs( map &mp, const int z_after, b stairs.emplace( pos + tripoint_above ); } // We did not find stairs directly above or below, so search the map for them - if( !stairs.has_value() ) { + // If there's empty space right below us, we can just go down that way. + if( !stairs.has_value() && get_map().tr_at( u.pos() ) != tr_ledge ) { for( const tripoint &dest : mp.points_in_rectangle( omtile_align_start, omtile_align_end ) ) { if( rl_dist( u.pos(), dest ) <= best && ( ( going_down_1 && mp.has_flag( ter_furn_flag::TFLAG_GOES_UP, dest ) ) || @@ -12258,6 +12262,10 @@ std::optional game::find_or_make_stairs( map &mp, const int z_after, b return stairs; } + if( u.has_effect( effect_gliding ) && get_map().tr_at( u.pos() ) == tr_ledge ) { + return stairs; + } + if( movez > 0 ) { if( !mp.has_flag( ter_furn_flag::TFLAG_GOES_DOWN, *stairs ) ) { if( !query_yn( _( "You may be unable to return back down these stairs. Continue up?" ) ) ) { diff --git a/src/iexamine.cpp b/src/iexamine.cpp index e2e738dc81ae6..2cfab53ef0a28 100644 --- a/src/iexamine.cpp +++ b/src/iexamine.cpp @@ -122,14 +122,17 @@ static const climbing_aid_id climbing_aid_furn_CLIMBABLE( "furn_CLIMBABLE" ); static const efftype_id effect_antibiotic( "antibiotic" ); static const efftype_id effect_bite( "bite" ); static const efftype_id effect_bleed( "bleed" ); +static const efftype_id effect_bouldering( "bouldering" ); static const efftype_id effect_disinfected( "disinfected" ); static const efftype_id effect_earphones( "earphones" ); +static const efftype_id effect_gliding( "gliding" ); static const efftype_id effect_incorporeal( "incorporeal" ); static const efftype_id effect_infected( "infected" ); static const efftype_id effect_mending( "mending" ); static const efftype_id effect_pblue( "pblue" ); static const efftype_id effect_pkill2( "pkill2" ); static const efftype_id effect_sleep( "sleep" ); +static const efftype_id effect_slow_descent( "slow_descent" ); static const efftype_id effect_strong_antibiotic( "strong_antibiotic" ); static const efftype_id effect_strong_antibiotic_visible( "strong_antibiotic_visible" ); static const efftype_id effect_teleglow( "teleglow" ); @@ -180,8 +183,11 @@ static const itype_id itype_unfinished_cac2( "unfinished_cac2" ); static const itype_id itype_unfinished_charcoal( "unfinished_charcoal" ); static const json_character_flag json_flag_ATTUNEMENT( "ATTUNEMENT" ); +static const json_character_flag json_flag_GLIDE( "GLIDE" ); +static const json_character_flag json_flag_LEVITATION( "LEVITATION" ); static const json_character_flag json_flag_PAIN_IMMUNE( "PAIN_IMMUNE" ); static const json_character_flag json_flag_SUPER_HEARING( "SUPER_HEARING" ); +static const json_character_flag json_flag_WING_GLIDE( "WING_GLIDE" ); static const material_id material_bone( "bone" ); static const material_id material_cac2powder( "cac2powder" ); @@ -5032,6 +5038,7 @@ void iexamine::ledge( Character &you, const tripoint &examp ) ledge_jump_across, ledge_fall_down, ledge_examine_furniture_below, + ledge_glide, }; map &here = get_map(); @@ -5039,6 +5046,26 @@ void iexamine::ledge( Character &you, const tripoint &examp ) you.posy() + 2 * sgn( examp.y - you.posy() ), you.posz() ); bool jump_target_valid = ( here.ter( jump_target ).obj().trap != tr_ledge ); + point jd( examp.xy() + point( -you.posx(), -you.posy() ) ); + int jump_direction = 0; + + if( jd.y > 0 && jd.x == 0 ) { + jump_direction = 0; //south + } else if( jd.y > 0 && jd.x < 0 ) { + jump_direction = 1; //southwest + } else if( jd.y == 0 && jd.x < 0 ) { + jump_direction = 2; //west + } else if( jd.y < 0 && jd.x < 0 ) { + jump_direction = 3; //northwest + } else if( jd.y < 0 && jd.x == 0 ) { + jump_direction = 4; //north + } else if( jd.y < 0 && jd.x > 0 ) { + jump_direction = 5; //northeast + } else if( jd.y == 0 && jd.x > 0 ) { + jump_direction = 6; //east + } else if( jd.y > 0 && jd.x > 0 ) { + jump_direction = 7; //southeast + } tripoint just_below = examp; just_below.z--; @@ -5056,9 +5083,12 @@ void iexamine::ledge( Character &you, const tripoint &examp ) cmenu.addentry( ledge_jump_across, jump_target_valid, 'j', ( jump_target_valid ? _( "Jump across." ) : _( "Can't jump across (need a small gap)." ) ) ); cmenu.addentry( ledge_fall_down, true, 'f', _( "Fall down." ) ); - + if( you.has_trait_flag( json_flag_GLIDE ) || you.has_trait_flag( json_flag_WING_GLIDE ) ) { + cmenu.addentry( ledge_glide, true, 'g', _( "Glide away." ) ); + } cmenu.query(); + creature_tracker &creatures = get_creature_tracker(); if( g->climb_down_menu_pick( examp, cmenu.ret ) ) { // This branch means the player chose some option generated by the climb menu. @@ -5141,6 +5171,46 @@ void iexamine::ledge( Character &you, const tripoint &examp ) } break; }*/ + case ledge_glide: { + // If player is grabbed, trapped, or somehow otherwise movement-impeded, first try to break free + if( !you.move_effects( false ) ) { + you.moves -= 100; + return; + } + // The carried weight check here is redundant, but we do it anyway for better player feedback + if( 100 * you.weight_carried() / you.weight_capacity() > 50 && + you.has_trait_flag( json_flag_WING_GLIDE ) ) { + add_msg( m_warning, _( "You are carrying too much to glide." ) ); + } else if( !you.can_fly() ) { + add_msg( m_warning, _( "You can't manage to get airborne in your current state." ) ); + } else { + int glide_distance = 5; + const weather_manager &weather = get_weather(); + add_msg( m_info, _( "You soar away from the ledge." ) ); + int angledifference = std::abs( weather.winddirection - jump_direction * 45 ); + // Handle cases where the difference wraps around due to compass directions + angledifference = std::min( angledifference, 360 - angledifference ); + if( angledifference <= 45 && weather.windspeed >= 12 ) { + add_msg( m_warning, _( "Your glide is aided by a tailwind." ) ); + glide_distance += 1; + } + // Check if the directions are greater than 135 degrees apart + else if( angledifference >= 135 && weather.windspeed >= 12 ) { + add_msg( m_warning, _( "Your glide is hindered by a headwind." ) ); + glide_distance -= 1; + } + if( jump_direction == 1 || jump_direction == 3 || jump_direction == 5 || jump_direction == 7 ) { + glide_distance = std::round( 0.7 * glide_distance ); + } + you.as_avatar()->grab( object_type::NONE ); + glide_activity_actor glide( &you, jump_direction, glide_distance ); + you.remove_effect( effect_bouldering ); + you.assign_activity( glide ); + you.add_effect( effect_gliding, 1_turns, true ); + you.setpos( examp ); + } + break; + } case ledge_fall_down: { if( query_yn( _( "Climbing might be safer. Really fall from the ledge?" ) ) ) { you.moves -= 100; @@ -5149,6 +5219,9 @@ void iexamine::ledge( Character &you, const tripoint &examp ) return; } // Step into open air, then fall... + if( you.has_effect_with_flag( json_flag_LEVITATION ) ) { + you.add_effect( effect_slow_descent, 1_seconds, false ); + } you.setpos( examp ); you.gravity_check(); } else { diff --git a/src/map.cpp b/src/map.cpp index 63c22de7fbba2..74261e0e2b3f4 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -124,6 +124,7 @@ static const efftype_id effect_boomered( "boomered" ); static const efftype_id effect_crushed( "crushed" ); static const efftype_id effect_fake_common_cold( "fake_common_cold" ); static const efftype_id effect_fake_flu( "fake_flu" ); +static const efftype_id effect_gliding( "gliding" ); static const efftype_id effect_pet( "pet" ); static const field_type_str_id field_fd_clairvoyant( "fd_clairvoyant" ); @@ -6128,6 +6129,11 @@ const trap &map::tr_at( const tripoint &p ) const return current_submap->get_trap( l ).obj(); } +const trap &map::tr_at( const tripoint_abs_ms &p ) const +{ + return tr_at( p.raw() ); +} + const trap &map::tr_at( const tripoint_bub_ms &p ) const { return tr_at( p.raw() ); @@ -9725,10 +9731,10 @@ field &map::get_field( const tripoint &p ) void map::creature_on_trap( Creature &c, const bool may_avoid ) const { - // boarded in a vehicle means the player is above the trap, like a flying monster and can - // never trigger the trap. + // gliding or boarded in a vehicle means the player is above the trap + // like a flying monster and can never trigger the trap. const Character *const you = c.as_character(); - if( you != nullptr && you->in_vehicle ) { + if( you != nullptr && ( you->in_vehicle || !you->has_effect( effect_gliding ) ) ) { return; } diff --git a/src/map.h b/src/map.h index 921d6c6c4f2b4..5c6a4f9fafc02 100644 --- a/src/map.h +++ b/src/map.h @@ -1498,6 +1498,7 @@ class map // TODO: fix point types (remove the first overload) const trap &tr_at( const tripoint &p ) const; + const trap &tr_at( const tripoint_abs_ms &p ) const; const trap &tr_at( const tripoint_bub_ms &p ) const; /// See @ref trap::can_see, which is called for the trap here. bool can_see_trap_at( const tripoint &p, const Character &c ) const; diff --git a/src/mutation.cpp b/src/mutation.cpp index b102ea2bb0b57..e9b9c46f6dfc1 100644 --- a/src/mutation.cpp +++ b/src/mutation.cpp @@ -2451,4 +2451,3 @@ std::string Character::visible_mutations( const int visibility_cap ) const return std::string(); } ); } - diff --git a/src/mutation.h b/src/mutation.h index cb23c1b236360..8bbd06724ce46 100644 --- a/src/mutation.h +++ b/src/mutation.h @@ -199,7 +199,7 @@ struct mutation_branch { bool destroys_gear = false; // Allow soft (fabric) gear on restricted body parts bool allow_soft_gear = false; - // IF any of the three are true, it drains that as the "cost" + // IF any of the four are true, it drains that as the "cost" bool fatigue = false; bool hunger = false; bool thirst = false; @@ -649,4 +649,4 @@ struct enum_traits { void test_crossing_threshold( Character &guy, const mutation_category_trait &m_category ); -#endif // CATA_SRC_MUTATION_H +#endif // CATA_SRC_MUTATION_H \ No newline at end of file diff --git a/src/mutation_data.cpp b/src/mutation_data.cpp index 3729fffec0ecc..c4782ac87ce81 100644 --- a/src/mutation_data.cpp +++ b/src/mutation_data.cpp @@ -1226,4 +1226,4 @@ bool mutation_is_in_category( const trait_id &mut, const mutation_category_id &c { return std::find( mutations_category[cat].begin(), mutations_category[cat].end(), mut ) != mutations_category[cat].end(); -} +} \ No newline at end of file diff --git a/src/suffer.cpp b/src/suffer.cpp index ce437911ece7a..b17c5084e329c 100644 --- a/src/suffer.cpp +++ b/src/suffer.cpp @@ -166,7 +166,7 @@ static const trait_id trait_UNSTABLE( "UNSTABLE" ); static const trait_id trait_VOMITOUS( "VOMITOUS" ); static const trait_id trait_WEB_SPINNER( "WEB_SPINNER" ); static const trait_id trait_WEB_WEAVER( "WEB_WEAVER" ); -static const trait_id trait_WINGS_INSECT( "WINGS_INSECT" ); +static const trait_id trait_WINGS_INSECT_active( "WINGS_INSECT_active" ); static const vitamin_id vitamin_vitC( "vitC" ); @@ -990,7 +990,7 @@ void suffer::from_other_mutations( Character &you ) here.spawn_item( position, "bone", 1 ); } - if( you.has_active_mutation( trait_WINGS_INSECT ) ) { + if( you.has_trait( trait_WINGS_INSECT_active ) ) { //~Sound of buzzing Insect Wings sounds::sound( position, 10, sounds::sound_t::movement, _( "BZZZZZ" ), false, "misc", "insect_wings" ); @@ -2082,4 +2082,4 @@ int Character::addiction_level( const addiction_id &type ) const return ad.type == type; } ); return iter != addictions.end() ? iter->intensity : 0; -} +} \ No newline at end of file diff --git a/src/trapfunc.cpp b/src/trapfunc.cpp index 2ae8f8506c015..56e3e92099381 100644 --- a/src/trapfunc.cpp +++ b/src/trapfunc.cpp @@ -56,7 +56,10 @@ static const efftype_id effect_in_pit( "in_pit" ); static const efftype_id effect_lightsnare( "lightsnare" ); static const efftype_id effect_ridden( "ridden" ); static const efftype_id effect_slimed( "slimed" ); +static const efftype_id effect_slow_descent( "slow_descent" ); +static const efftype_id effect_strengthened_gravity( "strengthened_gravity" ); static const efftype_id effect_tetanus( "tetanus" ); +static const efftype_id effect_weakened_gravity( "weakened_gravity" ); static const flag_id json_flag_LEVITATION( "LEVITATION" ); static const flag_id json_flag_PROXIMITY( "PROXIMITY" ); @@ -833,9 +836,10 @@ bool trapfunc::pit( const tripoint &p, Creature *c, item * ) monster *z = dynamic_cast( c ); Character *you = dynamic_cast( c ); if( you != nullptr ) { - if( you->has_flag( json_flag_WINGS_2 ) || ( one_in( 2 ) && - you->has_flag( json_flag_WINGS_1 ) ) ) { - you->add_msg_if_player( _( "You flap your wings and flutter down gracefully." ) ); + if( you->can_fly() && ( you->has_flag( json_flag_WINGS_1 ) || + you->has_flag( json_flag_WINGS_2 ) ) ) { + you->add_msg_player_or_npc( _( "You spread your wings to slow your fall." ), + _( " spreads their wings to slow their fall." ) ); } else if( you->has_active_bionic( bio_shock_absorber ) ) { you->add_msg_if_player( m_info, _( "You hit the ground hard, but your grav chute handles the impact admirably!" ) ); @@ -885,9 +889,10 @@ bool trapfunc::pit_spikes( const tripoint &p, Creature *c, item * ) if( you != nullptr ) { int dodge = you->get_dodge(); int damage = pit_effectiveness( p ) * rng( 20, 50 ); - if( you->has_flag( json_flag_WINGS_2 ) || ( one_in( 2 ) && - you->has_flag( json_flag_WINGS_1 ) ) ) { - you->add_msg_if_player( _( "You flap your wings and flutter down gracefully." ) ); + if( you->can_fly() && ( you->has_flag( json_flag_WINGS_1 ) || + you->has_flag( json_flag_WINGS_2 ) ) ) { + you->add_msg_player_or_npc( _( "You spread your wings to slow your fall." ), + _( " spreads their wings to slow their fall." ) ); } else if( you->has_active_bionic( bio_shock_absorber ) ) { you->add_msg_if_player( m_info, _( "You hit the ground hard, but your grav chute handles the impact admirably!" ) ); @@ -970,9 +975,10 @@ bool trapfunc::pit_glass( const tripoint &p, Creature *c, item * ) if( you != nullptr ) { int dodge = you->get_dodge(); int damage = pit_effectiveness( p ) * rng( 15, 35 ); - if( you->has_flag( json_flag_WINGS_2 ) || ( one_in( 2 ) && - you->has_flag( json_flag_WINGS_1 ) ) ) { - you->add_msg_if_player( _( "You flap your wings and flutter down gracefully." ) ); + if( you->can_fly() && ( you->has_flag( json_flag_WINGS_1 ) || + you->has_flag( json_flag_WINGS_2 ) ) ) { + you->add_msg_player_or_npc( _( "You spread your wings to slow your fall." ), + _( " spreads their wings to slow their fall." ) ); } else if( you->has_active_bionic( bio_shock_absorber ) ) { you->add_msg_if_player( m_info, _( "You hit the ground hard, but your grav chute handles the impact admirably!" ) ); @@ -1199,7 +1205,7 @@ bool trapfunc::ledge( const tripoint &p, Creature *c, item * ) return false; } - if( c->has_effect_with_flag( json_flag_LEVITATION ) ) { + if( c->has_effect_with_flag( json_flag_LEVITATION ) && !c->has_effect( effect_slow_descent ) ) { return false; } @@ -1252,6 +1258,15 @@ bool trapfunc::ledge( const tripoint &p, Creature *c, item * ) Character *you = dynamic_cast( c ); if( you == nullptr ) { c->setpos( where ); + if( c->get_size() == creature_size::tiny ) { + height = std::max( 0, height - 1 ); + } + if( c->has_effect( effect_weakened_gravity ) ) { + height = std::max( 0, height - 1 ); + } + if( c->has_effect( effect_strengthened_gravity ) ) { + height += 1; + } c->impact( height * 10, where ); return true; } @@ -1271,11 +1286,26 @@ bool trapfunc::ledge( const tripoint &p, Creature *c, item * ) } else { you->setpos( where ); } - if( you->has_flag( json_flag_WINGS_2 ) || ( one_in( 2 ) && - you->has_flag( json_flag_WINGS_1 ) ) ) { - you->add_msg_player_or_npc( _( "You flap your wings and flutter down gracefully." ), - _( " flaps their wings and flutters down gracefully." ) ); - } else if( you->has_active_bionic( bio_shock_absorber ) ) { + if( you->get_size() == creature_size::tiny ) { + height = std::max( 0, height - 1 ); + } + if( you->has_effect( effect_weakened_gravity ) ) { + height = std::max( 0, height - 1 ); + } + if( you->has_effect( effect_strengthened_gravity ) ) { + height += 1; + } + if( you->can_fly() && you->has_flag( json_flag_WINGS_1 ) ) { + you->add_msg_player_or_npc( _( "You spread your wings to slow your fall." ), + _( " spreads their wings to slow their fall." ) ); + height = std::max( 0, height - 1 ); + } + if( you->can_fly() && you->has_flag( json_flag_WINGS_2 ) ) { + you->add_msg_player_or_npc( _( "You spread your wings to slow your fall." ), + _( " spreads their wings to slow their fall." ) ); + height = std::max( 0, height - 2 ); + } + if( you->has_active_bionic( bio_shock_absorber ) ) { you->add_msg_if_player( m_info, _( "You hit the ground hard, but your grav chute handles the impact admirably!" ) ); } else if( !jetpack.is_null() ) { @@ -1674,4 +1704,4 @@ const trap_function &trap_function_from_string( const std::string &function_name debugmsg( "Could not find a trapfunc function matching '%s'!", function_name ); static const trap_function null_fun = trapfunc::none; return null_fun; -} +} \ No newline at end of file diff --git a/src/vehicle.cpp b/src/vehicle.cpp index f62986f97bddc..e796d7584e583 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -115,6 +115,8 @@ static const itype_id itype_water_clean( "water_clean" ); static const itype_id itype_water_faucet( "water_faucet" ); static const itype_id itype_water_purifier( "water_purifier" ); +static const json_character_flag json_flag_MUSCLE_VEH_BOOST( "MUSCLE_VEH_BOOST" ); + static const proficiency_id proficiency_prof_aircraft_mechanic( "prof_aircraft_mechanic" ); static const proficiency_id proficiency_prof_athlete_basic( "prof_athlete_basic" ); static const proficiency_id proficiency_prof_athlete_expert( "prof_athlete_expert" ); @@ -1085,10 +1087,15 @@ units::power vehicle::part_vpower_w( const vehicle_part &vp, const bool at_full_ } else if( vpi.fuel_type == fuel_type_muscle ) { if( const Character *muscle_user = get_passenger( vp_index ) ) { // Calculate virtual strength bonus from cycling proficiency + float muscle_veh_boost_bonus = 0; const float athlete_form_bonus = muscle_user->get_proficiency_bonus( "athlete", proficiency_bonus_type::strength ); + if( muscle_user->has_trait_flag( json_flag_MUSCLE_VEH_BOOST ) ) { + muscle_veh_boost_bonus = 8; + } ///\EFFECT_STR increases power produced for MUSCLE_* vehicles - const float muscle_multiplier = muscle_user->str_cur - 8 + athlete_form_bonus; + const float muscle_multiplier = muscle_user->str_cur - 8 + athlete_form_bonus + + muscle_veh_boost_bonus; const float weary_multiplier = muscle_user->exertion_adjusted_move_multiplier(); const float engine_multiplier = vpi.engine_info->muscle_power_factor; pwr += units::from_watt( muscle_multiplier * weary_multiplier * engine_multiplier );