diff --git a/data/json/furniture_and_terrain/LIXA_furniture_and_terrain.json b/data/json/furniture_and_terrain/LIXA_furniture_and_terrain.json index 91ab17915d723..9277af32518b7 100644 --- a/data/json/furniture_and_terrain/LIXA_furniture_and_terrain.json +++ b/data/json/furniture_and_terrain/LIXA_furniture_and_terrain.json @@ -10,7 +10,7 @@ "light_emitted": 120, "looks_like": "t_carpet_green", "connect_groups": "INDOORFLOOR", - "flags": [ "TRANSPARENT", "FLAMMABLE_HARD", "SUPPORTS_ROOF", "COLLAPSES", "INDOORS", "FLAT", "RUG", "EASY_DECONSTRUCT" ], + "flags": [ "TRANSPARENT", "FLAMMABLE_HARD", "SUPPORTS_ROOF", "COLLAPSES", "INDOORS", "FLAT", "RUG" ], "bash": { "str_min": 4, "str_max": 12, diff --git a/data/json/furniture_and_terrain/terrain-manufactured.json b/data/json/furniture_and_terrain/terrain-manufactured.json index 9d7ae0a3f398c..9b6c660c265cf 100644 --- a/data/json/furniture_and_terrain/terrain-manufactured.json +++ b/data/json/furniture_and_terrain/terrain-manufactured.json @@ -1376,6 +1376,7 @@ "sound_fail": "whump!", "items": [ { "item": "rock_large", "count": [ 0, 20 ] }, { "item": "sharp_rock", "count": [ 0, 16 ] } ] }, + "deconstruct": { "ter_set": "t_dirt", "items": [ { "item": "rock_large", "count": 22 } ] }, "flags": [ "TRANSPARENT", "THIN_OBSTACLE", "SHORT", "ROUGH", "UNSTABLE", "PERMEABLE", "EASY_DECONSTRUCT" ] }, { @@ -1494,6 +1495,7 @@ "HIDE_PLACE", "EASY_DECONSTRUCT" ], + "deconstruct": { "ter_set": "t_pit_shallow", "items": [ { "item": "stick", "count": 12 }, { "item": "pine_bough", "count": 24 } ] }, "bash": { "str_min": 4, "str_max": 60, @@ -1526,6 +1528,10 @@ "HIDE_PLACE", "EASY_DECONSTRUCT" ], + "deconstruct": { + "ter_set": "t_pit_shallow", + "items": [ { "item": "stick", "count": 12 }, { "item": "pine_bough", "count": 24 }, { "item": "withered", "count": 80 } ] + }, "bash": { "str_min": 4, "str_max": 60, diff --git a/data/json/mapgen/microlab/microlab_generic_edge.json b/data/json/mapgen/microlab/microlab_generic_edge.json index d60909219f5e2..4f79fd31cd13d 100644 --- a/data/json/mapgen/microlab/microlab_generic_edge.json +++ b/data/json/mapgen/microlab/microlab_generic_edge.json @@ -8,6 +8,15 @@ "set": [ { "line": "terrain", "id": "t_concrete_wall", "x": 0, "y": 0, "x2": 23, "y2": 0 } ] } }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "concrete_wall_hole_ew", + "object": { + "mapgensize": [ 24, 24 ], + "set": [ { "line": "terrain", "id": "t_strconc_floor", "x": 11, "y": 0, "x2": 12, "y2": 0 } ] + } + }, { "type": "mapgen", "method": "json", @@ -23,6 +32,15 @@ "set": [ { "line": "terrain", "id": "t_concrete_wall", "x": 0, "y": 0, "x2": 0, "y2": 23 } ] } }, + { + "type": "mapgen", + "method": "json", + "nested_mapgen_id": "concrete_wall_hole_ns", + "object": { + "mapgensize": [ 24, 24 ], + "set": [ { "line": "terrain", "id": "t_strconc_floor", "x": 0, "y": 11, "x2": 0, "y2": 12 } ] + } + }, { "type": "mapgen", "method": "json", @@ -46,6 +64,10 @@ { "else_chunks": [ "concrete_wall_ns" ], "x": 23, "y": 0, "neighbors": { "east": "microlab" } }, { "else_chunks": [ "concrete_wall_ew" ], "x": 0, "y": 23, "neighbors": { "south": "microlab" } }, { "else_chunks": [ "concrete_wall_ns" ], "x": 0, "y": 0, "neighbors": { "west": "microlab" } }, + { "chunks": [ "concrete_wall_hole_ew" ], "x": 0, "y": 0, "neighbors": { "north": "mlb_generic_subhallway" } }, + { "chunks": [ "concrete_wall_hole_ns" ], "x": 23, "y": 0, "neighbors": { "east": "mlb_generic_subhallway" } }, + { "chunks": [ "concrete_wall_hole_ew" ], "x": 0, "y": 23, "neighbors": { "south": "mlb_generic_subhallway" } }, + { "chunks": [ "concrete_wall_hole_ns" ], "x": 0, "y": 0, "neighbors": { "west": "mlb_generic_subhallway" } }, { "else_chunks": [ "concrete_corner" ], "x": 0, "y": 0, "neighbors": { "north_west": "microlab" } }, { "else_chunks": [ "concrete_corner" ], "x": 23, "y": 0, "neighbors": { "north_east": "microlab" } }, { "else_chunks": [ "concrete_corner" ], "x": 23, "y": 23, "neighbors": { "south_east": "microlab" } }, diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index a9e0b6ae047a9..6d5fbd243974f 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -10294,6 +10294,49 @@ "flags": [ "SOCIAL2" ], "category": [ "CATTLE", "LUPINE", "MOUSE", "RAT", "CHIROPTERAN", "ELFA" ] }, + { + "type": "mutation", + "id": "AMOG", + "name": { "str": "Alpha Member of the Group" }, + "points": 1, + "vitamin_cost": 160, + "types": [ "SOCIAL" ], + "cancels": [ "ASOCIAL1", "ASOCIAL2", "SOCIAL1", "SOCIAL2" ], + "description": "You are the natural leader of any group. It's just part of who you are, groups naturally form around you and you need these groups to energize you. Sometimes however, you give in to your worst impulses and this trait will flip.", + "starting_trait": true, + "mixed_effect": true, + "purifiable": false, + "flags": [ "SOCIAL2" ], + "category": [ "ALPHA" ], + "social_modifiers": { "intimidate": 5, "persuade": 15, "lie": 10 }, + "triggers": [ + [ + { + "condition": { "or": [ { "math": [ "u_val('morale')", "<", "-75" ] }, { "math": [ "u_val('morale')", "<", "75" ] } ] } + } + ], + [ + { "condition": { "or": [ { "math": [ "u_val('stim')", "<", "-50" ] }, { "math": [ "u_val('stim')", "<", "50" ] } ] } } + ] + ], + "transform": { "target": "DARK_TRIAD", "msg_transform": "You give in to your worst instincts.", "active": false, "moves": 10 } + }, + { + "type": "mutation", + "id": "DARK_TRIAD", + "name": { "str": "Dark Triad" }, + "types": [ "SOCIAL" ], + "cancels": [ "ASOCIAL1", "ASOCIAL2", "SOCIAL1", "SOCIAL2" ], + "description": "You are a god among mortals and they are all so very dissapointing. Why should you even bother to hide your dissapointment in their failures?", + "valid": false, + "mixed_effect": true, + "points": -1, + "flags": [ "ASOCIAL2" ], + "category": [ "ALPHA" ], + "triggers": [ [ { "condition": { "math": [ "time_since(u_transform_time)", ">", "time('36 h')" ] } } ] ], + "transform": { "target": "AMOG", "msg_transform": "Maybe you can make better decisions this time.", "active": false, "moves": 10 }, + "social_modifiers": { "intimidate": -10, "persuade": -25, "lie": -15 } + }, { "type": "mutation", "id": "ASOCIAL1", diff --git a/data/json/npcs/tacoma_ranch/NPC_ranch_foreman.json b/data/json/npcs/tacoma_ranch/NPC_ranch_foreman.json index eb969be449a37..e5b69d85f6b54 100644 --- a/data/json/npcs/tacoma_ranch/NPC_ranch_foreman.json +++ b/data/json/npcs/tacoma_ranch/NPC_ranch_foreman.json @@ -204,11 +204,18 @@ "id": "MISSION_RANCH_FOREMAN_2", "type": "mission_definition", "name": { "str": "Find 25 Blankets" }, - "goal": "MGOAL_FIND_ITEM", + "goal": "MGOAL_CONDITION", + "goal_condition": { + "u_has_items_sum": [ + { "item": "blanket", "amount": 25 }, + { "item": "fur_blanket", "amount": 25 }, + { "item": "down_blanket", "amount": 25 }, + { "item": "blanket_weighted", "amount": 25 }, + { "item": "electric_blanket", "amount": 25 } + ] + }, "difficulty": 5, "value": 50000, - "item": "blanket", - "count": 25, "origins": [ "ORIGIN_SECONDARY" ], "followup": "MISSION_RANCH_FOREMAN_3", "dialogue": { @@ -261,6 +268,17 @@ ], "place_npcs": [ { "class": "ranch_construction_2", "x": 19, "y": 8 } ] } + ], + "effect": [ + { + "u_consume_item_sum": [ + { "item": "blanket", "amount": 25 }, + { "item": "fur_blanket", "amount": 25 }, + { "item": "down_blanket", "amount": 25 }, + { "item": "blanket_weighted", "amount": 25 }, + { "item": "electric_blanket", "amount": 25 } + ] + } ] } }, diff --git a/data/json/recipes/appliances/oven.json b/data/json/recipes/appliances/oven.json index 63dd1bd3fc00b..9ea2415ac378b 100644 --- a/data/json/recipes/appliances/oven.json +++ b/data/json/recipes/appliances/oven.json @@ -22,136 +22,5 @@ [ [ "element", 4 ], [ "crude_heating_element", 6 ] ], [ [ "cable", 1 ] ] ] - }, - { - "result": "oven", - "type": "uncraft", - "activity_level": "MODERATE_EXERCISE", - "time": "45 m", - "skill_used": "fabrication", - "qualities": [ { "id": "SCREW", "level": 1 }, { "id": "WRENCH", "level": 1 } ], - "proficiencies": [ { "proficiency": "prof_appliance_repair", "skill_penalty": 0 } ], - "components": [ - [ [ "oven_stovetop", 1 ] ], - [ [ "oven_cabinet", 1 ] ], - [ [ "oven_controls", 1 ] ], - [ [ "oven_door", 1 ] ], - [ [ "oven_broiler_pan", 1 ] ], - [ [ "heavy_wire_rack", 2 ] ], - [ [ "element", 4 ] ], - [ [ "cable", 4 ] ] - ] - }, - { - "result": "oven_stovetop", - "type": "uncraft", - "activity_level": "MODERATE_EXERCISE", - "time": "30 m", - "skill_used": "fabrication", - "proficiencies": [ { "proficiency": "prof_appliance_repair", "skill_penalty": 0 } ], - "qualities": [ - { "id": "SCREW", "level": 1 }, - { "id": "WRENCH", "level": 1 }, - { "id": "HAMMER", "level": 1 }, - { "id": "SAW_M", "level": 1 } - ], - "components": [ [ [ "sheet_metal_small", 2 ] ], [ [ "spring", 1 ] ], [ [ "hinge", 1 ] ], [ [ "wire", 1 ] ], [ [ "e_scrap", 4 ] ] ] - }, - { - "result": "oven_cabinet", - "type": "uncraft", - "activity_level": "MODERATE_EXERCISE", - "time": "1 h", - "skill_used": "fabrication", - "proficiencies": [ { "proficiency": "prof_appliance_repair", "skill_penalty": 0 } ], - "qualities": [ - { "id": "SCREW", "level": 1 }, - { "id": "WRENCH", "level": 1 }, - { "id": "HAMMER", "level": 1 }, - { "id": "SAW_M", "level": 1 } - ], - "//": "Pilot lights shouldn't be part of an electric oven but we don't have gas ones yet. Can be removed when that changes.", - "components": [ - [ [ "sheet_metal", 2 ] ], - [ [ "sheet_metal_small", 2 ] ], - [ [ "rock_wool_bat", 3 ] ], - [ [ "element", 2 ] ], - [ [ "scrap", 4 ] ], - [ [ "e_scrap", 2 ] ], - [ [ "cable", 2 ] ], - [ [ "pilot_light", 1 ] ], - [ [ "motor_micro", 1 ] ], - [ [ "fanblade_metal", 1 ] ], - [ [ "thermometer", 1 ] ], - [ [ "pipe_fittings", 4 ] ] - ] - }, - { - "result": "oven_door", - "type": "uncraft", - "activity_level": "MODERATE_EXERCISE", - "time": "1 h", - "skill_used": "fabrication", - "proficiencies": [ { "proficiency": "prof_appliance_repair", "skill_penalty": 0 } ], - "qualities": [ - { "id": "SCREW", "level": 1 }, - { "id": "WRENCH", "level": 1 }, - { "id": "HAMMER_FINE", "level": 1 }, - { "id": "SAW_M", "level": 1 } - ], - "components": [ - [ [ "reinforced_glass_pane", 1 ] ], - [ [ "sheet_metal_small", 2 ] ], - [ [ "rock_wool_bat", 1 ] ], - [ [ "element", 2 ] ], - [ [ "scrap", 4 ] ], - [ [ "e_scrap", 2 ] ], - [ [ "cable", 2 ] ], - [ [ "hinge", 2 ] ] - ] - }, - { - "result": "oven_broiler_pan", - "type": "uncraft", - "activity_level": "MODERATE_EXERCISE", - "time": "30 m", - "skill_used": "fabrication", - "proficiencies": [ { "proficiency": "prof_appliance_repair", "skill_penalty": 0 } ], - "qualities": [ - { "id": "SCREW", "level": 1 }, - { "id": "WRENCH", "level": 1 }, - { "id": "HAMMER", "level": 1 }, - { "id": "SAW_M", "level": 1 } - ], - "components": [ [ [ "sheet_metal", 1 ] ], [ [ "sheet_metal_small", 2 ] ], [ [ "scrap", 2 ] ], [ [ "bearing", 12 ] ] ] - }, - { - "result": "oven_controls", - "type": "uncraft", - "activity_level": "MODERATE_EXERCISE", - "time": "1 h", - "skill_used": "fabrication", - "proficiencies": [ - { "proficiency": "prof_elec_soldering", "skill_penalty": 0 }, - { "proficiency": "prof_elec_circuits", "skill_penalty": 0 } - ], - "qualities": [ { "id": "SCREW", "level": 1 } ], - "tools": [ - [ - [ "soldering_iron", -1 ], - [ "soldering_iron_portable", -1 ], - [ "integrated_electrokit", -1 ], - [ "integrated_welder", -1 ] - ] - ], - "components": [ - [ [ "plastic_chunk", 4 ] ], - [ [ "sheet_metal_small", 1 ] ], - [ [ "scrap", 2 ] ], - [ [ "e_scrap", 6 ] ], - [ [ "cable", 2 ] ], - [ [ "solder_wire", 8 ] ], - [ [ "small_lcd_screen", 1 ] ] - ] } ] diff --git a/data/json/recipes/other/parts.json b/data/json/recipes/other/parts.json index adbe157a5e212..9194c1cf90864 100644 --- a/data/json/recipes/other/parts.json +++ b/data/json/recipes/other/parts.json @@ -120,42 +120,6 @@ "qualities": [ { "id": "SCREW", "level": 1 } ], "components": [ [ [ "circuit", 1 ] ], [ [ "e_scrap", 3 ] ], [ [ "cable", 1 ] ] ] }, - { - "//": "TODO MAKE A RECIPE FOR THIS AND A BOOK", - "type": "uncraft", - "activity_level": "LIGHT_EXERCISE", - "result": "exodii_wire_kit", - "skill_used": "electronics", - "difficulty": 1, - "time": "4 m", - "using": [ [ "soldering_standard", 4 ] ], - "qualities": [ { "id": "SCREW", "level": 1 } ], - "components": [ - [ [ "exodii_micro_computer", 1 ] ], - [ [ "exodii_micro_module", 1 ] ], - [ [ "e_scrap", 10 ] ], - [ [ "clockworks", 1 ] ], - [ [ "solder_wire", 2 ] ] - ] - }, - { - "//": "TODO MAKE A RECIPE FOR THIS AND A BOOK", - "type": "uncraft", - "activity_level": "LIGHT_EXERCISE", - "result": "exodii_ac_kit", - "skill_used": "electronics", - "difficulty": 1, - "time": "4 m", - "using": [ [ "soldering_standard", 4 ] ], - "qualities": [ { "id": "SCREW", "level": 1 } ], - "components": [ - [ [ "exodii_micro_module", 1 ] ], - [ [ "e_scrap", 10 ] ], - [ [ "clockworks", 2 ] ], - [ [ "solder_wire", 2 ] ], - [ [ "pipe", 1 ] ] - ] - }, { "type": "recipe", "activity_level": "LIGHT_EXERCISE", diff --git a/data/json/uncraft/appliances/oven.json b/data/json/uncraft/appliances/oven.json new file mode 100644 index 0000000000000..e3c49dc8d52df --- /dev/null +++ b/data/json/uncraft/appliances/oven.json @@ -0,0 +1,133 @@ +[ + { + "result": "oven", + "type": "uncraft", + "activity_level": "MODERATE_EXERCISE", + "time": "45 m", + "skill_used": "fabrication", + "qualities": [ { "id": "SCREW", "level": 1 }, { "id": "WRENCH", "level": 1 } ], + "proficiencies": [ { "proficiency": "prof_appliance_repair", "skill_penalty": 0 } ], + "components": [ + [ [ "oven_stovetop", 1 ] ], + [ [ "oven_cabinet", 1 ] ], + [ [ "oven_controls", 1 ] ], + [ [ "oven_door", 1 ] ], + [ [ "oven_broiler_pan", 1 ] ], + [ [ "heavy_wire_rack", 2 ] ], + [ [ "element", 4 ] ], + [ [ "cable", 4 ] ] + ] + }, + { + "result": "oven_stovetop", + "type": "uncraft", + "activity_level": "MODERATE_EXERCISE", + "time": "30 m", + "skill_used": "fabrication", + "proficiencies": [ { "proficiency": "prof_appliance_repair", "skill_penalty": 0 } ], + "qualities": [ + { "id": "SCREW", "level": 1 }, + { "id": "WRENCH", "level": 1 }, + { "id": "HAMMER", "level": 1 }, + { "id": "SAW_M", "level": 1 } + ], + "components": [ [ [ "sheet_metal_small", 2 ] ], [ [ "spring", 1 ] ], [ [ "hinge", 1 ] ], [ [ "wire", 1 ] ], [ [ "e_scrap", 4 ] ] ] + }, + { + "result": "oven_cabinet", + "type": "uncraft", + "activity_level": "MODERATE_EXERCISE", + "time": "1 h", + "skill_used": "fabrication", + "proficiencies": [ { "proficiency": "prof_appliance_repair", "skill_penalty": 0 } ], + "qualities": [ + { "id": "SCREW", "level": 1 }, + { "id": "WRENCH", "level": 1 }, + { "id": "HAMMER", "level": 1 }, + { "id": "SAW_M", "level": 1 } + ], + "//": "Pilot lights shouldn't be part of an electric oven but we don't have gas ones yet. Can be removed when that changes.", + "components": [ + [ [ "sheet_metal", 2 ] ], + [ [ "sheet_metal_small", 2 ] ], + [ [ "rock_wool_bat", 3 ] ], + [ [ "element", 2 ] ], + [ [ "scrap", 4 ] ], + [ [ "e_scrap", 2 ] ], + [ [ "cable", 2 ] ], + [ [ "pilot_light", 1 ] ], + [ [ "motor_micro", 1 ] ], + [ [ "fanblade_metal", 1 ] ], + [ [ "thermometer", 1 ] ], + [ [ "pipe_fittings", 4 ] ] + ] + }, + { + "result": "oven_door", + "type": "uncraft", + "activity_level": "MODERATE_EXERCISE", + "time": "1 h", + "skill_used": "fabrication", + "proficiencies": [ { "proficiency": "prof_appliance_repair", "skill_penalty": 0 } ], + "qualities": [ + { "id": "SCREW", "level": 1 }, + { "id": "WRENCH", "level": 1 }, + { "id": "HAMMER_FINE", "level": 1 }, + { "id": "SAW_M", "level": 1 } + ], + "components": [ + [ [ "reinforced_glass_pane", 1 ] ], + [ [ "sheet_metal_small", 2 ] ], + [ [ "rock_wool_bat", 1 ] ], + [ [ "element", 2 ] ], + [ [ "scrap", 4 ] ], + [ [ "e_scrap", 2 ] ], + [ [ "cable", 2 ] ], + [ [ "hinge", 2 ] ] + ] + }, + { + "result": "oven_broiler_pan", + "type": "uncraft", + "activity_level": "MODERATE_EXERCISE", + "time": "30 m", + "skill_used": "fabrication", + "proficiencies": [ { "proficiency": "prof_appliance_repair", "skill_penalty": 0 } ], + "qualities": [ + { "id": "SCREW", "level": 1 }, + { "id": "WRENCH", "level": 1 }, + { "id": "HAMMER", "level": 1 }, + { "id": "SAW_M", "level": 1 } + ], + "components": [ [ [ "sheet_metal", 1 ] ], [ [ "sheet_metal_small", 2 ] ], [ [ "scrap", 2 ] ], [ [ "bearing", 12 ] ] ] + }, + { + "result": "oven_controls", + "type": "uncraft", + "activity_level": "MODERATE_EXERCISE", + "time": "1 h", + "skill_used": "fabrication", + "proficiencies": [ + { "proficiency": "prof_elec_soldering", "skill_penalty": 0 }, + { "proficiency": "prof_elec_circuits", "skill_penalty": 0 } + ], + "qualities": [ { "id": "SCREW", "level": 1 } ], + "tools": [ + [ + [ "soldering_iron", -1 ], + [ "soldering_iron_portable", -1 ], + [ "integrated_electrokit", -1 ], + [ "integrated_welder", -1 ] + ] + ], + "components": [ + [ [ "plastic_chunk", 4 ] ], + [ [ "sheet_metal_small", 1 ] ], + [ [ "scrap", 2 ] ], + [ [ "e_scrap", 6 ] ], + [ [ "cable", 2 ] ], + [ [ "solder_wire", 8 ] ], + [ [ "small_lcd_screen", 1 ] ] + ] + } +] diff --git a/data/json/uncraft/resources/alien.json b/data/json/uncraft/resources/alien.json new file mode 100644 index 0000000000000..e5697127c3496 --- /dev/null +++ b/data/json/uncraft/resources/alien.json @@ -0,0 +1,38 @@ +[ + { + "//": "TODO MAKE A RECIPE FOR THIS AND A BOOK", + "type": "uncraft", + "activity_level": "LIGHT_EXERCISE", + "result": "exodii_wire_kit", + "skill_used": "electronics", + "difficulty": 1, + "time": "4 m", + "using": [ [ "soldering_standard", 4 ] ], + "qualities": [ { "id": "SCREW", "level": 1 } ], + "components": [ + [ [ "exodii_micro_computer", 1 ] ], + [ [ "exodii_micro_module", 1 ] ], + [ [ "e_scrap", 10 ] ], + [ [ "clockworks", 1 ] ], + [ [ "solder_wire", 2 ] ] + ] + }, + { + "//": "TODO MAKE A RECIPE FOR THIS AND A BOOK", + "type": "uncraft", + "activity_level": "LIGHT_EXERCISE", + "result": "exodii_ac_kit", + "skill_used": "electronics", + "difficulty": 1, + "time": "4 m", + "using": [ [ "soldering_standard", 4 ] ], + "qualities": [ { "id": "SCREW", "level": 1 } ], + "components": [ + [ [ "exodii_micro_module", 1 ] ], + [ [ "e_scrap", 10 ] ], + [ [ "clockworks", 2 ] ], + [ [ "solder_wire", 2 ] ], + [ [ "pipe", 1 ] ] + ] + } +] diff --git a/data/mods/Isolation-Protocol/EOC/elevator_eoc.json b/data/mods/Isolation-Protocol/EOC/elevator_eoc.json index 5f238e0ed0d77..af0d071d47ac1 100644 --- a/data/mods/Isolation-Protocol/EOC/elevator_eoc.json +++ b/data/mods/Isolation-Protocol/EOC/elevator_eoc.json @@ -22,7 +22,15 @@ "id": "EOC_ISO_DECIDE_LEVEL", "condition": { "math": [ "ISO_CURRENT_LEVEL % SAFEPOINT_INTERVAL", "==", "0" ] }, "effect": [ { "run_eocs": [ "EOC_ISO_MICROLAB_SAFE_TP" ] } ], - "false_effect": [ { "run_eocs": [ "EOC_ISO_MICROLAB_TP" ] } ] + "false_effect": [ + { + "switch": { "math": [ "rand(1)" ] }, + "cases": [ + { "case": 0, "effect": { "run_eocs": [ "EOC_ISO_MICROLAB_TP" ] } }, + { "case": 1, "effect": { "run_eocs": [ "EOC_ISO_MICROLAB_EMBED_TP" ] } } + ] + } + ] }, { "type": "effect_on_condition", @@ -37,6 +45,19 @@ { "u_teleport": { "global_val": "new_map" }, "force": true } ] }, + { + "type": "effect_on_condition", + "id": "EOC_ISO_MICROLAB_EMBED_TP", + "effect": [ + { + "u_location_variable": { "global_val": "new_map" }, + "target_params": { "om_terrain": "iso_elevator_microlab_embed", "z": -1, "random": true, "cant_see": true, "search_range": 180 }, + "terrain": "t_elevator", + "target_max_radius": 30 + }, + { "u_teleport": { "global_val": "new_map" }, "force": true } + ] + }, { "type": "effect_on_condition", "id": "EOC_ISO_MICROLAB_SAFE_TP", diff --git a/data/mods/Isolation-Protocol/Map/levels/microlab_chunks_level.json b/data/mods/Isolation-Protocol/Map/levels/microlab_chunks_level.json new file mode 100644 index 0000000000000..56d3e337a21f6 --- /dev/null +++ b/data/mods/Isolation-Protocol/Map/levels/microlab_chunks_level.json @@ -0,0 +1,172 @@ +[ + { + "type": "overmap_special", + "id": "iso_mslc", + "subtype": "mutable", + "locations": [ "iso_nether" ], + "city_distance": [ 0, -1 ], + "city_sizes": [ 0, -1 ], + "occurrences": [ 4, 4 ], + "flags": [ "ISO_MAP" ], + "check_for_locations": [ + [ [ 0, 0, 0 ], [ "iso_nether" ] ], + [ [ 0, 0, -1 ], [ "iso_nether" ] ], + [ [ 4, 0, -1 ], [ "iso_nether" ] ], + [ [ 3, 0, -1 ], [ "iso_nether" ] ], + [ [ 2, 0, -1 ], [ "iso_nether" ] ], + [ [ 1, 0, -1 ], [ "iso_nether" ] ], + [ [ -4, 0, -1 ], [ "iso_nether" ] ], + [ [ -3, 0, -1 ], [ "iso_nether" ] ], + [ [ -2, 0, -1 ], [ "iso_nether" ] ], + [ [ -1, 0, -1 ], [ "iso_nether" ] ], + [ [ 0, -4, -1 ], [ "iso_nether" ] ], + [ [ 0, -3, -1 ], [ "iso_nether" ] ], + [ [ 0, -2, -1 ], [ "iso_nether" ] ], + [ [ 0, -1, -1 ], [ "iso_nether" ] ], + [ [ 0, 4, -1 ], [ "iso_nether" ] ], + [ [ 0, 3, -1 ], [ "iso_nether" ] ], + [ [ 0, 2, -1 ], [ "iso_nether" ] ], + [ [ 0, 1, -1 ], [ "iso_nether" ] ] + ], + "joins": [ + "hallway_to_hallway", + "duct_to_surface", + "duct_to_entrance", + { "id": "hallway_to_microlab", "opposite": "microlab_to_hallway" }, + { "id": "microlab_to_hallway", "opposite": "hallway_to_microlab" }, + "microlab_to_microlab", + "microlab_entry_to_station", + "microlab_station_to_subway" + ], + "overmaps": { + "surface": { "overmap": "nether_glass_impassable_north", "below": "duct_to_surface", "locations": [ "iso_nether" ] }, + "closing_chunk": { "overmap": "iso_elevator_microlab_embed_next_north", "north": "microlab_to_hallway" }, + "microlab": { + "overmap": "microlab_generic", + "north": { "id": "microlab_to_microlab", "alternatives": [ "microlab_to_hallway" ] }, + "east": { "id": "microlab_to_microlab", "alternatives": [ "microlab_to_hallway" ] }, + "south": { "id": "microlab_to_microlab", "alternatives": [ "microlab_to_hallway" ] }, + "west": { "id": "microlab_to_microlab", "alternatives": [ "microlab_to_hallway" ] } + }, + "microlab_edge": { + "overmap": "microlab_generic_edge", + "north": { "id": "microlab_to_microlab", "type": "available", "alternatives": [ "microlab_to_hallway" ] }, + "east": { "id": "microlab_to_microlab", "type": "available", "alternatives": [ "microlab_to_hallway" ] }, + "south": { "id": "microlab_to_microlab", "type": "available", "alternatives": [ "microlab_to_hallway" ] }, + "west": { "id": "microlab_to_microlab", "type": "available", "alternatives": [ "microlab_to_hallway" ] } + }, + "microlab_subhallway": { "overmap": "mlb_generic_subhallway_north", "north": "microlab_to_microlab", "south": "hallway_to_microlab" }, + "iso_elevator": { + "overmap": "iso_elevator_microlab_embed_north", + "above": "duct_to_surface", + "north": { "id": "microlab_to_microlab", "type": "available", "alternatives": [ "microlab_to_hallway" ] }, + "east": { "id": "microlab_to_microlab", "type": "available", "alternatives": [ "microlab_to_hallway" ] }, + "south": { "id": "microlab_to_microlab", "type": "available", "alternatives": [ "microlab_to_hallway" ] }, + "west": { "id": "microlab_to_microlab", "type": "available", "alternatives": [ "microlab_to_hallway" ] } + }, + "iso_next_level_elevator": { + "overmap": "iso_elevator_microlab_embed_next_north", + "north": { "id": "microlab_to_microlab", "type": "available", "alternatives": [ "microlab_to_hallway" ] }, + "east": { "id": "microlab_to_microlab", "type": "available", "alternatives": [ "microlab_to_hallway" ] }, + "south": { "id": "microlab_to_microlab", "type": "available", "alternatives": [ "microlab_to_hallway" ] }, + "west": { "id": "microlab_to_microlab", "type": "available", "alternatives": [ "microlab_to_hallway" ] } + } + }, + "root": "surface", + "phases": [ + [ + { + "name": "start_chunk", + "chunk": [ + { "overmap": "microlab_subhallway", "pos": [ 0, 2, 0 ] }, + { "overmap": "iso_elevator", "pos": [ 0, 0, 0 ] }, + { "overmap": "microlab_edge", "pos": [ 0, 1, 0 ] }, + { "overmap": "microlab_edge", "pos": [ 1, 1, 0 ] }, + { "overmap": "microlab_edge", "pos": [ 1, 0, 0 ] } + ], + "max": 1 + } + ], + [ + { + "name": "microlab_chunk", + "chunk": [ + { "overmap": "microlab_subhallway", "pos": [ 0, 1, 0 ] }, + { "overmap": "microlab_edge", "pos": [ 0, 0, 0 ] }, + { "overmap": "microlab_edge", "pos": [ 0, -1, 0 ] }, + { "overmap": "microlab_edge", "pos": [ 1, -1, 0 ] }, + { "overmap": "microlab_edge", "pos": [ 1, 0, 0 ] } + ], + "max": 1 + } + ], + [ + { + "name": "microlab_chunk_1", + "chunk": [ + { "overmap": "microlab_subhallway", "pos": [ 0, 1, 0 ] }, + { "overmap": "microlab_edge", "pos": [ 0, 0, 0 ] }, + { "overmap": "microlab_edge", "pos": [ 0, -1, 0 ] }, + { "overmap": "microlab_edge", "pos": [ 1, -1, 0 ] }, + { "overmap": "microlab_edge", "pos": [ 1, 0, 0 ] }, + { "overmap": "microlab_subhallway", "pos": [ 2, 0, 0 ], "rot": "west" } + ], + "max": 1 + } + ], + [ + { + "name": "microlab_chunkk_2", + "chunk": [ + { "overmap": "microlab_subhallway", "pos": [ 0, 1, 0 ] }, + { "overmap": "microlab_edge", "pos": [ 0, 0, 0 ] }, + { "overmap": "microlab_edge", "pos": [ 0, -1, 0 ] }, + { "overmap": "microlab_edge", "pos": [ 1, -1, 0 ] }, + { "overmap": "microlab_edge", "pos": [ 1, 0, 0 ] } + ], + "max": { "poisson": 2 } + } + ], + [ + { + "name": "microlab_chunk_1", + "chunk": [ + { "overmap": "microlab_subhallway", "pos": [ 0, 1, 0 ] }, + { "overmap": "microlab_edge", "pos": [ 0, 0, 0 ] }, + { "overmap": "microlab_edge", "pos": [ 0, -1, 0 ] }, + { "overmap": "microlab_edge", "pos": [ 1, -1, 0 ] }, + { "overmap": "microlab_edge", "pos": [ 1, 0, 0 ] }, + { "overmap": "microlab_subhallway", "pos": [ 2, 0, 0 ], "rot": "west" } + ], + "max": { "poisson": 0.5 } + } + ], + [ + { + "name": "microlab_chunkk_2", + "chunk": [ + { "overmap": "microlab_subhallway", "pos": [ 0, 1, 0 ] }, + { "overmap": "microlab_edge", "pos": [ 0, 0, 0 ] }, + { "overmap": "microlab_edge", "pos": [ 0, -1, 0 ] }, + { "overmap": "microlab_edge", "pos": [ 1, -1, 0 ] }, + { "overmap": "microlab_edge", "pos": [ 1, 0, 0 ] } + ], + "max": { "poisson": 2 } + } + ], + [ + { + "name": "microlab_final_chunk", + "chunk": [ + { "overmap": "microlab_edge", "pos": [ 0, 0, 0 ] }, + { "overmap": "microlab_edge", "pos": [ 0, -1, 0 ] }, + { "overmap": "iso_next_level_elevator", "pos": [ 1, -1, 0 ] }, + { "overmap": "microlab_edge", "pos": [ 1, 0, 0 ] } + ], + "max": 10 + } + ], + [ { "overmap": "closing_chunk", "weight": 1 } ] + ] + } +] diff --git a/data/mods/Isolation-Protocol/Map/mapgen/elevator.json b/data/mods/Isolation-Protocol/Map/mapgen/elevator.json index b8f5cad6adf1d..7adf9193ff05f 100644 --- a/data/mods/Isolation-Protocol/Map/mapgen/elevator.json +++ b/data/mods/Isolation-Protocol/Map/mapgen/elevator.json @@ -98,5 +98,77 @@ "palettes": [ "microlab_generic" ], "terrain": { "`": "t_open_air", "E": "t_elevator_control_iso" } } + }, + { + "type": "mapgen", + "method": "json", + "om_terrain": "iso_elevator_microlab_embed", + "object": { + "fill_ter": "t_strconc_floor", + "rows": [ + "||||||||||| |||||||||||", + "|####|XXX= =XXX|####|", + "|####|XXX= =XXX|####|", + "|####|XXX= =XXX|####|", + "|####|XXX= =XXX|####|", + "|#||||==== ====||||#|", + "|#|XX= YYPPPPYY =XX|#|", + "|#|XX= PPPP =XX|#|", + "|#|XX= PPPP =XX|#|", + "|#||||^ YYPPPPYY ^||||#|", + "|||||| ||||||", + " YYPPPPYY ", + " YYPPPPYY ", + "|||||||| |||| ||||||||", + "|####|--22-##-22--|####|", + "|####|-Eee-##-eeE-|####|", + "|####|-eee-##-eee-|####|", + "|####|--22-##-22--|####|", + "|#|||||| |||| ||||||#|", + "|#|XXX= YY =XXX|#|", + "|#|XXX=YPPPPPPPPY=XXX|#|", + "|#|XXX=YPPPPPPPPY=XXX|#|", + "|#|XXX= YY =XXX|#|", + "||||||||||| |||||||||||" + ], + "palettes": [ "microlab" ], + "terrain": { "`": "t_open_air", "E": "t_elevator_control_off" } + } + }, + { + "type": "mapgen", + "method": "json", + "om_terrain": "iso_elevator_microlab_embed_next", + "object": { + "fill_ter": "t_strconc_floor", + "rows": [ + "||||||||||| |||||||||||", + "|####|XXX= =XXX|####|", + "|####|XXX= =XXX|####|", + "|####|XXX= =XXX|####|", + "|####|XXX= =XXX|####|", + "|#||||==== ====||||#|", + "|#|XX= YYPPPPYY =XX|#|", + "|#|XX= PPPP =XX|#|", + "|#|XX= PPPP =XX|#|", + "|#||||^ YYPPPPYY ^||||#|", + "|||||| ||||||", + " YYPPPPYY ", + " YYPPPPYY ", + "|||||||| |||| ||||||||", + "|####|--22-##-22--|####|", + "|####|-Eee-##-eeE-|####|", + "|####|-eee-##-eee-|####|", + "|####|--22-##-22--|####|", + "|#|||||| |||| ||||||#|", + "|#|XXX= YY =XXX|#|", + "|#|XXX=YPPPPPPPPY=XXX|#|", + "|#|XXX=YPPPPPPPPY=XXX|#|", + "|#|XXX= YY =XXX|#|", + "||||||||||| |||||||||||" + ], + "palettes": [ "microlab" ], + "terrain": { "`": "t_open_air", "E": "t_elevator_control_iso" } + } } ] diff --git a/data/mods/Isolation-Protocol/Map/mapgen/microlab_subhallway.json b/data/mods/Isolation-Protocol/Map/mapgen/microlab_subhallway.json new file mode 100644 index 0000000000000..8ab20a9969650 --- /dev/null +++ b/data/mods/Isolation-Protocol/Map/mapgen/microlab_subhallway.json @@ -0,0 +1,110 @@ +[ + { + "type": "mapgen", + "om_terrain": "mlb_generic_subhallway", + "method": "json", + "object": { + "fill_ter": "t_strconc_floor", + "rows": [ + "##########| |##########", + "#######||||YY||||#######", + "#######|XX= =XX|#######", + "#######|XX= =XX|#######", + "#######|XX= =XX|#######", + "#######|XX= =XX|#######", + "#######||||YY||||#######", + "#######|XX= =XX|#######", + "#######|XX= =XX|#######", + "#######|XX= =XX|#######", + "#######|XX= =XX|#######", + "#######||||YY||||#######", + "#######|XX= =XX|#######", + "#######|XX= =XX|#######", + "#######|XX= =XX|#######", + "#######|XX= =XX|#######", + "#######||||YY||||#######", + "#######|XX= =XX|#######", + "#######|XX= =XX|#######", + "#######|XX= =XX|#######", + "#######|XX= =XX|#######", + "#######|||| ||||#######", + "##########|YY|##########", + "##########| |##########" + ], + "palettes": [ "microlab" ], + "place_nested": [ { "chunks": [ { "param": "unique_foe", "fallback": "mnm_more_density" } ], "x": 0, "y": 0 } ] + } + }, + { + "type": "mapgen", + "om_terrain": "mlb_generic_subhallway", + "method": "json", + "object": { + "fill_ter": "t_strconc_floor", + "rows": [ + "##########| |##########", + "##########|YY||#########", + "##########| |#########", + "##########| |#########", + "##########| |#########", + "##########| |#########", + "##########| |#########", + "####||||||| |#########", + "####|i i | |#########", + "####| | |#########", + "####|2|2| | |#########", + "####|;|;| Y |#########", + "####||||| Y |#########", + "####|;|;| Y |#########", + "####|2|2| | |#########", + "####| | |#########", + "####|i i | |#########", + "####||||||| |#########", + "##########| |#########", + "##########| |#########", + "##########| |#########", + "##########| |#########", + "##########|YY||#########", + "##########| |##########" + ], + "palettes": [ "microlab" ], + "place_nested": [ { "chunks": [ { "param": "unique_foe", "fallback": "mnm_more_density" } ], "x": 0, "y": 0 } ] + } + }, + { + "type": "mapgen", + "om_terrain": "mlb_generic_subhallway", + "method": "json", + "object": { + "fill_ter": "t_strconc_floor", + "rows": [ + "##########| |##########", + "##########|YY|##########", + "##########| |##########", + "##########| |##########", + "##########| |##########", + "##########| |##########", + "##########| |##########", + "##########| |##########", + "##########| |##########", + "##########| |##########", + "##########| |##########", + "##########|YY|##########", + "##########|YY|##########", + "##########| |##########", + "##########| |##########", + "##########| |##########", + "##########| |##########", + "##########| |##########", + "##########| |##########", + "##########| |##########", + "##########| |##########", + "##########| |##########", + "##########|YY|##########", + "##########| |##########" + ], + "palettes": [ "microlab" ], + "place_nested": [ { "chunks": [ { "param": "unique_foe", "fallback": "mnm_more_density" } ], "x": 0, "y": 0 } ] + } + } +] diff --git a/data/mods/Isolation-Protocol/Map/overmap.json b/data/mods/Isolation-Protocol/Map/overmap.json index 4df9585732dff..bf03446879955 100644 --- a/data/mods/Isolation-Protocol/Map/overmap.json +++ b/data/mods/Isolation-Protocol/Map/overmap.json @@ -1,12 +1,25 @@ [ { "type": "overmap_terrain", - "id": [ "iso_first_elevator", "iso_elevator", "iso_elevator_safe", "iso_next_level_elevator" ], + "id": [ + "iso_first_elevator", + "iso_elevator", + "iso_elevator_safe", + "iso_next_level_elevator", + "iso_elevator_microlab_embed", + "iso_elevator_microlab_embed_next" + ], "name": "lab elevator", "color": "light_red", "sym": "L", "see_cost": "high" }, + { + "type": "overmap_terrain", + "id": "mlb_generic_subhallway", + "copy-from": "microlab_generic", + "flags": [ "RISK_EXTREME", "SOURCE_CHEMISTRY", "SOURCE_MEDICINE" ] + }, { "type": "overmap_terrain", "id": [ "iso_hallway_isolated" ], diff --git a/data/mods/Megafauna/furniture_and_terrain/terrain-manufactured.json b/data/mods/Megafauna/furniture_and_terrain/terrain-manufactured.json index 56d9a6c9e4109..00f5764a7e6ff 100644 --- a/data/mods/Megafauna/furniture_and_terrain/terrain-manufactured.json +++ b/data/mods/Megafauna/furniture_and_terrain/terrain-manufactured.json @@ -12,7 +12,7 @@ "floor_bedding_warmth": 800, "comfort": 1, "connect_groups": "INDOORFLOOR", - "flags": [ "CONTAINER", "FLAMMABLE_ASH", "REDUCE_SCENT", "INDOORS", "MOUNTABLE", "HIDE_PLACE", "EASY_DECONSTRUCT", "NO_SIGHT" ], + "flags": [ "CONTAINER", "FLAMMABLE_ASH", "REDUCE_SCENT", "INDOORS", "MOUNTABLE", "HIDE_PLACE", "NO_SIGHT" ], "bash": { "str_min": 4, "str_max": 60, diff --git a/data/mods/Xedra_Evolved/furniture_and_terrain/terrain-flora.json b/data/mods/Xedra_Evolved/furniture_and_terrain/terrain-flora.json index 11bbd68837984..c763c1810cc32 100644 --- a/data/mods/Xedra_Evolved/furniture_and_terrain/terrain-flora.json +++ b/data/mods/Xedra_Evolved/furniture_and_terrain/terrain-flora.json @@ -411,17 +411,7 @@ "coverage": 80, "floor_bedding_warmth": 2000, "comfort": 3, - "flags": [ - "TRANSPARENT", - "CONTAINER", - "FLAMMABLE_ASH", - "THIN_OBSTACLE", - "REDUCE_SCENT", - "INDOORS", - "MOUNTABLE", - "HIDE_PLACE", - "EASY_DECONSTRUCT" - ], + "flags": [ "TRANSPARENT", "CONTAINER", "FLAMMABLE_ASH", "THIN_OBSTACLE", "REDUCE_SCENT", "INDOORS", "MOUNTABLE", "HIDE_PLACE" ], "bash": { "str_min": 12, "str_max": 60, diff --git a/doc/EFFECT_ON_CONDITION.md b/doc/EFFECT_ON_CONDITION.md index edb697e4161d7..95e49f4cfe8e8 100644 --- a/doc/EFFECT_ON_CONDITION.md +++ b/doc/EFFECT_ON_CONDITION.md @@ -660,6 +660,73 @@ check do you have 3 manuals in inventory { "u_has_item_category": "manuals", "count": 3 } ``` + + +### `u_has_items_sum`, `npc_has_items_sum` +- type: array of pairs, pair is string or [variable object](##variable-object) and int or [variable object](##variable-object) +- return true if alpha or beta talker has enough items from the list +- `item` is an item that should be checked; +- `amount` is amount of items that should be found +- may be used in pair with `_consume_item_sum` + +#### Valid talkers: + +| Avatar | Character | NPC | Monster | Furniture | Item | +| ------ | --------- | --------- | ---- | ------- | --- | +| ✔️ | ✔️ | ✔️ | ❌ | ❌ | ❌ | + +#### Examples +check do you have 10 blankets of any type in the list +```json + { + "type": "effect_on_condition", + "id": "EOC_TEST", + "condition": { + "u_has_items_sum": [ + { "item": "blanket", "amount": 10 }, + { "item": "blanket_fur", "amount": 10 }, + { "item": "electric_blanket", "amount": 10 } + ] + }, + "effect": [ { "u_message": "true" } ], + "false_effect": [ { "u_message": "false" } ] + }, +``` + +Check do you have enough blankets to cover required amount (for example, it return true if you have 5 `blanket` and 10 `electric_blanket` (each contribute 50% to the desired amount)) +```json + { + "type": "effect_on_condition", + "id": "EOC_TEST", + "condition": { + "u_has_items_sum": [ + { "item": "blanket", "amount": 10 }, + { "item": "blanket_fur", "amount": 15 }, + { "item": "electric_blanket", "amount": 20 } + ] + }, + "effect": [ { "u_message": "true" } ], + "false_effect": [ { "u_message": "false" } ] + }, +``` + +Variables are also supported +```json + { + "type": "effect_on_condition", + "id": "EOC_TEST", + "condition": { + "u_has_items_sum": [ + { "item": { "global_val": "foo" }, "amount": { "math": "20 + 2" } }, + { "item": "blanket_fur", "amount": 15 }, + { "item": "electric_blanket", "amount": 20 } + ] + }, + "effect": [ { "u_message": "true" } ], + "false_effect": [ { "u_message": "false" } ] + }, +``` + ### `u_has_bionics`, `npc_has_bionics` - type: string or [variable object](##variable-object) - return true if alpha or beta talker has specific bionic; `ANY` can be used to return true if any bionic is presented @@ -3454,6 +3521,85 @@ removes morale type, delivered by `morale_id` ``` + +#### `u_consume_item_sum`, `npc_consume_item_sum` +Consumes all items you have in your inventory, treating count as weight +Effect does not validate do player actually has enough items to consume, use `_has_items_sum` +See examples for more info + +| Syntax | Optionality | Value | Info | +| ------ | ----------- | ------ | ---- | +| "u_unset_flag" / "npc_unset_flag" | **mandatory** | array of pairs, in pair is string or [variable object](##variable-object) | runs the effect | +| "item" | **mandatory** | string or [variable object](##variable-object) | id of item that should be removed | +| "amount" | **mandatory** | int or [variable object](##variable-object) | amount of items or charges that should be removed | + +##### Valid talkers: + +| Avatar | Character | NPC | Monster | Furniture | Item | +| ------ | --------- | --------- | ---- | ------- | --- | +| ✔️ | ✔️ | ✔️ | ❌ | ❌ | ❌ | + +##### Examples +Consume 10 blankets. Effect allows to be consumed any item, so in this case player may have 3 `blanket`, 2 `blanket_fur`, and 5 `electric_blanket`, and effect would consume all of it +```json + { + "type": "effect_on_condition", + "id": "EOC_TEST", + "effect": [ + { + "u_consume_item_sum": [ + { "item": "blanket", "amount": 10 }, + { "item": "blanket_fur", "amount": 10 }, + { "item": "electric_blanket", "amount": 10 } + ] + } + ] + }, +``` +Effect is order dependant, meaning first entry in json would be consumed first, then second and so on. Having 5 `blanket`, 10 `blanket_fur` and 5 `electric_blanket` would result in 5 `blanket` and 5 `blanket_fur` being consumed + + +Variable `amount` is also supported. In this case amount would be also treated as the weight; In the next example, having 10 `blanket`, 10 `blanket_fur` and 10 `electric_blanket` would be treated as covering 100% of requirement, 10 `blanket` delivering 40%, 10 `blanket_fur` delivering another 40%, and 10 `electric_blanket` delivering the last 20% +```json + { + "type": "effect_on_condition", + "id": "EOC_TEST", + "effect": [ + { + "u_consume_item_sum": [ + { "item": "blanket", "amount": 25 }, + { "item": "blanket_fur", "amount": 25 }, + { "item": "electric_blanket", "amount": 50 } + ] + } + ] + }, +``` +Because of how variable amount is calculated, it is recommended to put the values with the smallest `amount` on the top; It would prevent code overshooting, as: +```c++ + // example: we have 99 blankets and 1 blanket_fur + // json below would result in 99 blankets and 1 blanket_fur consumed +{ "item": "blanket", "amount": 100 }, { "item": "blanket_fur", "amount": 2 } + +// this json, however, would result in 1 blanket_fur and 50 blanket consumed +{ "item": "blanket_fur", "amount": 2 }, { "item": "blanket", "amount": 100 } +``` + +Variables are also supported +```json + { + "type": "effect_on_condition", + "id": "EOC_TEST", + "effect": [ + { + "u_consume_item_sum": [ + { "item": { "global_val": "foo" }, "amount": { "math": "20 + 2" } } + ] + } + ] + }, +``` + #### `u_add_faction_trust` Your character gains trust with the speaking NPC's faction, which affects which items become available for trading from shopkeepers of that faction. Can be used only in `talk_topic`, as the code relies on the NPC you talk with to obtain info about it's faction diff --git a/doc/ITEM_SPAWN.md b/doc/ITEM_SPAWN.md index 3851e38dbe920..2fd34ae56a1f9 100644 --- a/doc/ITEM_SPAWN.md +++ b/doc/ITEM_SPAWN.md @@ -97,6 +97,7 @@ Each entry can have more values (shown above as `...`). They allow further prop "container-group": "", "entry-wrapper": "", "sealed": +"active": "custom-flags": , "variant": "artifact": @@ -112,6 +113,8 @@ Each entry can have more values (shown above as `...`). They allow further prop `sealed`: If true, a container will be sealed when the item spawns. Default is `true`. +`active`: If true, item would be spawned activated. Be sure to use active versions of item, like `flashlight_on` instead of `flashlight`. Default is `false` + `custom-flags`: An array of flags that will be applied to this item. `variant`: A valid itype variant id for this item. diff --git a/src/character_body.cpp b/src/character_body.cpp index 9be887b1b179a..d63b22fc8a051 100644 --- a/src/character_body.cpp +++ b/src/character_body.cpp @@ -1199,9 +1199,9 @@ bodypart_id Character::body_window( const std::string &menu_header, bool bitten = has_effect( effect_bite, bp.id() ); bool infected = has_effect( effect_infected, bp.id() ); bool bandaged = has_effect( effect_bandaged, bp.id() ); - const int b_power = get_effect_int( effect_bandaged, bp ); - const int d_power = get_effect_int( effect_disinfected, bp ); - int new_b_power = static_cast( std::floor( bandage_power ) ); + const int b_power = get_effect_int( effect_bandaged, bp ) + 1; + const int d_power = get_effect_int( effect_disinfected, bp ) + 1; + int new_b_power = static_cast( std::floor( bandage_power ) ) + 1; if( bandaged ) { const effect &eff = get_effect( effect_bandaged, bp ); if( new_b_power > eff.get_max_intensity() ) { diff --git a/src/condition.cpp b/src/condition.cpp index fbe570aaffdbe..2cb499424da1d 100644 --- a/src/condition.cpp +++ b/src/condition.cpp @@ -876,6 +876,45 @@ conditional_t::func f_has_items( const JsonObject &jo, const std::string_view me } } +conditional_t::func f_has_items_sum( const JsonObject &jo, const std::string_view member, + bool is_npc ) +{ + std::vector> item_and_amount; + + for( const JsonObject jsobj : jo.get_array( member ) ) { + const str_or_var item = get_str_or_var( jsobj.get_member( "item" ), "item", true ); + const dbl_or_var amount = get_dbl_or_var( jsobj, "amount", true, 1 ); + item_and_amount.emplace_back( item, amount ); + } + return [item_and_amount, is_npc]( dialogue & d ) { + add_msg_debug( debugmode::DF_TALKER, "using _has_items_sum:" ); + + itype_id item_to_find; + double percent = 0.0f; + double count_desired; + double count_present; + double charges_present; + double total_present; + for( const auto &pair : item_and_amount ) { + item_to_find = itype_id( pair.first.evaluate( d ) ); + count_desired = pair.second.evaluate( d ); + count_present = d.actor( is_npc )->get_amount( item_to_find ); + charges_present = d.actor( is_npc )->charges_of( item_to_find ); + total_present = std::max( count_present, charges_present ); + percent += total_present / count_desired; + + add_msg_debug( debugmode::DF_TALKER, + "item: %s, count_desired: %f, count_present: %f, charges_present: %f, total_present: %f, percent: %f", + item_to_find.str(), count_desired, count_present, charges_present, total_present, percent ); + + if( percent >= 1 ) { + return true; + } + } + return false; + }; +} + conditional_t::func f_has_item_with_flag( const JsonObject &jo, std::string_view member, bool is_npc ) { @@ -2456,6 +2495,7 @@ parsers = { {"u_has_item", "npc_has_item", jarg::member, &conditional_fun::f_has_item }, {"u_has_item_with_flag", "npc_has_item_with_flag", jarg::member, &conditional_fun::f_has_item_with_flag }, {"u_has_items", "npc_has_items", jarg::member, &conditional_fun::f_has_items }, + {"u_has_items_sum", "npc_has_items_sum", jarg::array, &conditional_fun::f_has_items_sum }, {"u_has_item_category", "npc_has_item_category", jarg::member, &conditional_fun::f_has_item_category }, {"u_has_bionics", "npc_has_bionics", jarg::member, &conditional_fun::f_has_bionics }, {"u_has_any_effect", "npc_has_any_effect", jarg::array, &conditional_fun::f_has_any_effect }, diff --git a/src/construction.cpp b/src/construction.cpp index 5209148a52955..addbc67329a7e 100644 --- a/src/construction.cpp +++ b/src/construction.cpp @@ -2045,8 +2045,12 @@ void construct::do_turn_deconstruct( const tripoint_bub_ms &p, Character &who ) auto deconstruct_items = []( const item_group_id & drop_group ) { std::string ret; + const Item_spawn_data *spawn_data = item_group::spawn_data_from_group( drop_group ); + if( spawn_data == nullptr ) { + return ret; + } const std::map> deconstruct_items = - item_group::spawn_data_from_group( drop_group )->every_item_min_max(); + spawn_data->every_item_min_max(); for( const auto &deconstruct_item : deconstruct_items ) { const int &min = deconstruct_item.second.first; const int &max = deconstruct_item.second.second; diff --git a/src/explosion.cpp b/src/explosion.cpp index da09dcf001fdb..96d3aa43a99de 100644 --- a/src/explosion.cpp +++ b/src/explosion.cpp @@ -22,6 +22,7 @@ #include "cata_utility.h" #include "character.h" #include "color.h" +#include "coordinates.h" #include "creature.h" #include "creature_tracker.h" #include "damage.h" @@ -177,7 +178,7 @@ float gurney_spherical( const double charge, const double mass ) } // (C1001) Compiler Internal Error on Visual Studio 2015 with Update 2 -static void do_blast( const Creature *source, const tripoint &p, const float power, +static void do_blast( map *m, const Creature *source, const tripoint_bub_ms &p, const float power, const float distance_factor, const bool fire ) { const float tile_dist = 1.0f; @@ -190,23 +191,22 @@ static void do_blast( const Creature *source, const tripoint &p, const float pow static constexpr std::array x_offset = { -1, 1, 0, 0, 1, -1, -1, 1, 0, 0 }; static constexpr std::array y_offset = { 0, 0, -1, 1, -1, 1, -1, 1, 0, 0 }; static constexpr std::array z_offset = { 0, 0, 0, 0, 0, 0, 0, 0, 1, -1 }; - map &here = get_map(); const size_t max_index = 10; - here.bash( p, fire ? power : ( 2 * power ), true, false, false ); + m->bash( p, fire ? power : ( 2 * power ), true, false, false ); - std::priority_queue< std::pair, std::vector< std::pair >, pair_greater_cmp_first > + std::priority_queue< std::pair, std::vector< std::pair >, pair_greater_cmp_first > open; - std::set closed; - std::set bashed{ p }; - std::map dist_map; + std::set closed; + std::set bashed{ p }; + std::map dist_map; open.emplace( 0.0f, p ); dist_map[p] = 0.0f; // Find all points to blast while( !open.empty() ) { // Add some random factor to effective distance to make it look cooler const float distance = open.top().first * rng_float( 1.0f, 1.2f ); - const tripoint pt = open.top().second; + const tripoint_bub_ms pt = open.top().second; open.pop(); if( closed.count( pt ) != 0 ) { @@ -220,7 +220,7 @@ static void do_blast( const Creature *source, const tripoint &p, const float pow continue; } - if( here.impassable( pt ) && pt != p ) { + if( m->impassable( pt ) && pt != p ) { // Don't propagate further continue; } @@ -229,8 +229,8 @@ static void do_blast( const Creature *source, const tripoint &p, const float pow // Don't check up/down (for now) - this will make 2D/3D balancing easier int empty_neighbors = 0; for( size_t i = 0; i < 8; i++ ) { - tripoint dest( pt + tripoint( x_offset[i], y_offset[i], z_offset[i] ) ); - if( closed.count( dest ) == 0 && here.valid_move( pt, dest, false, true ) ) { + tripoint_bub_ms dest( pt + tripoint_rel_ms( x_offset[i], y_offset[i], z_offset[i] ) ); + if( closed.count( dest ) == 0 && m->valid_move( pt, dest, false, true ) ) { empty_neighbors++; } } @@ -238,8 +238,8 @@ static void do_blast( const Creature *source, const tripoint &p, const float pow empty_neighbors = std::max( 1, empty_neighbors ); // Iterate over all neighbors. Bash all of them, propagate to some for( size_t i = 0; i < max_index; i++ ) { - tripoint dest( pt + tripoint( x_offset[i], y_offset[i], z_offset[i] ) ); - if( closed.count( dest ) != 0 || !here.inbounds( dest ) ) { + tripoint_bub_ms dest( pt + tripoint_rel_ms( x_offset[i], y_offset[i], z_offset[i] ) ); + if( closed.count( dest ) != 0 || !m->inbounds( dest ) ) { continue; } @@ -252,21 +252,21 @@ static void do_blast( const Creature *source, const tripoint &p, const float pow force / 2; if( z_offset[i] == 0 ) { // Horizontal - no floor bashing - here.bash( dest, bash_force, true, false, false ); + m->bash( dest, bash_force, true, false, false ); } else if( z_offset[i] > 0 ) { // Should actually bash through the floor first, but that's not really possible yet - here.bash( dest, bash_force, true, false, true ); - } else if( !here.valid_move( pt, dest, false, true ) ) { + m->bash( dest, bash_force, true, false, true ); + } else if( !m->valid_move( pt, dest, false, true ) ) { // Only bash through floor if it doesn't exist // Bash the current tile's floor, not the one's below - here.bash( pt, bash_force, true, false, true ); + m->bash( pt, bash_force, true, false, true ); } } float next_dist = distance; next_dist += ( x_offset[i] == 0 || y_offset[i] == 0 ) ? tile_dist : diag_dist; if( z_offset[i] != 0 ) { - if( !here.valid_move( pt, dest, false, true ) ) { + if( !m->valid_move( pt, dest, false, true ) ) { continue; } @@ -280,37 +280,46 @@ static void do_blast( const Creature *source, const tripoint &p, const float pow } } - // Draw the explosion - std::map explosion_colors; - for( const tripoint &pt : closed ) { - if( here.impassable( pt ) ) { - continue; - } + // Draw the explosion, but only if the explosion center is within the reality bubble + map &bubble_map = get_map(); + if( bubble_map.inbounds( m->getabs( p ) ) ) { + std::map explosion_colors; + for( const tripoint_bub_ms &pt : closed ) { + const tripoint_bub_ms bubble_pos( bubble_map.bub_from_abs( m->getabs( pt ) ) ); - const float force = power * std::pow( distance_factor, dist_map.at( pt ) ); - nc_color col = c_red; - if( force < 10 ) { - col = c_white; - } else if( force < 30 ) { - col = c_yellow; + if( !bubble_map.inbounds( bubble_pos ) ) { + continue; + } + if( m->impassable( pt ) ) { + continue; + } + + const float force = power * std::pow( distance_factor, dist_map.at( pt ) ); + nc_color col = c_red; + if( force < 10 ) { + col = c_white; + } else if( force < 30 ) { + col = c_yellow; + } + + explosion_colors[bubble_pos.raw()] = col; } - explosion_colors[pt] = col; + draw_custom_explosion( get_player_character().pos(), explosion_colors ); } - draw_custom_explosion( get_player_character().pos(), explosion_colors ); - creature_tracker &creatures = get_creature_tracker(); - Creature *mutable_source = source == nullptr ? nullptr : creatures.creature_at( source->pos() ); - for( const tripoint &pt : closed ) { + // Must use the reality bubble pos, because that's what the creature tracker works with. + Creature *mutable_source = source == nullptr ? nullptr : creatures.creature_at( source->pos_bub() ); + for( const tripoint_bub_ms &pt : closed ) { const float force = power * std::pow( distance_factor, dist_map.at( pt ) ); if( force < 1.0f ) { // Too weak to matter continue; } - if( here.has_items( pt ) ) { - here.smash_items( pt, force, _( "force of the explosion" ) ); + if( m->has_items( pt ) ) { + m->smash_items( pt, force, _( "force of the explosion" ) ); } if( fire ) { @@ -318,16 +327,18 @@ static void do_blast( const Creature *source, const tripoint &p, const float pow if( force > 10.0f || x_in_y( force, 10.0f ) ) { intensity++; } - here.add_field( pt, fd_fire, intensity ); + m->add_field( pt, fd_fire, intensity ); } - if( const optional_vpart_position vp = here.veh_at( pt ) ) { + if( const optional_vpart_position vp = m->veh_at( pt ) ) { // TODO: Make this weird unit used by vehicle::damage more sensible - vp->vehicle().damage( here, vp->part_index(), force, + vp->vehicle().damage( m[0], vp->part_index(), force, fire ? damage_heat : damage_bash, false ); } - Creature *critter = creatures.creature_at( pt, true ); + // Translate to reality bubble coordinates to work with the creature tracker. + const tripoint_bub_ms bubble_pos( bubble_map.bub_from_abs( m->getabs( pt ) ) ); + Creature *critter = creatures.creature_at( bubble_pos, true ); if( critter == nullptr ) { continue; } @@ -348,8 +359,10 @@ static void do_blast( const Creature *source, const tripoint &p, const float pow } // Print messages for all NPCs - pl->add_msg_player_or_npc( m_bad, _( "You're caught in the explosion!" ), - _( " is caught in the explosion!" ) ); + if( bubble_map.inbounds( bubble_pos ) ) { + pl->add_msg_player_or_npc( m_bad, _( "You're caught in the explosion!" ), + _( " is caught in the explosion!" ) ); + } struct blastable_part { bodypart_id bp; @@ -386,8 +399,9 @@ static void do_blast( const Creature *source, const tripoint &p, const float pow } } -static std::vector shrapnel( const Creature *source, const tripoint &src, int power, - int casing_mass, float per_fragment_mass, int range = -1 ) +static std::vector shrapnel( map *m, const Creature *source, + const tripoint_bub_ms &src, int power, + int casing_mass, float per_fragment_mass, int range = -1 ) { // The gurney equation wants the total mass of the casing. const float fragment_velocity = gurney_spherical( power, casing_mass ); @@ -396,7 +410,7 @@ static std::vector shrapnel( const Creature *source, const tripoint &s int fragment_count = casing_mass / fragment_mass; // Contains all tiles reached by fragments. - std::vector distrib; + std::vector distrib; projectile proj; proj.speed = fragment_velocity; @@ -412,38 +426,41 @@ static std::vector shrapnel( const Creature *source, const tripoint &s cata::mdarray &obstacle_cache = caches->obstacle_cache; cata::mdarray &visited_cache = caches->visited_cache; - map &here = get_map(); // TODO: Calculate range based on max effective range for projectiles. // Basically bisect between 0 and map diameter using shrapnel_calc(). // Need to update shadowcasting to support limiting range without adjusting initial distance. - const tripoint_range area = here.points_on_zlevel( src.z ); + const tripoint_range area = m->bub_points_on_zlevel( src.z() ); - here.build_obstacle_cache( area.min(), area.max() + tripoint_south_east, obstacle_cache ); + m->build_obstacle_cache( area.min().raw(), area.max().raw() + tripoint_south_east, obstacle_cache ); // Shadowcasting normally ignores the origin square, // so apply it manually to catch monsters standing on the explosive. // This "blocks" some fragments, but does not apply deceleration. - fragment_cloud initial_cloud = accumulate_fragment_cloud( obstacle_cache[src.x][src.y], + fragment_cloud initial_cloud = accumulate_fragment_cloud( obstacle_cache[src.x()][src.y()], { fragment_velocity, static_cast( fragment_count ) }, 1 ); - visited_cache[src.x][src.y] = initial_cloud; - visited_cache[src.x][src.y].density = static_cast( fragment_count / 2.0 ); + visited_cache[src.x()][src.y()] = initial_cloud; + visited_cache[src.x()][src.y()].density = static_cast( fragment_count / 2.0 ); castLightAll - ( visited_cache, obstacle_cache, point_bub_ms( src.xy() ), 0, initial_cloud ); + ( visited_cache, obstacle_cache, src.xy(), 0, initial_cloud ); creature_tracker &creatures = get_creature_tracker(); - Creature *mutable_source = source == nullptr ? nullptr : creatures.creature_at( source->pos() ); + // Creature tracker works on reality bubble coordinates, so feeding it with those coordinates from the critter is correct. + Creature *mutable_source = source == nullptr ? nullptr : creatures.creature_at( source->pos_bub() ); + map &bubble_map = get_map(); // Now visited_caches are populated with density and velocity of fragments. - for( const tripoint &target : area ) { - fragment_cloud &cloud = visited_cache[target.x][target.y]; + for( const tripoint_bub_ms &target : area ) { + fragment_cloud &cloud = visited_cache[target.x()][target.y()]; if( cloud.density <= MIN_FRAGMENT_DENSITY || cloud.velocity <= MIN_EFFECTIVE_VELOCITY ) { continue; } distrib.emplace_back( target ); int damage = ballistic_damage( cloud.velocity, fragment_mass ); - Creature *critter = creatures.creature_at( target ); + // Translate to reality bubble coordinates to work with the creature tracker. + const tripoint_bub_ms bubble_pos( bubble_map.bub_from_abs( m->getabs( target ) ) ); + Creature *critter = creatures.creature_at( bubble_pos ); if( damage > 0 && critter && !critter->is_dead_state() ) { std::poisson_distribution<> d( cloud.density ); int hits = d( rng_get_engine() ); @@ -476,14 +493,17 @@ static std::vector shrapnel( const Creature *source, const tripoint &s } } int total_hits = damaging_hits + non_damaging_hits; - multi_projectile_hit_message( critter, total_hits, damage_taken, n_gettext( "bomb fragment", - "bomb fragments", total_hits ) ); + if( bubble_map.inbounds( + bubble_pos ) ) { // Only report on critters in the reality bubble. Should probably be only for visible critters... + multi_projectile_hit_message( critter, total_hits, damage_taken, n_gettext( "bomb fragment", + "bomb fragments", total_hits ) ); + } } - if( here.impassable( target ) ) { - if( optional_vpart_position vp = here.veh_at( target ) ) { - vp->vehicle().damage( here, vp->part_index(), damage / 10 ); + if( m->impassable( target ) ) { + if( optional_vpart_position vp = m->veh_at( target ) ) { + vp->vehicle().damage( m[0], vp->part_index(), damage / 10 ); } else { - here.bash( target, damage / 100, true ); + m->bash( target, damage / 100, true ); } } } @@ -503,23 +523,41 @@ void explosion( const Creature *source, const tripoint &p, float power, float fa explosion( source, p, data ); } +// Blocks activation of maps loaded by process_explosions. Activation would trigger a recursive +// activation of whatever caused the explosion triggering the map loading, leading to a recursive +// death spiral. Activation could also trigger further explosions leading to a cascade effect which +// is also undesirable. Such explosions should not be triggered recursively, but by a direct action. +static bool process_explosions_in_progress = false; + +bool explosion_processing_active() +{ + return process_explosions_in_progress; +} + void explosion( const Creature *source, const tripoint &p, const explosion_data &ex ) { _explosions.emplace_back( source, get_map().getglobal( p ), ex ); } -void _make_explosion( const Creature *source, const tripoint &p, const explosion_data &ex ) +void _make_explosion( map *m, const Creature *source, const tripoint_bub_ms &p, + const explosion_data &ex ) { - int noise = ex.power * ( ex.fire ? 2 : 10 ); - noise = ( noise > ex.max_noise ) ? ex.max_noise : noise; - if( noise >= 30 ) { - sounds::sound( p, noise, sounds::sound_t::combat, _( "a huge explosion!" ), false, "explosion", - "huge" ); - } else if( noise >= 4 ) { - sounds::sound( p, noise, sounds::sound_t::combat, _( "an explosion!" ), false, "explosion", - "default" ); - } else if( noise > 0 ) { - sounds::sound( p, 3, sounds::sound_t::combat, _( "a loud pop!" ), false, "explosion", "small" ); + if( get_map().inbounds( m->getabs( p ) ) ) { + tripoint_bub_ms bubble_pos = get_map().bub_from_abs( m->getabs( p ) ); + int noise = ex.power * ( ex.fire ? 2 : 10 ); + noise = ( noise > ex.max_noise ) ? ex.max_noise : noise; + + if( noise >= 30 ) { + sounds::sound( bubble_pos, noise, sounds::sound_t::combat, _( "a huge explosion!" ), false, + "explosion", + "huge" ); + } else if( noise >= 4 ) { + sounds::sound( bubble_pos, noise, sounds::sound_t::combat, _( "an explosion!" ), false, "explosion", + "default" ); + } else if( noise > 0 ) { + sounds::sound( bubble_pos, 3, sounds::sound_t::combat, _( "a loud pop!" ), false, "explosion", + "small" ); + } } if( ex.distance_factor >= 1.0f ) { @@ -527,21 +565,21 @@ void _make_explosion( const Creature *source, const tripoint &p, const explosion } else if( ex.distance_factor > 0.0f && ex.power > 0.0f ) { // Power rescaled to mean grams of TNT equivalent, this scales it roughly back to where // it was before until we re-do blasting power to be based on TNT-equivalent directly. - do_blast( source, p, ex.power / 15.0, ex.distance_factor, ex.fire ); + do_blast( m, source, p, ex.power / 15.0, ex.distance_factor, ex.fire ); } - map &here = get_map(); const shrapnel_data &shr = ex.shrapnel; if( shr.casing_mass > 0 ) { - auto shrapnel_locations = shrapnel( source, p, ex.power, shr.casing_mass, shr.fragment_mass ); + std::vector shrapnel_locations = shrapnel( m, source, p, ex.power, shr.casing_mass, + shr.fragment_mass ); // If explosion drops shrapnel... if( shr.recovery > 0 && !shr.drop.is_null() ) { // Extract only passable tiles affected by shrapnel - std::vector tiles; - for( const tripoint &e : shrapnel_locations ) { - if( here.passable( e ) ) { + std::vector tiles; + for( const tripoint_bub_ms &e : shrapnel_locations ) { + if( m->passable( e ) ) { tiles.push_back( e ); } } @@ -552,8 +590,8 @@ void _make_explosion( const Creature *source, const tripoint &p, const explosion std::shuffle( tiles.begin(), tiles.end(), rng_get_engine() ); tiles.resize( std::min( static_cast( tiles.size() ), qty ) ); - for( const tripoint &e : tiles ) { - here.add_item_or_charges( e, item( shr.drop, calendar::turn, item::solitary_tag{} ) ); + for( const tripoint_bub_ms &e : tiles ) { + m->add_item_or_charges( e, item( shr.drop, calendar::turn, item::solitary_tag{} ) ); } } } @@ -927,12 +965,25 @@ void process_explosions() _explosions.clear(); for( const queued_explosion &ex : explosions_copy ) { - const tripoint p = get_map().getlocal( ex.pos ); - if( p.x < 0 || p.x >= MAPSIZE_X || p.y < 0 || p.y >= MAPSIZE_Y ) { - debugmsg( "Explosion origin (%d, %d, %d) is out-of-bounds", p.x, p.y, p.z ); - continue; + const int safe_range = ex.data.safe_range(); + map *bubble_map = &get_map(); + const tripoint_bub_ms bubble_pos( bubble_map->bub_from_abs( ex.pos ) ); + + if( bubble_pos.x() - safe_range < 0 || bubble_pos.x() + safe_range > MAPSIZE_X || + bubble_pos.y() - safe_range < 0 || bubble_pos.y() + safe_range > MAPSIZE_Y ) { + map m; + const tripoint_abs_sm origo( project_to( ex.pos ) - point_rel_sm{ HALF_MAPSIZE, HALF_MAPSIZE} ); + // Create a map centered around the explosion point to allow an explosion with a radius of up to 5 submaps + // to be created without being cut off by the map's boundary. That also means there is no need for the map + // to actually overlap the reality bubble, so a large explosion can be detonated without blowing up the PC + // or have a vehicle run into a crater suddenly appearing just in front of it. + process_explosions_in_progress = true; + m.load( origo, false, false ); + process_explosions_in_progress = false; + _make_explosion( &m, ex.source, m.bub_from_abs( ex.pos ), ex.data ); + } else { + _make_explosion( bubble_map, ex.source, bubble_map->bub_from_abs( ex.pos ), ex.data ); } - _make_explosion( ex.source, p, ex.data ); } } diff --git a/src/explosion.h b/src/explosion.h index 5b0bc512c06ea..fd1dda77ba2f6 100644 --- a/src/explosion.h +++ b/src/explosion.h @@ -8,6 +8,7 @@ #include #include "coordinates.h" +#include "map.h" #include "type_id.h" class Creature; @@ -78,8 +79,13 @@ void explosion( bool fire = false, int casing_mass = 0, float frag_mass = 0.05 ); +// Explosion processing is loading a map on which to execute the explosion. Processing that +// would potentially set off additional explosions should not be performed. They should wait +// until triggered normally. +bool explosion_processing_active(); void explosion( const Creature *source, const tripoint &p, const explosion_data &ex ); -void _make_explosion( const Creature *source, const tripoint &p, const explosion_data &ex ); +void _make_explosion( map *m, const Creature *source, const tripoint_bub_ms &p, + const explosion_data &ex ); /** Triggers a flashbang explosion at p. */ void flashbang( const tripoint &p, bool player_immune = false ); diff --git a/src/inventory_ui.cpp b/src/inventory_ui.cpp index d803d8b512fa7..732fb821acd1e 100644 --- a/src/inventory_ui.cpp +++ b/src/inventory_ui.cpp @@ -2924,7 +2924,7 @@ drop_location inventory_selector::get_only_choice() const for( const inventory_column *col : columns ) { const std::vector ent = col->get_entries( return_item, true ); if( !ent.empty() ) { - return { ent.front()->any_item(), static_cast( ent.front()->get_available_count() ) }; + return { ent.front()->any_item(), static_cast( ent.front()->chosen_count ) }; } } diff --git a/src/item_factory.cpp b/src/item_factory.cpp index 306c581c611f8..d679ccc037bee 100644 --- a/src/item_factory.cpp +++ b/src/item_factory.cpp @@ -5037,6 +5037,9 @@ void Item_factory::add_entry( Item_group &ig, const JsonObject &obj, const std:: modifier.sealed = obj.get_bool( "sealed" ); use_modifier = true; } + if( obj.has_member( "active" ) ) { + sptr->active = obj.get_bool( "active" ); + } std::vector custom_flags; use_modifier |= load_string( custom_flags, obj, "custom-flags" ); modifier.custom_flags.clear(); diff --git a/src/item_group.cpp b/src/item_group.cpp index 2de7f563de4dd..7bf96ba71cf38 100644 --- a/src/item_group.cpp +++ b/src/item_group.cpp @@ -230,6 +230,11 @@ item Single_item_creator::create_single_without_container( const time_point &bir if( one_in( 3 ) && tmp.has_flag( flag_VARSIZE ) ) { tmp.set_flag( flag_FIT ); } + + if( active.has_value() ) { + tmp.active = *active; + } + if( components_items ) { for( itype_id component_id : *components_items ) { if( !component_id.is_valid() ) { diff --git a/src/item_group.h b/src/item_group.h index 8116a946b10a4..56d71be35bc5a 100644 --- a/src/item_group.h +++ b/src/item_group.h @@ -205,6 +205,7 @@ class Item_spawn_data */ std::optional> components_items; bool sealed = true; + std::optional active = std::nullopt; struct relic_generator { relic_procgen_data::generation_rules rules; diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index e1ec97e88eedc..7006e9b25244c 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -3719,19 +3719,19 @@ void heal_actor::info( const item &, std::vector &dump ) const if( bandages_power > 0 ) { dump.emplace_back( "HEAL", _( "Base bandaging quality: " ), - texitify_base_healing_power( static_cast( bandages_power ) ) ); + texitify_base_healing_power( static_cast( bandages_power + 1 ) ) ); if( g != nullptr ) { dump.emplace_back( "HEAL", _( "Actual bandaging quality: " ), - texitify_healing_power( get_bandaged_level( player_character ) ) ); + texitify_healing_power( get_bandaged_level( player_character + 1 ) ) ); } } if( disinfectant_power > 0 ) { dump.emplace_back( "HEAL", _( "Base disinfecting quality: " ), - texitify_base_healing_power( static_cast( disinfectant_power ) ) ); + texitify_base_healing_power( static_cast( disinfectant_power + 1 ) ) ); if( g != nullptr ) { dump.emplace_back( "HEAL", _( "Actual disinfecting quality: " ), - texitify_healing_power( get_disinfected_level( player_character ) ) ); + texitify_healing_power( get_disinfected_level( player_character + 1 ) ) ); } } if( bleed > 0 ) { diff --git a/src/magic.cpp b/src/magic.cpp index ae3846deae82f..b7e20a9da5e45 100644 --- a/src/magic.cpp +++ b/src/magic.cpp @@ -975,20 +975,20 @@ int spell::duration( const Creature &caster ) const { dialogue d( get_talker_for( caster ), nullptr ); const int leveled_duration = min_leveled_duration( caster ); - float duration; - + int return_value; if( has_flag( spell_flag::RANDOM_DURATION ) ) { - return rng( std::min( leveled_duration, static_cast( type->max_duration.evaluate( d ) ) ), - std::max( leveled_duration, - static_cast( type->max_duration.evaluate( d ) ) ) ); + return_value = rng( std::min( leveled_duration, + static_cast( type->max_duration.evaluate( d ) ) ), + std::max( leveled_duration, + static_cast( type->max_duration.evaluate( d ) ) ) ); } else { if( type->max_duration.evaluate( d ) >= type->min_duration.evaluate( d ) ) { - return std::min( leveled_duration, static_cast( type->max_duration.evaluate( d ) ) ); + return_value = std::min( leveled_duration, static_cast( type->max_duration.evaluate( d ) ) ); } else { - return std::max( leveled_duration, static_cast( type->max_duration.evaluate( d ) ) ); + return_value = std::max( leveled_duration, static_cast( type->max_duration.evaluate( d ) ) ); } } - return std::max( duration * temp_duration_multiplyer, 0.0f ); + return std::max( return_value * temp_duration_multiplyer, 0.0f ); } std::string spell::duration_string( const Creature &caster ) const diff --git a/src/map.cpp b/src/map.cpp index dc8c2cbea7526..566af69b0287f 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -8377,16 +8377,18 @@ void map::load( const tripoint_abs_sm &w, const bool update_vehicle, } rebuild_vehicle_level_caches(); - // actualize after loading all submaps to prevent errors - // with entities at the edges - for( int gridx = 0; gridx < my_MAPSIZE; gridx++ ) { - for( int gridy = 0; gridy < my_MAPSIZE; gridy++ ) { - const int zmin = zlevels ? -OVERMAP_DEPTH : abs_sub.z(); - const int zmax = zlevels ? OVERMAP_HEIGHT : abs_sub.z(); - for( int gridz = zmin; gridz <= zmax; gridz++ ) { - actualize( {gridx, gridy, gridz } ); - if( pump_events ) { - inp_mngr.pump_events(); + if( !explosion_handler::explosion_processing_active() ) { + // actualize after loading all submaps to prevent errors + // with entities at the edges + for( int gridx = 0; gridx < my_MAPSIZE; gridx++ ) { + for( int gridy = 0; gridy < my_MAPSIZE; gridy++ ) { + const int zmin = zlevels ? -OVERMAP_DEPTH : abs_sub.z(); + const int zmax = zlevels ? OVERMAP_HEIGHT : abs_sub.z(); + for( int gridz = zmin; gridz <= zmax; gridz++ ) { + actualize( { gridx, gridy, gridz } ); + if( pump_events ) { + inp_mngr.pump_events(); + } } } } diff --git a/src/mapdata.cpp b/src/mapdata.cpp index bd7099becb282..b275474b9caf0 100644 --- a/src/mapdata.cpp +++ b/src/mapdata.cpp @@ -1039,6 +1039,10 @@ void ter_t::check() const debugmsg( "ter %s has invalid emission %s set", id.c_str(), e.str().c_str() ); } } + if( has_flag( ter_furn_flag::TFLAG_EASY_DECONSTRUCT ) && !deconstruct.can_do ) { + debugmsg( "ter %s has EASY_DECONSTRUCT flag but cannot be deconstructed", + id.c_str(), deconstruct.drop_group.c_str() ); + } } furn_t::furn_t() : open( furn_str_id::NULL_ID() ), close( furn_str_id::NULL_ID() ) {} diff --git a/src/npctalk.cpp b/src/npctalk.cpp index af21227fabc29..224b7901af22d 100644 --- a/src/npctalk.cpp +++ b/src/npctalk.cpp @@ -3448,6 +3448,78 @@ talk_effect_fun_t::func f_consume_item( const JsonObject &jo, std::string_view m }; } +talk_effect_fun_t::func f_consume_item_sum( const JsonObject &jo, std::string_view member, + const std::string_view, bool is_npc ) +{ + // Only after i implemented it, i realised it is a bad way to make it + // What it should be is an expansion of f_run_inv_eocs + // that allow to pick multiple ids and limit it to some amount of it + + std::vector> item_and_amount; + + for( const JsonObject jsobj : jo.get_array( member ) ) { + const str_or_var item = get_str_or_var( jsobj.get_member( "item" ), "item", true ); + const dbl_or_var amount = get_dbl_or_var( jsobj, "amount", true, 1 ); + item_and_amount.emplace_back( item, amount ); + } + + return [item_and_amount, is_npc]( dialogue & d ) { + add_msg_debug( debugmode::DF_TALKER, "using _consume_item_sum:" ); + + itype_id item_to_remove; + double percent = 0.0f; + double count_desired; + double count_present; + double charges_present; + + for( const auto &pair : item_and_amount ) { + item_to_remove = itype_id( pair.first.evaluate( d ) ); + count_desired = pair.second.evaluate( d ); + count_present = d.actor( is_npc )->get_amount( item_to_remove ); + charges_present = d.actor( is_npc )->charges_of( item_to_remove ); + if( charges_present > count_present ) { + percent += charges_present / count_desired; + // if percent is equal or less than 1, it is safe to remove all charges_present + // otherwise loop to remove charges one by one + if( percent <= 1 ) { + d.actor( is_npc )->use_charges( item_to_remove, charges_present, true ); + + add_msg_debug( debugmode::DF_TALKER, + "removing item: %s, count_desired: %f, charges_present: %f, percent: %f, removing all", + item_to_remove.c_str(), count_desired, charges_present, percent ); + } else { + percent -= charges_present / count_desired; + while( percent < 1.0f ) { + percent += 1 / count_desired; + d.actor( is_npc )->use_charges( item_to_remove, 1, true ); + add_msg_debug( debugmode::DF_TALKER, + "removing item: %s, count_desired: %f, charges_present: %f, percent: %f, removing one by one", + item_to_remove.c_str(), count_desired, charges_present, percent ); + } + } + } else { + percent += count_present / count_desired; + if( percent <= 1 ) { + d.actor( is_npc )->use_amount( item_to_remove, count_present ); + add_msg_debug( debugmode::DF_TALKER, + "removing item: %s, count_desired: %f, count_present: %f, percent: %f, removing all", + item_to_remove.c_str(), count_desired, count_present, percent ); + } else { + percent -= count_present / count_desired; + while( percent < 1.0f ) { + percent += 1 / count_desired; + d.actor( is_npc )->use_amount( item_to_remove, 1 ); + + add_msg_debug( debugmode::DF_TALKER, + "removing item: %s, count_desired: %f, count_present: %f, percent: %f, removing one by one", + item_to_remove.c_str(), count_desired, count_present, percent ); + } + } + } + } + }; +} + talk_effect_fun_t::func f_remove_item_with( const JsonObject &jo, std::string_view member, const std::string_view, bool is_npc ) { @@ -6655,6 +6727,7 @@ parsers = { { "u_unset_flag", "npc_unset_flag", jarg::member, &talk_effect_fun::f_unset_flag }, { "u_activate", "npc_activate", jarg::member, &talk_effect_fun::f_activate }, { "u_consume_item", "npc_consume_item", jarg::member, &talk_effect_fun::f_consume_item }, + { "u_consume_item_sum", "npc_consume_item_sum", jarg::array, &talk_effect_fun::f_consume_item_sum }, { "u_remove_item_with", "npc_remove_item_with", jarg::member, &talk_effect_fun::f_remove_item_with }, { "u_bulk_trade_accept", "npc_bulk_trade_accept", jarg::member, &talk_effect_fun::f_bulk_trade_accept }, { "u_bulk_donate", "npc_bulk_donate", jarg::member, &talk_effect_fun::f_bulk_trade_accept }, diff --git a/src/pixel_minimap.cpp b/src/pixel_minimap.cpp index c983bdc93dfd8..a469e3f4d9470 100644 --- a/src/pixel_minimap.cpp +++ b/src/pixel_minimap.cpp @@ -506,7 +506,8 @@ void pixel_minimap::render_critters( const tripoint ¢er ) mixture = lerp_clamped( 0, 100, std::max( s, 0.0f ) ); } - const level_cache &access_cache = get_map().access_cache( center.z ); + const map &m = get_map(); + const level_cache &access_cache = m.access_cache( center.z ); const point start( center.x - total_tiles_count.x / 2, center.y - total_tiles_count.y / 2 ); const point beacon_size = { @@ -518,6 +519,10 @@ void pixel_minimap::render_critters( const tripoint ¢er ) for( int y = 0; y < total_tiles_count.y; y++ ) { for( int x = 0; x < total_tiles_count.x; x++ ) { const tripoint p = start + tripoint( x, y, center.z ); + if( !m.inbounds( p ) ) { + // p might be out-of-bounds when peeking at submap boundary. Example: center=(64,59,-5), start=(4,-1) -> p=(4,-1,-5) + continue; + } const lit_level lighting = access_cache.visibility_cache[p.x][p.y]; if( lighting == lit_level::DARK || lighting == lit_level::BLANK ) { diff --git a/src/ui.cpp b/src/ui.cpp index fb1c076070b41..cb0c423d65220 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -61,9 +61,6 @@ void uilist_impl::on_resized() void uilist_impl::draw_controls() { - float hotkey_width = - ImGui::CalcTextSize( "[X]" ).x + ImGui::GetStyle().ItemSpacing.x; - if( !parent.text.empty() ) { cataimgui::draw_colored_text( parent.text ); ImGui::Separator(); @@ -81,47 +78,58 @@ void uilist_impl::draw_controls() ImGui::TableNextRow(); ImGui::TableSetColumnIndex( 1 ); + float entry_height = ImGui::GetTextLineHeightWithSpacing(); if( ImGui::BeginChild( "scroll", parent.calculated_menu_size, false ) ) { - // It would be natural to make the entries into buttons, or - // combos, or other pre-built ui elements. For now I am mostly - // going to copy the style of the original textual ui elements. - for( size_t i = 0; i < parent.fentries.size(); i++ ) { - auto entry = parent.entries[parent.fentries[i]]; - ImGui::PushID( i ); - ImGuiSelectableFlags_ flags = !entry.enabled ? ImGuiSelectableFlags_Disabled : - ImGuiSelectableFlags_None; - bool is_selected = static_cast( i ) == parent.fselected; - if( ImGui::Selectable( "", is_selected, flags | ImGuiSelectableFlags_AllowItemOverlap ) || - ImGui::IsItemHovered() ) { - parent.fselected = i; - parent.selected = parent.fentries[parent.fselected]; - } - ImGui::SameLine( 0, 0 ); - if( is_selected ) { - ImGui::SetItemDefaultFocus(); - ImGui::SetScrollHereY(); - } + if( ImGui::BeginTable( "menu items", 3, ImGuiTableFlags_SizingFixedFit ) ) { + ImGui::TableSetupColumn( "hotkey", ImGuiTableColumnFlags_WidthFixed, + parent.calculated_hotkey_width ); + ImGui::TableSetupColumn( "primary", ImGuiTableColumnFlags_WidthFixed, + parent.calculated_label_width ); + ImGui::TableSetupColumn( "secondary", ImGuiTableColumnFlags_WidthFixed, + parent.calculated_secondary_width ); + + // It would be natural to make the entries into buttons, or + // combos, or other pre-built ui elements. For now I am mostly + // going to copy the style of the original textual ui elements. + for( size_t i = 0; i < parent.fentries.size(); i++ ) { + auto entry = parent.entries[parent.fentries[i]]; + ImGui::TableNextRow( ImGuiTableRowFlags_None, entry_height ); + ImGui::TableSetColumnIndex( 0 ); + + ImVec2 rowMin = ImGui::GetCursorScreenPos(); + ImVec2 rowMax = ImVec2( rowMin.x + parent.calculated_menu_size.x, rowMin.y + entry_height ); + bool is_hovered = ImGui::IsMouseHoveringRect( rowMin, rowMax, false ); + if( is_hovered ) { + ImGui::TableSetBgColor( ImGuiTableBgTarget_RowBg1, + ImColor( ImGui::GetStyle().Colors[ ImGuiCol_HeaderHovered ] ) ); + parent.fselected = i; + } + bool is_selected = static_cast( i ) == parent.fselected; + if( is_selected ) { + ImGui::SetItemDefaultFocus(); + ImGui::SetScrollHereY(); + ImGui::TableSetBgColor( ImGuiTableBgTarget_RowBg1, + ImColor( ImGui::GetStyle().Colors[ ImGuiCol_HeaderActive ] ) ); + } - if( entry.hotkey.has_value() ) { - ImGui::Text( "%c", '[' ); - ImGui::SameLine( 0, 0 ); - nc_color color = is_selected ? parent.hilight_color : parent.hotkey_color; - cataimgui::draw_colored_text( entry.hotkey.value().short_description(), - color ); - ImGui::SameLine( 0, 0 ); - ImGui::Text( "%c", ']' ); - ImGui::SameLine(); - } else { - ImGui::SetCursorPosX( hotkey_width ); + if( entry.hotkey.has_value() ) { + nc_color color = is_selected ? parent.hilight_color : parent.hotkey_color; + cataimgui::draw_colored_text( entry.hotkey.value().short_description(), + color ); + } + ImGui::TableSetColumnIndex( 1 ); + nc_color color = ( is_selected ? + parent.hilight_color : + ( entry.enabled || entry.force_color ? + entry.text_color : + parent.disabled_color ) ); + cataimgui::draw_colored_text( entry.txt, color ); + ImGui::TableSetColumnIndex( 2 ); + if( !entry.ctxt.empty() ) { + cataimgui::draw_colored_text( entry.ctxt, color ); + } } - nc_color color = ( is_selected ? - parent.hilight_color : - ( entry.enabled || entry.force_color ? - entry.text_color : - parent.disabled_color ) ); - cataimgui::draw_colored_text( entry.txt, - color ); - ImGui::PopID(); + ImGui::EndTable(); } } ImGui::EndChild(); @@ -137,7 +145,7 @@ void uilist_impl::draw_controls() std::string description; if( !parent.footer_text.empty() ) { description = parent.footer_text; - } else if( parent.selected >= -1 ) { + } else if( parent.selected >= 0 ) { description = parent.entries[parent.selected].desc; } cataimgui::draw_colored_text( description ); @@ -357,6 +365,9 @@ void uilist::init() desired_bounds = std::nullopt; calculated_bounds = { -1.f, -1.f, -1.f, -1.f }; calculated_menu_size = { 0.0, 0.0 }; + calculated_hotkey_width = 0.0; + calculated_label_width = 0.0; + calculated_secondary_width = 0.0; extra_space_left = 0.0; extra_space_right = 0.0; ret = UILIST_WAIT_INPUT; @@ -556,6 +567,8 @@ static ImVec2 calc_size( const std::string_view line ) void uilist::calc_data() { + ImGuiStyle s = ImGui::GetStyle(); + std::vector autoassign; for( size_t i = 0; i < entries.size(); i++ ) { if( entries[ i ].enabled ) { @@ -594,13 +607,13 @@ void uilist::calc_data() bool has_titlebar = !title.empty(); if( has_titlebar ) { title_size = calc_size( title ); - title_size.y += ImGui::GetStyle().FramePadding.y * 2.0; + title_size.y += s.FramePadding.y * 2.0; } ImVec2 text_size = {}; if( !text.empty() ) { text_size = calc_size( text ); - text_size.y += ImGui::GetStyle().ItemSpacing.y * 2.0; + text_size.y += s.ItemSpacing.y * 2.0; } ImVec2 desc_size = {}; @@ -614,10 +627,10 @@ void uilist::calc_data() if( desc_size.y <= 0.0 ) { desc_enabled = false; } - desc_size.y += ImGui::GetStyle().ItemSpacing.y * 2.0; + desc_size.y += s.ItemSpacing.y * 2.0; } float additional_height = title_size.y + text_size.y + desc_size.y + 2.0 * - ( ImGui::GetStyle().FramePadding.y + ImGui::GetStyle().WindowBorderSize ); + ( s.FramePadding.y + s.WindowBorderSize ); if( vmax * ImGui::GetTextLineHeightWithSpacing() + additional_height > ImGui::GetMainViewport()->Size.y ) { @@ -625,26 +638,40 @@ void uilist::calc_data() ImGui::GetTextLineHeightWithSpacing() ); } - calculated_menu_size = { 0.0, 0.0 }; + float padding = 2.0f * s.CellPadding.x; + calculated_hotkey_width = ImGui::CalcTextSize( "X" ).x; + calculated_label_width = 0.0; + calculated_secondary_width = 0.0; for( int fentry : fentries ) { - calculated_menu_size.x = std::max( calculated_menu_size.x, calc_size( entries[fentry].txt ).x ); + calculated_label_width = std::max( calculated_label_width, calc_size( entries[fentry].txt ).x ); + calculated_secondary_width = std::max( calculated_secondary_width, + calc_size( entries[fentry].ctxt ).x ); } - calculated_menu_size.x += ImGui::CalcTextSize( " [X] " ).x; + calculated_menu_size = { 0.0, 0.0 }; + calculated_menu_size.x += calculated_hotkey_width + padding; + calculated_menu_size.x += calculated_label_width + padding; + calculated_menu_size.x += calculated_secondary_width + padding; calculated_menu_size.y = std::min( ImGui::GetMainViewport()->Size.y - additional_height, - vmax * ImGui::GetTextLineHeightWithSpacing() ) + ( ImGui::GetStyle().FramePadding.y * 2.0 ); + vmax * ImGui::GetTextLineHeightWithSpacing() ) + ( s.FramePadding.y * 2.0 ); extra_space_left = 0.0; extra_space_right = 0.0; if( callback != nullptr ) { - extra_space_left = callback->desired_extra_space_left( ) + ImGui::GetStyle().FramePadding.x; - extra_space_right = callback->desired_extra_space_right( ) + ImGui::GetStyle().FramePadding.x; + extra_space_left = callback->desired_extra_space_left( ) + s.FramePadding.x; + extra_space_right = callback->desired_extra_space_right( ) + s.FramePadding.x; } float longest_line_width = std::max( std::max( title_size.x, text_size.x ), std::max( calculated_menu_size.x, desc_size.x ) ); calculated_bounds.w = extra_space_left + extra_space_right + longest_line_width - + 2 * ( ImGui::GetStyle().WindowPadding.x + ImGui::GetStyle().WindowBorderSize ); + + 2 * ( s.WindowPadding.x + s.WindowBorderSize ); calculated_bounds.h = calculated_menu_size.y + additional_height; + + if( longest_line_width > calculated_menu_size.x ) { + calculated_menu_size.x = longest_line_width; + calculated_label_width = calculated_menu_size.x - calculated_hotkey_width - padding - + calculated_secondary_width - padding - padding; + } } void uilist::setup() diff --git a/src/ui.h b/src/ui.h index 07898fef06302..fc408f7806561 100644 --- a/src/ui.h +++ b/src/ui.h @@ -460,6 +460,9 @@ class uilist // NOLINT(cata-xy) private: ImVec2 calculated_menu_size; cataimgui::bounds calculated_bounds; + float calculated_hotkey_width; + float calculated_label_width; + float calculated_secondary_width; float extra_space_left; float extra_space_right; std::vector fentries; diff --git a/tests/reloading_test.cpp b/tests/reloading_test.cpp index 259ae769dc1cf..8b45cb77db047 100644 --- a/tests/reloading_test.cpp +++ b/tests/reloading_test.cpp @@ -4,6 +4,7 @@ #include #include +#include "activity_actor_definitions.h" #include "avatar.h" #include "calendar.h" #include "cata_catch.h" @@ -798,9 +799,9 @@ TEST_CASE( "reload_gun_with_integral_magazine_using_speedloader", "[reload],[gun // Make sure the player doesn't drop anything :P dummy.wear_item( item( "backpack", calendar::turn_zero ) ); - item_location ammo = dummy.i_add( item( "38_special", calendar::turn_zero, - item::default_charges_tag{} ) ); item_location speedloader = dummy.i_add( item( "38_speedloader", calendar::turn_zero, false ) ); + item_location ammo = dummy.i_add( item( "38_special", calendar::turn_zero, + speedloader->remaining_ammo_capacity() ) ); item_location gun = dummy.i_add( item( "sw_619", calendar::turn_zero, false ) ); REQUIRE( dummy.has_item( *ammo ) ); @@ -815,9 +816,19 @@ TEST_CASE( "reload_gun_with_integral_magazine_using_speedloader", "[reload],[gun REQUIRE( speedloader_success ); REQUIRE( speedloader->remaining_ammo_capacity() == 0 ); - bool success = gun->reload( dummy, speedloader, speedloader->ammo_remaining() ); + // This automatically selects the speedloader as ammo + // as long as dummy has nothing else available. + // If there are multiple options, it will crash from opening a ui. + item::reload_option opt = dummy.select_ammo( gun ); - REQUIRE( success ); + REQUIRE( opt ); + + dummy.assign_activity( reload_activity_actor( std::move( opt ) ) ); + if( !!dummy.activity ) { + process_activity( dummy ); + } + + //REQUIRE( success ); REQUIRE( gun->remaining_ammo_capacity() == 0 ); // Speedloader is still in inventory. REQUIRE( dummy.has_item( *speedloader ) );