diff --git a/data/json/effects.json b/data/json/effects.json index fb08b9c0515f3..74e0d0e91362c 100644 --- a/data/json/effects.json +++ b/data/json/effects.json @@ -4712,5 +4712,22 @@ "pain_max_val": [ 35 ] }, "show_in_info": true + }, + { + "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 1cf52a4f458f6..d629525cebf1f 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 @@ -685,5 +685,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/effects_on_condition/mutation_eocs/prosthetics_eocs.json b/data/json/effects_on_condition/mutation_eocs/prosthetics_eocs.json index d145a2511177a..78e779c001296 100644 --- a/data/json/effects_on_condition/mutation_eocs/prosthetics_eocs.json +++ b/data/json/effects_on_condition/mutation_eocs/prosthetics_eocs.json @@ -368,16 +368,7 @@ "and": [ { "or": [ { "u_has_trait": "NO_LEFT_ARM" }, { "u_has_trait": "NO_RIGHT_ARM" } ] }, { - "and": [ - { - "or": [ - { "u_has_trait": "INSECT_ARMS" }, - { "u_has_trait": "ARACHNID_ARMS" }, - { "u_has_trait": "AMORPHOUS" }, - { "u_has_trait": "ARM_TENTACLES" } - ] - } - ] + "and": [ { "or": [ { "u_has_trait": "AMORPHOUS" }, { "u_has_trait": "ARM_TENTACLES" }, { "u_has_trait": "WINGS_BIRD" } ] } ] } ] }, diff --git a/data/json/enchantments.json b/data/json/enchantments.json index a793b2457757c..bff2fa9f750df 100644 --- a/data/json/enchantments.json +++ b/data/json/enchantments.json @@ -195,5 +195,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/mutation_category.json b/data/json/mutations/mutation_category.json index e5db51461f8fe..0b2ed66435a7e 100644 --- a/data/json/mutations/mutation_category.json +++ b/data/json/mutations/mutation_category.json @@ -281,7 +281,7 @@ "name": "Crustacean", "threshold_mut": "THRESH_CRUSTACEAN", "mutagen_message": "You suddenly want to burrow under some sand or find some nice rocky alcove to hide behind.", - "memorial_message": "Dreamed of walking along the sea floor", + "memorial_message": "Dreamed of walking along the sea floor.", "vitamin": "mutagen_crustacean", "base_removal_chance": 33, "base_removal_cost_mul": 2.5 diff --git a/data/json/mutations/mutation_limbs.json b/data/json/mutations/mutation_limbs.json index 222745df610c1..183f072d58db0 100644 --- a/data/json/mutations/mutation_limbs.json +++ b/data/json/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/json/mutations/mutations.json b/data/json/mutations/mutations.json index 3bb85dfa4c121..f43a139997999 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -447,6 +447,7 @@ "allowed_items": [ "ARM_PROSTHETIC" ], "enchantments": [ "ENCH_NO_LEFT_ARM" ], "cancels": [ + "ARM_FEATHERS", "BARBS", "NAILS", "CLAWS", @@ -454,7 +455,6 @@ "CLAWS_ST", "CLAWS_RETRACT", "CLAWS_TENTACLE", - "ARM_FEATHERS", "TALONS", "BENDY1", "PAWS", @@ -480,6 +480,7 @@ "allowed_items": [ "ARM_PROSTHETIC" ], "enchantments": [ "ENCH_NO_RIGHT_ARM" ], "cancels": [ + "ARM_FEATHERS", "CRUSTACEAN_CLAW", "BARBS", "NAILS", @@ -489,7 +490,6 @@ "CLAWS_RETRACT", "CLAWS_TENTACLE", "TALONS", - "ARM_FEATHERS", "BENDY1", "PAWS", "PAWS_LARGE", @@ -5583,12 +5583,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", @@ -5597,17 +5600,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", @@ -6653,8 +6665,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", @@ -8058,19 +8070,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.", - "category": [ "RAPTOR" ], + "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 f54c103dd254c..f468654854fb0 100644 --- a/data/json/player_activities.json +++ b/data/json/player_activities.json @@ -1124,6 +1124,18 @@ "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 + }, { "id": "ACT_SALINE_INFUSE", "type": "activity_type", diff --git a/data/mods/Magiclysm/mutations/mutations.json b/data/mods/Magiclysm/mutations/mutations.json index 6df5f5cfb5d2c..7b0eb3f335977 100644 --- a/data/mods/Magiclysm/mutations/mutations.json +++ b/data/mods/Magiclysm/mutations/mutations.json @@ -445,11 +445,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 3518d39428c23..20b4f022bc46a 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 @@ -391,6 +392,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. @@ -405,6 +408,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. @@ -450,8 +454,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 f06058f5d1b7d..5dc2b8e3bc546 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" ); @@ -1460,6 +1465,167 @@ std::unique_ptr<activity_actor> 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." ), + _( "<npcname> 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." ), + _( "<npcname> 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." ), + _( "<npcname> 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." ), + _( "<npcname> 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<activity_actor> 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." ), + _( "<npcname> 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<int> &parts, const std::vector<int> &racks ) : parts( parts ), racks( racks ) @@ -7446,6 +7612,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 3a2a2dd86355b..23e6d04ce4288 100644 --- a/src/activity_actor_definitions.h +++ b/src/activity_actor_definitions.h @@ -342,6 +342,34 @@ class hotwire_car_activity_actor : public activity_actor static std::unique_ptr<activity_actor> 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<int>( 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<activity_actor> clone() const override { + return std::make_unique<glide_activity_actor>( *this ); + } + + void serialize( JsonOut &jsout ) const override; + static std::unique_ptr<activity_actor> deserialize( JsonValue &jsin ); +}; + class bikerack_racking_activity_actor : public activity_actor { private: diff --git a/src/character.cpp b/src/character.cpp index ef6cd7ab2b3a2..eaf74dbf3df30 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -237,6 +237,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" ); @@ -309,6 +310,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" ); @@ -341,6 +344,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" ); @@ -1582,7 +1589,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<monster> mons = g->shared_from( z ); if( mons == nullptr ) { @@ -1606,6 +1612,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 ); @@ -11091,12 +11098,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<tripoint> 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!" ), + _( "<npcname> stumbles!" ) ); + setpos( dest ); + mod_moves( -100 ); + break; + } + } +} + double Character::vomit_mod() { double mod = mutation_value( "vomit_multiplier" ); @@ -12542,6 +12596,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 609c771c84d7b..f0eee80ee42bc 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; @@ -2535,6 +2536,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; @@ -4054,4 +4057,4 @@ std::string get_stat_name( character_stat Stat ); extern template bool Character::can_lift<item>( const item &obj ) const; extern template bool Character::can_lift<vehicle>( 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 c522ed858d089..5a170ad371528 100644 --- a/src/do_turn.cpp +++ b/src/do_turn.cpp @@ -5,6 +5,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" @@ -444,6 +445,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 a8fdda7e78e25..bc0c270bfb929 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -232,6 +232,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" ); @@ -11842,7 +11843,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; @@ -12105,7 +12107,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 ); } @@ -12184,7 +12187,8 @@ std::optional<tripoint> 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 ) ) || @@ -12250,6 +12254,10 @@ std::optional<tripoint> 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 eb9b6bd38fc24..eb0fba493673e 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" ); @@ -5018,6 +5024,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(); @@ -5025,6 +5032,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--; @@ -5042,9 +5069,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. @@ -5127,6 +5157,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; @@ -5135,6 +5205,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 ab896a0052b3a..ae01f1aebbe91 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" ); @@ -6094,6 +6095,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() ); @@ -9698,10 +9704,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 c79e469e464ab..b36625824420b 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 d4ae930861877..72459ee3c84f1 100644 --- a/src/mutation.cpp +++ b/src/mutation.cpp @@ -2466,4 +2466,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 76980df4675d0..65e1223d0e066 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; @@ -651,4 +651,4 @@ struct enum_traits<mutagen_technique> { 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 b6e511ac7314c..b0f74cc4e422b 100644 --- a/src/mutation_data.cpp +++ b/src/mutation_data.cpp @@ -1227,4 +1227,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 2f33325723812..5521d6dba676f 100644 --- a/src/suffer.cpp +++ b/src/suffer.cpp @@ -178,7 +178,7 @@ static const trait_id trait_VINES3( "VINES3" ); 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" ); @@ -1120,7 +1120,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" ); @@ -2212,4 +2212,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<monster *>( c ); Character *you = dynamic_cast<Character *>( 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." ), + _( "<npcname> 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." ), + _( "<npcname> 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." ), + _( "<npcname> 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<Character *>( 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." ), - _( "<npcname> 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." ), + _( "<npcname> 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." ), + _( "<npcname> 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 4bdd5abb376cb..83e98ba717fe4 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 );