diff --git a/data/json/effects.json b/data/json/effects.json index 8da3f3bc2596b..0bb313c6008e3 100644 --- a/data/json/effects.json +++ b/data/json/effects.json @@ -4637,5 +4637,28 @@ ], "max_intensity": 4, "rating": "neutral" + }, + { + "type": "effect_type", + "id": "cramped_space", + "name": [ "Cramped Space" ], + "desc": [ "You are painfully crammed into a space too small for your body." ], + "miss_messages": [ [ "You don't have any room to move!", 1 ] ], + "apply_message": "You are squeezed uncomfortably into the cramped space.", + "remove_message": "It feels good having room to move again.", + "rating": "bad", + "resist_traits": [ "BENDY1", "BENDY2", "BENDY3" ], + "resist_effects": [ "incorporeal" ], + "pain_sizing": true, + "hurt_sizing": true, + "base_mods": { + "dex_mod": [ -2 ], + "str_mod": [ -2 ], + "speed_mod": [ -20 ], + "pain_min": [ 2 ], + "pain_chance": [ 100, 500 ], + "pain_max_val": [ 35 ] + }, + "show_in_info": true } ] diff --git a/data/json/furniture_and_terrain/furniture-storage.json b/data/json/furniture_and_terrain/furniture-storage.json index 6dee0739cbb13..0d6528114093e 100644 --- a/data/json/furniture_and_terrain/furniture-storage.json +++ b/data/json/furniture_and_terrain/furniture-storage.json @@ -1402,7 +1402,8 @@ "move_cost_mod": 3, "coverage": 70, "required_str": 16, - "flags": [ "CONTAINER", "PLACE_ITEM", "BLOCKSDOOR", "HIDE_PLACE", "NO_SIGHT", "FIRE_CONTAINER" ], + "max_volume": "1500 L", + "flags": [ "CONTAINER", "PLACE_ITEM", "BLOCKSDOOR", "HIDE_PLACE", "NO_SIGHT", "FIRE_CONTAINER", "LIQUIDCONT" ], "deconstruct": { "items": [ { "item": "frame", "count": [ 2, 3 ] }, diff --git a/data/json/items/vehicle/seating.json b/data/json/items/vehicle/seating.json index 04d053c75af91..f76391117e365 100644 --- a/data/json/items/vehicle/seating.json +++ b/data/json/items/vehicle/seating.json @@ -2,13 +2,13 @@ { "type": "GENERIC", "id": "seat", - "name": { "str": "seat" }, + "name": { "str": "bucket seat" }, "description": "A soft car seat made from synthetic fabric.", "weight": "9071 g", "to_hit": -4, "color": "red", "symbol": "0", - "material": [ "cotton", "steel" ], + "material": [ "nylon", "steel" ], "volume": "7500 ml", "category": "veh_parts", "price": 35000, @@ -19,9 +19,35 @@ "type": "GENERIC", "copy-from": "seat", "id": "seat_leather", - "name": { "str": "leather seat" }, + "name": { "str": "leather bucket seat" }, "description": "A soft car seat covered with leather.", - "material": [ "leather", "cotton", "steel" ], + "material": [ "leather", "nylon", "steel" ], + "price": 60000, + "price_postapoc": 250 + }, + { + "type": "GENERIC", + "id": "seat_bench", + "name": { "str": "bench seat" }, + "description": "A wide, flat seat like you'd find in the back of a sedan, upholstered with soft nylon.", + "weight": "9071 g", + "to_hit": -4, + "color": "red", + "symbol": "0", + "material": [ "nylon", "steel" ], + "volume": "7500 ml", + "category": "veh_parts", + "price": 35000, + "price_postapoc": 100, + "melee_damage": { "bash": 4 } + }, + { + "type": "GENERIC", + "copy-from": "seat", + "id": "seat_bench_leather", + "name": { "str": "leather bench seat" }, + "description": "A wide, flat seat like you'd find in the back of a sedan, upholstered with leather.", + "material": [ "leather", "nylon", "steel" ], "price": 60000, "price_postapoc": 250 }, diff --git a/data/json/npcs/talk_tags.json b/data/json/npcs/talk_tags.json index 111e9088b7e95..b60b504c54d12 100644 --- a/data/json/npcs/talk_tags.json +++ b/data/json/npcs/talk_tags.json @@ -1015,18 +1015,23 @@ "category": "", "//": "NPC complaint if a HUGE NPC is taking pain from being inside a vehicle. Key points are that the NPC is HUGE and they're in a confined vehicle, along with some hints on how to resolve it.", "text": [ - "Can I get out and walk? This vehicle is too small.", + "Can I get out and walk? This spot is too cramped.", "How about we make the next vehicle a convertible?", "This vehicle is too small.", + "Argh, I don't fit!", "I'm not a contortionist!", - "I can't fit in your tiny human vehicle.", - "This sucks. I'm too big.", + "There's not enough room for me here.", + "This spot is squeezing the life out of me.", + "I can hardly breathe, crammed in like this.", + "I'm like a sardine over here!.", + "Ugh, I'm getting squashed.", + "How about we stop and stretch our legs?", + "I could use a break from being wedged in like this.", + "This is like being in a trash compactor.", + ", I can barely move!", "This space was not designed for big boned people.", - "So the fact I am huge isn't an issue to you? As it is to me.", - "I don't like being a mutant crammed into your tiny little vehicle.", - "Getting awfully cramped in here.", - "I don't think this vehicle was designed for someone like me.", - "It's tighter than a nun's cunt in here!'" + "I don't like being crammed in like this.", + "Getting awfully cramped in here." ] }, { diff --git a/data/json/recipes/other/parts.json b/data/json/recipes/other/parts.json index b763008be05c6..71b63ae53ccc2 100644 --- a/data/json/recipes/other/parts.json +++ b/data/json/recipes/other/parts.json @@ -763,7 +763,7 @@ { "proficiency": "prof_welding_basic", "skill_penalty": 0.5 }, { "proficiency": "prof_welding" } ], - "components": [ [ [ "pipe", 4 ] ], [ [ "spring", 2 ] ], [ [ "cotton_patchwork", 20 ], [ "sheet", 1 ] ], [ [ "pipe_fittings", 4 ] ] ] + "components": [ [ [ "pipe", 4 ] ], [ [ "spring", 2 ] ], [ [ "nylon", 20 ], [ "sheet_nylon", 3 ] ], [ [ "pipe_fittings", 4 ] ] ] }, { "type": "recipe", @@ -790,7 +790,55 @@ [ [ "spring", 2 ] ], [ [ "leather", 12 ], [ "fur", 12 ], [ "tanned_hide", 3 ], [ "tanned_pelt", 3 ] ], [ [ "pipe_fittings", 4 ] ], - [ [ "cotton_patchwork", 8 ] ] + [ [ "nylon", 8 ] ] + ] + }, + { + "type": "recipe", + "activity_level": "MODERATE_EXERCISE", + "result": "seat_bench", + "category": "CC_OTHER", + "subcategory": "CSC_OTHER_PARTS", + "skill_used": "fabrication", + "difficulty": 1, + "time": "2 h", + "reversible": { "time": "20 m" }, + "autolearn": true, + "//": "20cm weld", + "using": [ [ "sewing_standard", 50 ], [ "welding_standard", 20 ] ], + "proficiencies": [ + { "proficiency": "prof_metalworking" }, + { "proficiency": "prof_welding_basic", "skill_penalty": 0.5 }, + { "proficiency": "prof_welding" } + ], + "components": [ [ [ "pipe", 4 ] ], [ [ "spring", 2 ] ], [ [ "nylon", 20 ], [ "sheet_nylon", 3 ] ], [ [ "pipe_fittings", 4 ] ] ] + }, + { + "type": "recipe", + "activity_level": "MODERATE_EXERCISE", + "result": "seat_bench_leather", + "category": "CC_OTHER", + "subcategory": "CSC_OTHER_PARTS", + "skill_used": "fabrication", + "difficulty": 1, + "time": "6 h", + "reversible": { "time": "20 m" }, + "autolearn": true, + "//": "20cm weld", + "using": [ [ "sewing_standard", 50 ], [ "welding_standard", 20 ] ], + "proficiencies": [ + { "proficiency": "prof_metalworking" }, + { "proficiency": "prof_welding_basic", "skill_penalty": 0.5 }, + { "proficiency": "prof_welding" }, + { "proficiency": "prof_leatherworking_basic" }, + { "proficiency": "prof_leatherworking" } + ], + "components": [ + [ [ "pipe", 4 ] ], + [ [ "spring", 2 ] ], + [ [ "leather", 12 ], [ "fur", 12 ], [ "tanned_hide", 3 ], [ "tanned_pelt", 3 ] ], + [ [ "pipe_fittings", 4 ] ], + [ [ "nylon", 8 ] ] ] }, { diff --git a/data/json/vehicleparts/cargo.json b/data/json/vehicleparts/cargo.json index fef17d9acd507..a5db7d6d5981e 100644 --- a/data/json/vehicleparts/cargo.json +++ b/data/json/vehicleparts/cargo.json @@ -225,7 +225,7 @@ "removal": { "skills": [ [ "mechanics", 2 ] ], "time": "30 m", "using": [ [ "vehicle_weld_removal", 1 ] ] }, "repair": { "skills": [ [ "mechanics", 4 ] ], "time": "6 m", "using": [ [ "repair_welding_standard", 5 ] ] } }, - "flags": [ "UNMOUNT_ON_DAMAGE", "CARGO", "PROTRUSION", "COVERED", "BOARDABLE" ], + "flags": [ "UNMOUNT_ON_DAMAGE", "CARGO", "PROTRUSION", "COVERED", "BOARDABLE", "HUGE_OK" ], "damage_reduction": { "all": 26 }, "variants": [ { "symbols": "=", "symbols_broken": "#" } ] }, diff --git a/data/json/vehicleparts/doors.json b/data/json/vehicleparts/doors.json index c6e67a23586e4..fe60b76c8c5ca 100644 --- a/data/json/vehicleparts/doors.json +++ b/data/json/vehicleparts/doors.json @@ -9,7 +9,7 @@ "damage_reduction": { "all": 20 }, "description": "A door. Has a window so you can see out of it, even when closed.", "durability": 225, - "flags": [ "CARGO", "OBSTACLE", "OPENABLE", "BOARDABLE", "WINDOW", "DOOR", "LOCKABLE_DOOR" ], + "flags": [ "CARGO", "CARGO_PASSABLE", "OBSTACLE", "OPENABLE", "BOARDABLE", "WINDOW", "DOOR", "LOCKABLE_DOOR", "HUGE_OK" ], "item": "frame", "location": "center", "looks_like": "t_door_metal_c", @@ -209,7 +209,7 @@ }, "breaks_into": [ { "item": "splinter", "count": [ 7, 9 ] } ], "extend": { "flags": [ "SIMPLE_PART" ] }, - "delete": { "flags": [ "CARGO" ] }, + "delete": { "flags": [ "CARGO", "CARGO_PASSABLE" ] }, "damage_reduction": { "all": 8 } }, { @@ -237,7 +237,7 @@ "repair": { "skills": [ [ "mechanics", 3 ] ], "time": "60 m", "using": [ [ "repair_welding_standard", 2 ] ] } }, "extend": { "flags": [ "LOW_FINAL_AIR_DRAG", "SIMPLE_PART", "NO_ROOF_NEEDED" ] }, - "delete": { "flags": [ "CARGO", "COVERED", "LOCKABLE_CARGO" ] } + "delete": { "flags": [ "CARGO", "CARGO_PASSABLE", "COVERED", "LOCKABLE_CARGO" ] } }, { "type": "vehicle_part", @@ -272,7 +272,7 @@ }, "breaks_into": "ig_vp_sheet_metal", "extend": { "flags": [ "OPAQUE", "SIMPLE_PART" ] }, - "delete": { "flags": [ "CARGO", "COVERED", "LOCKABLE_CARGO", "WINDOW" ] }, + "delete": { "flags": [ "CARGO", "CARGO_PASSABLE", "COVERED", "LOCKABLE_CARGO", "WINDOW" ] }, "damage_reduction": { "all": 7 } }, { diff --git a/data/json/vehicleparts/seats.json b/data/json/vehicleparts/seats.json index de27e4385a2b5..567a9288cc744 100644 --- a/data/json/vehicleparts/seats.json +++ b/data/json/vehicleparts/seats.json @@ -1,7 +1,7 @@ [ { "entries": [ - { "count": [ 2, 8 ], "item": "cotton_patchwork" }, + { "count": [ 2, 8 ], "item": "nylon" }, { "count": [ 1, 3 ], "item": "pipe" }, { "count": [ 0, 2 ], "item": "spring" }, { "count": [ 3, 5 ], "item": "scrap" } @@ -13,7 +13,7 @@ { "entries": [ { "count": [ 1, 5 ], "item": "leather" }, - { "count": [ 1, 3 ], "item": "cotton_patchwork" }, + { "count": [ 1, 3 ], "item": "nylon" }, { "count": [ 1, 3 ], "item": "pipe" }, { "count": [ 0, 2 ], "item": "spring" }, { "count": [ 3, 5 ], "item": "scrap" } @@ -31,7 +31,7 @@ "comfort": 2, "damage_modifier": 60, "damage_reduction": { "all": 2, "bash": 5 }, - "description": "A place to sit, or sleep if you're desperate.", + "description": "A soft upholstered car seat, contoured to hold one person.", "durability": 300, "flags": [ "SEAT", "BOARDABLE", "CARGO", "BELTABLE" ], "floor_bedding_warmth": 200, @@ -39,7 +39,7 @@ "location": "center", "looks_like": "reclining_seat", "//": "weld repairs would mostly be the frame and springs of the seat, so low 6cm cost per quadrant", - "name": { "str": "seat" }, + "name": { "str": "bucket seat" }, "requirements": { "install": { "skills": [ [ "mechanics", 1 ] ], "time": "30 m", "using": [ [ "vehicle_wrench_2", 1 ] ] }, "removal": { "skills": [ [ "mechanics", 2 ] ], "time": "15 m", "using": [ [ "vehicle_wrench_2", 1 ] ] }, @@ -49,7 +49,7 @@ "using": [ [ "repair_welding_standard", 1 ], [ "sewing_standard", 8 ] ] } }, - "size": "25 L", + "size": "80 L", "type": "vehicle_part", "variants_bases": [ { "id": "windshield", "label": "Windshield" }, { "id": "swivel_chair", "label": "Swivel Chair" } ], "variants": [ @@ -67,7 +67,8 @@ "breaks_into": "ig_vp_seat_leather", "copy-from": "seat", "item": "seat_leather", - "name": { "str": "leather seat" }, + "name": { "str": "leather bucket seat" }, + "description": "A leather car seat for one.", "type": "vehicle_part", "variants_bases": [ { "id": "windshield", "label": "Windshield" } ] }, @@ -75,11 +76,11 @@ "id": "reclining_seat", "copy-from": "seat", "comfort": 3, - "description": "A place to sit, with an adjustable backrest.", + "description": "A soft seat with an adjustable backrest, making it a reasonably comfortable place to sleep.", "durability": 100, + "size": "84000 ml", "floor_bedding_warmth": 350, - "name": { "str": "reclining seat" }, - "size": "6250 ml", + "name": { "str": "reclining bucket seat" }, "extend": { "flags": [ "BED" ] }, "type": "vehicle_part", "variants_bases": [ { "id": "windshield", "label": "Windshield" } ] @@ -90,18 +91,18 @@ "copy-from": "reclining_seat", "item": "seat_leather", "looks_like": "seat_leather", - "name": { "str": "reclining leather seat" }, + "name": { "str": "reclining leather bucket seat" }, "type": "vehicle_part", "variants_bases": [ { "id": "windshield", "label": "Windshield" } ] }, { "id": "seat_back", "copy-from": "seat", - "comfort": 4, - "description": "A benchlike seat. Mostly found in the second row, also known as the back or rear seat.", + "comfort": 3, + "description": "A soft, wide seat with a high back, the kind often used in back seats or older cars. It might be a decent place to sleep.", "floor_bedding_warmth": 500, - "name": { "str": "back seat" }, - "size": "30 L", + "item": "seat_bench", + "name": { "str": "bench seat" }, "extend": { "flags": [ "BED" ] }, "type": "vehicle_part" }, @@ -109,9 +110,9 @@ "id": "seat_back_leather", "copy-from": "seat_back", "breaks_into": "ig_vp_seat_leather", - "item": "seat_leather", + "item": "seat_bench_leather", "looks_like": "seat_leather", - "name": { "str": "leather back seat" }, + "name": { "str": "leather bench seat" }, "type": "vehicle_part" }, { @@ -165,7 +166,7 @@ }, { "id": "metal_bench", - "copy-from": "seat", + "copy-from": "seat_back", "breaks_into": "ig_vp_prison_bench", "comfort": 0, "floor_bedding_warmth": 0, @@ -179,8 +180,7 @@ "removal": { "skills": [ [ "mechanics", 2 ] ], "time": "30 m", "using": "vehicle_weld_removal" }, "repair": { "skills": [ [ "mechanics", 2 ] ], "time": "60 m", "using": [ [ "repair_welding_standard", 2 ] ] } }, - "size": "0 ml", - "delete": { "flags": [ "CARGO" ] }, + "size": "62500 ml", "type": "vehicle_part" } ] diff --git a/data/json/vehicleparts/vehicle_parts.json b/data/json/vehicleparts/vehicle_parts.json index 68f433948604f..fc66af13eb98a 100644 --- a/data/json/vehicleparts/vehicle_parts.json +++ b/data/json/vehicleparts/vehicle_parts.json @@ -121,7 +121,8 @@ "STEERABLE", "UNMOUNT_ON_DAMAGE", "UNSTABLE_WHEEL", - "WHEEL" + "WHEEL", + "HUGE_OK" ], "damage_reduction": { "all": 2 }, "variants": [ { "symbols": "H", "symbols_broken": "M" } ] @@ -160,7 +161,8 @@ "STEERABLE", "UNMOUNT_ON_DAMAGE", "UNSTABLE_WHEEL", - "WHEEL" + "WHEEL", + "HUGE_OK" ], "damage_reduction": { "all": 20 }, "variants": [ { "symbols": "H", "symbols_broken": "M" } ] @@ -187,7 +189,16 @@ "repair": { "skills": [ [ "fabrication", 1 ] ], "time": "5 m", "using": [ [ "repair_welding_standard", 1 ] ] } }, "breaks_into": [ { "item": "scrap", "count": [ 4, 6 ] } ], - "flags": [ "ENGINE", "BOARDABLE", "E_STARTS_INSTANTLY", "ANIMAL_CTRL", "HARNESS_human", "STEERABLE", "UNMOUNT_ON_DAMAGE" ], + "flags": [ + "ENGINE", + "BOARDABLE", + "E_STARTS_INSTANTLY", + "ANIMAL_CTRL", + "HARNESS_human", + "STEERABLE", + "UNMOUNT_ON_DAMAGE", + "HUGE_OK" + ], "damage_reduction": { "all": 2 }, "variants": [ { "symbols": "-", "symbols_broken": "~" } ] }, @@ -202,7 +213,7 @@ "damage_modifier": 60, "durability": 95, "description": "A small but comfortable bed.", - "size": "50 L", + "size": "100 L", "item": "mattress", "comfort": 4, "floor_bedding_warmth": 700, @@ -298,6 +309,7 @@ "color": "white", "durability": 400, "description": "An aisle.", + "size": "100 L", "item": "sheet_metal", "location": "center", "//": "1m square sheet of metal, likely affixed with bolts in places, assume 200cm weld to install and 50cm weld to repair", @@ -314,7 +326,7 @@ }, "repair": { "skills": [ [ "mechanics", 2 ] ], "time": "6 m", "using": [ [ "repair_welding_standard", 5 ] ] } }, - "flags": [ "AISLE", "BOARDABLE" ], + "flags": [ "AISLE", "BOARDABLE", "CARGO" ], "breaks_into": "ig_vp_sheet_metal", "damage_reduction": { "all": 28 }, "variants": [ @@ -348,7 +360,7 @@ "description": "A collapsible aisle.", "folded_volume": "12500 ml", "item": "foldwoodframe", - "flags": [ "AISLE", "BOARDABLE" ] + "flags": [ "AISLE", "BOARDABLE", "CARGO" ] }, { "type": "vehicle_part", @@ -358,7 +370,7 @@ "categories": [ "cargo" ], "color": "white", "durability": 400, - "description": "An aisle. A hatch lets you access a cargo space beneath it.", + "description": "An aisle. A hatch lets you access a cargo space beneath it, keeping items stored here out of the way.", "size": "87500 ml", "item": "cargo_aisle", "location": "center", @@ -376,7 +388,7 @@ }, "repair": { "skills": [ [ "mechanics", 2 ] ], "time": "10 m", "using": [ [ "repair_welding_standard", 6 ] ] } }, - "flags": [ "AISLE", "BOARDABLE", "CARGO", "LOCKABLE_CARGO", "COVERED" ], + "flags": [ "AISLE", "BOARDABLE", "CARGO", "CARGO_PASSABLE", "LOCKABLE_CARGO", "COVERED" ], "breaks_into": "ig_vp_sheet_metal", "damage_reduction": { "all": 28 }, "variants": [ { "symbols": "=", "symbols_broken": "#" } ] @@ -863,7 +875,7 @@ "size": "75 L", "item": "frame_wood_light", "location": "center", - "flags": [ "CARGO", "BOARDABLE", "COVERED" ], + "flags": [ "CARGO", "BOARDABLE", "COVERED", "HUGE_OK" ], "breaks_into": [ { "item": "splinter", "count": [ 3, 5 ] }, { "item": "string_36", "count": [ 10, 15 ] } ], "requirements": { "install": { "time": "10 m", "using": [ [ "rope_natural_short", 1 ] ] }, @@ -2205,7 +2217,7 @@ }, "repair": { "skills": [ [ "mechanics", 2 ] ], "time": "20 m", "using": [ [ "repair_welding_standard", 12 ] ] } }, - "flags": [ "BOARDABLE", "CARGO", "COVERED" ], + "flags": [ "BOARDABLE", "CARGO", "COVERED", "HUGE_OK" ], "breaks_into": [ { "item": "steel_lump", "count": [ 6, 8 ] }, { "item": "steel_chunk", "count": [ 6, 8 ] }, @@ -2224,7 +2236,8 @@ "durability": 250, "description": "A cargo space for carrying livestock. 'e'xamine it to capture an animal next to you, or to release the animal currently contained. When selecting an animal to capture, choose its tile relative to you, not the part.", "//1": "240cm weld to install, 60cm per damage quadrant to repair", - "size": "200 L", + "size": "300 L", + "comfort": 2, "item": "livestock_carrier", "location": "center", "requirements": { @@ -2240,7 +2253,7 @@ }, "repair": { "skills": [ [ "mechanics", 2 ] ], "time": "20 m", "using": [ [ "repair_welding_standard", 12 ] ] } }, - "flags": [ "BOARDABLE", "CARGO", "COVERED", "CAPTURE_MONSTER_VEH" ], + "flags": [ "BOARDABLE", "CARGO", "COVERED", "CAPTURE_MONSTER_VEH", "HUGE_OK", "BED", "BELTABLE" ], "breaks_into": [ { "item": "steel_lump", "count": [ 6, 8 ] }, { "item": "steel_chunk", "count": [ 6, 8 ] }, @@ -2471,7 +2484,7 @@ "broken_color": "brown", "damage_modifier": 20, "durability": 75, - "description": "A place to sit.", + "description": "A place to sit, barely.", "item": "frame_wood_light", "location": "center", "requirements": { @@ -2495,14 +2508,16 @@ "damage_modifier": 60, "durability": 125, "description": "A place to sit.", + "comfort": 1, "item": "frame_wood", "location": "center", + "size": "62500 ml", "requirements": { "install": { "skills": [ [ "mechanics", 1 ] ], "time": "30 m", "using": [ [ "vehicle_nail_install", 1 ] ] }, "removal": { "skills": [ [ "mechanics", 2 ] ], "time": "15 m", "using": [ [ "vehicle_nail_removal", 1 ] ] }, "repair": { "skills": [ [ "mechanics", 2 ] ], "time": "30 m", "using": [ [ "adhesive", 2 ] ] } }, - "flags": [ "SEAT", "BOARDABLE", "BELTABLE" ], + "flags": [ "SEAT", "BOARDABLE", "BELTABLE", "CARGO" ], "breaks_into": [ { "item": "splinter", "count": [ 7, 9 ] } ], "damage_reduction": { "all": 8 }, "variants": [ { "symbols": "#", "symbols_broken": "*" } ] diff --git a/data/json/vehicleparts/vp_flags.json b/data/json/vehicleparts/vp_flags.json index 2b1a728c467e0..9a06d4cb567a4 100644 --- a/data/json/vehicleparts/vp_flags.json +++ b/data/json/vehicleparts/vp_flags.json @@ -666,5 +666,15 @@ "id": "NEEDS_WHEEL_MOUNT_SKATEBOARD", "type": "json_flag", "requires_flag": "WHEEL_MOUNT_SKATEBOARD" + }, + { + "id": "CARGO_PASSABLE", + "type": "json_flag", + "info": "Items stored here won't get in the way of any passengers." + }, + { + "id": "HUGE_OK", + "type": "json_flag", + "info": "Even when roofed, this part is roomy enough to comfortably admit very large creatures, like cows and horses." } ] diff --git a/src/character.cpp b/src/character.cpp index ca21d8fe776b7..8797308b8c0d6 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -223,6 +223,7 @@ static const efftype_id effect_contacts( "contacts" ); static const efftype_id effect_controlled( "controlled" ); static const efftype_id effect_corroding( "corroding" ); static const efftype_id effect_cough_suppress( "cough_suppress" ); +static const efftype_id effect_cramped_space( "cramped_space" ); static const efftype_id effect_darkness( "darkness" ); static const efftype_id effect_deaf( "deaf" ); static const efftype_id effect_dermatik( "dermatik" ); @@ -5091,24 +5092,9 @@ void Character::update_needs( int rate_multiplier ) mod_painkiller( -std::min( get_painkiller(), rate_multiplier ) ); } - // Huge folks take penalties for cramming themselves in vehicles - if( in_vehicle && get_size() == creature_size::huge && - !( has_flag( json_flag_PAIN_IMMUNE ) || has_effect( effect_narcosis ) ) ) { - vehicle *veh = veh_pointer_or_null( get_map().veh_at( pos() ) ); - // it's painful to work the controls, but passengers in open topped vehicles are fine - if( veh && ( veh->enclosed_at( pos() ) || veh->player_in_control( *this ) ) ) { - add_msg_if_player( m_bad, - _( "You're cramping up from stuffing yourself in this vehicle." ) ); - if( is_npc() ) { - npc &as_npc = dynamic_cast( *this ); - as_npc.complain_about( "cramped_vehicle", 1_hours, "", false ); - } - - mod_pain( rng( 4, 6 ) ); - mod_focus( -1 ); - } - } + // Being stuck in a tight space sucks. } + needs_rates Character::calc_needs_rates() const { const effect &sleep = get_effect( effect_sleep ); @@ -5600,7 +5586,7 @@ Character::comfort_response_t Character::base_comfort_value( const tripoint &p ) } else { comfort -= here.move_cost( p ); } - if( vp->vehicle().enclosed_at( p ) && get_size() == creature_size::huge ) { + if( has_effect( effect_cramped_space ) ) { comfort = static_cast( comfort_level::impossible ); } } @@ -7974,6 +7960,61 @@ std::string Character::weapname_ammo() const } } +// Tests to see if a character has room to enter a vehicle tile. +bool Character::move_in_vehicle( Creature *c, const tripoint &dest_loc ) const +{ + map &m = get_map(); + const optional_vpart_position vp_there = m.veh_at( dest_loc ); + if( vp_there ) { + vehicle &veh = vp_there->vehicle(); + units::volume capacity = 0_ml; + units::volume free_cargo = 0_ml; + auto cargo_parts = veh.get_parts_at( dest_loc, "CARGO", part_status_flag::any ); + for( vehicle_part *&part : cargo_parts ) { + vehicle_stack contents = veh.get_items( *part ); + const vpart_info &vpinfo = part->info(); + const optional_vpart_position vp = m.veh_at( dest_loc ); + if( !vp.part_with_feature( "CARGO_PASSABLE", true ) ) { + capacity += vpinfo.size; + free_cargo += contents.free_volume(); + } + } + if( capacity > 0_ml ) { + // First, we'll try to squeeze in. Open-topped vehicle parts have more room for us. + if( !veh.enclosed_at( dest_loc ) ) { + free_cargo *= 1.2; + } + const creature_size size = get_size(); + if( ( size == creature_size::tiny && free_cargo < 15625_ml ) || + ( size == creature_size::small && free_cargo < 31250_ml ) || + ( size == creature_size::medium && free_cargo < 62500_ml ) || + ( size == creature_size::large && free_cargo < 125000_ml ) || + ( size == creature_size::huge && free_cargo < 250000_ml ) ) { + if( ( size == creature_size::tiny && free_cargo < 11719_ml ) || + ( size == creature_size::small && free_cargo < 23438_ml ) || + ( size == creature_size::medium && free_cargo < 46875_ml ) || + ( size == creature_size::large && free_cargo < 93750_ml ) || + ( size == creature_size::huge && free_cargo < 187500_ml ) ) { + add_msg_if_player( m_warning, _( "There's not enough room for you to fit there." ) ); + return false; // Even if we squeeze, there's no room. + } + c->add_effect( effect_cramped_space, 2_turns, true ); + return true; + } + } + const optional_vpart_position vp = m.veh_at( dest_loc ); + // Sufficiently gigantic characters aren't comfortable in stock seats, roof or no. + if( in_vehicle && get_size() == creature_size::huge && !vp.part_with_feature( "AISLE", true ) && + !vp.part_with_feature( "HUGE_OK", true ) && !has_effect( effect_cramped_space ) ) { + add_msg_if_player( m_warning, _( "You barely fit in this tiny human vehicle." ) ); + add_msg_if_npc( m_warning, _( "%s has to really cram their huge body to fit." ), c->disp_name() ); + c->add_effect( effect_cramped_space, 2_turns, true ); + return true; + } + } + return true; +} + void Character::on_hit( Creature *source, bodypart_id bp_hit, float /*difficulty*/, dealt_projectile_attack const *const proj ) { @@ -10861,6 +10902,62 @@ void Character::process_effects() terminating_effects.pop(); } + // Being stuck in tight spaces sucks. TODO: could be expanded to apply to non-vehicle conditions. + if( has_effect( effect_cramped_space ) && !in_vehicle ) { + remove_effect( effect_cramped_space ); + } + // Check all of this here to ensure the player can't sit in a comfortable seat and then drop 50 liters of junk in their own lap. + if( in_vehicle ) { + map &here = get_map(); + const tripoint your_pos = pos(); + if( is_npc() && !has_effect( effect_narcosis ) && has_effect( effect_cramped_space ) ) { + npc &as_npc = dynamic_cast( *this ); + as_npc.complain_about( "cramped_vehicle", 1_hours, "", false ); + } + const optional_vpart_position vp_there = here.veh_at( your_pos ); + if( vp_there ) { + vehicle &veh = vp_there->vehicle(); + units::volume capacity = 0_ml; + units::volume free_cargo = 0_ml; + auto cargo_parts = veh.get_parts_at( your_pos, "CARGO", part_status_flag::any ); + for( vehicle_part *&part : cargo_parts ) { + vehicle_stack contents = veh.get_items( *part ); + const vpart_info &vpinfo = part->info(); + const optional_vpart_position vp = here.veh_at( your_pos ); + if( !vp.part_with_feature( "CARGO_PASSABLE", true ) ) { + capacity += vpinfo.size; + free_cargo += contents.free_volume(); + } + } + const creature_size size = get_size(); + if( capacity > 0_ml ) { + // Open-topped vehicle parts have more room. + if( !veh.enclosed_at( your_pos ) ) { + free_cargo *= 1.2; + } + if( ( size == creature_size::tiny && free_cargo < 15625_ml ) || + ( size == creature_size::small && free_cargo < 31250_ml ) || + ( size == creature_size::medium && free_cargo < 62500_ml ) || + ( size == creature_size::large && free_cargo < 125000_ml ) || + ( size == creature_size::huge && free_cargo < 250000_ml ) ) { + if( !has_effect( effect_cramped_space ) ) { + add_effect( effect_cramped_space, 2_turns, true ); + } + return; + } + } + const optional_vpart_position vp = here.veh_at( your_pos ); + if( get_size() == creature_size::huge && !vp.part_with_feature( "AISLE", true ) && + !vp.part_with_feature( "HUGE_OK", true ) ) { + if( !has_effect( effect_cramped_space ) ) { + add_effect( effect_cramped_space, 2_turns, true ); + } + return; + } + } + remove_effect( effect_cramped_space ); + } + if( has_effect( effect_boomered ) && ( is_avatar() || is_npc() ) && one_in( 10 ) && !has_trait( trait_COMPOUND_EYES ) && !has_effect( effect_pre_conjunctivitis_bacterial ) && !has_effect( effect_pre_conjunctivitis_viral ) && !has_effect( effect_conjunctivitis_bacterial ) && diff --git a/src/character.h b/src/character.h index c947f303408db..f0fe1f7279fd0 100644 --- a/src/character.h +++ b/src/character.h @@ -1386,6 +1386,9 @@ class Character : public Creature, public visitable void dismount(); void forced_dismount(); + /** Attempt to enter a tile in a vehicle */ + bool move_in_vehicle( Creature *c, const tripoint &dest_loc ) const; + bool is_deaf() const; bool is_mute() const; // Get the specified limb score. If bp is defined, only the scores from that body part type are summed. diff --git a/src/game.cpp b/src/game.cpp index 9ef2750940183..272bcb464b0bd 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -10420,6 +10420,10 @@ bool game::walk_move( const tripoint &dest_loc, const bool via_ramp, const bool } u.set_underwater( false ); + if( vp_there && !u.move_in_vehicle( static_cast( &u ), dest_loc ) ) { + return false; + } + if( !shifting_furniture && !pushing && is_dangerous_tile( dest_loc ) ) { std::vector harmful_stuff = get_dangerous_tile( dest_loc ); if( harmful_stuff.size() == 1 && harmful_stuff[0] == "ledge" ) { diff --git a/src/monmove.cpp b/src/monmove.cpp index 50db8e34a16e5..af0891254dadc 100644 --- a/src/monmove.cpp +++ b/src/monmove.cpp @@ -50,6 +50,7 @@ #include "translations.h" #include "trap.h" #include "units.h" +#include "veh_type.h" #include "vehicle.h" #include "viewer.h" #include "vpart_position.h" @@ -58,6 +59,7 @@ static const damage_type_id damage_cut( "cut" ); static const efftype_id effect_bouldering( "bouldering" ); static const efftype_id effect_countdown( "countdown" ); +static const efftype_id effect_cramped_space( "cramped_space" ); static const efftype_id effect_docile( "docile" ); static const efftype_id effect_downed( "downed" ); static const efftype_id effect_dragging( "dragging" ); @@ -103,6 +105,7 @@ static const mon_flag_str_id mon_flag_PATH_AVOID_DANGER_1( "PATH_AVOID_DANGER_1" static const mon_flag_str_id mon_flag_PATH_AVOID_DANGER_2( "PATH_AVOID_DANGER_2" ); static const mon_flag_str_id mon_flag_PATH_AVOID_FALL( "PATH_AVOID_FALL" ); static const mon_flag_str_id mon_flag_PATH_AVOID_FIRE( "PATH_AVOID_FIRE" ); +static const mon_flag_str_id mon_flag_PLASTIC( "PLASTIC" ); static const mon_flag_str_id mon_flag_PRIORITIZE_TARGETS( "PRIORITIZE_TARGETS" ); static const mon_flag_str_id mon_flag_PUSH_MON( "PUSH_MON" ); static const mon_flag_str_id mon_flag_PUSH_VEH( "PUSH_VEH" ); @@ -112,6 +115,7 @@ static const mon_flag_str_id mon_flag_SEES( "SEES" ); static const mon_flag_str_id mon_flag_SHORTACIDTRAIL( "SHORTACIDTRAIL" ); static const mon_flag_str_id mon_flag_SLUDGETRAIL( "SLUDGETRAIL" ); static const mon_flag_str_id mon_flag_SMALLSLUDGETRAIL( "SMALLSLUDGETRAIL" ); +static const mon_flag_str_id mon_flag_SMALL_HIDER( "SMALL_HIDER" ); static const mon_flag_str_id mon_flag_SMELLS( "SMELLS" ); static const mon_flag_str_id mon_flag_STUMBLES( "STUMBLES" ); static const mon_flag_str_id mon_flag_SUNDEATH( "SUNDEATH" ); @@ -163,6 +167,64 @@ static bool z_is_valid( int z ) return z >= -OVERMAP_DEPTH && z <= OVERMAP_HEIGHT; } +bool monster::monster_move_in_vehicle( const tripoint &p ) const +{ + map &m = get_map(); + monster critter = *this; + const optional_vpart_position vp = m.veh_at( p ); + if( vp.has_value() ) { + vehicle &veh = vp->vehicle(); + units::volume capacity = 0_ml; + units::volume free_cargo = 0_ml; + auto cargo_parts = veh.get_parts_at( p, "CARGO", part_status_flag::any ); + for( vehicle_part *&part : cargo_parts ) { + vehicle_stack contents = veh.get_items( *part ); + const vpart_info &vpinfo = part->info(); + if( !vp.part_with_feature( "CARGO_PASSABLE", true ) ) { + capacity += vpinfo.size; + free_cargo += contents.free_volume(); + } + } + if( capacity > 0_ml ) { + // First, we'll try to squeeze in. Open-topped vehicle parts have more room to step over cargo. + if( !veh.enclosed_at( p ) ) { + free_cargo *= 1.2; + } + if( !veh.enclosed_at( p ) && flies() ) { + return true; // No amount of cargo will block a flying monster if there's no roof. + } + const creature_size size = get_size(); + if( ( size == creature_size::tiny && free_cargo < 15625_ml ) || + ( size == creature_size::small && free_cargo < 31250_ml ) || + ( size == creature_size::medium && free_cargo < 62500_ml ) || + ( size == creature_size::large && free_cargo < 125000_ml ) || + ( size == creature_size::huge && free_cargo < 250000_ml ) ) { + if( ( size == creature_size::tiny && free_cargo < 11719_ml ) || + ( size == creature_size::small && free_cargo < 23438_ml ) || + ( size == creature_size::medium && free_cargo < 46875_ml ) || + ( size == creature_size::large && free_cargo < 93750_ml ) || + ( size == creature_size::huge && free_cargo < 187500_ml ) || + ( get_volume() > 850000_ml && !vp.part_with_feature( "HUGE_OK", true ) ) ) { + return false; // Return false if there's just no room whatsoever. Anything over 850 liters will simply never fit in a vehicle part that isn't specifically made for it. + // I'm sorry but you can't let a kaiju ride shotgun. + } + if( ( type->bodytype == "snake" || type->bodytype == "blob" || type->bodytype == "fish" || + has_flag( mon_flag_PLASTIC ) || has_flag( mon_flag_SMALL_HIDER ) ) ) { + return true; // Return true if we're wiggly enough to be fine with cramped space. + } + critter.add_effect( effect_cramped_space, 2_turns, true ); + return true; // Otherwise we add the effect and return true. + } + if( size == creature_size::huge && !vp.part_with_feature( "AISLE", true ) && + !vp.part_with_feature( "HUGE_OK", true ) ) { + critter.add_effect( effect_cramped_space, 2_turns, true ); + return true; // Sufficiently gigantic creatures have trouble in stock seats, roof or no. + } + } + } + return true; +} + bool monster::will_move_to( const tripoint &p ) const { map &here = get_map(); @@ -177,11 +239,15 @@ bool monster::will_move_to( const tripoint &p ) const } if( !here.has_vehicle_floor( p ) ) { - if( ( !can_submerge() && !flies() ) && here.has_flag( ter_furn_flag::TFLAG_DEEP_WATER, p ) ) { + if( !can_submerge() && !flies() && here.has_flag( ter_furn_flag::TFLAG_DEEP_WATER, p ) ) { return false; } } + if( here.veh_at( p ).part_with_feature( VPFLAG_CARGO, true ) && !monster_move_in_vehicle( p ) ) { + return false; + } + if( digs() && !here.has_flag( ter_furn_flag::TFLAG_DIGGABLE, p ) && !here.has_flag( ter_furn_flag::TFLAG_BURROWABLE, p ) ) { return false; diff --git a/src/monster.cpp b/src/monster.cpp index d25d2146a74ba..bb2ea56357524 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -64,6 +64,9 @@ #include "trap.h" #include "type_id.h" #include "units.h" +#include "veh_type.h" +#include "vehicle.h" +#include "vpart_position.h" #include "viewer.h" #include "weakpoint.h" #include "weather.h" @@ -82,6 +85,7 @@ static const efftype_id effect_beartrap( "beartrap" ); static const efftype_id effect_bleed( "bleed" ); static const efftype_id effect_blind( "blind" ); static const efftype_id effect_bouldering( "bouldering" ); +static const efftype_id effect_cramped_space( "cramped_space" ); static const efftype_id effect_critter_underfed( "critter_underfed" ); static const efftype_id effect_critter_well_fed( "critter_well_fed" ); static const efftype_id effect_crushed( "crushed" ); @@ -3336,6 +3340,57 @@ void monster::process_effects() } } + // Apply or remove the cramped_space effect, which needs specific information about the monster's surroundings. + map &here = get_map(); + const tripoint z_pos = pos(); + const optional_vpart_position vp = here.veh_at( z_pos ); + if( has_effect( effect_cramped_space ) && !vp.has_value() ) { + remove_effect( effect_cramped_space ); + } + if( vp.has_value() ) { + vehicle &veh = vp->vehicle(); + units::volume capacity = 0_ml; + units::volume free_cargo = 0_ml; + auto cargo_parts = veh.get_parts_at( z_pos, "CARGO", part_status_flag::any ); + for( vehicle_part *&part : cargo_parts ) { + vehicle_stack contents = veh.get_items( *part ); + const vpart_info &vpinfo = part->info(); + if( !vp.part_with_feature( "CARGO_PASSABLE", true ) ) { + capacity += vpinfo.size; + free_cargo += contents.free_volume(); + } + } + const creature_size size = get_size(); + if( capacity > 0_ml ) { + // Open-topped vehicle parts have more room, and are always free space for fliers. + if( !veh.enclosed_at( z_pos ) ) { + free_cargo *= 1.2; + if( flies() ) { + remove_effect( effect_cramped_space ); + return; + } + } + if( ( size == creature_size::tiny && free_cargo < 15625_ml ) || + ( size == creature_size::small && free_cargo < 31250_ml ) || + ( size == creature_size::medium && free_cargo < 62500_ml ) || + ( size == creature_size::large && free_cargo < 125000_ml ) || + ( size == creature_size::huge && free_cargo < 250000_ml ) ) { + if( !has_effect( effect_cramped_space ) ) { + add_effect( effect_cramped_space, 2_turns, true ); + } + return; + } + } + if( get_size() == creature_size::huge && !vp.part_with_feature( "AISLE", true ) && + !vp.part_with_feature( "HUGE_OK", true ) ) { + if( !has_effect( effect_cramped_space ) ) { + add_effect( effect_cramped_space, 2_turns, true ); + } + return; + } + remove_effect( effect_cramped_space ); + } + Creature::process_effects(); } diff --git a/src/monster.h b/src/monster.h index 48f571552d2d2..1ec70f3285b36 100644 --- a/src/monster.h +++ b/src/monster.h @@ -212,6 +212,8 @@ class monster : public Creature bool will_move_to( const tripoint &p ) const; bool know_danger_at( const tripoint &p ) const; + bool monster_move_in_vehicle( const tripoint &p ) const; + bool will_reach( const point &p ); // Do we have plans to get to (x, y)? int turns_to_reach( const point &p ); // How long will it take? diff --git a/src/npc.cpp b/src/npc.cpp index 0761ab47f678c..79d20a9efe487 100644 --- a/src/npc.cpp +++ b/src/npc.cpp @@ -3517,8 +3517,9 @@ std::set npc::get_path_avoid() const } } - for( const tripoint &p : here.points_in_radius( pos(), 5 ) ) { - if( sees_dangerous_field( p ) ) { + for( const tripoint &p : here.points_in_radius( pos(), 6 ) ) { + if( sees_dangerous_field( p ) || ( here.veh_at( p ).part_with_feature( VPFLAG_CARGO, true ) && + !move_in_vehicle( const_cast( this ), p ) ) ) { ret.insert( p ); } } diff --git a/src/npcmove.cpp b/src/npcmove.cpp index 40dda025bbfb8..c3cc4aba030f6 100644 --- a/src/npcmove.cpp +++ b/src/npcmove.cpp @@ -420,6 +420,9 @@ bool npc::could_move_onto( const tripoint &p ) const if( !here.passable( p ) ) { return false; } + if( !move_in_vehicle( const_cast( this ), p ) ) { + return false; + } if( !sees_dangerous_field( p ) ) { return true; @@ -1785,7 +1788,6 @@ void npc::execute_action( npc_action action ) case npc_follow_embarked: { const optional_vpart_position vp = here.veh_at( player_character.pos() ); - if( !vp ) { debugmsg( "Following an embarked player with no vehicle at their location?" ); // TODO: change to wait? - for now pause @@ -1810,12 +1812,15 @@ void npc::execute_action( npc_action action ) if( passenger != this && passenger != nullptr ) { continue; } - - // a seat is available if either unassigned or assigned to us + // A seat is available if we can move there and it's either unassigned or assigned to us auto available_seat = [&]( const vehicle_part & pt ) { + tripoint target = veh->global_part_pos3( pt ); if( !pt.is_seat() ) { return false; } + if( !could_move_onto( target ) ) { + return false; + } const npc *who = pt.crew(); return !who || who->getID() == getID(); }; @@ -2810,6 +2815,7 @@ bool npc::can_open_door( const tripoint &p, const bool inside ) const bool npc::can_move_to( const tripoint &p, bool no_bashing ) const { map &here = get_map(); + // Allow moving into any bashable spots, but penalize them during pathing // Doors are not passable for hallucinations return( rl_dist( pos(), p ) <= 1 && here.has_floor_or_water( p ) && !g->is_dangerous_tile( p ) && @@ -2837,6 +2843,20 @@ void npc::move_to( const tripoint &pt, bool no_bashing, std::set *nomo } } + if( here.veh_at( p ).part_with_feature( VPFLAG_CARGO, true ) && !move_in_vehicle( this, p ) ) { + auto other_points = here.get_dir_circle( pos(), p ); + for( const tripoint &ot : other_points ) { + if( could_move_onto( ot ) && ( nomove == nullptr || nomove->find( ot ) == nomove->end() ) ) { + p = ot; + break; + } else { + path.clear(); + move_pause(); + return; + } + } + } + recoil = MAX_RECOIL; if( has_effect( effect_stunned ) || has_effect( effect_psi_stunned ) ) { @@ -3064,12 +3084,12 @@ void npc::move_to( const tripoint &pt, bool no_bashing, std::set *nomo if( here.veh_at( p ).part_with_feature( VPFLAG_BOARDABLE, true ) ) { here.board_vehicle( p, this ); } - here.creature_on_trap( *this ); here.creature_in_field( *this ); } } + void npc::move_to_next() { while( !path.empty() && pos() == path[0] ) { diff --git a/src/veh_type.cpp b/src/veh_type.cpp index 1dc955f6988b9..22cfb24c0cd68 100644 --- a/src/veh_type.cpp +++ b/src/veh_type.cpp @@ -117,6 +117,7 @@ static const std::unordered_map vpart_bitflag_map = { "WINDOW", VPFLAG_WINDOW }, { "CURTAIN", VPFLAG_CURTAIN }, { "CARGO", VPFLAG_CARGO }, + { "CARGO_PASSABLE", VPFLAG_CARGO_PASSABLE }, { "INTERNAL", VPFLAG_INTERNAL }, { "SOLAR_PANEL", VPFLAG_SOLAR_PANEL }, { "WIND_TURBINE", VPFLAG_WIND_TURBINE }, @@ -137,7 +138,8 @@ static const std::unordered_map vpart_bitflag_map = { "ROOF", VPFLAG_ROOF }, { "CABLE_PORTS", VPFLAG_CABLE_PORTS }, { "BATTERY", VPFLAG_BATTERY }, - { "POWER_TRANSFER", VPFLAG_POWER_TRANSFER } + { "POWER_TRANSFER", VPFLAG_POWER_TRANSFER }, + { "HUGE_OK", VPFLAG_HUGE_OK } }; static std::map vpart_migrations; diff --git a/src/veh_type.h b/src/veh_type.h index 8ca09d8995715..82552a0d8a0e4 100644 --- a/src/veh_type.h +++ b/src/veh_type.h @@ -92,6 +92,7 @@ enum vpart_bitflags : int { VPFLAG_WINDOW, VPFLAG_CURTAIN, VPFLAG_CARGO, + VPFLAG_CARGO_PASSABLE, VPFLAG_INTERNAL, VPFLAG_SOLAR_PANEL, VPFLAG_WATER_WHEEL, @@ -110,6 +111,7 @@ enum vpart_bitflags : int { VPFLAG_CABLE_PORTS, VPFLAG_BATTERY, VPFLAG_POWER_TRANSFER, + VPFLAG_HUGE_OK, NUM_VPFLAGS };