diff --git a/.clang-tidy b/.clang-tidy index be36a697880fb..56deba94276df 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -26,7 +26,6 @@ readability-*,\ -bugprone-undefined-memory-manipulation,\ -bugprone-unused-return-value,\ -bugprone-use-after-move,\ --cert-dcl16-c,\ -cert-dcl21-cpp,\ -cert-dcl50-cpp,\ -cert-dcl58-cpp,\ @@ -85,11 +84,12 @@ readability-*,\ -readability-redundant-string-init,\ -readability-simplify-boolean-expr,\ -readability-static-accessed-through-instance,\ --readability-string-compare,\ --readability-uppercase-literal-suffix,\ " WarningsAsErrors: '*' HeaderFilterRegex: '(src|test).*' FormatStyle: none +CheckOptions: + - key: readability-uppercase-literal-suffix.NewSuffixes + value: 'L;UL;LL;ULL' # vim:tw=0 diff --git a/data/json/bionics.json b/data/json/bionics.json index 76d7d698d314c..21978d4ebfe5d 100644 --- a/data/json/bionics.json +++ b/data/json/bionics.json @@ -106,16 +106,6 @@ "fake_item": "bio_blade_weapon", "flags": [ "BIONIC_TOGGLED", "BIONIC_WEAPON", "BIONIC_NPC_USABLE" ] }, - { - "id": "bio_blaster", - "type": "bionic", - "name": "Fusion Blaster Arm", - "description": "Your left arm has been surgically replaced by a heavy-duty fusion blaster! You may use your energy banks to fire a damaging heat ray. However, you are unable to use or carry two-handed items, and your strength limits what you can use with your one hand.", - "occupied_bodyparts": [ [ "ARM_L", 20 ], [ "HAND_L", 5 ] ], - "act_cost": 50, - "fake_item": "bio_blaster_gun", - "flags": [ "BIONIC_GUN" ] - }, { "id": "bio_shotgun", "type": "bionic", diff --git a/data/json/field_type.json b/data/json/field_type.json index 91f14e1f1622c..62a1b95d0052b 100644 --- a/data/json/field_type.json +++ b/data/json/field_type.json @@ -10,6 +10,7 @@ "type": "field_type", "legacy_enum_id": 1, "intensity_levels": [ { "name": "blood splatter", "color": "red" }, { "name": "blood stain" }, { "name": "puddle of blood" } ], + "underwater_age_speedup": "25 minutes", "half_life": "2 days", "phase": "liquid", "accelerated_decay": true, @@ -20,6 +21,7 @@ "type": "field_type", "legacy_enum_id": 2, "intensity_levels": [ { "name": "bile splatter", "color": "pink" }, { "name": "bile stain" }, { "name": "puddle of bile" } ], + "underwater_age_speedup": "25 minutes", "half_life": "1 days", "phase": "liquid", "accelerated_decay": true, @@ -34,6 +36,7 @@ { "name": "bloody meat chunks", "color": "light_red" }, { "name": "heap of gore", "color": "red" } ], + "underwater_age_speedup": "25 minutes", "half_life": "2 days", "phase": "solid", "accelerated_decay": true, @@ -48,6 +51,7 @@ { "name": "shattered branches and leaves" }, { "name": "broken vegetation tangle", "color": "green" } ], + "underwater_age_speedup": "25 minutes", "half_life": "2 days", "phase": "solid", "accelerated_decay": true, @@ -86,6 +90,7 @@ { "name": "acid streak" }, { "name": "pool of acid", "color": "green" } ], + "underwater_age_speedup": "2 minutes", "priority": 2, "half_life": "2 minutes", "phase": "liquid", @@ -349,6 +354,7 @@ { "name": "plant sap stain" }, { "name": "puddle of resin" } ], + "underwater_age_speedup": "25 minutes", "half_life": "2 days", "phase": "liquid", "accelerated_decay": true, @@ -359,6 +365,7 @@ "type": "field_type", "legacy_enum_id": 29, "intensity_levels": [ { "name": "bug blood splatter", "color": "green" }, { "name": "bug blood stain" }, { "name": "puddle of bug blood" } ], + "underwater_age_speedup": "25 minutes", "half_life": "2 days", "phase": "liquid", "accelerated_decay": true, @@ -373,6 +380,7 @@ { "name": "hemolymph stain" }, { "name": "puddle of hemolymph" } ], + "underwater_age_speedup": "25 minutes", "half_life": "2 days", "phase": "liquid", "accelerated_decay": true, @@ -387,6 +395,7 @@ { "name": "shattered bug leg", "color": "green" }, { "name": "torn insect organs", "color": "yellow" } ], + "underwater_age_speedup": "25 minutes", "half_life": "2 days", "phase": "solid", "accelerated_decay": true, @@ -401,6 +410,7 @@ { "name": "icky mess" }, { "name": "heap of squishy gore", "color": "dark_gray" } ], + "underwater_age_speedup": "25 minutes", "half_life": "2 days", "phase": "solid", "accelerated_decay": true, diff --git a/data/json/furniture.json b/data/json/furniture.json index 5762b8081af0b..0e64bfe564fdd 100644 --- a/data/json/furniture.json +++ b/data/json/furniture.json @@ -1,49 +1,4 @@ [ - { - "type": "furniture", - "id": "f_hay", - "name": "hay", - "description": "A bale of hay. You could sleep on it, if desperate.", - "symbol": "#", - "bgcolor": "brown", - "move_cost_mod": 3, - "comfort": 2, - "floor_bedding_warmth": 100, - "required_str": 6, - "flags": [ "TRANSPARENT", "CONTAINER", "FLAMMABLE_ASH", "ORGANIC", "MOUNTABLE", "SHORT", "EASY_DECONSTRUCT" ], - "deconstruct": { "items": [ { "item": "straw_pile", "count": 40 }, { "item": "rope_makeshift_30", "count": 1 } ] }, - "bash": { - "str_min": 1, - "str_max": 12, - "sound": "whish!", - "sound_fail": "whish.", - "items": [ { "item": "straw_pile", "count": [ 6, 10 ] }, { "item": "rope_makeshift_6", "count": [ 1, 3 ] } ] - } - }, - { - "type": "furniture", - "id": "f_woodchips", - "name": "pile of woodchips", - "symbol": "X", - "description": "Pile of chipped wood pieces. You can move it with a shovel.", - "color": "brown", - "move_cost_mod": 6, - "max_volume": 3000, - "required_str": -1, - "flags": [ - "TRANSPARENT", - "UNSTABLE", - "ROUGH", - "PLACE_ITEM", - "MOUNTABLE", - "CONTAINER", - "SEALED", - "ALLOW_FIELD_EFFECT", - "TINY", - "RUBBLE" - ], - "examine_action": "rubble" - }, { "type": "furniture", "id": "f_rubble", @@ -153,133 +108,6 @@ "flags": [ "TRANSPARENT", "NOCOLLIDE", "CONTAINER", "SEALED", "PLACE_ITEM", "RUBBLE" ], "examine_action": "rubble" }, - { - "type": "furniture", - "id": "f_barricade_road", - "name": "road barricade", - "symbol": "#", - "bgcolor": "yellow", - "description": "A road barricade. For barricading roads.", - "move_cost_mod": -1, - "coverage": 30, - "required_str": 5, - "flags": [ "CLIMB_SIMPLE", "TRANSPARENT", "FLAMMABLE_ASH", "ORGANIC", "MOUNTABLE", "THIN_OBSTACLE", "CLIMBABLE", "PERMEABLE" ], - "examine_action": "chainfence", - "deconstruct": { "items": [ { "item": "2x4", "count": 6 }, { "item": "nail", "charges": [ 6, 8 ] } ] }, - "bash": { - "str_min": 3, - "str_max": 40, - "sound": "smash!", - "sound_fail": "whump.", - "items": [ { "item": "2x4", "count": [ 2, 6 ] }, { "item": "nail", "charges": [ 4, 8 ] }, { "item": "splinter", "count": 1 } ] - } - }, - { - "type": "furniture", - "id": "f_sandbag_half", - "name": "sandbag barricade", - "symbol": "#", - "bgcolor": "brown", - "description": "A sandbag barricade, typically used for blocking bullets.", - "move_cost_mod": -1, - "coverage": 60, - "required_str": -1, - "flags": [ - "CLIMB_SIMPLE", - "TRANSPARENT", - "MOUNTABLE", - "BLOCKSDOOR", - "SHORT", - "EASY_DECONSTRUCT", - "THIN_OBSTACLE", - "CLIMBABLE", - "PERMEABLE" - ], - "examine_action": "chainfence", - "deconstruct": { "items": [ { "item": "sandbag", "count": 16 } ] }, - "bash": { - "str_min": 12, - "str_max": 60, - "sound": "rrrip!", - "sound_fail": "whump.", - "items": [ { "item": "bag_canvas", "count": [ 10, 16 ] }, { "item": "material_sand", "charges": [ 800, 960 ] } ] - } - }, - { - "type": "furniture", - "id": "f_sandbag_wall", - "name": "sandbag wall", - "symbol": "#", - "bgcolor": "brown", - "move_cost_mod": -1, - "coverage": 95, - "description": "A sandbag wall.", - "required_str": -1, - "flags": [ "NOITEM", "BLOCKSDOOR", "EASY_DECONSTRUCT", "MINEABLE", "BLOCK_WIND" ], - "deconstruct": { "items": [ { "item": "sandbag", "count": 20 } ], "furn_set": "f_sandbag_half" }, - "bash": { - "str_min": 24, - "str_max": 80, - "sound": "rrrip!", - "sound_fail": "whump.", - "furn_set": "f_sandbag_half", - "items": [ { "item": "bag_canvas", "count": [ 15, 20 ] }, { "item": "material_sand", "charges": [ 1000, 1200 ] } ] - } - }, - { - "type": "furniture", - "id": "f_earthbag_half", - "name": "earthbag barricade", - "symbol": "#", - "looks_like": "f_sandbag_half", - "bgcolor": "brown", - "description": "An earthbag barricade, typically used for blocking bullets.", - "move_cost_mod": -1, - "coverage": 60, - "required_str": -1, - "flags": [ - "CLIMB_SIMPLE", - "TRANSPARENT", - "MOUNTABLE", - "BLOCKSDOOR", - "SHORT", - "EASY_DECONSTRUCT", - "THIN_OBSTACLE", - "CLIMBABLE", - "PERMEABLE" - ], - "examine_action": "chainfence", - "deconstruct": { "items": [ { "item": "sandbag", "count": 16 } ] }, - "bash": { - "str_min": 12, - "str_max": 60, - "sound": "rrrip!", - "sound_fail": "whump.", - "items": [ { "item": "bag_canvas", "count": [ 10, 16 ] }, { "item": "material_sand", "charges": [ 40, 48 ] } ] - } - }, - { - "type": "furniture", - "id": "f_earthbag_wall", - "name": "earthbag wall", - "symbol": "#", - "looks_like": "f_sandbag_wall", - "bgcolor": "brown", - "move_cost_mod": -1, - "coverage": 95, - "description": "An earthbag wall.", - "required_str": -1, - "flags": [ "NOITEM", "BLOCKSDOOR", "EASY_DECONSTRUCT", "MINEABLE", "BLOCK_WIND" ], - "deconstruct": { "items": [ { "item": "earthbag", "count": 20 } ], "furn_set": "f_earthbag_half" }, - "bash": { - "str_min": 24, - "str_max": 80, - "sound": "rrrip!", - "sound_fail": "whump.", - "furn_set": "f_earthbag_half", - "items": [ { "item": "bag_canvas", "count": [ 15, 20 ] }, { "item": "material_sand", "charges": [ 50, 60 ] } ] - } - }, { "type": "furniture", "id": "f_bulletin", @@ -377,161 +205,6 @@ ] } }, - { - "type": "furniture", - "id": "f_bed", - "name": "bed", - "symbol": "#", - "description": "This is a bed. A luxury in these times. Quite comfortable to sleep in.", - "color": "magenta", - "move_cost_mod": 3, - "coverage": 40, - "comfort": 5, - "floor_bedding_warmth": 1000, - "required_str": -1, - "deconstruct": { "items": [ { "item": "mattress", "count": 1 } ], "furn_set": "f_bed_frame" }, - "max_volume": 4000, - "flags": [ "TRANSPARENT", "FLAMMABLE_ASH", "PLACE_ITEM", "ORGANIC", "MOUNTABLE", "CAN_SIT", "EASY_DECONSTRUCT" ], - "bash": { - "str_min": 12, - "str_max": 40, - "sound": "crunch!", - "sound_fail": "whump.", - "items": [ - { "item": "2x4", "count": [ 5, 8 ] }, - { "item": "nail", "charges": [ 6, 8 ] }, - { "item": "splinter", "count": [ 3, 6 ] }, - { "item": "rag", "count": [ 40, 55 ] }, - { "item": "scrap", "count": [ 10, 20 ] } - ] - } - }, - { - "type": "furniture", - "id": "f_bed_frame", - "name": "bed frame", - "symbol": "#", - "description": "This is an empty bed frame. With a mattress on it, it would be a nice place to sleep. Sleeping on it right now wouldn't be great.", - "color": "brown", - "move_cost_mod": 4, - "coverage": 40, - "required_str": 5, - "deconstruct": { "items": [ { "item": "2x4", "count": 12 }, { "item": "nail", "charges": [ 8, 10 ] } ] }, - "max_volume": 4000, - "flags": [ "TRANSPARENT", "FLAMMABLE_ASH", "PLACE_ITEM", "ORGANIC", "MOUNTABLE", "CAN_SIT" ], - "bash": { - "str_min": 10, - "str_max": 40, - "sound": "crunch!", - "sound_fail": "whack.", - "items": [ - { "item": "2x4", "count": [ 5, 8 ] }, - { "item": "nail", "charges": [ 6, 8 ] }, - { "item": "splinter", "count": [ 3, 6 ] } - ] - } - }, - { - "type": "furniture", - "id": "f_floor_mattress", - "name": "mattress", - "description": "A comfortable mattress has been tossed on the floor for sleeping here. It's not quite as comfy as a real bed, but it's pretty close.", - "symbol": "0", - "color": "magenta", - "move_cost_mod": 3, - "coverage": 40, - "comfort": 4, - "floor_bedding_warmth": 800, - "required_str": 7, - "deployed_item": "mattress", - "examine_action": "deployed_furniture", - "flags": [ "TRANSPARENT", "SHORT", "FLAMMABLE_ASH", "PLACE_ITEM", "ORGANIC", "MOUNTABLE" ], - "deconstruct": { "items": [ { "item": "mattress", "count": 1 } ] }, - "bash": { - "str_min": 8, - "str_max": 30, - "sound": "rrrrip!", - "sound_fail": "whump.", - "items": [ { "item": "rag", "count": [ 40, 55 ] } ] - } - }, - { - "type": "furniture", - "id": "f_toilet", - "name": "toilet", - "symbol": "&", - "color": "white", - "description": "A porcelain throne. Emergency water source, from the tank, and provider of relief.", - "move_cost_mod": 2, - "coverage": 30, - "required_str": -1, - "flags": [ "TRANSPARENT", "FLAMMABLE_HARD", "MOUNTABLE", "LIQUIDCONT" ], - "examine_action": "toilet", - "bash": { - "str_min": 8, - "str_max": 30, - "sound": "porcelain breaking!", - "sound_fail": "whunk!", - "items": [ { "item": "cu_pipe", "prob": 50 }, { "item": "ceramic_shard", "count": [ 2, 8 ] } ] - } - }, - { - "type": "furniture", - "id": "f_makeshift_bed", - "name": "makeshift bed", - "symbol": "#", - "description": "Not as comfortable as a real bed, but it will suffice.", - "color": "magenta", - "move_cost_mod": 3, - "coverage": 40, - "comfort": 4, - "floor_bedding_warmth": 500, - "required_str": 10, - "deconstruct": { - "items": [ { "item": "2x4", "count": 4 }, { "item": "rag", "count": [ 30, 35 ] }, { "item": "nail", "charges": [ 4, 6 ] } ] - }, - "max_volume": 4000, - "flags": [ "TRANSPARENT", "FLAMMABLE_ASH", "ORGANIC", "MOUNTABLE", "SHORT" ], - "bash": { - "str_min": 8, - "str_max": 30, - "sound": "crunch!", - "sound_fail": "whump.", - "items": [ - { "item": "2x4", "count": [ 1, 3 ] }, - { "item": "nail", "charges": [ 2, 6 ] }, - { "item": "splinter", "count": [ 1, 4 ] }, - { "item": "rag", "count": [ 20, 30 ] } - ] - } - }, - { - "type": "furniture", - "id": "f_straw_bed", - "name": "straw bed", - "symbol": "#", - "description": "Kinda itches when you lay on it.", - "color": "magenta", - "move_cost_mod": 3, - "coverage": 35, - "comfort": 2, - "floor_bedding_warmth": 200, - "required_str": -1, - "deconstruct": { "items": [ { "item": "stick", "count": 4 }, { "item": "straw_pile", "count": [ 7, 8 ] } ] }, - "max_volume": 4000, - "flags": [ "TRANSPARENT", "FLAMMABLE_ASH", "ORGANIC", "MOUNTABLE", "SHORT", "EASY_DECONSTRUCT" ], - "bash": { - "str_min": 6, - "str_max": 20, - "sound": "crunch!", - "sound_fail": "whump.", - "items": [ - { "item": "stick", "count": [ 2, 3 ] }, - { "item": "straw_pile", "count": [ 7, 8 ] }, - { "item": "splinter", "count": [ 1, 2 ] } - ] - } - }, { "type": "furniture", "id": "f_leaves_pile", @@ -555,70 +228,6 @@ "items": [ { "item": "withered", "count": [ 45, 50 ] } ] } }, - { - "type": "furniture", - "id": "f_sink", - "name": "sink", - "symbol": "&", - "description": "Emergency relief provider. Water isn't running, so it's basically useless.", - "color": "white", - "move_cost_mod": 2, - "coverage": 60, - "required_str": -1, - "flags": [ "TRANSPARENT", "FLAMMABLE_HARD", "CONTAINER", "PLACE_ITEM", "MOUNTABLE" ], - "bash": { - "str_min": 8, - "str_max": 30, - "sound": "porcelain breaking!", - "sound_fail": "whunk!", - "items": [ - { "item": "cu_pipe", "prob": 50 }, - { "item": "water_faucet", "prob": 50 }, - { "item": "ceramic_shard", "count": [ 2, 8 ] } - ] - } - }, - { - "type": "furniture", - "id": "f_oven", - "name": "oven", - "symbol": "#", - "description": "Used for heating and cooking food with electricity. Doesn't look like it's working, although it still has parts. It might be safe to light a fire inside of it, if you had to.", - "color": "dark_gray", - "move_cost_mod": 2, - "coverage": 60, - "required_str": 10, - "insulation": 6, - "flags": [ "PLACE_ITEM", "TRANSPARENT", "FIRE_CONTAINER", "CONTAINER", "BLOCKSDOOR", "MOUNTABLE" ], - "examine_action": "fireplace", - "deconstruct": { - "items": [ - { "item": "sheet_metal", "count": [ 2, 6 ] }, - { "item": "sheet_metal_small", "count": [ 0, 4 ] }, - { "item": "steel_chunk", "count": [ 2, 3 ] }, - { "item": "scrap", "count": [ 2, 6 ] }, - { "item": "element", "count": [ 1, 4 ] }, - { "item": "cable", "charges": [ 1, 3 ] }, - { "item": "pilot_light", "count": 1 } - ] - }, - "max_volume": 160, - "bash": { - "str_min": 8, - "str_max": 30, - "sound": "metal screeching!", - "sound_fail": "clang!", - "items": [ - { "item": "sheet_metal", "count": [ 1, 4 ] }, - { "item": "sheet_metal_small", "count": [ 8, 12 ] }, - { "item": "steel_chunk", "count": [ 0, 3 ] }, - { "item": "scrap", "count": [ 0, 6 ] }, - { "item": "element", "count": [ 1, 3 ] }, - { "item": "cable", "charges": [ 1, 3 ] }, - { "item": "pilot_light", "count": 1 } - ] - } - }, { "type": "furniture", "id": "f_woodstove", @@ -661,58 +270,6 @@ "items": [ { "item": "rock", "count": [ 15, 30 ] } ] } }, - { - "type": "furniture", - "id": "f_shower", - "name": "shower", - "symbol": "~", - "description": "You would be able to clean yourself if water was running.", - "color": "white", - "move_cost_mod": 0, - "coverage": 35, - "required_str": -1, - "flags": [ "TRANSPARENT", "FLAMMABLE_HARD", "CONTAINER", "PLACE_ITEM", "BLOCKSDOOR" ], - "bash": { - "str_min": 6, - "str_max": 30, - "sound": "porcelain breaking!", - "sound_fail": "whunk!", - "sound_vol": 16, - "sound_fail_vol": 12, - "items": [ - { "item": "cu_pipe", "count": [ 0, 2 ] }, - { "item": "scrap_copper", "count": [ 0, 2 ] }, - { "item": "ceramic_shard", "count": [ 2, 6 ] }, - { "item": "glass_shard", "count": [ 1, 2 ] } - ] - } - }, - { - "type": "furniture", - "id": "f_bathtub", - "name": "bathtub", - "symbol": "~", - "description": "You could lay in and take a soothing bath, if there were running water. The plug is intact, so you could use it to store liquids.", - "color": "white", - "move_cost_mod": 2, - "coverage": 30, - "required_str": -1, - "flags": [ "TRANSPARENT", "FLAMMABLE_HARD", "CONTAINER", "PLACE_ITEM", "BLOCKSDOOR", "MOUNTABLE" ], - "max_volume": 800, - "examine_action": "keg", - "keg_capacity": 600, - "bash": { - "str_min": 12, - "str_max": 50, - "sound": "porcelain breaking!", - "sound_fail": "whunk!", - "items": [ - { "item": "cu_pipe", "prob": 50 }, - { "item": "water_faucet", "prob": 50 }, - { "item": "ceramic_shard", "count": [ 6, 18 ] } - ] - } - }, { "type": "furniture", "id": "f_stool", @@ -992,24 +549,6 @@ "items": [ { "item": "2x4", "count": [ 1, 3 ] }, { "item": "nail", "charges": [ 2, 6 ] }, { "item": "splinter", "count": 1 } ] } }, - { - "type": "furniture", - "id": "f_lane", - "name": "lane guard", - "description": "Used to be used for keeping traffic.", - "symbol": "#", - "color": "brown", - "move_cost_mod": 1, - "required_str": -1, - "flags": [ "TRANSPARENT", "FLAMMABLE_ASH", "ORGANIC", "MOUNTABLE", "SHORT" ], - "bash": { - "str_min": 6, - "str_max": 30, - "sound": "smash!", - "sound_fail": "whump.", - "items": [ { "item": "2x4", "count": [ 1, 3 ] }, { "item": "nail", "charges": [ 2, 6 ] }, { "item": "splinter", "count": 1 } ] - } - }, { "type": "furniture", "id": "f_table", @@ -1235,98 +774,6 @@ "items": [ { "item": "2x4", "count": [ 2, 6 ] }, { "item": "nail", "charges": [ 4, 8 ] }, { "item": "splinter", "count": 1 } ] } }, - { - "type": "furniture", - "id": "f_fridge", - "name": "refrigerator", - "symbol": "{", - "description": "Freeze your food with the amazing science of electricity! Oh wait, none is flowing. Well, as long as you don't open it, maybe it'll stay cool for awhile.", - "color": "light_cyan", - "move_cost_mod": -1, - "coverage": 90, - "required_str": 10, - "insulation": 3, - "flags": [ "CONTAINER", "PLACE_ITEM", "BLOCKSDOOR", "MINEABLE" ], - "deconstruct": { - "items": [ - { "item": "sheet_metal", "count": [ 2, 6 ] }, - { "item": "sheet_metal_small", "count": [ 0, 4 ] }, - { "item": "steel_chunk", "count": [ 2, 3 ] }, - { "item": "scrap", "count": [ 2, 8 ] }, - { "item": "cable", "charges": [ 1, 3 ] }, - { "item": "hose", "count": 1 }, - { "item": "condensor_coil", "count": 1 }, - { "item": "evaporator_coil", "count": 1 }, - { "item": "refrigerant_tank", "count": 1 }, - { "item": "thermostat", "count": 1 }, - { "item": "motor_tiny", "count": 1 } - ] - }, - "max_volume": 800, - "bash": { - "str_min": 18, - "str_max": 50, - "sound": "metal screeching!", - "sound_fail": "clang!", - "items": [ - { "item": "sheet_metal", "count": [ 1, 4 ] }, - { "item": "sheet_metal_small", "count": [ 8, 12 ] }, - { "item": "steel_chunk", "count": [ 0, 3 ] }, - { "item": "scrap", "count": [ 2, 8 ] }, - { "item": "cable", "charges": [ 1, 2 ] }, - { "item": "hose", "count": 1 }, - { "item": "cu_pipe", "count": [ 2, 4 ] }, - { "item": "scrap_copper", "count": [ 1, 2 ] }, - { "item": "motor_tiny", "prob": 25 } - ] - } - }, - { - "type": "furniture", - "id": "f_glass_fridge", - "name": "glass door fridge", - "symbol": "{", - "color": "light_cyan", - "description": "Wow! See INTO your fridge before you open it and discover it's not working!", - "move_cost_mod": -1, - "coverage": 90, - "required_str": 10, - "insulation": 2, - "flags": [ "PLACE_ITEM", "BLOCKSDOOR" ], - "deconstruct": { - "items": [ - { "item": "sheet_metal", "count": [ 2, 5 ] }, - { "item": "sheet_metal_small", "count": [ 0, 3 ] }, - { "item": "steel_chunk", "count": [ 2, 3 ] }, - { "item": "scrap", "count": [ 2, 6 ] }, - { "item": "cable", "charges": [ 1, 3 ] }, - { "item": "hose", "count": 1 }, - { "item": "glass_sheet", "count": 1 }, - { "item": "cu_pipe", "count": [ 3, 6 ] }, - { "item": "refrigerant_tank", "count": 1 }, - { "item": "motor_tiny", "count": 1 } - ] - }, - "max_volume": 1000, - "bash": { - "str_min": 12, - "str_max": 50, - "sound": "metal screeching!", - "sound_fail": "clang!", - "items": [ - { "item": "sheet_metal", "count": [ 1, 3 ] }, - { "item": "sheet_metal_small", "count": [ 6, 9 ] }, - { "item": "steel_chunk", "count": [ 0, 3 ] }, - { "item": "scrap", "count": [ 2, 8 ] }, - { "item": "cable", "charges": [ 1, 3 ] }, - { "item": "hose", "count": 1 }, - { "item": "cu_pipe", "count": [ 1, 4 ] }, - { "item": "scrap_copper", "count": [ 0, 2 ] }, - { "item": "glass_shard", "count": [ 3, 6 ] }, - { "item": "motor_tiny", "prob": 25 } - ] - } - }, { "type": "furniture", "id": "f_dresser", @@ -1420,107 +867,26 @@ "id": "f_bookcase", "name": "bookcase", "symbol": "{", - "description": "Stores books. Y'know, those things. Who reads books anymore?", - "color": "brown", - "move_cost_mod": -1, - "coverage": 80, - "required_str": 9, - "flags": [ "FLAMMABLE", "PLACE_ITEM", "ORGANIC", "BLOCKSDOOR" ], - "deconstruct": { - "items": [ - { "item": "2x4", "count": 12 }, - { "item": "wood_panel", "count": [ 0, 1 ] }, - { "item": "nail", "charges": [ 12, 16 ] } - ] - }, - "max_volume": 8000, - "bash": { - "str_min": 6, - "str_max": 40, - "sound": "smash!", - "sound_fail": "whump.", - "items": [ { "item": "2x4", "count": [ 2, 6 ] }, { "item": "nail", "charges": [ 4, 12 ] }, { "item": "splinter", "count": 1 } ] - } - }, - { - "type": "furniture", - "id": "f_washer", - "name": "washing machine", - "description": "You could wash your dirty clothes if electricity was running.", - "symbol": "{", - "bgcolor": "white", - "move_cost_mod": -1, - "coverage": 60, - "required_str": 12, - "max_volume": 120, - "flags": [ "CONTAINER", "PLACE_ITEM", "BLOCKSDOOR", "FLAT_SURF", "MINEABLE" ], - "deconstruct": { - "items": [ - { "item": "pipe", "count": 1 }, - { "item": "scrap", "count": [ 2, 6 ] }, - { "item": "steel_chunk", "count": [ 1, 3 ] }, - { "item": "sheet_metal_small", "count": [ 0, 4 ] }, - { "item": "sheet_metal", "count": [ 2, 6 ] }, - { "item": "cable", "charges": [ 1, 15 ] }, - { "item": "hose", "count": [ 1, 2 ] }, - { "item": "motor_small", "count": 1 }, - { "item": "cu_pipe", "count": [ 2, 5 ] } - ] - }, - "bash": { - "str_min": 18, - "str_max": 50, - "sound": "metal screeching!", - "sound_fail": "clang!", - "items": [ - { "item": "scrap", "count": [ 2, 7 ] }, - { "item": "steel_chunk", "count": [ 0, 3 ] }, - { "item": "sheet_metal_small", "count": [ 8, 12 ] }, - { "item": "sheet_metal", "count": [ 1, 4 ] }, - { "item": "cable", "charges": [ 1, 15 ] }, - { "item": "hose", "count": [ 0, 2 ] }, - { "item": "cu_pipe", "count": [ 1, 4 ] }, - { "item": "scrap_copper", "count": [ 0, 2 ] } - ] - } - }, - { - "type": "furniture", - "id": "f_dryer", - "name": "dryer", - "description": "'Dry your clothes!' would be what you'd do if electricity was running.", - "symbol": "{", - "bgcolor": "white", + "description": "Stores books. Y'know, those things. Who reads books anymore?", + "color": "brown", "move_cost_mod": -1, - "coverage": 60, - "required_str": 12, - "max_volume": 150, - "flags": [ "CONTAINER", "PLACE_ITEM", "BLOCKSDOOR", "FLAT_SURF", "MINEABLE" ], + "coverage": 80, + "required_str": 9, + "flags": [ "FLAMMABLE", "PLACE_ITEM", "ORGANIC", "BLOCKSDOOR" ], "deconstruct": { "items": [ - { "item": "scrap", "count": [ 2, 6 ] }, - { "item": "steel_chunk", "count": [ 1, 3 ] }, - { "item": "sheet_metal_small", "count": [ 0, 4 ] }, - { "item": "sheet_metal", "count": [ 2, 6 ] }, - { "item": "element", "count": [ 2, 3 ] }, - { "item": "cable", "charges": [ 1, 15 ] }, - { "item": "motor_small", "charges": 1 }, - { "item": "cu_pipe", "count": [ 1, 3 ] } + { "item": "2x4", "count": 12 }, + { "item": "wood_panel", "count": [ 0, 1 ] }, + { "item": "nail", "charges": [ 12, 16 ] } ] }, + "max_volume": 8000, "bash": { - "str_min": 18, - "str_max": 50, - "sound": "metal screeching!", - "sound_fail": "clang!", - "items": [ - { "item": "scrap", "count": [ 0, 6 ] }, - { "item": "steel_chunk", "count": [ 0, 3 ] }, - { "item": "sheet_metal_small", "count": [ 8, 12 ] }, - { "item": "sheet_metal", "count": [ 1, 4 ] }, - { "item": "element", "count": [ 1, 3 ] }, - { "item": "cable", "charges": [ 1, 15 ] } - ] + "str_min": 6, + "str_max": 40, + "sound": "smash!", + "sound_fail": "whump.", + "items": [ { "item": "2x4", "count": [ 2, 6 ] }, { "item": "nail", "charges": [ 4, 12 ] }, { "item": "splinter", "count": 1 } ] } }, { @@ -4769,137 +4135,6 @@ ] } }, - { - "type": "furniture", - "id": "f_home_furnace", - "name": "furnace", - "looks_like": "t_sewage_pipe", - "description": "A gas-powered forced-air central heating unit, with an internal fan to push the air through a building's air ducts and keep it warm.", - "symbol": "0", - "bgcolor": "white", - "move_cost_mod": -1, - "coverage": 60, - "required_str": -1, - "max_volume": 4000, - "flags": [ "CONTAINER", "PLACE_ITEM", "FIRE_CONTAINER", "SUPPRESS_SMOKE", "BLOCKSDOOR" ], - "examine_action": "fireplace", - "deconstruct": { - "items": [ - { "item": "pipe", "count": 1 }, - { "item": "scrap", "count": [ 2, 6 ] }, - { "item": "steel_chunk", "count": [ 1, 3 ] }, - { "item": "sheet_metal", "count": [ 2, 6 ] }, - { "item": "cable", "charges": [ 1, 15 ] }, - { "item": "cu_pipe", "count": [ 2, 5 ] }, - { "item": "thermostat", "count": 1 }, - { "item": "motor_small", "count": 1 } - ] - }, - "bash": { - "str_min": 18, - "str_max": 50, - "sound": "metal screeching!", - "sound_fail": "clang!", - "items": [ - { "item": "scrap", "count": [ 2, 7 ] }, - { "item": "steel_chunk", "count": [ 0, 3 ] }, - { "item": "sheet_metal", "count": [ 2, 6 ] }, - { "item": "cable", "charges": [ 1, 15 ] }, - { "item": "hose", "count": [ 0, 2 ] }, - { "item": "cu_pipe", "count": [ 1, 4 ] }, - { "item": "scrap_copper", "count": [ 0, 2 ] } - ] - } - }, - { - "type": "furniture", - "id": "f_air_conditioner", - "name": "cooling unit", - "looks_like": "t_machinery_light", - "description": "A big, blocky metal device for refrigerating large areas.", - "symbol": "{", - "bgcolor": "white", - "move_cost_mod": -1, - "coverage": 55, - "required_str": -1, - "max_volume": 4000, - "deconstruct": { - "items": [ - { "item": "pipe", "count": 1 }, - { "item": "scrap", "count": [ 2, 6 ] }, - { "item": "steel_chunk", "count": [ 1, 3 ] }, - { "item": "sheet_metal", "count": [ 2, 6 ] }, - { "item": "cable", "charges": [ 1, 15 ] }, - { "item": "cu_pipe", "count": [ 2, 5 ] }, - { "item": "hose", "count": [ 0, 2 ] }, - { "item": "thermostat", "count": 1 }, - { "item": "refrigerant_tank", "count": 1 }, - { "item": "motor_small", "count": 1 } - ] - }, - "bash": { - "str_min": 18, - "str_max": 50, - "sound": "metal screeching!", - "sound_fail": "clang!", - "items": [ - { "item": "scrap", "count": [ 2, 7 ] }, - { "item": "steel_chunk", "count": [ 0, 3 ] }, - { "item": "sheet_metal", "count": [ 2, 6 ] }, - { "item": "cable", "charges": [ 1, 15 ] }, - { "item": "hose", "count": [ 0, 2 ] }, - { "item": "cu_pipe", "count": [ 1, 4 ] }, - { "item": "scrap_copper", "count": [ 0, 2 ] } - ] - } - }, - { - "type": "furniture", - "id": "f_water_heater", - "name": "water heater", - "looks_like": "f_standing_tank", - "description": "An insulated metal tank that holds water, kept to a temperature by a small gas flame.", - "symbol": "0", - "bgcolor": "white", - "move_cost_mod": -1, - "coverage": 55, - "required_str": -1, - "max_volume": 4000, - "flags": [ "CONTAINER", "PLACE_ITEM", "LIQUIDCONT", "NOITEM", "SEALED" ], - "examine_action": "keg", - "keg_capacity": 240, - "deconstruct": { - "items": [ - { "item": "pipe", "count": 1 }, - { "item": "scrap", "count": [ 2, 6 ] }, - { "item": "steel_chunk", "count": [ 1, 3 ] }, - { "item": "sheet_metal", "count": [ 2, 6 ] }, - { "item": "cable", "charges": [ 1, 15 ] }, - { "item": "cu_pipe", "count": [ 2, 5 ] }, - { "item": "hose", "count": [ 0, 2 ] }, - { "item": "pilot_light", "count": 1 }, - { "item": "thermostat", "count": 1 }, - { "item": "water_faucet", "count": 1 }, - { "item": "metal_tank", "count": 1 } - ] - }, - "bash": { - "str_min": 18, - "str_max": 50, - "sound": "metal screeching!", - "sound_fail": "clang!", - "items": [ - { "item": "scrap", "count": [ 2, 7 ] }, - { "item": "steel_chunk", "count": [ 0, 3 ] }, - { "item": "sheet_metal", "count": [ 2, 6 ] }, - { "item": "cable", "charges": [ 1, 15 ] }, - { "item": "hose", "count": [ 0, 2 ] }, - { "item": "cu_pipe", "count": [ 1, 4 ] }, - { "item": "scrap_copper", "count": [ 0, 2 ] }, - { "item": "water_faucet", "count": [ 0, 1 ] } - ] - } - }, { "type": "furniture", "id": "f_arc_furnace", @@ -6292,88 +5527,6 @@ ] } }, - { - "type": "furniture", - "id": "f_air_filter", - "name": "central air filter", - "description": "Cleans out dust mites, smoke particles, and more!", - "symbol": "#", - "bgcolor": "white", - "move_cost_mod": -1, - "coverage": 50, - "required_str": -1, - "looks_like": "f_air_conditioner", - "deconstruct": { - "items": [ - { "item": "scrap", "count": [ 2, 6 ] }, - { "item": "steel_chunk", "count": [ 1, 3 ] }, - { "item": "sheet_metal_small", "count": [ 0, 4 ] }, - { "item": "sheet_metal", "count": [ 2, 4 ] }, - { "item": "cable", "charges": [ 1, 15 ] }, - { "item": "hose", "count": [ 3, 6 ] }, - { "item": "motor_small", "count": 1 }, - { "item": "solder_wire", "charges": [ 1, 15 ] } - ] - }, - "bash": { - "str_min": 12, - "str_max": 50, - "sound": "metal screeching!", - "sound_fail": "clang!", - "items": [ - { "item": "scrap", "count": [ 2, 7 ] }, - { "item": "steel_chunk", "count": [ 0, 3 ] }, - { "item": "sheet_metal_small", "count": [ 8, 12 ] }, - { "item": "sheet_metal", "count": [ 1, 2 ] }, - { "item": "cable", "charges": [ 1, 15 ] }, - { "item": "hose", "count": [ 0, 1 ] }, - { "item": "e_scrap", "count": [ 5, 10 ] }, - { "item": "plastic_chunk", "count": [ 0, 2 ] } - ] - } - }, - { - "type": "furniture", - "id": "f_water_purifier", - "looks_like": "f_water_heater", - "name": "water purifier", - "description": "This removes ions dissolved in the water, making it pretty clean, if you care about that kind of thing.", - "symbol": "W", - "bgcolor": "blue", - "move_cost_mod": -1, - "coverage": 50, - "required_str": -1, - "deconstruct": { - "items": [ - { "item": "scrap", "count": [ 2, 6 ] }, - { "item": "steel_chunk", "count": [ 1, 3 ] }, - { "item": "sheet_metal_small", "count": [ 0, 4 ] }, - { "item": "sheet_metal", "count": [ 2, 4 ] }, - { "item": "cable", "charges": [ 1, 15 ] }, - { "item": "hose", "count": [ 3, 6 ] }, - { "item": "motor_small", "count": 1 }, - { "item": "solder_wire", "charges": [ 1, 15 ] }, - { "item": "cu_pipe", "count": [ 2, 5 ] } - ] - }, - "bash": { - "str_min": 15, - "str_max": 50, - "sound": "metal screeching!", - "sound_fail": "clang!", - "items": [ - { "item": "scrap", "count": [ 2, 7 ] }, - { "item": "steel_chunk", "count": [ 0, 3 ] }, - { "item": "sheet_metal_small", "count": [ 8, 12 ] }, - { "item": "sheet_metal", "count": [ 1, 2 ] }, - { "item": "cable", "charges": [ 1, 15 ] }, - { "item": "hose", "count": [ 0, 1 ] }, - { "item": "e_scrap", "count": [ 5, 10 ] }, - { "item": "plastic_chunk", "count": [ 0, 2 ] }, - { "item": "cu_pipe", "count": [ 1, 3 ] } - ] - } - }, { "type": "furniture", "id": "f_server", @@ -6494,51 +5647,6 @@ ] } }, - { - "type": "furniture", - "id": "f_dishwasher", - "name": "dishwasher", - "looks_like": "f_oven", - "description": "This metal box used to spray hot water and soap at dirty dishes to make them clean and to save people an unpleasant chore. Now, with the power gone and it sitting for a while, it's starting to smell a bit off.", - "symbol": "{", - "bgcolor": "white", - "move_cost_mod": -1, - "coverage": 60, - "required_str": 13, - "max_volume": 800, - "flags": [ "CONTAINER", "PLACE_ITEM", "BLOCKSDOOR", "FLAT_SURF", "MINEABLE" ], - "deconstruct": { - "items": [ - { "item": "pipe", "count": 1 }, - { "item": "scrap", "count": [ 2, 6 ] }, - { "item": "steel_chunk", "count": [ 1, 3 ] }, - { "item": "sheet_metal_small", "count": [ 0, 4 ] }, - { "item": "sheet_metal", "count": [ 2, 4 ] }, - { "item": "blanket", "count": 1 }, - { "item": "cable", "charges": [ 1, 15 ] }, - { "item": "hose", "count": [ 1, 2 ] }, - { "item": "motor_small", "count": 1 }, - { "item": "cu_pipe", "count": [ 2, 5 ] } - ] - }, - "bash": { - "str_min": 18, - "str_max": 50, - "sound": "metal screeching!", - "sound_fail": "clang!", - "items": [ - { "item": "scrap", "count": [ 2, 7 ] }, - { "item": "steel_chunk", "count": [ 0, 3 ] }, - { "item": "sheet_metal_small", "count": [ 8, 12 ] }, - { "item": "sheet_metal", "count": [ 1, 2 ] }, - { "item": "rag", "count": [ 5, 10 ] }, - { "item": "cable", "charges": [ 1, 15 ] }, - { "item": "hose", "count": [ 0, 1 ] }, - { "item": "cu_pipe", "count": [ 1, 4 ] }, - { "item": "scrap_copper", "count": [ 0, 2 ] } - ] - } - }, { "type": "furniture", "id": "f_workbench", diff --git a/data/json/furniture_and_terrain/furniture-appliances.json b/data/json/furniture_and_terrain/furniture-appliances.json new file mode 100644 index 0000000000000..e03424739eb7b --- /dev/null +++ b/data/json/furniture_and_terrain/furniture-appliances.json @@ -0,0 +1,385 @@ +[ + { + "type": "furniture", + "id": "f_air_conditioner", + "name": "cooling unit", + "looks_like": "t_machinery_light", + "description": "A big, blocky metal device for refrigerating large areas.", + "symbol": "{", + "bgcolor": "white", + "move_cost_mod": -1, + "coverage": 55, + "required_str": -1, + "max_volume": 4000, + "deconstruct": { + "items": [ + { "item": "pipe", "count": 1 }, + { "item": "scrap", "count": [ 2, 6 ] }, + { "item": "steel_chunk", "count": [ 1, 3 ] }, + { "item": "sheet_metal", "count": [ 2, 6 ] }, + { "item": "cable", "charges": [ 1, 15 ] }, + { "item": "cu_pipe", "count": [ 2, 5 ] }, + { "item": "hose", "count": [ 0, 2 ] }, + { "item": "thermostat", "count": 1 }, + { "item": "refrigerant_tank", "count": 1 }, + { "item": "motor_small", "count": 1 } + ] + }, + "bash": { + "str_min": 18, + "str_max": 50, + "sound": "metal screeching!", + "sound_fail": "clang!", + "items": [ + { "item": "scrap", "count": [ 2, 7 ] }, + { "item": "steel_chunk", "count": [ 0, 3 ] }, + { "item": "sheet_metal", "count": [ 2, 6 ] }, + { "item": "cable", "charges": [ 1, 15 ] }, + { "item": "hose", "count": [ 0, 2 ] }, + { "item": "cu_pipe", "count": [ 1, 4 ] }, + { "item": "scrap_copper", "count": [ 0, 2 ] } + ] + } + }, + { + "type": "furniture", + "id": "f_air_filter", + "name": "central air filter", + "description": "Cleans out dust mites, smoke particles, and more!", + "symbol": "#", + "bgcolor": "white", + "move_cost_mod": -1, + "coverage": 50, + "required_str": -1, + "looks_like": "f_air_conditioner", + "deconstruct": { + "items": [ + { "item": "scrap", "count": [ 2, 6 ] }, + { "item": "steel_chunk", "count": [ 1, 3 ] }, + { "item": "sheet_metal_small", "count": [ 0, 4 ] }, + { "item": "sheet_metal", "count": [ 2, 4 ] }, + { "item": "cable", "charges": [ 1, 15 ] }, + { "item": "hose", "count": [ 3, 6 ] }, + { "item": "motor_small", "count": 1 }, + { "item": "solder_wire", "charges": [ 1, 15 ] } + ] + }, + "bash": { + "str_min": 12, + "str_max": 50, + "sound": "metal screeching!", + "sound_fail": "clang!", + "items": [ + { "item": "scrap", "count": [ 2, 7 ] }, + { "item": "steel_chunk", "count": [ 0, 3 ] }, + { "item": "sheet_metal_small", "count": [ 8, 12 ] }, + { "item": "sheet_metal", "count": [ 1, 2 ] }, + { "item": "cable", "charges": [ 1, 15 ] }, + { "item": "hose", "count": [ 0, 1 ] }, + { "item": "e_scrap", "count": [ 5, 10 ] }, + { "item": "plastic_chunk", "count": [ 0, 2 ] } + ] + } + }, + { + "type": "furniture", + "id": "f_dishwasher", + "name": "dishwasher", + "looks_like": "f_oven", + "description": "This metal box used to spray hot water and soap at dirty dishes to make them clean and to save people an unpleasant chore. Now, with the power gone and it sitting for a while, it's starting to smell a bit off.", + "symbol": "{", + "bgcolor": "white", + "move_cost_mod": -1, + "coverage": 60, + "required_str": 13, + "max_volume": 800, + "flags": [ "CONTAINER", "PLACE_ITEM", "BLOCKSDOOR", "FLAT_SURF", "MINEABLE" ], + "deconstruct": { + "items": [ + { "item": "pipe", "count": 1 }, + { "item": "scrap", "count": [ 2, 6 ] }, + { "item": "steel_chunk", "count": [ 1, 3 ] }, + { "item": "sheet_metal_small", "count": [ 0, 4 ] }, + { "item": "sheet_metal", "count": [ 2, 4 ] }, + { "item": "blanket", "count": 1 }, + { "item": "cable", "charges": [ 1, 15 ] }, + { "item": "hose", "count": [ 1, 2 ] }, + { "item": "motor_small", "count": 1 }, + { "item": "cu_pipe", "count": [ 2, 5 ] } + ] + }, + "bash": { + "str_min": 18, + "str_max": 50, + "sound": "metal screeching!", + "sound_fail": "clang!", + "items": [ + { "item": "scrap", "count": [ 2, 7 ] }, + { "item": "steel_chunk", "count": [ 0, 3 ] }, + { "item": "sheet_metal_small", "count": [ 8, 12 ] }, + { "item": "sheet_metal", "count": [ 1, 2 ] }, + { "item": "rag", "count": [ 5, 10 ] }, + { "item": "cable", "charges": [ 1, 15 ] }, + { "item": "hose", "count": [ 0, 1 ] }, + { "item": "cu_pipe", "count": [ 1, 4 ] }, + { "item": "scrap_copper", "count": [ 0, 2 ] } + ] + } + }, + { + "type": "furniture", + "id": "f_dryer", + "name": "dryer", + "description": "'Dry your clothes!' would be what you'd do if electricity was running.", + "symbol": "{", + "bgcolor": "white", + "move_cost_mod": -1, + "coverage": 60, + "required_str": 12, + "max_volume": 150, + "flags": [ "CONTAINER", "PLACE_ITEM", "BLOCKSDOOR", "FLAT_SURF", "MINEABLE" ], + "deconstruct": { + "items": [ + { "item": "scrap", "count": [ 2, 6 ] }, + { "item": "steel_chunk", "count": [ 1, 3 ] }, + { "item": "sheet_metal_small", "count": [ 0, 4 ] }, + { "item": "sheet_metal", "count": [ 2, 6 ] }, + { "item": "element", "count": [ 2, 3 ] }, + { "item": "cable", "charges": [ 1, 15 ] }, + { "item": "motor_small", "charges": 1 }, + { "item": "cu_pipe", "count": [ 1, 3 ] } + ] + }, + "bash": { + "str_min": 18, + "str_max": 50, + "sound": "metal screeching!", + "sound_fail": "clang!", + "items": [ + { "item": "scrap", "count": [ 0, 6 ] }, + { "item": "steel_chunk", "count": [ 0, 3 ] }, + { "item": "sheet_metal_small", "count": [ 8, 12 ] }, + { "item": "sheet_metal", "count": [ 1, 4 ] }, + { "item": "element", "count": [ 1, 3 ] }, + { "item": "cable", "charges": [ 1, 15 ] } + ] + } + }, + { + "type": "furniture", + "id": "f_fridge", + "name": "refrigerator", + "symbol": "{", + "description": "Freeze your food with the amazing science of electricity! Oh wait, none is flowing. Well, as long as you don't open it, maybe it'll stay cool for awhile.", + "color": "light_cyan", + "move_cost_mod": -1, + "coverage": 90, + "required_str": 10, + "insulation": 3, + "flags": [ "CONTAINER", "PLACE_ITEM", "BLOCKSDOOR", "MINEABLE" ], + "deconstruct": { + "items": [ + { "item": "sheet_metal", "count": [ 2, 6 ] }, + { "item": "sheet_metal_small", "count": [ 0, 4 ] }, + { "item": "steel_chunk", "count": [ 2, 3 ] }, + { "item": "scrap", "count": [ 2, 8 ] }, + { "item": "cable", "charges": [ 1, 3 ] }, + { "item": "hose", "count": 1 }, + { "item": "condensor_coil", "count": 1 }, + { "item": "evaporator_coil", "count": 1 }, + { "item": "refrigerant_tank", "count": 1 }, + { "item": "thermostat", "count": 1 }, + { "item": "motor_tiny", "count": 1 } + ] + }, + "max_volume": 800, + "bash": { + "str_min": 18, + "str_max": 50, + "sound": "metal screeching!", + "sound_fail": "clang!", + "items": [ + { "item": "sheet_metal", "count": [ 1, 4 ] }, + { "item": "sheet_metal_small", "count": [ 8, 12 ] }, + { "item": "steel_chunk", "count": [ 0, 3 ] }, + { "item": "scrap", "count": [ 2, 8 ] }, + { "item": "cable", "charges": [ 1, 2 ] }, + { "item": "hose", "count": 1 }, + { "item": "cu_pipe", "count": [ 2, 4 ] }, + { "item": "scrap_copper", "count": [ 1, 2 ] }, + { "item": "motor_tiny", "prob": 25 } + ] + } + }, + { + "type": "furniture", + "id": "f_glass_fridge", + "name": "glass door fridge", + "symbol": "{", + "color": "light_cyan", + "description": "Wow! See INTO your fridge before you open it and discover it's not working!", + "move_cost_mod": -1, + "coverage": 90, + "required_str": 10, + "insulation": 2, + "flags": [ "PLACE_ITEM", "BLOCKSDOOR" ], + "deconstruct": { + "items": [ + { "item": "sheet_metal", "count": [ 2, 5 ] }, + { "item": "sheet_metal_small", "count": [ 0, 3 ] }, + { "item": "steel_chunk", "count": [ 2, 3 ] }, + { "item": "scrap", "count": [ 2, 6 ] }, + { "item": "cable", "charges": [ 1, 3 ] }, + { "item": "hose", "count": 1 }, + { "item": "glass_sheet", "count": 1 }, + { "item": "cu_pipe", "count": [ 3, 6 ] }, + { "item": "refrigerant_tank", "count": 1 }, + { "item": "motor_tiny", "count": 1 } + ] + }, + "max_volume": 1000, + "bash": { + "str_min": 12, + "str_max": 50, + "sound": "metal screeching!", + "sound_fail": "clang!", + "items": [ + { "item": "sheet_metal", "count": [ 1, 3 ] }, + { "item": "sheet_metal_small", "count": [ 6, 9 ] }, + { "item": "steel_chunk", "count": [ 0, 3 ] }, + { "item": "scrap", "count": [ 2, 8 ] }, + { "item": "cable", "charges": [ 1, 3 ] }, + { "item": "hose", "count": 1 }, + { "item": "cu_pipe", "count": [ 1, 4 ] }, + { "item": "scrap_copper", "count": [ 0, 2 ] }, + { "item": "glass_shard", "count": [ 3, 6 ] }, + { "item": "motor_tiny", "prob": 25 } + ] + } + }, + { + "type": "furniture", + "id": "f_home_furnace", + "name": "furnace", + "looks_like": "t_sewage_pipe", + "description": "A gas-powered forced-air central heating unit, with an internal fan to push the air through a building's air ducts and keep it warm.", + "symbol": "0", + "bgcolor": "white", + "move_cost_mod": -1, + "coverage": 60, + "required_str": -1, + "max_volume": 4000, + "flags": [ "CONTAINER", "PLACE_ITEM", "FIRE_CONTAINER", "SUPPRESS_SMOKE", "BLOCKSDOOR" ], + "examine_action": "fireplace", + "deconstruct": { + "items": [ + { "item": "pipe", "count": 1 }, + { "item": "scrap", "count": [ 2, 6 ] }, + { "item": "steel_chunk", "count": [ 1, 3 ] }, + { "item": "sheet_metal", "count": [ 2, 6 ] }, + { "item": "cable", "charges": [ 1, 15 ] }, + { "item": "cu_pipe", "count": [ 2, 5 ] }, + { "item": "thermostat", "count": 1 }, + { "item": "motor_small", "count": 1 } + ] + }, + "bash": { + "str_min": 18, + "str_max": 50, + "sound": "metal screeching!", + "sound_fail": "clang!", + "items": [ + { "item": "scrap", "count": [ 2, 7 ] }, + { "item": "steel_chunk", "count": [ 0, 3 ] }, + { "item": "sheet_metal", "count": [ 2, 6 ] }, + { "item": "cable", "charges": [ 1, 15 ] }, + { "item": "hose", "count": [ 0, 2 ] }, + { "item": "cu_pipe", "count": [ 1, 4 ] }, + { "item": "scrap_copper", "count": [ 0, 2 ] } + ] + } + }, + { + "type": "furniture", + "id": "f_washer", + "name": "washing machine", + "description": "You could wash your dirty clothes if electricity was running.", + "symbol": "{", + "bgcolor": "white", + "move_cost_mod": -1, + "coverage": 60, + "required_str": 12, + "max_volume": 120, + "flags": [ "CONTAINER", "PLACE_ITEM", "BLOCKSDOOR", "FLAT_SURF", "MINEABLE" ], + "deconstruct": { + "items": [ + { "item": "pipe", "count": 1 }, + { "item": "scrap", "count": [ 2, 6 ] }, + { "item": "steel_chunk", "count": [ 1, 3 ] }, + { "item": "sheet_metal_small", "count": [ 0, 4 ] }, + { "item": "sheet_metal", "count": [ 2, 6 ] }, + { "item": "cable", "charges": [ 1, 15 ] }, + { "item": "hose", "count": [ 1, 2 ] }, + { "item": "motor_small", "count": 1 }, + { "item": "cu_pipe", "count": [ 2, 5 ] } + ] + }, + "bash": { + "str_min": 18, + "str_max": 50, + "sound": "metal screeching!", + "sound_fail": "clang!", + "items": [ + { "item": "scrap", "count": [ 2, 7 ] }, + { "item": "steel_chunk", "count": [ 0, 3 ] }, + { "item": "sheet_metal_small", "count": [ 8, 12 ] }, + { "item": "sheet_metal", "count": [ 1, 4 ] }, + { "item": "cable", "charges": [ 1, 15 ] }, + { "item": "hose", "count": [ 0, 2 ] }, + { "item": "cu_pipe", "count": [ 1, 4 ] }, + { "item": "scrap_copper", "count": [ 0, 2 ] } + ] + } + }, + { + "type": "furniture", + "id": "f_oven", + "name": "oven", + "symbol": "#", + "description": "Used for heating and cooking food with electricity. Doesn't look like it's working, although it still has parts. It might be safe to light a fire inside of it, if you had to.", + "color": "dark_gray", + "move_cost_mod": 2, + "coverage": 60, + "required_str": 10, + "insulation": 6, + "flags": [ "PLACE_ITEM", "TRANSPARENT", "FIRE_CONTAINER", "CONTAINER", "BLOCKSDOOR", "MOUNTABLE" ], + "examine_action": "fireplace", + "deconstruct": { + "items": [ + { "item": "sheet_metal", "count": [ 2, 6 ] }, + { "item": "sheet_metal_small", "count": [ 0, 4 ] }, + { "item": "steel_chunk", "count": [ 2, 3 ] }, + { "item": "scrap", "count": [ 2, 6 ] }, + { "item": "element", "count": [ 1, 4 ] }, + { "item": "cable", "charges": [ 1, 3 ] }, + { "item": "pilot_light", "count": 1 } + ] + }, + "max_volume": 160, + "bash": { + "str_min": 8, + "str_max": 30, + "sound": "metal screeching!", + "sound_fail": "clang!", + "items": [ + { "item": "sheet_metal", "count": [ 1, 4 ] }, + { "item": "sheet_metal_small", "count": [ 8, 12 ] }, + { "item": "steel_chunk", "count": [ 0, 3 ] }, + { "item": "scrap", "count": [ 0, 6 ] }, + { "item": "element", "count": [ 1, 3 ] }, + { "item": "cable", "charges": [ 1, 3 ] }, + { "item": "pilot_light", "count": 1 } + ] + } + } +] diff --git a/data/json/furniture_and_terrain/furniture-barriers.json b/data/json/furniture_and_terrain/furniture-barriers.json new file mode 100644 index 0000000000000..ee255fd786f8a --- /dev/null +++ b/data/json/furniture_and_terrain/furniture-barriers.json @@ -0,0 +1,147 @@ +[ + { + "type": "furniture", + "id": "f_barricade_road", + "name": "road barricade", + "symbol": "#", + "bgcolor": "yellow", + "description": "A road barricade. For barricading roads.", + "move_cost_mod": -1, + "coverage": 30, + "required_str": 5, + "flags": [ "CLIMB_SIMPLE", "TRANSPARENT", "FLAMMABLE_ASH", "ORGANIC", "MOUNTABLE", "THIN_OBSTACLE", "CLIMBABLE", "PERMEABLE" ], + "examine_action": "chainfence", + "deconstruct": { "items": [ { "item": "2x4", "count": 6 }, { "item": "nail", "charges": [ 6, 8 ] } ] }, + "bash": { + "str_min": 3, + "str_max": 40, + "sound": "smash!", + "sound_fail": "whump.", + "items": [ { "item": "2x4", "count": [ 2, 6 ] }, { "item": "nail", "charges": [ 4, 8 ] }, { "item": "splinter", "count": 1 } ] + } + }, + { + "type": "furniture", + "id": "f_earthbag_half", + "name": "earthbag barricade", + "symbol": "#", + "looks_like": "f_sandbag_half", + "bgcolor": "brown", + "description": "An earthbag barricade, typically used for blocking bullets.", + "move_cost_mod": -1, + "coverage": 60, + "required_str": -1, + "flags": [ + "CLIMB_SIMPLE", + "TRANSPARENT", + "MOUNTABLE", + "BLOCKSDOOR", + "SHORT", + "EASY_DECONSTRUCT", + "THIN_OBSTACLE", + "CLIMBABLE", + "PERMEABLE" + ], + "examine_action": "chainfence", + "deconstruct": { "items": [ { "item": "sandbag", "count": 16 } ] }, + "bash": { + "str_min": 12, + "str_max": 60, + "sound": "rrrip!", + "sound_fail": "whump.", + "items": [ { "item": "bag_canvas", "count": [ 10, 16 ] }, { "item": "material_sand", "charges": [ 40, 48 ] } ] + } + }, + { + "type": "furniture", + "id": "f_earthbag_wall", + "name": "earthbag wall", + "symbol": "#", + "looks_like": "f_sandbag_wall", + "bgcolor": "brown", + "move_cost_mod": -1, + "coverage": 95, + "description": "An earthbag wall.", + "required_str": -1, + "flags": [ "NOITEM", "BLOCKSDOOR", "EASY_DECONSTRUCT", "MINEABLE", "BLOCK_WIND" ], + "deconstruct": { "items": [ { "item": "earthbag", "count": 20 } ], "furn_set": "f_earthbag_half" }, + "bash": { + "str_min": 24, + "str_max": 80, + "sound": "rrrip!", + "sound_fail": "whump.", + "furn_set": "f_earthbag_half", + "items": [ { "item": "bag_canvas", "count": [ 15, 20 ] }, { "item": "material_sand", "charges": [ 50, 60 ] } ] + } + }, + { + "type": "furniture", + "id": "f_lane", + "name": "lane guard", + "description": "Used to be used for keeping traffic.", + "symbol": "#", + "color": "brown", + "move_cost_mod": 1, + "required_str": -1, + "flags": [ "TRANSPARENT", "FLAMMABLE_ASH", "ORGANIC", "MOUNTABLE", "SHORT" ], + "bash": { + "str_min": 6, + "str_max": 30, + "sound": "smash!", + "sound_fail": "whump.", + "items": [ { "item": "2x4", "count": [ 1, 3 ] }, { "item": "nail", "charges": [ 2, 6 ] }, { "item": "splinter", "count": 1 } ] + } + }, + { + "type": "furniture", + "id": "f_sandbag_half", + "name": "sandbag barricade", + "symbol": "#", + "bgcolor": "brown", + "description": "A sandbag barricade, typically used for blocking bullets.", + "move_cost_mod": -1, + "coverage": 60, + "required_str": -1, + "flags": [ + "CLIMB_SIMPLE", + "TRANSPARENT", + "MOUNTABLE", + "BLOCKSDOOR", + "SHORT", + "EASY_DECONSTRUCT", + "THIN_OBSTACLE", + "CLIMBABLE", + "PERMEABLE" + ], + "examine_action": "chainfence", + "deconstruct": { "items": [ { "item": "sandbag", "count": 16 } ] }, + "bash": { + "str_min": 12, + "str_max": 60, + "sound": "rrrip!", + "sound_fail": "whump.", + "items": [ { "item": "bag_canvas", "count": [ 10, 16 ] }, { "item": "material_sand", "charges": [ 800, 960 ] } ] + } + }, + { + "type": "furniture", + "id": "f_sandbag_wall", + "name": "sandbag wall", + "symbol": "#", + "bgcolor": "brown", + "move_cost_mod": -1, + "coverage": 95, + "description": "A sandbag wall.", + "required_str": -1, + "flags": [ "NOITEM", "BLOCKSDOOR", "EASY_DECONSTRUCT", "MINEABLE", "BLOCK_WIND" ], + "deconstruct": { "items": [ { "item": "sandbag", "count": 20 } ], "furn_set": "f_sandbag_half" }, + "bash": { + "str_min": 24, + "str_max": 80, + "sound": "rrrip!", + "sound_fail": "whump.", + "furn_set": "f_sandbag_half", + "items": [ { "item": "bag_canvas", "count": [ 15, 20 ] }, { "item": "material_sand", "charges": [ 1000, 1200 ] } ] + } + } +] diff --git a/data/json/furniture_and_terrain/furniture-plumbing.json b/data/json/furniture_and_terrain/furniture-plumbing.json new file mode 100644 index 0000000000000..c5d75460cb346 --- /dev/null +++ b/data/json/furniture_and_terrain/furniture-plumbing.json @@ -0,0 +1,186 @@ +[ + { + "type": "furniture", + "id": "f_bathtub", + "name": "bathtub", + "symbol": "~", + "description": "You could lay in and take a soothing bath, if there were running water. The plug is intact, so you could use it to store liquids.", + "color": "white", + "move_cost_mod": 2, + "coverage": 30, + "required_str": -1, + "flags": [ "TRANSPARENT", "FLAMMABLE_HARD", "CONTAINER", "PLACE_ITEM", "BLOCKSDOOR", "MOUNTABLE" ], + "max_volume": 800, + "examine_action": "keg", + "keg_capacity": 600, + "bash": { + "str_min": 12, + "str_max": 50, + "sound": "porcelain breaking!", + "sound_fail": "whunk!", + "items": [ + { "item": "cu_pipe", "prob": 50 }, + { "item": "water_faucet", "prob": 50 }, + { "item": "ceramic_shard", "count": [ 6, 18 ] } + ] + } + }, + { + "type": "furniture", + "id": "f_shower", + "name": "shower", + "symbol": "~", + "description": "You would be able to clean yourself if water was running.", + "color": "white", + "move_cost_mod": 0, + "coverage": 35, + "required_str": -1, + "flags": [ "TRANSPARENT", "FLAMMABLE_HARD", "CONTAINER", "PLACE_ITEM", "BLOCKSDOOR" ], + "bash": { + "str_min": 6, + "str_max": 30, + "sound": "porcelain breaking!", + "sound_fail": "whunk!", + "sound_vol": 16, + "sound_fail_vol": 12, + "items": [ + { "item": "cu_pipe", "count": [ 0, 2 ] }, + { "item": "scrap_copper", "count": [ 0, 2 ] }, + { "item": "ceramic_shard", "count": [ 2, 6 ] }, + { "item": "glass_shard", "count": [ 1, 2 ] } + ] + } + }, + { + "type": "furniture", + "id": "f_sink", + "name": "sink", + "symbol": "&", + "description": "Emergency relief provider. Water isn't running, so it's basically useless.", + "color": "white", + "move_cost_mod": 2, + "coverage": 60, + "required_str": -1, + "flags": [ "TRANSPARENT", "FLAMMABLE_HARD", "CONTAINER", "PLACE_ITEM", "MOUNTABLE" ], + "bash": { + "str_min": 8, + "str_max": 30, + "sound": "porcelain breaking!", + "sound_fail": "whunk!", + "items": [ + { "item": "cu_pipe", "prob": 50 }, + { "item": "water_faucet", "prob": 50 }, + { "item": "ceramic_shard", "count": [ 2, 8 ] } + ] + } + }, + { + "type": "furniture", + "id": "f_toilet", + "name": "toilet", + "symbol": "&", + "color": "white", + "description": "A porcelain throne. Emergency water source, from the tank, and provider of relief.", + "move_cost_mod": 2, + "coverage": 30, + "required_str": -1, + "flags": [ "TRANSPARENT", "FLAMMABLE_HARD", "MOUNTABLE", "LIQUIDCONT" ], + "examine_action": "toilet", + "bash": { + "str_min": 8, + "str_max": 30, + "sound": "porcelain breaking!", + "sound_fail": "whunk!", + "items": [ { "item": "cu_pipe", "prob": 50 }, { "item": "ceramic_shard", "count": [ 2, 8 ] } ] + } + }, + { + "type": "furniture", + "id": "f_water_heater", + "name": "water heater", + "looks_like": "f_standing_tank", + "description": "An insulated metal tank that holds water, kept to a temperature by a small gas flame.", + "symbol": "0", + "bgcolor": "white", + "move_cost_mod": -1, + "coverage": 55, + "required_str": -1, + "max_volume": 4000, + "flags": [ "CONTAINER", "PLACE_ITEM", "LIQUIDCONT", "NOITEM", "SEALED" ], + "examine_action": "keg", + "keg_capacity": 240, + "deconstruct": { + "items": [ + { "item": "pipe", "count": 1 }, + { "item": "scrap", "count": [ 2, 6 ] }, + { "item": "steel_chunk", "count": [ 1, 3 ] }, + { "item": "sheet_metal", "count": [ 2, 6 ] }, + { "item": "cable", "charges": [ 1, 15 ] }, + { "item": "cu_pipe", "count": [ 2, 5 ] }, + { "item": "hose", "count": [ 0, 2 ] }, + { "item": "pilot_light", "count": 1 }, + { "item": "thermostat", "count": 1 }, + { "item": "water_faucet", "count": 1 }, + { "item": "metal_tank", "count": 1 } + ] + }, + "bash": { + "str_min": 18, + "str_max": 50, + "sound": "metal screeching!", + "sound_fail": "clang!", + "items": [ + { "item": "scrap", "count": [ 2, 7 ] }, + { "item": "steel_chunk", "count": [ 0, 3 ] }, + { "item": "sheet_metal", "count": [ 2, 6 ] }, + { "item": "cable", "charges": [ 1, 15 ] }, + { "item": "hose", "count": [ 0, 2 ] }, + { "item": "cu_pipe", "count": [ 1, 4 ] }, + { "item": "scrap_copper", "count": [ 0, 2 ] }, + { "item": "water_faucet", "count": [ 0, 1 ] } + ] + } + }, + { + "type": "furniture", + "id": "f_water_purifier", + "looks_like": "f_water_heater", + "name": "water purifier", + "description": "This removes ions dissolved in the water, making it pretty clean, if you care about that kind of thing.", + "symbol": "W", + "bgcolor": "blue", + "move_cost_mod": -1, + "coverage": 50, + "required_str": -1, + "deconstruct": { + "items": [ + { "item": "scrap", "count": [ 2, 6 ] }, + { "item": "steel_chunk", "count": [ 1, 3 ] }, + { "item": "sheet_metal_small", "count": [ 0, 4 ] }, + { "item": "sheet_metal", "count": [ 2, 4 ] }, + { "item": "cable", "charges": [ 1, 15 ] }, + { "item": "hose", "count": [ 3, 6 ] }, + { "item": "motor_small", "count": 1 }, + { "item": "solder_wire", "charges": [ 1, 15 ] }, + { "item": "cu_pipe", "count": [ 2, 5 ] } + ] + }, + "bash": { + "str_min": 15, + "str_max": 50, + "sound": "metal screeching!", + "sound_fail": "clang!", + "items": [ + { "item": "scrap", "count": [ 2, 7 ] }, + { "item": "steel_chunk", "count": [ 0, 3 ] }, + { "item": "sheet_metal_small", "count": [ 8, 12 ] }, + { "item": "sheet_metal", "count": [ 1, 2 ] }, + { "item": "cable", "charges": [ 1, 15 ] }, + { "item": "hose", "count": [ 0, 1 ] }, + { "item": "e_scrap", "count": [ 5, 10 ] }, + { "item": "plastic_chunk", "count": [ 0, 2 ] }, + { "item": "cu_pipe", "count": [ 1, 3 ] } + ] + } + } +] diff --git a/data/json/furniture_and_terrain/furniture-rural.json b/data/json/furniture_and_terrain/furniture-rural.json new file mode 100644 index 0000000000000..db76bf183f21a --- /dev/null +++ b/data/json/furniture_and_terrain/furniture-rural.json @@ -0,0 +1,47 @@ +[ + { + "type": "furniture", + "id": "f_hay", + "name": "hay", + "description": "A bale of hay. You could sleep on it, if desperate.", + "symbol": "#", + "bgcolor": "brown", + "move_cost_mod": 3, + "comfort": 2, + "floor_bedding_warmth": 100, + "required_str": 6, + "flags": [ "TRANSPARENT", "CONTAINER", "FLAMMABLE_ASH", "ORGANIC", "MOUNTABLE", "SHORT", "EASY_DECONSTRUCT" ], + "deconstruct": { "items": [ { "item": "straw_pile", "count": 40 }, { "item": "rope_makeshift_30", "count": 1 } ] }, + "bash": { + "str_min": 1, + "str_max": 12, + "sound": "whish!", + "sound_fail": "whish.", + "items": [ { "item": "straw_pile", "count": [ 6, 10 ] }, { "item": "rope_makeshift_6", "count": [ 1, 3 ] } ] + } + }, + { + "type": "furniture", + "id": "f_woodchips", + "name": "pile of woodchips", + "symbol": "X", + "description": "Pile of chipped wood pieces. You can move it with a shovel.", + "color": "brown", + "move_cost_mod": 6, + "max_volume": 3000, + "required_str": -1, + "flags": [ + "TRANSPARENT", + "UNSTABLE", + "ROUGH", + "PLACE_ITEM", + "MOUNTABLE", + "CONTAINER", + "SEALED", + "ALLOW_FIELD_EFFECT", + "TINY", + "RUBBLE" + ], + "examine_action": "rubble" + } +] diff --git a/data/json/furniture_and_terrain/furniture-sleep.json b/data/json/furniture_and_terrain/furniture-sleep.json new file mode 100644 index 0000000000000..113436af9f6c0 --- /dev/null +++ b/data/json/furniture_and_terrain/furniture-sleep.json @@ -0,0 +1,137 @@ +[ + { + "type": "furniture", + "id": "f_bed", + "name": "bed", + "symbol": "#", + "description": "This is a bed. A luxury in these times. Quite comfortable to sleep in.", + "color": "magenta", + "move_cost_mod": 3, + "coverage": 40, + "comfort": 5, + "floor_bedding_warmth": 1000, + "required_str": -1, + "deconstruct": { "items": [ { "item": "mattress", "count": 1 } ], "furn_set": "f_bed_frame" }, + "max_volume": 4000, + "flags": [ "TRANSPARENT", "FLAMMABLE_ASH", "PLACE_ITEM", "ORGANIC", "MOUNTABLE", "CAN_SIT", "EASY_DECONSTRUCT" ], + "bash": { + "str_min": 12, + "str_max": 40, + "sound": "crunch!", + "sound_fail": "whump.", + "items": [ + { "item": "2x4", "count": [ 5, 8 ] }, + { "item": "nail", "charges": [ 6, 8 ] }, + { "item": "splinter", "count": [ 3, 6 ] }, + { "item": "rag", "count": [ 40, 55 ] }, + { "item": "scrap", "count": [ 10, 20 ] } + ] + } + }, + { + "type": "furniture", + "id": "f_bed_frame", + "name": "bed frame", + "symbol": "#", + "description": "This is an empty bed frame. With a mattress on it, it would be a nice place to sleep. Sleeping on it right now wouldn't be great.", + "color": "brown", + "move_cost_mod": 4, + "coverage": 40, + "required_str": 5, + "deconstruct": { "items": [ { "item": "2x4", "count": 12 }, { "item": "nail", "charges": [ 8, 10 ] } ] }, + "max_volume": 4000, + "flags": [ "TRANSPARENT", "FLAMMABLE_ASH", "PLACE_ITEM", "ORGANIC", "MOUNTABLE", "CAN_SIT" ], + "bash": { + "str_min": 10, + "str_max": 40, + "sound": "crunch!", + "sound_fail": "whack.", + "items": [ + { "item": "2x4", "count": [ 5, 8 ] }, + { "item": "nail", "charges": [ 6, 8 ] }, + { "item": "splinter", "count": [ 3, 6 ] } + ] + } + }, + { + "type": "furniture", + "id": "f_floor_mattress", + "name": "mattress", + "description": "A comfortable mattress has been tossed on the floor for sleeping here. It's not quite as comfy as a real bed, but it's pretty close.", + "symbol": "0", + "color": "magenta", + "move_cost_mod": 3, + "coverage": 40, + "comfort": 4, + "floor_bedding_warmth": 800, + "required_str": 7, + "deployed_item": "mattress", + "examine_action": "deployed_furniture", + "flags": [ "TRANSPARENT", "SHORT", "FLAMMABLE_ASH", "PLACE_ITEM", "ORGANIC", "MOUNTABLE" ], + "deconstruct": { "items": [ { "item": "mattress", "count": 1 } ] }, + "bash": { + "str_min": 8, + "str_max": 30, + "sound": "rrrrip!", + "sound_fail": "whump.", + "items": [ { "item": "rag", "count": [ 40, 55 ] } ] + } + }, + { + "type": "furniture", + "id": "f_makeshift_bed", + "name": "makeshift bed", + "symbol": "#", + "description": "Not as comfortable as a real bed, but it will suffice.", + "color": "magenta", + "move_cost_mod": 3, + "coverage": 40, + "comfort": 4, + "floor_bedding_warmth": 500, + "required_str": 10, + "deconstruct": { + "items": [ { "item": "2x4", "count": 4 }, { "item": "rag", "count": [ 30, 35 ] }, { "item": "nail", "charges": [ 4, 6 ] } ] + }, + "max_volume": 4000, + "flags": [ "TRANSPARENT", "FLAMMABLE_ASH", "ORGANIC", "MOUNTABLE", "SHORT" ], + "bash": { + "str_min": 8, + "str_max": 30, + "sound": "crunch!", + "sound_fail": "whump.", + "items": [ + { "item": "2x4", "count": [ 1, 3 ] }, + { "item": "nail", "charges": [ 2, 6 ] }, + { "item": "splinter", "count": [ 1, 4 ] }, + { "item": "rag", "count": [ 20, 30 ] } + ] + } + }, + { + "type": "furniture", + "id": "f_straw_bed", + "name": "straw bed", + "symbol": "#", + "description": "Kinda itches when you lay on it.", + "color": "magenta", + "move_cost_mod": 3, + "coverage": 35, + "comfort": 2, + "floor_bedding_warmth": 200, + "required_str": -1, + "deconstruct": { "items": [ { "item": "stick", "count": 4 }, { "item": "straw_pile", "count": [ 7, 8 ] } ] }, + "max_volume": 4000, + "flags": [ "TRANSPARENT", "FLAMMABLE_ASH", "ORGANIC", "MOUNTABLE", "SHORT", "EASY_DECONSTRUCT" ], + "bash": { + "str_min": 6, + "str_max": 20, + "sound": "crunch!", + "sound_fail": "whump.", + "items": [ + { "item": "stick", "count": [ 2, 3 ] }, + { "item": "straw_pile", "count": [ 7, 8 ] }, + { "item": "splinter", "count": [ 1, 2 ] } + ] + } + } +] diff --git a/data/json/item_groups.json b/data/json/item_groups.json index 648c1ea79f8f4..0f2ac9ad85573 100644 --- a/data/json/item_groups.json +++ b/data/json/item_groups.json @@ -3968,7 +3968,6 @@ [ "plut_cell", 10 ], [ "standard_template_construct", 5 ], [ "nanomaterial", 5 ], - [ "ftk93", 1 ], [ "canister_goo", 8 ], [ "UPS_off", 5 ], [ "adv_UPS_off", 3 ], @@ -5486,7 +5485,6 @@ [ "bio_time_freeze", 10 ], [ "bio_teleport", 10 ], [ "bio_probability_travel", 10 ], - [ "bio_blaster", 10 ], [ "bio_laser", 10 ], [ "bio_emp", 10 ], [ "bio_emp_armgun", 10 ], @@ -5879,7 +5877,6 @@ [ "bio_ads", 10 ], [ "bio_ods", 10 ], [ "bio_uncanny_dodge", 10 ], - [ "bio_blaster", 10 ], [ "bio_laser", 10 ], [ "bio_emp", 10 ], [ "bio_emp_armgun", 10 ], @@ -5947,7 +5944,6 @@ [ "bio_ads", 10 ], [ "bio_ods", 10 ], [ "bio_uncanny_dodge", 10 ], - [ "bio_blaster", 5 ], [ "bio_laser", 15 ], [ "bio_emp", 10 ], [ "bio_emp_armgun", 10 ], @@ -6558,7 +6554,6 @@ [ "bio_shotgun", 10 ], [ "arrow_cf", 5 ], [ "spray_can", 50 ], - [ "bio_blaster", 10 ], [ "tac_helmet", 5 ], [ "tac_fullhelmet", 2 ], [ "helmet_lobster", 2 ], @@ -6629,7 +6624,6 @@ [ "bio_metabolics", 10 ], [ "survivormap", 1 ], [ "miner_hat", 10 ], - [ "bio_blaster", 10 ], [ "honey_ant", 30 ], [ "tool_black_powder_charge", 1 ] ] @@ -6982,7 +6976,6 @@ [ "556_incendiary", 2 ], [ "762_51", 6 ], [ "762_51_incendiary", 6 ], - [ "laser_pack", 10 ], [ "40mm_concussive", 10 ], [ "40mm_frag", 8 ], [ "40mm_incendiary", 6 ], @@ -7009,7 +7002,6 @@ [ "m249", 1 ], [ "m240", 1 ], [ "m27iar", 1 ], - [ "ftk93", 1 ], [ "m320", 5 ], [ "m320_mod", 10 ], [ "ugl_buttstock", 10 ], @@ -7419,7 +7411,6 @@ [ "mininuke", 1 ], [ "crack", 8 ], [ "crackpipe", 37 ], - [ "bio_blaster", 10 ], [ "laser_sight", 15 ], [ "rail_laser_sight", 10 ], [ "lsd", 10 ], @@ -8733,7 +8724,6 @@ [ "bio_str_enhancer", 12 ], [ "bio_infrared", 25 ], [ "bio_targeting", 20 ], - [ "bio_blaster", 15 ], [ "bio_emp", 20 ], [ "bio_chain_lightning", 15 ], [ "bio_adrenaline", 20 ], diff --git a/data/json/itemgroups/guns.json b/data/json/itemgroups/guns.json index ebcbe636eaebf..344350e42a9ac 100644 --- a/data/json/itemgroups/guns.json +++ b/data/json/itemgroups/guns.json @@ -224,12 +224,7 @@ "type": "item_group", "id": "guns_energy", "//": "Assorted factory crafted energy weapons.", - "items": [ - { "item": "laser_rifle", "prob": 30 }, - { "item": "emp_gun", "prob": 10 }, - { "item": "v29", "prob": 70 }, - { "item": "ftk93", "prob": 10 } - ] + "items": [ { "item": "laser_rifle", "prob": 30 }, { "item": "emp_gun", "prob": 10 }, { "item": "v29", "prob": 70 } ] }, { "type": "item_group", diff --git a/data/json/itemgroups/main.json b/data/json/itemgroups/main.json index 6898e8a91933a..b81618ce96f39 100644 --- a/data/json/itemgroups/main.json +++ b/data/json/itemgroups/main.json @@ -22,7 +22,6 @@ [ "dump_pouch", 100 ], [ "electrohack", 30 ], [ "fetus", 10 ], - [ "ftk93", 10 ], [ "hazmat_suit", 50 ], [ "hk_g80", 20 ], [ "hk_g80mag", 35 ], @@ -34,7 +33,6 @@ [ "l-stick", 10 ], [ "large_repairkit", 20 ], [ "standard_template_construct", 15 ], - [ "laser_pack", 100 ], [ "laser_rifle", 10 ], [ "m240", 10 ], [ "m249", 10 ], @@ -86,7 +84,6 @@ [ "12mm", 10 ], [ "bot_laserturret", 10 ], [ "alloy_plate", 10 ], - [ "ftk93", 10 ], [ "solar_panel_v3", 10 ], [ "q_solarpack", 10 ], [ "can_sealer", 10 ], @@ -96,7 +93,6 @@ [ "hk_g80mag", 10 ], [ "emp_gun", 10 ], [ "knife_rm42", 10 ], - [ "laser_pack", 10 ], [ "laser_rifle", 10 ], [ "mininuke", 10 ], [ "adv_UPS_off", 10 ], @@ -149,8 +145,7 @@ [ "mutagen_alpha", 0 ], [ "mutagen_chimera", 0 ], [ "mutagen_elfa", 0 ], - [ "mutagen_medical", 0 ], - [ "unbio_blaster_gun", 0 ] + [ "mutagen_medical", 0 ] ] }, { diff --git a/data/json/items/ammo.json b/data/json/items/ammo.json index 6f796efa78c19..41816b20f7845 100644 --- a/data/json/items/ammo.json +++ b/data/json/items/ammo.json @@ -385,25 +385,6 @@ "stack_size": 100, "count": 20 }, - { - "type": "AMMO", - "id": "laser_pack", - "price": 80000, - "name": "fusion pack", - "symbol": "=", - "color": "light_green", - "description": "In the middle of the 21st Century, military powers began to look towards energy based weapons. The result was the standard fusion pack, capable of delivering bolts of superheated gas at near light speed with no recoil.", - "material": "plastic", - "volume": 1, - "weight": 68, - "ammo_type": "fusion", - "damage": 12, - "pierce": 15, - "range": 30, - "stack_size": 40, - "count": 20, - "effects": [ "NEVER_MISFIRES" ] - }, { "type": "AMMO", "id": "66mm_HEAT", diff --git a/data/json/items/ammo_types.json b/data/json/items/ammo_types.json index 5cbee34032180..9b8042eca079d 100644 --- a/data/json/items/ammo_types.json +++ b/data/json/items/ammo_types.json @@ -317,12 +317,6 @@ "name": "plutonium slurry", "default": "plut_slurry" }, - { - "type": "ammunition_type", - "id": "fusion", - "name": "fusion cell", - "default": "laser_pack" - }, { "type": "ammunition_type", "id": "12mm", diff --git a/data/json/items/bionics.json b/data/json/items/bionics.json index c94a318f1b9ad..8abdbeb1b64b4 100644 --- a/data/json/items/bionics.json +++ b/data/json/items/bionics.json @@ -128,15 +128,6 @@ "price": 8000, "difficulty": 8 }, - { - "id": "bio_blaster", - "copy-from": "bionic_general", - "type": "BIONIC_ITEM", - "name": "Fusion Blaster Arm CBM", - "description": "Your left arm has been replaced by a heavy-duty fusion blaster! You may use your energy banks to fire a damaging heat ray. However, you are unable to use or carry two-handed items, and your strength limits what you can use with your one hand.", - "price": 220000, - "difficulty": 3 - }, { "id": "bio_shotgun", "copy-from": "bionic_general", diff --git a/data/json/items/book/maps.json b/data/json/items/book/maps.json index d725f120c46f3..018f76d73ba6e 100644 --- a/data/json/items/book/maps.json +++ b/data/json/items/book/maps.json @@ -23,7 +23,17 @@ "use_action": { "type": "reveal_map", "radius": 180, - "terrain": [ "hiway", "road", "bridge", "fema_entrance", "bunker", "outpost", "silo", "shelter", "police" ], + "terrain": [ + "hiway", + "road", + "bridge", + "fema_entrance", + "bunker", + "outpost", + { "om_terrain": "silo", "om_terrain_match_type": "TYPE" }, + { "om_terrain": "shelter", "om_terrain_match_type": "TYPE" }, + "police" + ], "message": "You add roads and facilities to your map." } }, @@ -51,7 +61,17 @@ "use_action": { "type": "reveal_map", "radius": 180, - "terrain": [ "hiway", "road", "bridge", "shelter", "hospital", "school", "police", "sub_station", "bank" ], + "terrain": [ + "hiway", + "road", + "bridge", + { "om_terrain": "shelter", "om_terrain_match_type": "TYPE" }, + "hospital", + "school", + "police", + "sub_station", + "bank" + ], "message": "You add roads and points of interest to your map." } }, diff --git a/data/json/items/generic.json b/data/json/items/generic.json index bf00dc3dcda13..c113ba220a747 100644 --- a/data/json/items/generic.json +++ b/data/json/items/generic.json @@ -832,7 +832,8 @@ "price": 1000, "material": [ "plastic", "aluminum" ], "weight": 2721, - "volume": 3 + "volume": 3, + "bashing": 6 }, { "type": "GENERIC", diff --git a/data/json/items/grenades.json b/data/json/items/grenades.json index fe8e639231500..d54757342157c 100644 --- a/data/json/items/grenades.json +++ b/data/json/items/grenades.json @@ -55,32 +55,39 @@ "type": "TOOL", "name": "makeshift grenade", "description": "An improvised explosive device cobbled together from parts. Use this item to pull the pin and light the fuse. You will then have some amount of time before it explodes; throwing it would be a good idea.", - "weight": 300, + "weight": 350, "price": 750, "material": [ "aluminum", "iron" ], "symbol": "*", "color": "green", + "explode_in_fire": true, + "explosion": { "power": 155, "distance_factor": 0.8, "shrapnel": { "casing_mass": 162, "fragment_mass": 0.6 } }, "use_action": { "target": "makeshift_grenade_act", "msg": "You pull the pin on the makeshift grenade.", - "rand_target_charges": [ 2, 10 ], + "rand_target_charges": [ 2, 7 ], "active": true, "menu_text": "Pull pin", "type": "transform" - } + }, + "flags": [ "RADIO_MODABLE", "RADIO_INVOKE_PROC", "BOMB" ] }, { "id": "makeshift_grenade_act", - "copy-from": "grenade_act", + "copy-from": "makeshift_grenade", "type": "TOOL", "name": "active makeshift grenade", - "weight": 300, - "material": [ "aluminum", "iron" ], + "description": "This is an active grenade, and will explode any second now. Better throw it!", + "price": 0, "rand_charges": [ 2, 7 ], + "max_charges": 7, + "turns_per_charge": 1, "use_action": { "type": "explosion", + "sound_volume": 0, + "sound_msg": "Tick.", "no_deactivate_msg": "You've already pulled the %s's pin; try throwing it instead.", - "explosion": { "power": 155, "distance_factor": 0.8, "shrapnel": { "casing_mass": 160, "fragment_mass": 1 } } + "explosion": { "power": 155, "distance_factor": 0.8, "shrapnel": { "casing_mass": 162, "fragment_mass": 0.6 } } }, "flags": [ "BOMB", "TRADER_AVOID" ] }, diff --git a/data/json/items/gun/bio.json b/data/json/items/gun/bio.json index 4e459a901a2a5..dcb4039fe7d92 100644 --- a/data/json/items/gun/bio.json +++ b/data/json/items/gun/bio.json @@ -19,29 +19,6 @@ "reload": 200, "flags": [ "NEVER_JAMS", "RELOAD_EJECT", "NO_UNWIELD", "TRADER_AVOID" ] }, - { - "id": "bio_blaster_gun", - "type": "GUN", - "name": "fusion blaster", - "description": "this a pseudo item", - "volume": 12, - "price": 0, - "material": [ "steel", "plastic" ], - "symbol": "(", - "color": "magenta", - "skill": "rifle", - "range": 30, - "ranged_damage": 22, - "pierce": 15, - "dispersion": 30, - "durability": 10, - "loudness": 12, - "modes": [ [ "DEFAULT", "single shot", 1 ], [ "burst", "triple shot", 3 ] ], - "reload": 500, - "built_in_mods": [ "bio_powershot" ], - "ammo_effects": [ "PLASMA" ], - "flags": [ "NEVER_JAMS", "TRADER_AVOID" ] - }, { "id": "bio_emp_gun", "type": "GUN", diff --git a/data/json/items/gun/ups.json b/data/json/items/gun/ups.json index 0a8ac863fdd6a..e5c64c664e3c4 100644 --- a/data/json/items/gun/ups.json +++ b/data/json/items/gun/ups.json @@ -110,40 +110,6 @@ "ammo_effects": [ "LASER", "INCENDIARY" ], "flags": [ "NEVER_JAMS", "NO_UNLOAD" ] }, - { - "id": "unbio_blaster_gun", - "type": "GUN", - "reload_noise_volume": 10, - "name": "fusion blaster rifle", - "description": "A cyborg's fusion blaster arm, cannibalized and converted into a rifle. This improvised weapon is powered by a standard UPS connection.", - "weight": 2680, - "volume": 10, - "price": 420000, - "to_hit": -1, - "bashing": 10, - "material": [ "steel", "plastic" ], - "symbol": "(", - "color": "magenta", - "skill": "rifle", - "range": 30, - "ranged_damage": 22, - "dispersion": 90, - "durability": 6, - "loudness": 15, - "ups_charges": 40, - "reload": 600, - "valid_mod_locations": [ - [ "emitter", 1 ], - [ "grip", 1 ], - [ "sights", 1 ], - [ "sling", 1 ], - [ "stock", 1 ], - [ "rail mount", 1 ], - [ "underbarrel mount", 1 ] - ], - "ammo_effects": [ "PLASMA", "EXPLOSIVE", "FLAME", "NEVER_JAMS" ], - "flags": [ "NO_UNLOAD" ] - }, { "id": "v29", "type": "GUN", diff --git a/data/json/items/handloaded_bullets.json b/data/json/items/handloaded_bullets.json index e3a226b217f50..6f65d802db53e 100644 --- a/data/json/items/handloaded_bullets.json +++ b/data/json/items/handloaded_bullets.json @@ -55,25 +55,5 @@ "range": 10, "recoil": 840, "effects": [ "COOKOFF", "SHOT", "RECYCLED" ] - }, - { - "type": "AMMO", - "id": "reloaded_laser_pack", - "price": 80, - "name": "bootleg fusion pack", - "symbol": "=", - "color": "light_green", - "description": "In the middle of the 21st Century, military powers began to look towards energy based weapons. The result was the standard fusion pack, capable of delivering bolts of superheated gas at near light speed with no recoil. This one has been hand-crafted from spare parts.", - "material": "plastic", - "volume": 1, - "weight": 68, - "bashing": 1, - "ammo_type": "fusion", - "damage": 11, - "pierce": 14, - "range": 30, - "stack_size": 40, - "count": 20, - "effects": [ "COOKOFF", "RECYCLED" ] } ] diff --git a/data/json/items/melee/spears_and_polearms.json b/data/json/items/melee/spears_and_polearms.json index d839f9e1ea4e2..3e473fa07ee8d 100644 --- a/data/json/items/melee/spears_and_polearms.json +++ b/data/json/items/melee/spears_and_polearms.json @@ -52,18 +52,37 @@ "qualities": [ [ "CUT", 1 ], [ "BUTCHER", -22 ] ], "flags": [ "FRAGILE_MELEE", "NONCONDUCTIVE", "SHEATH_SPEAR", "REACH_ATTACK" ] }, + { + "id": "spear_spike", + "type": "TOOL", + "category": "weapons", + "name": "spike on a stick", + "description": "A flimsy pole made of wood with a basic metal spike tied to it. It's barely sharp, and crudely constructed, but it will keep the zombies out of arm's reach until you can find something better.", + "weight": 1487, + "volume": 5, + "price": 400, + "to_hit": 1, + "bashing": 4, + "cutting": 13, + "material": [ "wood", "iron" ], + "symbol": "/", + "color": "brown", + "techniques": "WBLOCK_1", + "qualities": [ [ "COOK", 1 ] ], + "flags": [ "SPEAR", "REACH_ATTACK", "NONCONDUCTIVE", "FRAGILE_MELEE", "SHEATH_SPEAR" ] + }, { "id": "spear_knife", "type": "TOOL", "category": "weapons", - "name": "knife spear", - "description": "A crude pole made of wood with an equally crude metal spike tied to it. It's long enough to slice from a distance, but its crude construction means it won't last long.", + "name": "simple knife spear", + "description": "A flimsy pole made of wood with a knife bound to the end. It's long enough to slice from a distance, but the knife isn't that well attached. You could take a bit more time to carefully split the shaft and attach the knife blade more permanently.", "weight": 1487, "volume": 5, "price": 700, "to_hit": 1, "bashing": 4, - "cutting": 16, + "cutting": 15, "material": [ "wood", "iron" ], "symbol": "/", "color": "brown", @@ -71,6 +90,25 @@ "qualities": [ [ "CUT", 1 ], [ "COOK", 1 ] ], "flags": [ "SPEAR", "REACH_ATTACK", "NONCONDUCTIVE", "FRAGILE_MELEE", "SHEATH_SPEAR" ] }, + { + "id": "spear_knife_superior", + "type": "TOOL", + "category": "weapons", + "name": "knife spear", + "description": "A sturdy wooden pole that has been carefully split and reinforced. At the split point, a sharp blade has been bolted into place and reinforced with layers of sturdy wrapped bindings.", + "weight": 1487, + "volume": 5, + "price": 12000, + "to_hit": 1, + "bashing": 4, + "cutting": 17, + "material": [ "wood", "iron" ], + "symbol": "/", + "color": "brown", + "techniques": "WBLOCK_1", + "qualities": [ [ "CUT", 1 ], [ "COOK", 1 ] ], + "flags": [ "SPEAR", "REACH_ATTACK", "NONCONDUCTIVE", "SHEATH_SPEAR" ] + }, { "id": "spear_homemade_halfpike", "type": "TOOL", @@ -368,6 +406,25 @@ "price": 9000, "qualities": [ [ "COOK", 1 ] ] }, + { + "id": "pike", + "type": "GENERIC", + "category": "weapons", + "name": "pike", + "description": "This is a medieval weapon consisting of a wood shaft tipped with an iron spearhead.", + "weight": 2500, + "volume": 14, + "price": 40000, + "to_hit": -2, + "bashing": 8, + "cutting": 25, + "material": [ "iron", "wood" ], + "symbol": "/", + "color": "brown", + "techniques": [ "IMPALE", "WBLOCK_2" ], + "qualities": [ [ "COOK", 1 ] ], + "flags": [ "DURABLE_MELEE", "SPEAR", "REACH_ATTACK", "REACH3", "NONCONDUCTIVE", "SHEATH_SPEAR" ] + }, { "id": "pike_fake", "type": "GENERIC", @@ -464,25 +521,6 @@ "techniques": [ "WBLOCK_1", "DEF_DISARM" ], "flags": [ "DURABLE_MELEE", "REACH_ATTACK" ] }, - { - "id": "pike", - "type": "GENERIC", - "category": "weapons", - "name": "pike", - "description": "This is a medieval weapon consisting of a wood shaft tipped with an iron spearhead.", - "weight": 2500, - "volume": 14, - "price": 40000, - "to_hit": -2, - "bashing": 8, - "cutting": 25, - "material": [ "iron", "wood" ], - "symbol": "/", - "color": "brown", - "techniques": [ "IMPALE", "WBLOCK_2" ], - "qualities": [ [ "COOK", 1 ] ], - "flags": [ "DURABLE_MELEE", "SPEAR", "REACH_ATTACK", "REACH3", "NONCONDUCTIVE", "SHEATH_SPEAR" ] - }, { "id": "spear_stone", "type": "GENERIC", diff --git a/data/json/items/ranged.json b/data/json/items/ranged.json index 173020557188c..d73ffed2d2291 100644 --- a/data/json/items/ranged.json +++ b/data/json/items/ranged.json @@ -133,41 +133,6 @@ [ "underbarrel", 1 ] ] }, - { - "id": "ftk93", - "type": "GUN", - "reload_noise_volume": 10, - "symbol": "(", - "color": "magenta", - "name": "FTK-93 fusion gun", - "description": "A very powerful fusion rifle developed shortly before the influx of monsters. It can only hold two rounds at a time, but a special superheating unit causes its bolts to be extremely deadly.", - "price": 980000, - "material": [ "steel", "plastic" ], - "ammo_effects": [ "PLASMA", "EXPLOSIVE", "FLAME" ], - "flags": [ "NEVER_JAMS" ], - "ups_charges": 5, - "skill": "rifle", - "ammo": "fusion", - "weight": 2267, - "volume": 14, - "bashing": 10, - "to_hit": -1, - "ranged_damage": 40, - "dispersion": 30, - "durability": 9, - "clip_size": 2, - "reload": 600, - "valid_mod_locations": [ - [ "accessories", 4 ], - [ "emitter", 1 ], - [ "grip", 1 ], - [ "rail", 1 ], - [ "sights", 1 ], - [ "sling", 1 ], - [ "stock", 1 ], - [ "underbarrel", 1 ] - ] - }, { "id": "LAW", "type": "GUN", diff --git a/data/json/items/tool_armor.json b/data/json/items/tool_armor.json index e5e87566aa88f..8d3661bc58d1c 100644 --- a/data/json/items/tool_armor.json +++ b/data/json/items/tool_armor.json @@ -444,7 +444,7 @@ "color": "blue", "name": "survivor headlamp", "description": "This is a custom made LED headlamp reinforced to be more durable, brighter, and with a larger and more efficient battery pack. The adjustable strap allows it to be comfortably worn on your head or attached to your helmet. Use it to turn it on.", - "price": 4000, + "price": 6500, "material": [ "plastic", "aluminum" ], "flags": [ "OVERSIZE", "BELTED", "ALLOWS_NATURAL_ATTACKS" ], "weight": 620, diff --git a/data/json/items/tools.json b/data/json/items/tools.json index 72e3e7d2e8773..0c515660d2e27 100644 --- a/data/json/items/tools.json +++ b/data/json/items/tools.json @@ -4442,7 +4442,7 @@ "use_action": { "target": "pipebomb_act", "msg": "You light the fuse on the pipe bomb.", - "target_charges": 5, + "target_charges": 7, "active": true, "need_fire": 1, "menu_text": "Light fuse", @@ -4464,8 +4464,8 @@ "material": "steel", "symbol": "*", "color": "light_gray", - "initial_charges": 3, - "max_charges": 5, + "initial_charges": 7, + "max_charges": 7, "turns_per_charge": 1, "use_action": { "type": "explosion", @@ -6213,7 +6213,7 @@ "use_action": { "target": "tool_rdx_sand_bomb_act", "msg": "You light the fuse on the sand bomb. Throw it!", - "target_charges": 5, + "target_charges": 20, "active": true, "need_fire": 1, "menu_text": "Light fuse", @@ -6234,8 +6234,8 @@ "material": [ "steel", "plastic" ], "symbol": ";", "color": "light_red", - "initial_charges": 5, - "max_charges": 5, + "initial_charges": 20, + "max_charges": 20, "turns_per_charge": 1, "explode_in_fire": true, "explosion": { diff --git a/data/json/mapgen/farm_dairy.json b/data/json/mapgen/farm_dairy.json index d7f8e4dd507b6..3685b4d5b37d0 100644 --- a/data/json/mapgen/farm_dairy.json +++ b/data/json/mapgen/farm_dairy.json @@ -1,45 +1,36 @@ [ - { - "name": "GROUP_COWS", - "type": "monstergroup", - "default": "mon_null", - "is_safe": true, - "monsters": [ - { "monster": "mon_cow", "freq": 100, "cost_multiplier": 10, "ends": 168 }, - { "monster": "mon_cow", "freq": 50, "cost_multiplier": 5, "starts": 168 } - ] - }, { "type": "mapgen", "method": "json", - "om_terrain": [ "dairy_farm_NW" ], + "om_terrain": [ [ "dairy_farm_NW", "dairy_farm_NE" ] ], "weight": 250, "object": { + "fill_ter": "t_dirt", "rows": [ - "........................", - ".$$$$$$$$$$$$$$$$$$$$$$$", - ".$......................", - ".$......................", - ".$......................", - ".$......................", - ".$......................", - ".$......................", - ".$......................", - ".$......................", - ".$......................", - ".$......................", - ".$......................", - ".$......................", - ".$......................", - ".$......................", - ".$......................", - ".$......................", - ".$......................", - ".$......................", - ".$......................", - ".$......................", - ".$......................", - ".$......................" + "................................................", + ".$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$.", + ".$............................................$.", + ".$............................................$.", + ".$............................................$.", + ".$............................................$.", + ".$............................................$.", + ".$............................................$.", + ".$............................................$.", + ".$............................................$.", + ".$............................................$.", + ".$............................................$.", + ".$............................................$.", + ".$............................................$.", + ".$............................................$.", + ".$............................................$.", + ".$............................................$.", + ".$............................................$.", + ".$............................................$.", + ".$............................................$.", + ".$............................................$.", + ".$............................................$.", + ".$............................................$.", + ".$............................................$." ], "terrain": { "$": "t_splitrail_fence", ".": [ "t_grass", "t_grass", "t_dirt", "t_dirt", "t_dirt" ] }, "place_monsters": [ @@ -47,153 +38,66 @@ { "monster": "GROUP_COWS", "x": [ 1, 12 ], "y": [ 1, 12 ], "density": 0.5 }, { "monster": "GROUP_COWS", "x": [ 1, 11 ], "y": [ 1, 10 ], "density": 0.5 }, { "monster": "GROUP_COWS", "x": [ 1, 9 ], "y": [ 1, 11 ], "density": 0.5 }, - { "monster": "GROUP_COWS", "x": [ 1, 15 ], "y": [ 1, 14 ], "density": 0.5 } + { "monster": "GROUP_COWS", "x": [ 1, 15 ], "y": [ 1, 14 ], "density": 0.5 }, + { "monster": "GROUP_COWS", "x": [ 25, 34 ], "y": [ 1, 9 ], "density": 0.5 }, + { "monster": "GROUP_COWS", "x": [ 25, 36 ], "y": [ 1, 12 ], "density": 0.5 }, + { "monster": "GROUP_COWS", "x": [ 25, 35 ], "y": [ 1, 10 ], "density": 0.5 }, + { "monster": "GROUP_COWS", "x": [ 25, 33 ], "y": [ 1, 11 ], "density": 0.5 }, + { "monster": "GROUP_COWS", "x": [ 25, 39 ], "y": [ 1, 12 ], "density": 0.5 } ] } }, { "type": "mapgen", "method": "json", - "om_terrain": [ "dairy_farm_NE" ], - "weight": 250, - "object": { - "rows": [ - "........................", - "$$$$$$$$$$$$$$$$$$$$$$$.", - "......................$.", - "......................$.", - "......................$.", - "......................$.", - "......................$.", - "......................$.", - "......................$.", - "......................$.", - "......................$.", - "......................$.", - "......................$.", - "......................$.", - "......................$.", - "......................$.", - "......................$.", - "......................$.", - "......................$.", - "......................$.", - "......................$.", - "......................$.", - "......................$.", - "......................$." - ], - "terrain": { "$": "t_splitrail_fence", ".": [ "t_grass", "t_grass", "t_dirt", "t_dirt", "t_dirt" ] }, - "place_monsters": [ - { "monster": "GROUP_COWS", "x": [ 1, 10 ], "y": [ 1, 9 ], "density": 0.5 }, - { "monster": "GROUP_COWS", "x": [ 1, 12 ], "y": [ 1, 12 ], "density": 0.5 }, - { "monster": "GROUP_COWS", "x": [ 1, 11 ], "y": [ 1, 10 ], "density": 0.5 }, - { "monster": "GROUP_COWS", "x": [ 1, 9 ], "y": [ 1, 11 ], "density": 0.5 }, - { "monster": "GROUP_COWS", "x": [ 1, 15 ], "y": [ 1, 12 ], "density": 0.5 } - ] - } - }, - { - "type": "mapgen", - "method": "json", - "om_terrain": [ "dairy_farm_SE" ], + "om_terrain": [ [ "dairy_farm_SW", "dairy_farm_SE" ] ], "weight": 250, "object": { "fill_ter": "t_floor", "rows": [ - "......................$.", - "......................$.", - "......................$.", - ".|*|+|*|....|*|+|*|...$.", - ".|_____|....|_____|...$.", - ".|O%_%O|....|___H_|...$.", - ".*O%_%O*....*_H___*...$.", - ".|O%_%O|....|_____|...$.", - ".|O%_%O|....|___H_|...$.", - ".|O%_%O|....|_____|...$.", - ".*O%_%O*....*_H___*...$.", - ".|O%_%O|....|_____|...$.", - ".|O%_%O|....|_____|...$.", - ".|O%_%O|....|_H___|...$.", - ".*O%_%O*....*_____*...$.", - ".|O%_%O|....|H____|...$.", - ".|O%_%O|....|_____|...$.", - ".|O%_%O|....|___H_|...$.", - ".|OOOOO|....|_____|...$.", - ".|*|||*|....|*|||*|...$.", - "......................$.", - "......................$.", - "$$$$$$$$$$$$$$$$$$$$$$$.", - "........................" - ], - "terrain": { - "$": "t_splitrail_fence", - "%": "t_milking_machine", - "*": "t_window_bars", - "+": "t_door_o", - ".": [ "t_grass", "t_grass", "t_dirt", "t_dirt", "t_dirt" ], - "O": "t_bulk_tank", - "_": "t_floor", - "|": "t_wall" - }, - "place_item": [ - { "item": "cattlefodder", "x": 13, "y": 15, "amount": [ 0, 4 ] }, - { "item": "cattlefodder", "x": 14, "y": 6, "amount": [ 0, 4 ] }, - { "item": "cattlefodder", "x": 14, "y": 10, "amount": [ 0, 4 ] }, - { "item": "cattlefodder", "x": 14, "y": 13, "amount": [ 0, 4 ] }, - { "item": "cattlefodder", "x": 16, "y": 5, "amount": [ 0, 4 ] }, - { "item": "cattlefodder", "x": 16, "y": 8, "amount": [ 0, 4 ] }, - { "item": "cattlefodder", "x": 16, "y": 17, "amount": [ 0, 4 ] } - ], - "furniture": { "H": "f_hay" } - } - }, - { - "type": "mapgen", - "method": "json", - "om_terrain": [ "dairy_farm_SW" ], - "weight": 250, - "object": { - "fill_ter": "t_floor", - "rows": [ - ".$......................", - ".$......................", - ".$......................", - ".$......................", - ".$......................", - ".$......................", - ".$......................", - ".$......................", - ".$......................", - ".$...||*||+||*||||......", - ".$...|A{{__Y____{|......", - ".$...|w_________{*......", - ".$...*______HHH__|......", - ".$...|__||'||||'||......", - ".$...|__|g_dd|t_c|O.....", - ".$...||'|__C_|C_c|X.....", - ".$...|_&|D_##|T_o*X.....", - ".$...*bs|D_##|_F&|......", - ".$...||||*|*||+|||......", - ".$........aa............", - ".$......................", - ".$......................", - ".$$$$$$$$$$$$$$$$$$fffff", - "........................" + ".$............................................$.", + ".$............................................$.", + ".$............................................$.", + ".$.......................|!|=|!|....|!|=|!|...$.", + ".$.......................|_____|....|_____|...$.", + ".$.......................|O%_%O|....|___H_|...$.", + ".$.......................!O%_%O!....!_H___!...$.", + ".$.......................|O%_%O|....|_____|...$.", + ".$.......................|O%_%O|....|___H_|...$.", + ".$...||*||+||*||||.......|O%_%O|....|_____|...$.", + ".$...|A{{__Y____{|4......!O%_%O!....!_H___!...$.", + ".$...|w_________{*.......|O%_%O|....|_____|...$.", + ".$...*______@@@__|.......|O%_%O|....|_____|...$.", + ".$...|__||'||||'||.......|O%_%O|....|_H___|...$.", + ".$...|__|g_dd|t_c|N......!O%_%O!....!_____!...$.", + ".$...||'|__C_|C_c|X......|O%_%O|....|H____|...$.", + ".$...|_&|D_##|T_o*X......|O%_%O|....|_____|...$.", + ".$...*bs|D_##|_F&|.......|O%_%O|....|___H_|...$.", + ".$...||||*|*||+|||.......|OOOOO|....|_____|...$.", + ".$........aa.............|!|||!|....|!|||!|...$.", + ".$............................................$.", + ".$............................................$.", + ".$$$$$$$$$$$$$$$$$$fffff$$$$$$$$$$$$$$$$$$$$$$$.", + "................................................" ], "terrain": { "$": "t_splitrail_fence", "'": "t_door_o", + "%": "t_milking_machine", + "!": "t_window_bars", "*": "t_window_domestic", + "=": "t_door_o", "+": "t_door_locked_interior", ".": [ "t_grass", "t_grass", "t_dirt", "t_dirt", "t_dirt" ], - "O": [ "t_grass", "t_grass", "t_dirt", "t_dirt", "t_dirt" ], + "N": [ "t_grass", "t_grass", "t_dirt", "t_dirt", "t_dirt" ], "X": [ "t_grass", "t_grass", "t_dirt", "t_dirt", "t_dirt" ], + "O": "t_bulk_tank", "Y": "t_floor", "_": "t_floor", "a": [ "t_grass", "t_grass", "t_dirt", "t_dirt", "t_dirt" ], "f": "t_splitrail_fencegate_c", - "|": "t_wall" + "|": "t_wall_w", + "4": "t_gutter_downspout" }, "furniture": { "#": "f_bed", @@ -202,8 +106,9 @@ "C": "f_chair", "D": "f_dresser", "F": "f_fridge", - "H": "f_sofa", - "O": "f_crate_o", + "@": "f_sofa", + "H": "f_hay", + "N": "f_crate_o", "T": "f_table", "X": "f_crate_c", "Y": "f_rack_coat", @@ -244,11 +149,57 @@ { "item": "guns_rifle_common", "x": 9, "y": 14, "chance": 50 }, { "item": "clothing_work_set", "x": 9, "y": 17, "chance": 90 } ], + "place_item": [ + { "item": "cattlefodder", "x": 37, "y": 15, "amount": [ 0, 4 ] }, + { "item": "cattlefodder", "x": 38, "y": 6, "amount": [ 0, 4 ] }, + { "item": "cattlefodder", "x": 38, "y": 10, "amount": [ 0, 4 ] }, + { "item": "cattlefodder", "x": 38, "y": 13, "amount": [ 0, 4 ] }, + { "item": "cattlefodder", "x": 39, "y": 5, "amount": [ 0, 4 ] }, + { "item": "cattlefodder", "x": 39, "y": 8, "amount": [ 0, 4 ] }, + { "item": "cattlefodder", "x": 39, "y": 17, "amount": [ 0, 4 ] } + ], "place_vehicles": [ { "vehicle": "cube_van_cheap", "x": 7, "y": 5, "chance": 50, "fuel": 300, "status": -1, "rotation": 0 }, { "vehicle": "quad_bike", "x": 15, "y": 6, "chance": 30, "fuel": 200, "status": -1, "rotation": 0 }, { "vehicle": "bicycle", "x": 13, "y": 8, "chance": 80, "fuel": 0, "status": 0, "rotation": 0 } ] } + }, + { + "type": "mapgen", + "method": "json", + "om_terrain": [ [ "dairy_farm_SW_roof", "dairy_farm_SE_roof" ] ], + "object": { + "fill_ter": "t_shingle_flat_roof", + "rows": [ + " ", + " ", + " ", + " ....... ....... ", + " ....... ....... ", + " ....... ....... ", + " ...&... ...&... ", + " ....... ....... ", + " ....... ....... ", + " |222222222223 ....... ....... ", + " |...........5 ....... ....... ", + " |...........3 ....... ....... ", + " |...........3 ...&... ...&... ", + " |.......N...3 ....... ....... ", + " |...........3 ....... ....... ", + " |...........3 ....... ....... ", + " |...&...=...3 ...&... ...&... ", + " |...........3 ....... ....... ", + " |-----------3 ....... ....... ", + " ....... ....... ", + " ", + " ", + " ", + " " + ], + "palettes": [ "roof_palette" ], + "terrain": { ".": "t_shingle_flat_roof" }, + "furniture": { "N": "f_TV_antenna" } + } } ] diff --git a/data/json/materials.json b/data/json/materials.json index 860a68b2313a2..65bb9fa02b68b 100644 --- a/data/json/materials.json +++ b/data/json/materials.json @@ -1210,9 +1210,9 @@ "bash_dmg_verb": "splintered", "cut_dmg_verb": "gouged", "burn_data": [ - { "fuel": 1, "smoke": 1, "burn": 0.02, "volume_per_turn": "1250_ml" }, - { "fuel": 2, "smoke": 1, "burn": 0.04, "volume_per_turn": "5000_ml" }, - { "fuel": 3, "smoke": 1, "burn": 0.06 } + { "fuel": 1, "smoke": 1, "burn": 0.01, "volume_per_turn": "200_ml" }, + { "fuel": 2, "smoke": 1, "burn": 0.02, "volume_per_turn": "825_ml" }, + { "fuel": 3, "smoke": 1, "burn": 0.03 } ], "burn_products": [ [ "ash", 0.018 ] ] }, diff --git a/data/json/monstergroups/monstergroups.json b/data/json/monstergroups/monstergroups.json index e1e6cc3d946ef..297a4c29097fa 100644 --- a/data/json/monstergroups/monstergroups.json +++ b/data/json/monstergroups/monstergroups.json @@ -4556,7 +4556,8 @@ "default": "mon_null", "monsters": [ { "monster": "mon_zombie_nullfield", "freq": 550, "cost_multiplier": 0 }, - { "monster": "mon_zombie_brute_shocker", "freq": 300, "cost_multiplier": 2 } + { "monster": "mon_zombie_brute_shocker", "freq": 300, "cost_multiplier": 2 }, + { "monster": "mon_skeleton_electric", "freq": 300, "cost_multiplier": 2 } ] }, { @@ -4615,6 +4616,16 @@ { "monster": "mon_lemming", "freq": 10, "cost_multiplier": 1 } ] }, + { + "name": "GROUP_COWS", + "type": "monstergroup", + "default": "mon_null", + "is_safe": true, + "monsters": [ + { "monster": "mon_cow", "freq": 100, "cost_multiplier": 10, "ends": 168 }, + { "monster": "mon_cow", "freq": 50, "cost_multiplier": 5, "starts": 168 } + ] + }, { "type": "monstergroup", "name": "GROUP_SMALL_STATION", diff --git a/data/json/monsters/monsters.json b/data/json/monsters/monsters.json index 45c5870cb95f8..e3f0a11358a9e 100644 --- a/data/json/monsters/monsters.json +++ b/data/json/monsters/monsters.json @@ -2545,6 +2545,41 @@ "death_function": [ "NORMAL" ], "flags": [ "SEES", "HEARS", "BLEED", "HARDTOSHOOT", "REVIVES", "NO_BREATHE", "POISON", "FILTHY" ] }, + { + "id": "mon_skeleton_electric", + "type": "MONSTER", + "name": "skeletal shocker", + "description": "Heavy, jagged bone plates have grown out of the surface of this zombie. Underneath, its flesh glows and crackles with the occasional jolt of electricity.", + "default_faction": "zombie", + "bodytype": "human", + "species": [ "ZOMBIE", "HUMAN" ], + "diff": 15, + "volume": "62500 ml", + "weight": 81500, + "hp": 105, + "speed": 90, + "material": [ "bone" ], + "symbol": "Z", + "color": "blue_white", + "aggression": 100, + "morale": 100, + "melee_skill": 4, + "melee_dice": 2, + "melee_dice_sides": 3, + "melee_cut": 3, + "dodge": 2, + "luminance": 8, + "armor_cut": 15, + "armor_stab": 30, + "armor_acid": 3, + "vision_day": 30, + "vision_night": 3, + "harvest": "CBM_CIV", + "special_attacks": [ [ "scratch", 10 ], { "type": "bite", "cooldown": 5 }, [ "SHOCKSTORM", 25 ] ], + "death_drops": "default_zombie_clothes", + "death_function": [ "NORMAL" ], + "flags": [ "SEES", "HEARS", "BLEED", "ELECTRIC", "REVIVES", "NO_BREATHE", "POISON", "FILTHY" ] + }, { "id": "mon_sludge_crawler", "type": "MONSTER", diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index 275a34dac7bfd..84e6b3c3365a9 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -328,6 +328,8 @@ "name": "Good Memory", "points": 3, "description": "You have an exceptional memory, and find it easy to remember things. Your skills will erode slightly slower than usual, and you can remember more terrain.", + "map_memory_capacity_multiplier": 2, + "skill_rust_multiplier": 0.66, "starting_trait": true, "valid": false, "cancels": [ "FORGETFUL" ] @@ -1018,6 +1020,8 @@ "points": -3, "description": "You have a hard time remembering things. Your skills will erode slightly faster than usual, and you can remember less terrain.", "social_modifiers": { "lie": -5 }, + "map_memory_capacity_multiplier": 0.5, + "skill_rust_multiplier": 1.33, "starting_trait": true, "category": [ "BEAST", "MEDICAL", "CHIMERA", "MOUSE", "INSECT" ], "cancels": [ "GOODMEMORY" ] diff --git a/data/json/obsolete.json b/data/json/obsolete.json index 91b369062b350..1ded27746b85f 100644 --- a/data/json/obsolete.json +++ b/data/json/obsolete.json @@ -19,5 +19,186 @@ "type": "recipe", "result": "reloaded_270", "obsolete": true + }, + { + "type": "recipe", + "result": "reloaded_laser_pack", + "obsolete": true + }, + { + "type": "recipe", + "result": "unbio_blaster_gun", + "obsolete": true + }, + { + "id": "bio_blaster", + "copy-from": "bionic_general", + "type": "BIONIC_ITEM", + "name": "Fusion Blaster Arm CBM", + "description": "Your left arm has been replaced by a heavy-duty fusion blaster! You may use your energy banks to fire a damaging heat ray. However, you are unable to use or carry two-handed items, and your strength limits what you can use with your one hand.", + "price": 220000, + "difficulty": 3 + }, + { + "id": "bio_blaster", + "type": "bionic", + "name": "Fusion Blaster Arm", + "description": "Your left arm has been surgically replaced by a heavy-duty fusion blaster! You may use your energy banks to fire a damaging heat ray. However, you are unable to use or carry two-handed items, and your strength limits what you can use with your one hand.", + "occupied_bodyparts": [ [ "ARM_L", 20 ], [ "HAND_L", 5 ] ], + "act_cost": 50, + "fake_item": "bio_blaster_gun", + "flags": [ "BIONIC_GUN" ] + }, + { + "id": "fusion_gun", + "copy-from": "turret", + "type": "vehicle_part", + "name": "mounted fusion gun", + "item": "ftk93", + "color": "magenta", + "broken_color": "magenta", + "difficulty": 6, + "breaks_into": [ { "item": "ftk93", "prob": 50 } ], + "requirements": { + "install": { "skills": [ [ "mechanics", 6 ], [ "electronics", 6 ] ], "time": 10000 }, + "removal": { "skills": [ [ "mechanics", 4 ] ] } + } + }, + { + "id": "bio_blaster_gun", + "type": "GUN", + "name": "fusion blaster", + "description": "this a pseudo item", + "volume": 12, + "price": 0, + "material": [ "steel", "plastic" ], + "symbol": "(", + "color": "magenta", + "skill": "rifle", + "range": 30, + "ranged_damage": 22, + "pierce": 15, + "dispersion": 30, + "durability": 10, + "loudness": 12, + "modes": [ [ "DEFAULT", "single shot", 1 ], [ "burst", "triple shot", 3 ] ], + "reload": 500, + "built_in_mods": [ "bio_powershot" ], + "ammo_effects": [ "PLASMA" ], + "flags": [ "NEVER_JAMS", "TRADER_AVOID" ] + }, + { + "id": "unbio_blaster_gun", + "type": "GUN", + "reload_noise_volume": 10, + "name": "fusion blaster rifle", + "description": "A cyborg's fusion blaster arm, cannibalized and converted into a rifle. This improvised weapon is powered by a standard UPS connection.", + "weight": 2680, + "volume": 10, + "price": 420000, + "to_hit": -1, + "bashing": 10, + "material": [ "steel", "plastic" ], + "symbol": "(", + "color": "magenta", + "skill": "rifle", + "range": 30, + "ranged_damage": 22, + "dispersion": 90, + "durability": 6, + "loudness": 15, + "ups_charges": 40, + "reload": 600, + "valid_mod_locations": [ + [ "emitter", 1 ], + [ "grip", 1 ], + [ "sights", 1 ], + [ "sling", 1 ], + [ "stock", 1 ], + [ "rail mount", 1 ], + [ "underbarrel mount", 1 ] + ], + "ammo_effects": [ "PLASMA", "EXPLOSIVE", "FLAME", "NEVER_JAMS" ], + "flags": [ "NO_UNLOAD" ] + }, + { + "id": "ftk93", + "type": "GUN", + "reload_noise_volume": 10, + "symbol": "(", + "color": "magenta", + "name": "FTK-93 fusion gun", + "description": "A very powerful fusion rifle developed shortly before the influx of monsters. It can only hold two rounds at a time, but a special superheating unit causes its bolts to be extremely deadly.", + "price": 980000, + "material": [ "steel", "plastic" ], + "ammo_effects": [ "PLASMA", "EXPLOSIVE", "FLAME" ], + "flags": [ "NEVER_JAMS" ], + "ups_charges": 5, + "skill": "rifle", + "ammo": "fusion", + "weight": 2267, + "volume": 14, + "bashing": 10, + "to_hit": -1, + "ranged_damage": 40, + "dispersion": 30, + "durability": 9, + "clip_size": 2, + "reload": 600, + "valid_mod_locations": [ + [ "accessories", 4 ], + [ "emitter", 1 ], + [ "grip", 1 ], + [ "rail", 1 ], + [ "sights", 1 ], + [ "sling", 1 ], + [ "stock", 1 ], + [ "underbarrel", 1 ] + ] + }, + { + "type": "ammunition_type", + "id": "fusion", + "name": "fusion cell", + "default": "laser_pack" + }, + { + "type": "AMMO", + "id": "laser_pack", + "price": 80000, + "name": "fusion pack", + "symbol": "=", + "color": "light_green", + "description": "In the middle of the 21st Century, military powers began to look towards energy based weapons. The result was the standard fusion pack, capable of delivering bolts of superheated gas at near light speed with no recoil.", + "material": "plastic", + "volume": 1, + "weight": 68, + "ammo_type": "fusion", + "damage": 12, + "pierce": 15, + "range": 30, + "stack_size": 40, + "count": 20, + "effects": [ "NEVER_MISFIRES" ] + }, + { + "type": "AMMO", + "id": "reloaded_laser_pack", + "price": 80, + "name": "bootleg fusion pack", + "symbol": "=", + "color": "light_green", + "description": "In the middle of the 21st Century, military powers began to look towards energy based weapons. The result was the standard fusion pack, capable of delivering bolts of superheated gas at near light speed with no recoil. This one has been hand-crafted from spare parts.", + "material": "plastic", + "volume": 1, + "weight": 68, + "bashing": 1, + "ammo_type": "fusion", + "damage": 11, + "pierce": 14, + "range": 30, + "stack_size": 40, + "count": 20, + "effects": [ "COOKOFF", "RECYCLED" ] } ] diff --git a/data/json/overmap/overmap_special/specials.json b/data/json/overmap/overmap_special/specials.json index 684241a662749..2278484b42e8d 100644 --- a/data/json/overmap/overmap_special/specials.json +++ b/data/json/overmap/overmap_special/specials.json @@ -3297,7 +3297,9 @@ { "point": [ 0, 0, 0 ], "overmap": "dairy_farm_NW_north" }, { "point": [ 1, 0, 0 ], "overmap": "dairy_farm_NE_north" }, { "point": [ 0, 1, 0 ], "overmap": "dairy_farm_SW_north" }, - { "point": [ 1, 1, 0 ], "overmap": "dairy_farm_SE_north" } + { "point": [ 0, 1, 1 ], "overmap": "dairy_farm_SW_roof_north" }, + { "point": [ 1, 1, 0 ], "overmap": "dairy_farm_SE_north" }, + { "point": [ 1, 1, 1 ], "overmap": "dairy_farm_SE_roof_north" } ], "connections": [ { "point": [ 0, 2, 0 ], "terrain": "road" } ], "locations": [ "wilderness" ], diff --git a/data/json/overmap/overmap_terrain/overmap_terrain_agricultural.json b/data/json/overmap/overmap_terrain/overmap_terrain_agricultural.json index 6772d2c7f61cc..329348e12b9ce 100644 --- a/data/json/overmap/overmap_terrain/overmap_terrain_agricultural.json +++ b/data/json/overmap/overmap_terrain/overmap_terrain_agricultural.json @@ -462,6 +462,15 @@ "see_cost": 5, "flags": [ "SIDEWALK" ] }, + { + "id": "dairy_farm_SE_roof", + "type": "overmap_terrain", + "name": "dairy farm roof", + "sym": "w", + "color": "brown", + "see_cost": 5, + "flags": [ "SIDEWALK" ] + }, { "id": "dairy_farm_SW", "type": "overmap_terrain", @@ -471,6 +480,15 @@ "see_cost": 5, "flags": [ "SIDEWALK" ] }, + { + "id": "dairy_farm_SW_roof", + "type": "overmap_terrain", + "name": "dairy farm roof", + "sym": "w", + "color": "brown", + "see_cost": 5, + "flags": [ "SIDEWALK" ] + }, { "id": "cemetery_small", "type": "overmap_terrain", diff --git a/data/json/recipes/ammo/shot.json b/data/json/recipes/ammo/shot.json index 33777145dc478..aacbf0c50796d 100644 --- a/data/json/recipes/ammo/shot.json +++ b/data/json/recipes/ammo/shot.json @@ -186,7 +186,7 @@ "skill_used": "fabrication", "difficulty": 2, "skills_required": [ "gun", 1 ], - "time": "0 m", + "time": "6 s", "batch_time_factors": [ 60, 5 ], "book_learn": [ [ "recipe_bullets", 3 ], [ "manual_shotgun", 2 ], [ "pocket_survival", 2 ], [ "mag_survival", 2 ] ], "charges": 1, diff --git a/data/json/recipes/armor/head.json b/data/json/recipes/armor/head.json index 785a2d196d9ae..0ca0ff84972ff 100644 --- a/data/json/recipes/armor/head.json +++ b/data/json/recipes/armor/head.json @@ -1301,7 +1301,7 @@ "category": "CC_ARMOR", "subcategory": "CSC_ARMOR_HEAD", "skill_used": "tailor", - "time": "0 m", + "time": "30 s", "autolearn": true, "tools": [ ], "components": [ [ [ "aluminum_foil", 4 ] ] ] diff --git a/data/json/recipes/armor/storage.json b/data/json/recipes/armor/storage.json index 4260e1575acf6..2d5940aead240 100644 --- a/data/json/recipes/armor/storage.json +++ b/data/json/recipes/armor/storage.json @@ -168,7 +168,7 @@ "category": "CC_ARMOR", "subcategory": "CSC_ARMOR_STORAGE", "skill_used": "survival", - "time": "0 m", + "time": "48 s", "reversible": true, "autolearn": true, "components": [ @@ -312,7 +312,7 @@ "category": "CC_ARMOR", "subcategory": "CSC_ARMOR_STORAGE", "skill_used": "survival", - "time": "0 m", + "time": "36 s", "reversible": true, "autolearn": true, "components": [ [ [ "raw_leather", 50 ], [ "raw_hleather", 50 ], [ "raw_fur", 50 ] ] ], @@ -324,7 +324,7 @@ "category": "CC_ARMOR", "subcategory": "CSC_ARMOR_STORAGE", "skill_used": "survival", - "time": "0 m", + "time": "36 s", "reversible": true, "autolearn": true, "components": [ [ [ "raw_tainted_leather", 50 ], [ "raw_tainted_fur", 50 ] ] ], @@ -433,7 +433,7 @@ "category": "CC_ARMOR", "subcategory": "CSC_ARMOR_STORAGE", "skill_used": "survival", - "time": "0 m", + "time": "48 s", "reversible": true, "autolearn": true, "using": [ [ "cordage", 1 ] ], @@ -456,7 +456,7 @@ "category": "CC_ARMOR", "subcategory": "CSC_ARMOR_STORAGE", "skill_used": "survival", - "time": "0 m", + "time": "48 s", "reversible": true, "autolearn": true, "components": [ [ [ "sheet", 1 ], [ "blanket", 1 ] ] ], @@ -766,7 +766,7 @@ "category": "CC_ARMOR", "subcategory": "CSC_ARMOR_STORAGE", "skill_used": "survival", - "time": "0 m", + "time": "36 s", "reversible": true, "autolearn": true, "components": [ [ [ "bag_canvas", 1 ] ] ], diff --git a/data/json/recipes/other/medical.json b/data/json/recipes/other/medical.json index ce42699e84654..d66fbe5ca1993 100644 --- a/data/json/recipes/other/medical.json +++ b/data/json/recipes/other/medical.json @@ -6,7 +6,7 @@ "subcategory": "CSC_OTHER_MEDICAL", "skill_used": "firstaid", "difficulty": 1, - "time": "0 m", + "time": "30 s", "autolearn": true, "components": [ [ [ "rag", 3 ], [ "medical_gauze", 1 ] ], @@ -20,7 +20,7 @@ "category": "CC_OTHER", "subcategory": "CSC_OTHER_MEDICAL", "skill_used": "tailor", - "time": "0 m", + "time": "9 s", "autolearn": true, "charges": 1, "components": [ [ [ "rag", 1 ] ] ] @@ -72,7 +72,7 @@ "subcategory": "CSC_OTHER_MEDICAL", "skill_used": "cooking", "autolearn": true, - "time": "0 m", + "time": "30 s", "components": [ [ [ "chem_ethanol", 250 ], [ "denat_alcohol", 250 ] ] ] }, { diff --git a/data/json/recipes/recipe_ammo.json b/data/json/recipes/recipe_ammo.json index a44aff5a457bb..3f112bf68ad66 100644 --- a/data/json/recipes/recipe_ammo.json +++ b/data/json/recipes/recipe_ammo.json @@ -565,7 +565,7 @@ "skill_used": "fabrication", "skills_required": [ "gun", 1 ], "difficulty": 3, - "time": "0 m", + "time": "30 s", "autolearn": true, "book_learn": [ [ "recipe_bullets", 2 ], [ "manual_shotgun", 2 ] ], "qualities": [ { "id": "SAW_M", "level": 1 } ], @@ -594,7 +594,7 @@ "skill_used": "fabrication", "skills_required": [ "gun", 1 ], "difficulty": 3, - "time": "0 m", + "time": "30 s", "autolearn": true, "book_learn": [ [ "recipe_bullets", 2 ], [ "manual_shotgun", 2 ] ], "qualities": [ { "id": "SAW_M", "level": 1 } ], @@ -612,7 +612,7 @@ "skill_used": "fabrication", "skills_required": [ "gun", 1 ], "difficulty": 3, - "time": "0 m", + "time": "30 s", "autolearn": true, "book_learn": [ [ "recipe_bullets", 2 ], [ "manual_shotgun", 2 ] ], "qualities": [ { "id": "SAW_M", "level": 1 } ], @@ -776,21 +776,6 @@ [ [ "fletching", 1, "LIST" ] ] ] }, - { - "type": "recipe", - "result": "reloaded_laser_pack", - "category": "CC_AMMO", - "subcategory": "CSC_AMMO_OTHER", - "skill_used": "fabrication", - "skills_required": [ "electronics", 5 ], - "difficulty": 3, - "time": "10 m", - "reversible": true, - "decomp_learn": 4, - "book_learn": [ [ "recipe_lab_elec", 4 ] ], - "qualities": [ { "id": "SCREW", "level": 1 } ], - "components": [ [ [ "superglue", 1 ] ], [ [ "plut_cell", 1 ] ] ] - }, { "type": "recipe", "result": "rebar_rail", diff --git a/data/json/recipes/recipe_deconstruction.json b/data/json/recipes/recipe_deconstruction.json index 741fc689be8bf..45e1df01a492b 100644 --- a/data/json/recipes/recipe_deconstruction.json +++ b/data/json/recipes/recipe_deconstruction.json @@ -2,42 +2,42 @@ { "result": "alarmclock", "type": "uncraft", - "time": "0 m", + "time": "36 s", "qualities": [ { "id": "SCREW", "level": 1 } ], "components": [ [ [ "scrap", 4 ] ], [ [ "clockworks", 2 ] ] ] }, { "result": "badge_cybercop", "type": "uncraft", - "time": "0 m", + "time": "30 s", "qualities": [ { "id": "HAMMER", "level": 1 } ], "components": [ [ [ "silver_small", 20 ] ] ] }, { "result": "badge_deputy", "type": "uncraft", - "time": "0 m", + "time": "30 s", "qualities": [ { "id": "HAMMER", "level": 1 } ], "components": [ [ [ "silver_small", 20 ] ] ] }, { "result": "badge_detective", "type": "uncraft", - "time": "0 m", + "time": "30 s", "qualities": [ { "id": "HAMMER", "level": 1 } ], "components": [ [ [ "gold_small", 20 ] ] ] }, { "result": "badge_marshal", "type": "uncraft", - "time": "0 m", + "time": "30 s", "qualities": [ { "id": "HAMMER", "level": 1 } ], "components": [ [ [ "silver_small", 20 ] ] ] }, { "result": "badge_swat", "type": "uncraft", - "time": "0 m", + "time": "30 s", "qualities": [ { "id": "HAMMER", "level": 1 } ], "components": [ [ [ "silver_small", 20 ] ] ] }, @@ -57,7 +57,7 @@ { "result": "bindle", "type": "uncraft", - "time": "0 m", + "time": "6 s", "components": [ [ [ "rag", 4 ] ], [ [ "stick", 1 ] ] ], "flags": [ "BLIND_HARD" ] }, @@ -163,14 +163,14 @@ { "result": "bowl_pewter", "type": "uncraft", - "time": "0 m", + "time": "30 s", "qualities": [ { "id": "HAMMER", "level": 1 } ], "components": [ [ [ "tin", 100 ] ] ] }, { "result": "box_cigarette", "type": "uncraft", - "time": "0 m", + "time": "6 s", "components": [ [ [ "paper", 1 ] ] ], "flags": [ "BLIND_EASY" ] }, @@ -877,14 +877,14 @@ { "result": "cell_phone", "type": "uncraft", - "time": "0 m", + "time": "30 s", "qualities": [ { "id": "SCREW", "level": 1 } ], "components": [ [ [ "scrap", 1 ] ], [ [ "small_lcd_screen", 1 ] ], [ [ "RAM", 1 ] ], [ [ "processor", 1 ] ] ] }, { "result": "smart_phone", "type": "uncraft", - "time": "0 m", + "time": "30 s", "qualities": [ { "id": "SCREW", "level": 1 } ], "components": [ [ [ "scrap", 1 ] ], [ [ "small_lcd_screen", 1 ] ], [ [ "RAM", 1 ] ], [ [ "processor", 1 ] ], [ [ "lens_small", 1 ] ] ] }, @@ -922,35 +922,35 @@ { "result": "collarpin", "type": "uncraft", - "time": "0 m", + "time": "30 s", "qualities": [ { "id": "HAMMER", "level": 1 } ], "components": [ [ [ "silver_small", 4 ] ] ] }, { "result": "copper_bracelet", "type": "uncraft", - "time": "0 m", + "time": "36 s", "qualities": [ { "id": "HAMMER", "level": 1 } ], "components": [ [ [ "copper", 43 ] ] ] }, { "result": "copper_ear", "type": "uncraft", - "time": "0 m", + "time": "30 s", "qualities": [ { "id": "HAMMER", "level": 1 } ], "components": [ [ [ "copper", 25 ] ] ] }, { "result": "creepy_doll", "type": "uncraft", - "time": "0 m", + "time": "30 s", "qualities": [ { "id": "SCREW", "level": 1 } ], "components": [ [ [ "scrap", 1 ] ], [ [ "plastic_chunk", 2 ] ], [ [ "rag", 2 ] ], [ [ "RAM", 1 ] ], [ [ "e_scrap", 2 ] ] ] }, { "result": "crown_golden", "type": "uncraft", - "time": "0 m", + "time": "36 s", "qualities": [ { "id": "HAMMER", "level": 1 } ], "components": [ [ [ "gold_small", 200 ] ] ] }, @@ -970,14 +970,14 @@ { "result": "diamond_dental_grill", "type": "uncraft", - "time": "0 m", + "time": "30 s", "qualities": [ { "id": "HAMMER", "level": 1 } ], "components": [ [ [ "gold_small", 2 ] ], [ [ "diamond", 1 ] ] ] }, { "result": "diamond_ring", "type": "uncraft", - "time": "0 m", + "time": "30 s", "qualities": [ { "id": "HAMMER", "level": 1 } ], "components": [ [ [ "gold_small", 2 ] ], [ [ "diamond", 1 ] ] ] }, @@ -1005,7 +1005,7 @@ { "result": "eink_tablet_pc", "type": "uncraft", - "time": "0 m", + "time": "30 s", "qualities": [ { "id": "SCREW", "level": 1 } ], "components": [ [ [ "scrap", 1 ] ], [ [ "RAM", 1 ] ], [ [ "processor", 1 ] ], [ [ "plastic_chunk", 2 ] ], [ [ "e_scrap", 2 ] ] ] }, @@ -1014,21 +1014,21 @@ "type": "uncraft", "skill_used": "fabrication", "difficulty": 1, - "time": "0 m", + "time": "30 s", "qualities": [ { "id": "WRENCH", "level": 1 } ], "components": [ [ [ "scrap", 3 ] ], [ [ "hose", 1 ] ], [ [ "jerrycan_big", 1 ] ] ] }, { "result": "fan", "type": "uncraft", - "time": "0 m", + "time": "30 s", "qualities": [ { "id": "SCREW", "level": 1 } ], "components": [ [ [ "scrap", 1 ] ], [ [ "plastic_chunk", 3 ] ], [ [ "motor_micro", 1 ] ], [ [ "cable", 2 ] ] ] }, { "result": "fancy_sunglasses", "type": "uncraft", - "time": "0 m", + "time": "6 s", "components": [ [ [ "glass_tinted", 1 ] ] ], "flags": [ "BLIND_EASY" ] }, @@ -1042,28 +1042,28 @@ { "result": "file", "type": "uncraft", - "time": "0 m", + "time": "6 s", "components": [ [ [ "paper", 3 ] ] ], "flags": [ "BLIND_EASY" ] }, { "result": "fitover_sunglasses", "type": "uncraft", - "time": "0 m", + "time": "6 s", "components": [ [ [ "glass_tinted", 1 ] ] ], "flags": [ "BLIND_EASY" ] }, { "result": "flyer", "type": "uncraft", - "time": "0 m", + "time": "6 s", "components": [ [ [ "paper", 1 ] ] ], "flags": [ "BLIND_EASY" ] }, { "result": "scorecard", "type": "uncraft", - "time": "0 m", + "time": "6 s", "components": [ [ [ "paper", 1 ] ] ], "flags": [ "BLIND_EASY" ] }, @@ -1091,19 +1091,19 @@ { "result": "glass", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "glass_shard", 1 ] ] ] }, { "result": "glass_bowl", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "glass_shard", 1 ] ] ] }, { "result": "glass_plate", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "glass_shard", 1 ] ] ] }, { @@ -1122,28 +1122,28 @@ { "result": "gold_bracelet", "type": "uncraft", - "time": "0 m", + "time": "36 s", "qualities": [ { "id": "HAMMER", "level": 1 } ], "components": [ [ [ "gold_small", 8 ] ] ] }, { "result": "gold_dental_grill", "type": "uncraft", - "time": "0 m", + "time": "30 s", "qualities": [ { "id": "HAMMER", "level": 1 } ], "components": [ [ [ "gold_small", 2 ] ] ] }, { "result": "gold_ear", "type": "uncraft", - "time": "0 m", + "time": "30 s", "qualities": [ { "id": "HAMMER", "level": 1 } ], "components": [ [ [ "gold_small", 2 ] ] ] }, { "result": "gold_watch", "type": "uncraft", - "time": "0 m", + "time": "36 s", "qualities": [ { "id": "HAMMER", "level": 1 }, { "id": "SCREW", "level": 1 } ], "components": [ [ [ "gold_small", 12 ] ], [ [ "clockworks", 1 ] ] ] }, @@ -1169,7 +1169,7 @@ { "result": "holy_symbol", "type": "uncraft", - "time": "0 m", + "time": "30 s", "qualities": [ { "id": "HAMMER", "level": 1 } ], "components": [ [ [ "gold_small", 4 ] ] ] }, @@ -1209,7 +1209,7 @@ { "result": "l_HFPack", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "l_long_45", 1 ] ], [ [ "l_enforcer_45", 1 ] ], @@ -1260,13 +1260,13 @@ { "result": "lighter", "type": "uncraft", - "time": "0 m", + "time": "6 s", "components": [ [ [ "pilot_light", 1 ] ], [ [ "plastic_chunk", 1 ] ] ] }, { "result": "locket", "type": "uncraft", - "time": "0 m", + "time": "30 s", "qualities": [ { "id": "HAMMER", "level": 1 } ], "components": [ [ [ "silver_small", 10 ] ] ] }, @@ -1281,14 +1281,14 @@ { "result": "makeshift_sling", "type": "uncraft", - "time": "0 m", + "time": "6 s", "components": [ [ [ "sheet", 1 ] ] ], "flags": [ "BLIND_HARD" ] }, { "result": "many_years_old_newspaper", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "paper", 2 ] ] ], "flags": [ "BLIND_EASY" ] }, @@ -1335,21 +1335,21 @@ { "result": "militarymap", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "paper", 10 ] ] ], "flags": [ "BLIND_EASY" ] }, { "result": "money_bundle", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "paper", 2 ] ] ], "flags": [ "BLIND_EASY" ] }, { "result": "months_old_newspaper", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "paper", 2 ] ] ], "flags": [ "BLIND_EASY" ] }, @@ -1400,21 +1400,21 @@ { "result": "necklace", "type": "uncraft", - "time": "0 m", + "time": "30 s", "qualities": [ { "id": "HAMMER", "level": 1 } ], "components": [ [ [ "silver_small", 10 ] ] ] }, { "result": "newest_newspaper", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "paper", 2 ] ] ], "flags": [ "BLIND_EASY" ] }, { "result": "one_year_old_newspaper", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "paper", 2 ] ] ], "flags": [ "BLIND_EASY" ] }, @@ -1432,7 +1432,7 @@ "type": "uncraft", "skill_used": "fabrication", "difficulty": 1, - "time": "0 m", + "time": "30 s", "qualities": [ { "id": "WRENCH", "level": 1 } ], "components": [ [ [ "scrap", 1 ] ], [ [ "plastic_chunk", 1 ] ], [ [ "metal_tank_little", 1 ] ] ] }, @@ -1454,7 +1454,7 @@ { "result": "pocketwatch", "type": "uncraft", - "time": "0 m", + "time": "36 s", "qualities": [ { "id": "SCREW", "level": 1 } ], "components": [ [ [ "gold_small", 3 ] ], [ [ "clockworks", 1 ] ] ] }, @@ -1542,7 +1542,7 @@ { "result": "radio_car_box", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "radio_car", 1 ] ], [ [ "radiocontrol", 1 ] ], [ [ "light_battery_cell", 2 ] ] ] }, { @@ -1570,27 +1570,27 @@ { "result": "ref_lighter", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "pilot_light", 1 ] ], [ [ "scrap", 1 ] ] ] }, { "result": "restaurantmap", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "paper", 10 ] ] ], "flags": [ "BLIND_EASY" ] }, { "result": "ring", "type": "uncraft", - "time": "0 m", + "time": "30 s", "qualities": [ { "id": "HAMMER", "level": 1 } ], "components": [ [ [ "gold_small", 2 ] ] ] }, { "result": "roadmap", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "paper", 10 ] ] ], "flags": [ "BLIND_EASY" ] }, @@ -1632,21 +1632,21 @@ { "result": "sf_watch", "type": "uncraft", - "time": "0 m", + "time": "36 s", "qualities": [ { "id": "HAMMER", "level": 1 }, { "id": "SCREW", "level": 1 } ], "components": [ [ [ "gold_small", 5 ] ], [ [ "silver_small", 12 ] ], [ [ "clockworks", 1 ] ] ] }, { "result": "silver_bracelet", "type": "uncraft", - "time": "0 m", + "time": "36 s", "qualities": [ { "id": "HAMMER", "level": 1 } ], "components": [ [ [ "silver_small", 10 ] ] ] }, { "result": "silver_ear", "type": "uncraft", - "time": "0 m", + "time": "30 s", "qualities": [ { "id": "HAMMER", "level": 1 } ], "components": [ [ [ "silver_small", 3 ] ] ] }, @@ -1655,14 +1655,14 @@ "type": "uncraft", "skill_used": "fabrication", "difficulty": 1, - "time": "0 m", + "time": "30 s", "qualities": [ { "id": "WRENCH", "level": 1 } ], "components": [ [ [ "scrap", 1 ] ], [ [ "plastic_chunk", 1 ] ], [ [ "metal_tank_little", 1 ] ] ] }, { "result": "small_relic", "type": "uncraft", - "time": "0 m", + "time": "30 s", "qualities": [ { "id": "HAMMER", "level": 1 } ], "components": [ [ [ "silver_small", 20 ] ] ] }, @@ -1671,7 +1671,7 @@ "type": "uncraft", "skill_used": "fabrication", "difficulty": 1, - "time": "0 m", + "time": "30 s", "qualities": [ { "id": "WRENCH", "level": 1 } ], "components": [ [ [ "plastic_chunk", 2 ] ], [ [ "canister_empty", 1 ] ] ] }, @@ -1741,28 +1741,28 @@ { "result": "sunglasses", "type": "uncraft", - "time": "0 m", + "time": "6 s", "components": [ [ [ "glass_tinted", 1 ] ] ], "flags": [ "BLIND_EASY" ] }, { "result": "survivormap", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "paper", 10 ] ] ], "flags": [ "BLIND_EASY" ] }, { "result": "survnote", "type": "uncraft", - "time": "0 m", + "time": "6 s", "components": [ [ [ "paper", 1 ] ] ], "flags": [ "BLIND_EASY" ] }, { "result": "talking_doll", "type": "uncraft", - "time": "0 m", + "time": "30 s", "qualities": [ { "id": "SCREW", "level": 1 } ], "components": [ [ [ "scrap", 1 ] ], [ [ "plastic_chunk", 2 ] ], [ [ "rag", 2 ] ], [ [ "RAM", 1 ] ], [ [ "e_scrap", 2 ] ] ] }, @@ -1770,7 +1770,7 @@ "result": "tank_top", "type": "uncraft", "skill_used": "tailor", - "time": "0 m", + "time": "30 s", "components": [ [ [ "rag", 4 ] ] ], "flags": [ "BLIND_HARD" ] }, @@ -1879,7 +1879,7 @@ { "result": "tieclip", "type": "uncraft", - "time": "0 m", + "time": "30 s", "qualities": [ { "id": "HAMMER", "level": 1 } ], "components": [ [ [ "silver_small", 4 ] ] ] }, @@ -1901,7 +1901,7 @@ { "result": "touristmap", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "paper", 10 ] ] ], "flags": [ "BLIND_EASY" ] }, @@ -1909,7 +1909,7 @@ "result": "tshirt", "type": "uncraft", "skill_used": "tailor", - "time": "0 m", + "time": "30 s", "components": [ [ [ "rag", 5 ] ] ], "flags": [ "BLIND_HARD" ] }, @@ -1917,21 +1917,21 @@ "result": "tshirt_tour", "type": "uncraft", "skill_used": "tailor", - "time": "0 m", + "time": "30 s", "components": [ [ [ "rag", 5 ] ] ], "flags": [ "BLIND_HARD" ] }, { "result": "usb_drive", "type": "uncraft", - "time": "0 m", + "time": "30 s", "qualities": [ { "id": "SCREW", "level": 1 } ], "components": [ [ [ "RAM", 1 ] ] ] }, { "result": "vac_sealer", "type": "uncraft", - "time": "0 m", + "time": "30 s", "qualities": [ { "id": "SCREW", "level": 1 } ], "components": [ [ [ "scrap", 4 ] ], [ [ "element", 2 ] ], [ [ "hose", 1 ] ], [ [ "power_supply", 1 ] ] ] }, @@ -1957,7 +1957,7 @@ { "result": "weeks_old_newspaper", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "paper", 2 ] ] ], "flags": [ "BLIND_EASY" ] }, @@ -1978,21 +1978,21 @@ { "result": "wrapper", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "paper", 2 ] ] ], "flags": [ "BLIND_EASY" ] }, { "result": "wristwatch", "type": "uncraft", - "time": "0 m", + "time": "30 s", "qualities": [ { "id": "SCREW", "level": 1 } ], "components": [ [ [ "plastic_chunk", 1 ] ], [ [ "processor", 1 ] ], [ [ "light_minus_battery_cell", 1 ] ] ] }, { "result": "years_old_newspaper", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "paper", 2 ] ] ], "flags": [ "BLIND_EASY" ] }, @@ -2160,7 +2160,7 @@ { "result": "1st_aid", "type": "uncraft", - "time": "0 m", + "time": "6 s", "components": [ [ [ "medical_tape", 20 ] ], [ [ "aspirin", 10 ] ], @@ -2175,7 +2175,7 @@ { "result": "garlic", "type": "uncraft", - "time": "0 m", + "time": "6 s", "components": [ [ [ "garlic_clove", 6 ] ] ], "flags": [ "BLIND_EASY" ] }, @@ -2189,7 +2189,7 @@ { "result": "styrofoam_cup", "type": "uncraft", - "time": "0 m", + "time": "6 s", "components": [ [ [ "paper", 10 ] ], [ [ "plastic_chunk", 2 ] ] ], "flags": [ "BLIND_EASY" ] }, @@ -2207,21 +2207,21 @@ "type": "uncraft", "skill_used": "fabrication", "difficulty": 1, - "time": "0 m", + "time": "48 s", "qualities": [ { "id": "SAW_M", "level": 1 } ], "components": [ [ [ "scrap", 7 ] ], [ [ "steel_chunk", 1 ] ], [ [ "platinum_small", 8 ] ], [ [ "pipe", 2 ] ] ] }, { "result": "fluid_preserved_brain", "type": "uncraft", - "time": "0 m", + "time": "6 s", "components": [ [ [ "chem_formaldehyde", 10 ] ], [ [ "human_brain_embalmed", 5 ] ], [ [ "jar_3l_glass", 1 ] ] ], "flags": [ "BLIND_EASY" ] }, { "result": "trailmap", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "paper", 10 ] ] ], "flags": [ "BLIND_EASY" ] }, diff --git a/data/json/recipes/recipe_deconstruction_package.json b/data/json/recipes/recipe_deconstruction_package.json index 3cfeeff72448d..737bc2305f38a 100644 --- a/data/json/recipes/recipe_deconstruction_package.json +++ b/data/json/recipes/recipe_deconstruction_package.json @@ -2,7 +2,7 @@ { "result": "mre_accessory", "type": "uncraft", - "time": "0 m", + "time": "4 s", "components": [ [ [ "pur_tablets", 6 ] ], [ [ "gummy_vitamins", 1 ] ], @@ -20,14 +20,14 @@ { "result": "mre_dessert", "type": "uncraft", - "time": "0 m", + "time": "4 s", "components": [ [ [ "dry_fruit", 1 ] ], [ [ "chocolate", 1 ] ], [ [ "candy2", 2 ] ], [ [ "cookies", 1 ] ], [ [ "bag_plastic", 1 ] ] ], "flags": [ "BLIND_EASY" ] }, { "result": "mre_chilibeans_box", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "mre_chilibeans", 4 ] ], [ [ "crackers", 2 ] ], @@ -42,7 +42,7 @@ { "result": "mre_bbqbeef_box", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "mre_bbqbeef", 4 ] ], [ [ "crackers", 2 ] ], @@ -57,7 +57,7 @@ { "result": "mre_chickennoodle_box", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "mre_chickennoodle", 4 ] ], [ [ "crackers", 2 ] ], @@ -72,7 +72,7 @@ { "result": "mre_spaghetti_box", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "mre_spaghetti", 4 ] ], [ [ "crackers", 2 ] ], @@ -87,7 +87,7 @@ { "result": "mre_chicken_box", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "mre_chicken", 4 ] ], [ [ "crackers", 2 ] ], @@ -102,7 +102,7 @@ { "result": "mre_beeftaco_box", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "mre_beeftaco", 4 ] ], [ [ "crackers", 2 ] ], @@ -117,7 +117,7 @@ { "result": "mre_beef_box", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "mre_beef", 4 ] ], [ [ "crackers", 2 ] ], @@ -132,7 +132,7 @@ { "result": "mre_meatball_box", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "mre_meatball", 4 ] ], [ [ "crackers", 2 ] ], @@ -147,7 +147,7 @@ { "result": "mre_beefstew_box", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "mre_beefstew", 4 ] ], [ [ "crackers", 2 ] ], @@ -162,7 +162,7 @@ { "result": "mre_chilimac_box", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "mre_chilimac", 4 ] ], [ [ "crackers", 2 ] ], @@ -177,7 +177,7 @@ { "result": "mre_veggy_box", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "mre_veggy", 4 ] ], [ [ "crackers", 2 ] ], @@ -192,7 +192,7 @@ { "result": "mre_macaronimarinara_box", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "mre_macaronimarinara", 4 ] ], [ [ "crackers", 2 ] ], @@ -207,7 +207,7 @@ { "result": "mre_cheesetort_box", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "mre_cheesetort", 4 ] ], [ [ "crackers", 2 ] ], @@ -222,7 +222,7 @@ { "result": "mre_mushroomfettuccine_box", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "mre_mushroomfettuccine", 4 ] ], [ [ "crackers", 2 ] ], @@ -237,7 +237,7 @@ { "result": "mre_mexicanchickenstew_box", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "mre_mexicanchickenstew", 4 ] ], [ [ "crackers", 2 ] ], @@ -252,7 +252,7 @@ { "result": "mre_chickenburritobowl_box", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "mre_chickenburritobowl", 4 ] ], [ [ "crackers", 2 ] ], @@ -267,7 +267,7 @@ { "result": "mre_maplesausage_box", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "mre_maplesausage", 4 ] ], [ [ "crackers", 2 ] ], @@ -282,7 +282,7 @@ { "result": "mre_ravioli_box", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "mre_ravioli", 4 ] ], [ [ "crackers", 2 ] ], @@ -297,7 +297,7 @@ { "result": "mre_pepperjackbeef_box", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "mre_pepperjackbeef", 4 ] ], [ [ "crackers", 2 ] ], @@ -312,7 +312,7 @@ { "result": "mre_hashbrownbacon_box", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "mre_hashbrownbacon", 4 ] ], [ [ "crackers", 2 ] ], @@ -327,7 +327,7 @@ { "result": "mre_lemontuna_box", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "mre_lemontuna", 4 ] ], [ [ "crackers", 2 ] ], @@ -342,7 +342,7 @@ { "result": "mre_asianbeef_box", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "mre_asianbeef", 4 ] ], [ [ "crackers", 2 ] ], @@ -357,7 +357,7 @@ { "result": "mre_chickenpesto_box", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "mre_chickenpesto", 4 ] ], [ [ "crackers", 2 ] ], @@ -372,7 +372,7 @@ { "result": "mre_southwestbeef_box", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "mre_southwestbeef", 4 ] ], [ [ "crackers", 2 ] ], @@ -387,7 +387,7 @@ { "result": "mre_hotdog_box", "type": "uncraft", - "time": "0 m", + "time": "30 s", "components": [ [ [ "mre_hotdog", 4 ] ], [ [ "crackers", 2 ] ], diff --git a/data/json/recipes/recipe_food.json b/data/json/recipes/recipe_food.json index e4f83e60d32ef..4085d8a0e93c5 100644 --- a/data/json/recipes/recipe_food.json +++ b/data/json/recipes/recipe_food.json @@ -19,7 +19,7 @@ "id_suffix": "using_water_purifier", "subcategory": "CSC_FOOD_DRINKS", "skill_used": "cooking", - "time": "0 m", + "time": "9 s", "autolearn": true, "tools": [ [ [ "water_purifier", 1 ] ] ], "components": [ [ [ "water", 1 ] ] ] @@ -82,7 +82,7 @@ "type": "recipe", "result": "meat_scrap_cooked", "copy-from": "meat_cooked", - "time": "0 m", + "time": "45 s", "tools": [ [ [ "surface_heat", 1, "LIST" ] ] ], "components": [ [ [ "meat_scrap", 1 ] ] ] }, @@ -1262,7 +1262,7 @@ "category": "CC_FOOD", "subcategory": "CSC_FOOD_SNACK", "skill_used": "cooking", - "time": "0 m", + "time": "18 s", "autolearn": true, "components": [ [ [ "popcorn", 1 ] ], [ [ "salt", 1 ], [ "seasoning_salt", 1 ] ] ] }, @@ -1658,7 +1658,7 @@ "category": "CC_FOOD", "subcategory": "CSC_FOOD_DRINKS", "skill_used": "cooking", - "time": "0 m", + "time": "30 s", "autolearn": true, "components": [ [ [ "lemonade_powder", 1 ] ], [ [ "water_clean", 1 ] ] ] }, @@ -2874,7 +2874,7 @@ "subcategory": "CSC_FOOD_VEGGI", "skill_used": "cooking", "difficulty": 1, - "time": "0 m", + "time": "18 s", "autolearn": true, "qualities": [ { "id": "COOK", "level": 2 }, { "id": "CUT", "level": 1 } ], "tools": [ [ [ "surface_heat", 2, "LIST" ] ] ], @@ -4823,7 +4823,7 @@ "category": "CC_FOOD", "subcategory": "CSC_FOOD_DRINKS", "skill_used": "cooking", - "time": "0 m", + "time": "1 s", "autolearn": true, "flags": [ "BLIND_HARD" ], "components": [ [ [ "chem_ethanol", 125 ] ], [ [ "water", 1 ], [ "water_clean", 1 ] ] ] @@ -4834,7 +4834,7 @@ "category": "CC_FOOD", "subcategory": "CSC_FOOD_DRINKS", "skill_used": "cooking", - "time": "0 m", + "time": "1 s", "charges": 1, "autolearn": true, "flags": [ "BLIND_EASY" ], @@ -4857,7 +4857,7 @@ "category": "CC_FOOD", "subcategory": "CSC_FOOD_DRINKS", "skill_used": "cooking", - "time": "0 m", + "time": "1 s", "charges": 1, "autolearn": true, "flags": [ "BLIND_EASY" ], diff --git a/data/json/recipes/recipe_others.json b/data/json/recipes/recipe_others.json index a1806db3c9d48..2ba0b58a88c67 100644 --- a/data/json/recipes/recipe_others.json +++ b/data/json/recipes/recipe_others.json @@ -1637,7 +1637,7 @@ "subcategory": "CSC_OTHER_TRAPS", "skill_used": "traps", "difficulty": 1, - "time": "0 m", + "time": "30 s", "autolearn": true, "using": [ [ "cordage", 1 ] ], "components": [ [ [ "superglue", 1 ] ] ] @@ -1661,7 +1661,7 @@ "category": "CC_OTHER", "subcategory": "CSC_OTHER_TRAPS", "skill_used": "traps", - "time": "0 m", + "time": "15 s", "reversible": true, "autolearn": true, "qualities": [ { "id": "HAMMER", "level": 1 } ], @@ -1853,7 +1853,7 @@ "subcategory": "CSC_OTHER_MATERIALS", "skill_used": "fabrication", "difficulty": 1, - "time": "0 m", + "time": "30 s", "autolearn": true, "using": [ [ "welding_standard", 2 ] ], "qualities": [ { "id": "HAMMER", "level": 2 } ], diff --git a/data/json/recipes/recipe_vehicle.json b/data/json/recipes/recipe_vehicle.json index fe98a0c4c7e11..8875b1d719eab 100644 --- a/data/json/recipes/recipe_vehicle.json +++ b/data/json/recipes/recipe_vehicle.json @@ -327,6 +327,6 @@ "time": "60 m", "using": [ [ "welding_standard", 5 ] ], "qualities": [ { "id": "HAMMER", "level": 2 }, { "id": "SAW_M", "level": 1 }, { "id": "WRENCH", "level": 1 } ], - "components": [ [ [ "sheet_metal", 3 ] ], [ [ "clockworks", 2 ] ] ] + "components": [ [ [ "sheet_metal", 3 ] ], [ [ "hinge", 2 ] ] ] } ] diff --git a/data/json/recipes/recipe_weapon.json b/data/json/recipes/recipe_weapon.json index b12ddb77ef88f..4d99eb16cfcae 100644 --- a/data/json/recipes/recipe_weapon.json +++ b/data/json/recipes/recipe_weapon.json @@ -88,7 +88,7 @@ "category": "CC_WEAPON", "subcategory": "CSC_WEAPON_PIERCING", "skill_used": "fabrication", - "time": "0 m", + "time": "48 s", "autolearn": true, "qualities": [ { "id": "CUT", "level": 1 } ], "components": [ [ [ "stick", 1 ], [ "2x4", 1 ], [ "broom", 1 ], [ "pool_cue", 1 ] ] ] @@ -310,22 +310,104 @@ "tools": [ [ [ "tongs", -1 ] ], [ [ "forge", 50 ], [ "oxy_torch", 10 ] ] ], "components": [ [ [ "javelin", 1 ] ], [ [ "scrap", 1 ] ] ] }, + { + "type": "recipe", + "result": "spear_spike", + "category": "CC_WEAPON", + "subcategory": "CSC_WEAPON_PIERCING", + "skill_used": "fabrication", + "time": "2 m", + "reversible": true, + "autolearn": true, + "components": [ + [ [ "stick_long", 1 ], [ "long_pole", 1 ], [ "broom", 1 ], [ "pool_cue", 1 ] ], + [ + [ "spike", 1 ], + [ "knife_steak", 1 ], + [ "knife_paring", 1 ], + [ "knife_folding", 1 ], + [ "scalpel", 1 ], + [ "xacto", 1 ] + ], + [ [ "cordage", 1, "LIST" ], [ "duct_tape", 50 ] ] + ] + }, { "type": "recipe", "result": "spear_knife", "category": "CC_WEAPON", "subcategory": "CSC_WEAPON_PIERCING", "skill_used": "fabrication", - "time": "1 m", + "time": "2 m", "reversible": true, "autolearn": true, "components": [ - [ [ "stick_long", 1 ], [ "broom", 1 ], [ "pool_cue", 1 ] ], - [ [ "spike", 1 ] ], + [ [ "stick_long", 1 ], [ "long_pole", 1 ], [ "broom", 1 ], [ "pool_cue", 1 ] ], + [ + [ "knife_butcher", 1 ], + [ "knife_chef", 1 ], + [ "knife_carving", 1 ], + [ "knife_vegetable_cleaver", 1 ], + [ "knife_meat_cleaver", 1 ], + [ "blade", 1 ], + [ "knife_combat", 1 ], + [ "knife_hunting", 1 ], + [ "knife_rambo", 1 ], + [ "knife_rm42", 1 ], + [ "knife_trench", 1 ], + [ "tanto", 1 ], + [ "kirpan", 1 ] + ], [ [ "rag", 1 ], [ "felt_patch", 1 ], [ "leather", 1 ], [ "fur", 1 ] ], [ [ "cordage", 1, "LIST" ], [ "duct_tape", 50 ] ] ] }, + { + "type": "recipe", + "result": "spear_knife_superior", + "category": "CC_WEAPON", + "subcategory": "CSC_WEAPON_PIERCING", + "skill_used": "fabrication", + "difficulty": 1, + "time": "30 m", + "reversible": true, + "autolearn": true, + "qualities": [ { "id": "CUT", "level": 1 }, { "id": "HAMMER", "level": 1 }, { "id": "DRILL", "level": 1 } ], + "components": [ + [ [ "stick_long", 1 ], [ "long_pole", 1 ], [ "pool_cue", 1 ] ], + [ + [ "knife_butcher", 1 ], + [ "knife_chef", 1 ], + [ "knife_carving", 1 ], + [ "knife_vegetable_cleaver", 1 ], + [ "knife_meat_cleaver", 1 ], + [ "blade", 1 ], + [ "knife_combat", 1 ], + [ "knife_hunting", 1 ], + [ "knife_rambo", 1 ], + [ "knife_rm42", 1 ], + [ "knife_trench", 1 ], + [ "tanto", 1 ], + [ "kirpan", 1 ] + ], + [ [ "nail", 2 ] ], + [ [ "cordage", 2, "LIST" ], [ "duct_tape", 100 ] ] + ] + }, + { + "type": "recipe", + "result": "spear_knife_superior", + "category": "CC_WEAPON", + "subcategory": "CSC_WEAPON_PIERCING", + "skill_used": "fabrication", + "id_suffix": "from simple version", + "difficulty": 1, + "time": "30 m", + "reversible": true, + "autolearn": true, + "qualities": [ { "id": "CUT", "level": 1 }, { "id": "HAMMER", "level": 1 }, { "id": "DRILL", "level": 1 } ], + "components": [ [ [ "spear_knife", 1 ] ], [ [ "nail", 2 ] ], [ [ "cordage", 1, "LIST" ], [ "duct_tape", 50 ] ] ] + }, { "type": "recipe", "result": "longbow", @@ -1216,7 +1298,7 @@ "subcategory": "CSC_WEAPON_MODS", "skill_used": "fabrication", "difficulty": 1, - "time": "0 m", + "time": "30 s", "reversible": true, "autolearn": true, "using": [ [ "cordage", 1 ] ], @@ -1243,7 +1325,7 @@ "subcategory": "CSC_WEAPON_MODS", "skill_used": "fabrication", "difficulty": 1, - "time": "0 m", + "time": "30 s", "reversible": true, "autolearn": true, "using": [ [ "cordage_short", 2 ] ], @@ -1257,7 +1339,7 @@ "skill_used": "mechanics", "skills_required": [ "gun", 1 ], "difficulty": 1, - "time": "0 m", + "time": "39 s", "autolearn": true, "qualities": [ { "id": "SAW_M_FINE", "level": 1 } ], "components": [ [ [ "rag", 4 ], [ "plastic_chunk", 4 ], [ "felt_patch", 4 ], [ "leather", 4 ] ], [ [ "pipe", 1 ] ] ] @@ -2209,26 +2291,6 @@ "flags": [ "BLIND_EASY" ], "components": [ [ [ "rock", 1 ] ], [ [ "socks", 1 ], [ "socks_wool", 1 ] ] ] }, - { - "type": "recipe", - "result": "unbio_blaster_gun", - "category": "CC_WEAPON", - "subcategory": "CSC_WEAPON_RANGED", - "skill_used": "electronics", - "skills_required": [ "fabrication", 4, "gun", 2 ], - "difficulty": 6, - "time": "30 m", - "book_learn": [ [ "recipe_lab_elec", 5 ] ], - "qualities": [ { "id": "SCREW_FINE", "level": 1 } ], - "tools": [ [ [ "soldering_standard", 20, "LIST" ], [ "small_repairkit", 20 ], [ "large_repairkit", 10 ] ] ], - "components": [ - [ [ "bio_blaster", 1 ] ], - [ [ "power_supply", 1 ] ], - [ [ "2x4", 1 ], [ "stick", 1 ] ], - [ [ "cable", 3 ] ], - [ [ "plastic_chunk", 3 ] ] - ] - }, { "type": "recipe", "result": "tonfa", diff --git a/data/json/recipes/weapon/explosive.json b/data/json/recipes/weapon/explosive.json index 30d742447c93f..7ca8d817d5bc9 100644 --- a/data/json/recipes/weapon/explosive.json +++ b/data/json/recipes/weapon/explosive.json @@ -5,7 +5,7 @@ "category": "CC_WEAPON", "subcategory": "CSC_WEAPON_EXPLOSIVE", "skill_used": "fabrication", - "time": "0 m", + "time": "30 s", "reversible": true, "autolearn": true, "components": [ @@ -194,7 +194,7 @@ "category": "CC_WEAPON", "subcategory": "CSC_WEAPON_EXPLOSIVE", "skill_used": "fabrication", - "time": "0 m", + "time": "30 s", "reversible": true, "autolearn": true, "components": [ diff --git a/data/json/vehicleparts/turret.json b/data/json/vehicleparts/turret.json index 3fa9e82f6f3ea..589dda94fc7f3 100644 --- a/data/json/vehicleparts/turret.json +++ b/data/json/vehicleparts/turret.json @@ -25,21 +25,6 @@ }, "extend": { "flags": [ "USE_TANKS" ] } }, - { - "id": "fusion_gun", - "copy-from": "turret", - "type": "vehicle_part", - "name": "mounted fusion gun", - "item": "ftk93", - "color": "magenta", - "broken_color": "magenta", - "difficulty": 6, - "breaks_into": [ { "item": "ftk93", "prob": 50 } ], - "requirements": { - "install": { "skills": [ [ "mechanics", 6 ], [ "electronics", 6 ] ], "time": 10000 }, - "removal": { "skills": [ [ "mechanics", 4 ] ] } - } - }, { "id": "laser_rifle", "copy-from": "turret", diff --git a/data/json/vehicles/helicopters.json b/data/json/vehicles/helicopters.json index c3cd8e6dfde31..e2f8f439ad9d5 100644 --- a/data/json/vehicles/helicopters.json +++ b/data/json/vehicles/helicopters.json @@ -115,8 +115,8 @@ { "x": -3, "y": 2, "part": "board_se" }, { "x": -2, "y": -5, "part": "turret_mount" }, { "x": -2, "y": 3, "part": "turret_mount" }, - { "x": -2, "y": -5, "part": "mounted_m134" }, - { "x": -2, "y": 3, "part": "mounted_m134" }, + { "x": -2, "y": -5, "part": "mounted_m134", "ammo": 80, "ammo_types": [ "308" ], "ammo_qty": [ 0, 500 ] }, + { "x": -2, "y": 3, "part": "mounted_m134", "ammo": 80, "ammo_types": [ "308" ], "ammo_qty": [ 0, 500 ] }, { "x": 4, "y": -1, "part": "windshield" }, { "x": -4, "y": -3, "part": "board_sw" }, { "x": -4, "y": 1, "part": "board_se" }, @@ -282,7 +282,7 @@ { "x": -2, "y": -4, "part": "board_horizontal" }, { "x": -3, "y": -4, "part": "board_sw" }, { "x": -2, "y": -5, "part": "turret_mount" }, - { "x": -2, "y": -5, "part": "mounted_m134" }, + { "x": -2, "y": -5, "part": "mounted_m134", "ammo": 80, "ammo_types": [ "308" ], "ammo_qty": [ 0, 500 ] }, { "x": 4, "y": -1, "part": "windshield" }, { "x": -4, "y": -3, "part": "board_sw" }, { "x": -4, "y": 1, "part": "board_se" }, @@ -408,8 +408,8 @@ { "x": -3, "y": 4, "part": "board_se" }, { "x": -2, "y": -3, "part": "turret_mount" }, { "x": -2, "y": 5, "part": "turret_mount" }, - { "x": -2, "y": -3, "part": "mounted_m134" }, - { "x": -2, "y": 5, "part": "mounted_m134" }, + { "x": -2, "y": -3, "part": "mounted_m134", "ammo": 80, "ammo_types": [ "308" ], "ammo_qty": [ 0, 500 ] }, + { "x": -2, "y": 5, "part": "mounted_m134", "ammo": 80, "ammo_types": [ "308" ], "ammo_qty": [ 0, 500 ] }, { "x": -4, "y": -1, "part": "board_sw" }, { "x": -4, "y": 3, "part": "board_se" }, { "x": -4, "y": 2, "part": "board_vertical" }, diff --git a/data/mods/Aftershock/maps/afs_item_groups.json b/data/mods/Aftershock/maps/afs_item_groups.json index bf75ce000d02c..6c3a57a236133 100644 --- a/data/mods/Aftershock/maps/afs_item_groups.json +++ b/data/mods/Aftershock/maps/afs_item_groups.json @@ -8,6 +8,7 @@ [ "v29", 7 ], [ "afs_hydraulic_gauntlet", 3 ], [ "afs_energy_saber_off", 3 ], + [ "ftk93", 5 ], [ "afs_hardlight_longbow", 2 ] ] }, @@ -31,7 +32,8 @@ [ "afs_bio_precision_solderers", 10 ], [ "afs_bio_missiles", 10 ], [ "afs_bio_linguistic_coprocessor", 10 ], - [ "afs_bio_dopamine_stimulators", 10 ] + [ "afs_bio_dopamine_stimulators", 10 ], + [ "bio_blaster", 5 ] ] }, { @@ -47,7 +49,7 @@ { "id": "bionics_mil", "type": "item_group", - "items": [ [ "bio_furnace", 10 ] ] + "items": [ [ "bio_furnace", 10 ], [ "bio_blaster", 5 ] ] }, { "id": "bionics_sci", @@ -77,7 +79,14 @@ { "id": "rare", "type": "item_group", - "items": [ [ "afs_calorie_pill", 10 ], [ "afs_sundew", 10 ], [ "afs_bag_of_holding", 1 ], [ "afs_atomic_smartphone", 5 ] ] + "items": [ + [ "afs_calorie_pill", 10 ], + [ "afs_sundew", 10 ], + [ "afs_bag_of_holding", 1 ], + [ "afs_atomic_smartphone", 5 ], + [ "laser_pack", 10 ], + [ "ftk93", 5 ] + ] }, { "id": "spider", diff --git a/data/mods/Aftershock/recipes/afs__recipes.json b/data/mods/Aftershock/recipes/afs__recipes.json index 5d0e937af997c..ed17a735a74c0 100644 --- a/data/mods/Aftershock/recipes/afs__recipes.json +++ b/data/mods/Aftershock/recipes/afs__recipes.json @@ -462,5 +462,40 @@ [ [ "scrap", 5 ] ], [ [ "can_drink_unsealed", 5 ], [ "can_food_unsealed", 5 ], [ "canister_empty", 5 ] ] ] + }, + { + "type": "recipe", + "result": "reloaded_laser_pack", + "category": "CC_AMMO", + "subcategory": "CSC_AMMO_OTHER", + "skill_used": "fabrication", + "skills_required": [ "electronics", 5 ], + "difficulty": 3, + "time": "10 m", + "reversible": true, + "decomp_learn": 4, + "book_learn": [ [ "recipe_lab_elec", 4 ] ], + "qualities": [ { "id": "SCREW", "level": 1 } ], + "components": [ [ [ "superglue", 1 ] ], [ [ "plut_cell", 1 ] ] ] + }, + { + "type": "recipe", + "result": "unbio_blaster_gun", + "category": "CC_WEAPON", + "subcategory": "CSC_WEAPON_RANGED", + "skill_used": "electronics", + "skills_required": [ "fabrication", 4, "gun", 2 ], + "difficulty": 6, + "time": "30 m", + "book_learn": [ [ "recipe_lab_elec", 5 ] ], + "qualities": [ { "id": "SCREW_FINE", "level": 1 } ], + "tools": [ [ [ "soldering_standard", 20, "LIST" ], [ "small_repairkit", 20 ], [ "large_repairkit", 10 ] ] ], + "components": [ + [ [ "bio_blaster", 1 ] ], + [ [ "power_supply", 1 ] ], + [ [ "2x4", 1 ], [ "stick", 1 ] ], + [ [ "cable", 3 ] ], + [ [ "plastic_chunk", 3 ] ] + ] } ] diff --git a/data/mods/Magiclysm/Spells/animist.json b/data/mods/Magiclysm/Spells/animist.json index 051ea261ea684..5f4aa87cdc815 100644 --- a/data/mods/Magiclysm/Spells/animist.json +++ b/data/mods/Magiclysm/Spells/animist.json @@ -144,6 +144,28 @@ "max_duration": 150000, "duration_increment": 4000 }, + { + "id": "necrotic_gaze", + "type": "SPELL", + "name": "Necrotic Gaze", + "description": "You use the power of your own blood to imbue necrotic energy into your gaze, damaging the target you look at.", + "valid_targets": [ "hostile" ], + "effect": "target_attack", + "spell_class": "ANIMIST", + "energy_source": "HP", + "flags": [ "NO_LEGS", "CONCENTRATE", "SOMATIC" ], + "min_damage": 10, + "max_damage": 160, + "damage_increment": 5, + "min_range": 3, + "max_range": 5, + "range_increment": 0.1, + "max_level": 20, + "difficulty": 3, + "base_casting_time": 100, + "base_energy_cost": 2, + "damage_type": "bio" + }, { "id": "create_rune_animist", "type": "SPELL", diff --git a/data/mods/Magiclysm/Spells/druid.json b/data/mods/Magiclysm/Spells/druid.json index 225734966eef1..54fa11bbe3ef2 100644 --- a/data/mods/Magiclysm/Spells/druid.json +++ b/data/mods/Magiclysm/Spells/druid.json @@ -132,7 +132,7 @@ "max_damage": 300, "max_level": 25, "base_energy_cost": 500, - "energy_cost_increment": 25, + "energy_increment": 25, "final_energy_cost": 1125, "difficulty": 4 }, @@ -182,7 +182,7 @@ "max_duration": 30000, "base_energy_cost": 675, "final_energy_cost": 475, - "energy_cost_increment": -5, + "energy_increment": -5, "flags": [ "HOSTILE_50", "CONCENTRATE", "SOMATIC", "VERBAL", "NO_LEGS" ], "effect": "summon", "effect_str": "mon_bear" diff --git a/data/mods/Magiclysm/Spells/kelvinist.json b/data/mods/Magiclysm/Spells/kelvinist.json index 2d46ca6c55fd6..2241b15b97d45 100644 --- a/data/mods/Magiclysm/Spells/kelvinist.json +++ b/data/mods/Magiclysm/Spells/kelvinist.json @@ -114,6 +114,10 @@ "base_energy_cost": 350, "energy_source": "MANA", "difficulty": 4, + "field_id": "fd_cold_air2", + "min_field_intensity": 0, + "max_field_intensity": 4, + "intensity_increment": 0.5, "damage_type": "cold" }, { @@ -141,31 +145,6 @@ "difficulty": 2, "damage_type": "fire" }, - { - "id": "iceball", - "type": "SPELL", - "name": "Hoary Blast", - "description": "A glowing chunk of ice bursts into being from your hand and explodes into a wave of intense cold on impact.", - "effect": "projectile_attack", - "valid_targets": [ "self", "ally", "hostile", "ground" ], - "flags": [ "SOMATIC", "VERBAL", "NO_LEGS" ], - "max_level": 20, - "min_damage": 24, - "max_damage": 68, - "damage_increment": 1.2, - "min_aoe": 3, - "max_aoe": 5, - "aoe_increment": 0.1, - "min_range": 6, - "max_range": 20, - "range_increment": 1, - "spell_class": "KELVINIST", - "base_casting_time": 150, - "base_energy_cost": 150, - "energy_source": "MANA", - "difficulty": 3, - "damage_type": "cold" - }, { "id": "frost_spray", "type": "SPELL", diff --git a/data/mods/Magiclysm/Spells/stormshaper.json b/data/mods/Magiclysm/Spells/stormshaper.json index 959d176a7db02..ebc1cfa751609 100644 --- a/data/mods/Magiclysm/Spells/stormshaper.json +++ b/data/mods/Magiclysm/Spells/stormshaper.json @@ -47,6 +47,10 @@ "final_casting_time": 100, "casting_time_increment": -5, "base_energy_cost": 140, + "field_id": "fd_electricity", + "min_field_intensity": 1, + "max_field_intensity": 1, + "field_chance": 1, "damage_type": "electric", "energy_source": "MANA" }, @@ -168,7 +172,7 @@ "base_casting_time": 120, "final_casting_time": 50, "casting_time_increment": -5, - "base_casting_cost": 55, + "base_energy_cost": 55, "damage_type": "electric", "effect": "projectile_attack" }, @@ -192,7 +196,7 @@ "difficulty": 20, "spell_class": "STORMSHAPER", "energy_source": "MANA", - "base_casting_cost": 500, + "base_energy_cost": 500, "base_casting_time": 300, "damage_type": "electric", "effect": "projectile_attack", diff --git a/data/mods/Magiclysm/furniture.json b/data/mods/Magiclysm/furniture.json index aa256785e4b14..354e894d0eb38 100644 --- a/data/mods/Magiclysm/furniture.json +++ b/data/mods/Magiclysm/furniture.json @@ -10,5 +10,16 @@ "required_str": 25, "flags": [ "TRANSLOCATOR", "MOUNTABLE", "ALLOW_FIELD_EFFECT" ], "examine_action": "translocator" + }, + { + "type": "furniture", + "id": "f_magic_circle", + "name": "Magic Circle", + "move_cost_mod": 0, + "symbol": "O", + "color": "red", + "required_str": -1, + "flags": [ "ALLOW_FIELD_EFFECT" ], + "description": "This is a rough magic circle, carved into the ground and decorated with blood, candles, and other small knicknacks." } ] diff --git a/data/mods/Magiclysm/itemgroups.json b/data/mods/Magiclysm/itemgroups/itemgroups.json similarity index 59% rename from data/mods/Magiclysm/itemgroups.json rename to data/mods/Magiclysm/itemgroups/itemgroups.json index f4379c3493db7..e85f214e66958 100644 --- a/data/mods/Magiclysm/itemgroups.json +++ b/data/mods/Magiclysm/itemgroups/itemgroups.json @@ -12,87 +12,27 @@ { "type": "item_group", "id": "homebooks", - "items": [ - [ "wizard_beginner", 3 ], - [ "wizard_advanced", 1 ], - [ "priest_beginner", 5 ], - [ "wizard_utility", 3 ], - [ "techno_idiots", 2 ] - ] + "items": [ { "group": "spellbook_loot_0", "prob": 4 } ] }, { "type": "item_group", "id": "manuals", - "items": [ [ "wizard_beginner", 3 ], [ "wizard_advanced", 1 ], [ "wizard_utility", 5 ], [ "recovery_spellbook", 8 ] ] + "items": [ { "group": "spellbook_loot_0", "prob": 3 } ] }, { "type": "item_group", "id": "lab_bookshelves", - "items": [ - [ "wizard_beginner", 3 ], - [ "wizard_advanced", 5 ], - [ "priest_beginner", 3 ], - [ "wizard_utility", 5 ], - [ "priest_advanced", 5 ], - [ "pyro", 6 ], - [ "winter_grasp", 6 ], - [ "tome_of_storms", 6 ], - [ "biomancer_spellbook", 6 ], - [ "druid_spellbook", 6 ], - [ "recovery_spellbook", 3 ], - [ "magus_spellbook", 6 ], - [ "eshaper_spellbook", 6 ], - [ "magus_spellbook_move", 3 ], - [ "summon_undead_spellbook", 2 ], - [ "techno_fundamentals", 6 ], - [ "techno_em", 5 ], - [ "techno_idiots", 3 ], - [ "alchemy_basic", 3 ] - ] + "items": [ { "group": "spellbook_loot_1", "prob": 3 }, [ "alchemy_basic", 3 ] ] }, { "type": "item_group", "id": "mansion_books", - "items": [ - [ "wizard_beginner", 10 ], - [ "wizard_advanced", 5 ], - [ "priest_beginner", 10 ], - [ "pyro", 3 ], - [ "winter_grasp", 3 ], - [ "tome_of_storms", 3 ], - [ "biomancer_spellbook", 2 ], - [ "druid_spellbook", 2 ], - [ "recovery_spellbook", 3 ], - [ "magus_spellbook", 3 ], - [ "eshaper_spellbook", 3 ], - [ "magus_spellbook_move", 2 ], - [ "summon_undead_spellbook", 1 ], - [ "techno_fundamentals", 2 ], - [ "techno_em", 1 ], - [ "techno_idiots", 3 ], - [ "alchemy_basic", 10 ] - ] + "items": [ { "group": "spellbook_loot_0", "prob": 6 }, [ "alchemy_basic", 10 ] ] }, { "type": "item_group", "id": "exotic_books", - "items": [ - [ "wizard_advanced", 12 ], - [ "pyro", 3 ], - [ "winter_grasp", 3 ], - [ "tome_of_storms", 3 ], - [ "priest_advanced", 6 ], - [ "biomancer_spellbook", 1 ], - [ "druid_spellbook", 1 ], - [ "recovery_spellbook", 3 ], - [ "magus_spellbook", 3 ], - [ "eshaper_spellbook", 3 ], - [ "magus_spellbook_move", 2 ], - [ "summon_undead_spellbook", 3 ], - [ "techno_fundamentals", 3 ], - [ "techno_em", 2 ], - [ "translocate_spellbook", 2 ] - ] + "items": [ { "group": "spellbook_loot_1", "prob": 3 } ] }, { "type": "item_group", @@ -146,27 +86,11 @@ "type": "item_group", "id": "magic_shop_books", "items": [ - [ "wizard_beginner", 30 ], - [ "wizard_advanced", 50 ], - [ "priest_beginner", 30 ], - [ "wizard_utility", 50 ], - [ "priest_advanced", 50 ], - [ "pyro", 60 ], - [ "winter_grasp", 60 ], - [ "tome_of_storms", 60 ], - [ "biomancer_spellbook", 60 ], - [ "druid_spellbook", 60 ], - [ "recovery_spellbook", 30 ], - [ "magus_spellbook", 60 ], - [ "eshaper_spellbook", 60 ], - [ "magus_spellbook_move", 30 ], - [ "techno_fundamentals", 40 ], - [ "techno_em", 30 ], - [ "techno_idiots", 60 ], - [ "translocate_spellbook", 20 ], - [ "stat_up_spellbook", 45 ], - [ "alchemy_basic", 30 ], - [ "lightning_storm_scroll", 12 ] + { "group": "spellbook_tier_0", "prob": 60 }, + { "group": "spellbook_tier_1", "prob": 50 }, + { "group": "spellbook_tier_2", "prob": 35 }, + { "group": "spellbook_tier_3", "prob": 20 }, + [ "alchemy_basic", 30 ] ] }, { diff --git a/data/mods/Magiclysm/itemgroups/spellbooks.json b/data/mods/Magiclysm/itemgroups/spellbooks.json new file mode 100644 index 0000000000000..04c93ede5b76a --- /dev/null +++ b/data/mods/Magiclysm/itemgroups/spellbooks.json @@ -0,0 +1,173 @@ +[ + { + "id": "spell_scroll_tier_0", + "type": "item_group", + "items": [ + [ "summon_scroll_smudged", 10 ], + [ "spell_scroll_smite", 40 ], + [ "spell_scroll_summon_zombie", 25 ], + [ "spell_scroll_light_healing", 50 ], + [ "spell_scroll_bio_acidicspray", 35 ], + [ "spell_scroll_create_atomic_light", 50 ], + [ "spell_scroll_blinding_flash", 50 ], + [ "spell_scroll_ethereal_grasp", 50 ], + [ "spell_scroll_druid_woodshaft", 50 ], + [ "spell_scroll_summon_cats", 65 ], + [ "spell_scroll_stonefist", 20 ], + [ "spell_scroll_eshaper_piercing_bolt", 40 ], + [ "spell_scroll_eshaper_rockbolt", 50 ], + [ "spell_scroll_create_lighter", 50 ], + [ "spell_scroll_chilling_touch", 50 ], + [ "spell_scroll_magic_missile", 50 ], + [ "spell_scroll_bless", 25 ], + [ "spell_scroll_create_atomic_lamp", 25 ], + [ "spell_scroll_taze", 25 ], + [ "spell_scroll_laze", 25 ], + [ "spell_scroll_lightning_blast", 20 ], + [ "spell_scroll_necrotic_gaze", 50 ] + ] + }, + { + "id": "spell_scroll_tier_1", + "type": "item_group", + "items": [ + [ "spell_scroll_summon_skeleton", 35 ], + [ "spell_scroll_pain_split", 25 ], + [ "spell_scroll_bio_grotesque", 40 ], + [ "spell_scroll_bio_fleshpouch", 30 ], + [ "spell_scroll_protection_aura", 50 ], + [ "spell_scroll_druid_veggrasp", 35 ], + [ "spell_scroll_druid_rootstrike", 40 ], + [ "spell_scroll_druid_naturebow1", 20 ], + [ "spell_scroll_seismic_stomp", 20 ], + [ "spell_scroll_eshaper_shardspray", 25 ], + [ "spell_scroll_point_flare", 50 ], + [ "spell_scroll_ice_spike", 50 ], + [ "spell_scroll_burning_hands", 50 ], + [ "spell_scroll_frost_spray", 50 ], + [ "spell_scroll_glide_ice", 25 ], + [ "spell_scroll_ice_shield", 25 ], + [ "spell_scroll_frost_armor", 35 ], + [ "spell_scroll_phase_door", 50 ], + [ "spell_scroll_jolt", 50 ], + [ "spell_scroll_lightning_bolt", 35 ], + [ "spell_scroll_windstrike", 50 ], + [ "spell_scroll_windrun", 35 ], + [ "spell_scroll_holy_blade", 25 ], + [ "spell_scroll_spirit_armor", 25 ], + [ "spell_scroll_quantum_tunnel_lesser", 50 ], + [ "spell_scroll_synaptic_stimulation", 20 ] + ] + }, + { + "id": "spell_scroll_tier_2", + "type": "item_group", + "items": [ + [ "spell_scroll_recover_mana", 25 ], + [ "spell_scroll_recover_pain", 25 ], + [ "spell_scroll_recover_fatigue", 25 ], + [ "spell_scroll_recover_stamina", 25 ], + [ "spell_scroll_recover_bionic_power", 25 ], + [ "spell_scroll_summon_decayed_pouncer", 35 ], + [ "spell_scroll_vicious_tentacle", 40 ], + [ "spell_scroll_bio_bonespear", 20 ], + [ "spell_scroll_megablast", 10 ], + [ "spell_scroll_eshaper_shardstorm", 50 ], + [ "spell_scroll_fireball", 50 ], + [ "spell_scroll_cone_cold", 50 ], + [ "spell_scroll_hoary_blast", 50 ], + [ "spell_scroll_gravity_well", 35 ], + [ "spell_scroll_magus_mana_bolt", 35 ], + [ "spell_scroll_magus_haste", 50 ], + [ "spell_scroll_magus_mana_beam", 35 ], + [ "spell_scroll_magus_escape", 50 ], + [ "spell_scroll_cats_grace", 50 ], + [ "spell_scroll_eagles_sight", 50 ], + [ "spell_scroll_ogres_strength", 50 ], + [ "spell_scroll_foxs_cunning", 50 ], + [ "spell_scroll_storm_hammer", 35 ], + [ "spell_scroll_animated_blade", 35 ], + [ "spell_scroll_mirror_image", 15 ] + ] + }, + { + "id": "spell_scroll_tier_3", + "type": "item_group", + "items": [ [ "spell_scroll_magus_mana_blast", 50 ], [ "lightning_storm_scroll", 50 ] ] + }, + { + "id": "spellbook_tier_0", + "type": "item_group", + "//": "These are spellbooks that a beginner would have, or scrolls that would have this tier or one tier higher of spells.", + "items": [ [ "wizard_beginner", 30 ], [ "priest_beginner", 30 ], [ "techno_idiots", 30 ] ] + }, + { + "id": "spellbook_tier_1", + "type": "item_group", + "items": [ [ "wizard_utility", 50 ], [ "priest_advanced", 50 ], [ "techno_fundamentals", 50 ], [ "stat_up_spellbook", 45 ] ] + }, + { + "id": "spellbook_tier_2", + "type": "item_group", + "items": [ + [ "winter_grasp", 60 ], + [ "tome_of_storms", 60 ], + [ "biomancer_spellbook", 60 ], + [ "druid_spellbook", 60 ], + [ "pyro", 60 ], + [ "eshaper_spellbook", 60 ], + [ "techno_em", 60 ], + [ "wizard_advanced", 60 ], + [ "stat_up_spellbook", 45 ] + ] + }, + { + "id": "spellbook_tier_3", + "type": "item_group", + "items": [ + [ "magus_spellbook", 15 ], + [ "magus_spellbook_move", 30 ], + [ "translocate_spellbook", 20 ], + [ "recovery_spellbook", 30 ] + ] + }, + { + "id": "spellbook_loot_0", + "type": "item_group", + "//": "the difference between tiers and loot are that loot should be easy to", + "//": "drop into an itemgroup or mapgen without having multiple entries for higher tier spellbooks.", + "//": "and loot tiers will not always have the lower tier spellbook tiers.", + "items": [ + { "group": "spellbook_tier_0", "prob": 60 }, + { "group": "spellbook_tier_1", "prob": 30 }, + { "group": "spellbook_tier_2", "prob": 9 }, + { "group": "spellbook_tier_3", "prob": 1 }, + { "group": "spell_scroll_tier_0", "prob": 300 }, + { "group": "spell_scroll_tier_1", "prob": 100 } + ] + }, + { + "id": "spellbook_loot_1", + "type": "item_group", + "items": [ + { "group": "spellbook_tier_0", "prob": 30 }, + { "group": "spellbook_tier_1", "prob": 50 }, + { "group": "spellbook_tier_2", "prob": 18 }, + { "group": "spellbook_tier_3", "prob": 2 }, + { "group": "spell_scroll_tier_1", "prob": 300 }, + { "group": "spell_scroll_tier_2", "prob": 100 } + ] + }, + { + "id": "spellbook_loot_2", + "type": "item_group", + "items": [ + { "group": "spellbook_tier_0", "prob": 5 }, + { "group": "spellbook_tier_1", "prob": 30 }, + { "group": "spellbook_tier_2", "prob": 50 }, + { "group": "spellbook_tier_3", "prob": 15 }, + { "group": "spell_scroll_tier_2", "prob": 300 }, + { "group": "spell_scroll_tier_3", "prob": 100 } + ] + } +] diff --git a/data/mods/Magiclysm/items/ethereal_items.json b/data/mods/Magiclysm/items/ethereal_items.json index 10c353220e68c..0143b89d7694f 100644 --- a/data/mods/Magiclysm/items/ethereal_items.json +++ b/data/mods/Magiclysm/items/ethereal_items.json @@ -20,7 +20,7 @@ "material_thickness": 4, "environmental_protection": 10, "qualities": [ [ "HAMMER", 1 ] ], - "flags": [ "VARSIZE", "STURDY", "UNARMED_WEAPON", "DURABLE_MELEE", "NONCONDUCTIVE" ], + "flags": [ "VARSIZE", "STURDY", "UNARMED_WEAPON", "DURABLE_MELEE", "NONCONDUCTIVE", "MAGIC_FOCUS" ], "techniques": [ "WBLOCK_3" ] }, { @@ -58,7 +58,7 @@ "material_thickness": 4, "environmental_protection": 10, "qualities": [ [ "HAMMER", 1 ] ], - "flags": [ "VARSIZE", "STURDY", "UNARMED_WEAPON", "DURABLE_MELEE", "NONCONDUCTIVE" ], + "flags": [ "VARSIZE", "STURDY", "UNARMED_WEAPON", "DURABLE_MELEE", "NONCONDUCTIVE", "MAGIC_FOCUS" ], "techniques": [ "WBLOCK_3" ] }, { @@ -143,7 +143,16 @@ "symbol": "/", "material": [ "superalloy" ], "techniques": [ "WBLOCK_2", "BRUTAL", "SWEEP" ], - "flags": [ "NONCONDUCTIVE", "BELT_CLIP", "LIGHT_15", "TRADER_AVOID", "UNBREAKABLE_MELEE", "NO_REPAIR", "NO_SALVAGE" ], + "flags": [ + "NONCONDUCTIVE", + "BELT_CLIP", + "LIGHT_15", + "TRADER_AVOID", + "UNBREAKABLE_MELEE", + "NO_REPAIR", + "NO_SALVAGE", + "MAGIC_FOCUS" + ], "volume": 5, "bashing": 30, "electric": 15, @@ -182,7 +191,16 @@ "weight": 1, "bashing": 10, "electric": 15, - "flags": [ "UNARMED_WEAPON", "UNBREAKABLE_MELEE", "NONCONDUCTIVE", "LIGHT_5", "TRADER_AVOID", "NO_REPAIR", "NO_SALVAGE" ] + "flags": [ + "UNARMED_WEAPON", + "UNBREAKABLE_MELEE", + "NONCONDUCTIVE", + "LIGHT_5", + "TRADER_AVOID", + "NO_REPAIR", + "NO_SALVAGE", + "MAGIC_FOCUS" + ] }, { "id": "tentacle_whip", @@ -204,7 +222,8 @@ "UNARMED_WEAPON", "UNBREAKABLE_MELEE", "NO_REPAIR", - "NO_SALVAGE" + "NO_SALVAGE", + "MAGIC_FOCUS" ], "cutting": 30, "pierce": 6, @@ -245,7 +264,8 @@ "SHEATH_SPEAR", "ONLY_ONE", "NO_REPAIR", - "NO_SALVAGE" + "NO_SALVAGE", + "MAGIC_FOCUS" ], "techniques": [ "WBLOCK_2", "WIDE", "SWEEP", "BRUTAL" ], "weight": 2175, diff --git a/data/mods/Magiclysm/items/spell_scrolls.json b/data/mods/Magiclysm/items/spell_scrolls.json new file mode 100644 index 0000000000000..85242aebe3074 --- /dev/null +++ b/data/mods/Magiclysm/items/spell_scrolls.json @@ -0,0 +1,597 @@ +[ + { + "abstract": "spell_scroll", + "name": "Spell Scroll", + "type": "GENERIC", + "weight": 475, + "volume": 2, + "price": 4000, + "material": [ "paper" ], + "symbol": "?", + "color": "white" + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_smite", + "name": "Scroll of Smite", + "description": "Evil has become pervasive throughout the world. Let your power be the light that shines in the darkness!", + "use_action": { "type": "learn_spell", "spells": [ "smite" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_recover_mana", + "name": "Scroll of Life Conversion", + "description": "You channel your life force itself into your spiritual energy. You spend hp to regain mana.", + "use_action": { "type": "learn_spell", "spells": [ "recover_mana" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_recover_pain", + "name": "Scroll of Mind Over Pain", + "description": "With an intense ritual that resembles crossfit, you manage to put some of your pain at bay.", + "use_action": { "type": "learn_spell", "spells": [ "recover_pain" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_summon_zombie", + "name": "Scroll of Summon Zombie", + "description": "An ethereal-looking zombie rises from the depths of the earth to fight for you. You may be able to summon more with a higher level in this spell.", + "use_action": { "type": "learn_spell", "spells": [ "summon_zombie" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_summon_skeleton", + "name": "Scroll of Summon Skeleton", + "description": "A ghostly skeleton rises from the depths of the earth to fight for you. You may be able to summon more with a higher level in this spell.", + "use_action": { "type": "learn_spell", "spells": [ "summon_skeleton" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_summon_decayed_pouncer", + "name": "Scroll of Summon Decayed Pouncer", + "description": "A decrepit looking large cat rises from the depths of the earth to fight for you. You may be able to summon more with a higher level in this spell.", + "use_action": { "type": "learn_spell", "spells": [ "summon_decayed_pouncer" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_light_healing", + "name": "Scroll of Cure Light Wounds", + "description": "Heals a little bit of damage on the target.", + "use_action": { "type": "learn_spell", "spells": [ "light_healing" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_pain_split", + "name": "Scroll of Pain Split", + "description": "Evens out damage among your limbs.", + "use_action": { "type": "learn_spell", "spells": [ "pain_split" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_vicious_tentacle", + "name": "Scroll of Vicious Tentacle", + "description": "This spell extrudes a long nasty whiplike tentacle of sharp bones and oozing acid from your body, it has a long reach attack and vicious damage.", + "use_action": { "type": "learn_spell", "spells": [ "vicious_tentacle" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_bio_grotesque", + "name": "Scroll of Grotesque Enhancement", + "description": "A spell that warps your body in alien ways to increase your physical abilities and strength.", + "use_action": { "type": "learn_spell", "spells": [ "bio_grotesque" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_bio_acidicspray", + "name": "Scroll of Acidic Spray", + "description": "When cast, the mage opens his mouth and sprays acid in a wide cone to dissolve his foes into goo. Just imagine what he'll do with the goo.", + "use_action": { "type": "learn_spell", "spells": [ "bio_acidicspray" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_bio_fleshpouch", + "name": "Scroll of Flesh Pouch", + "description": "This spell grows a large pouch out of your skin on your back, allowing you to store your gear in it.", + "use_action": { "type": "learn_spell", "spells": [ "bio_fleshpouch" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_bio_bonespear", + "name": "Scroll of Conjure Bonespear", + "description": "This spell creates a long shaft of bone with a wicked point and blades along its length.", + "use_action": { "type": "learn_spell", "spells": [ "bio_bonespear" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_megablast", + "name": "Scroll of Megablast", + "description": "You always wanted to fire energy beams like in the animes you watched as a kid. Now you can!", + "use_action": { "type": "learn_spell", "spells": [ "megablast" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_create_atomic_light", + "name": "Scroll of Magical Light", + "description": "Creates a magical light.", + "use_action": { "type": "learn_spell", "spells": [ "create_atomic_light" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_blinding_flash", + "name": "Scroll of Blinding Flash", + "description": "Blind enemies for a short time with a sudden, dazzling light. Higher levels deal slightly higher damage.", + "use_action": { "type": "learn_spell", "spells": [ "blinding_flash" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_ethereal_grasp", + "name": "Scroll of Ethereal Grasp", + "description": "A mass of spectral hands emerge from the ground, slowing everything in range. Higher levels allow a bigger AoE, and longer effect.", + "use_action": { "type": "learn_spell", "spells": [ "ethereal_grasp" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_protection_aura", + "name": "Scroll of Aura of Protection", + "description": "Encases your whole body in a magical aura that protects you from the environment.", + "use_action": { "type": "learn_spell", "spells": [ "protection_aura" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_druid_veggrasp", + "name": "Scroll of Vegetative Grasp", + "description": "This spell causes roots and vines to burst forth from the ground and grab your foes, slowing them and doing a small amount of damage as they dig in.", + "use_action": { "type": "learn_spell", "spells": [ "druid_veggrasp" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_druid_rootstrike", + "name": "Scroll of Root Strike", + "description": "This spell causes roots to spear out the ground and stab into your foes in an arc, impaling them.", + "use_action": { "type": "learn_spell", "spells": [ "druid_rootstrike" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_druid_woodshaft", + "name": "Scroll of Wooden Shaft", + "description": "This spell creates a projectile of hardwood that shoots forth from the caster's hand at high speed to stab into an enemy.", + "use_action": { "type": "learn_spell", "spells": [ "druid_woodshaft" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_druid_naturebow1", + "name": "Scroll of Nature's Bow", + "description": "This spell conjures a magical wooden recurve bow that fires endless arrows for as long as it lasts.", + "use_action": { "type": "learn_spell", "spells": [ "druid_naturebow1" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_recover_fatigue", + "name": "Scroll of Nature's Trance", + "description": "Your connection to living things allows you to go into a magical trance. This allows you to recover fatige quickly in exchange for mana.", + "use_action": { "type": "learn_spell", "spells": [ "recover_fatigue" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_summon_cats", + "name": "Scroll of Bag of Cats", + "description": "Are you the crazy cat lady?", + "use_action": { "type": "learn_spell", "spells": [ "summon_cats" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_stonefist", + "name": "Scroll of Stonefist", + "description": "Encases your arms and hands in a sheath of magical stone, you can punch and defend yourself with it in melee combat.", + "use_action": { "type": "learn_spell", "spells": [ "stonefist" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_seismic_stomp", + "name": "Scroll of Seismic Stomp", + "description": "Focusing mana into your leg, you stomp your foot and send out a shockwave, knocking enemies around you onto the ground.", + "use_action": { "type": "learn_spell", "spells": [ "seismic_stomp" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_recover_stamina", + "name": "Scroll of Stone's Endurance", + "description": "You focus on the stones beneath you and draw from their agelessness. Your mana is converted to stamina.", + "use_action": { "type": "learn_spell", "spells": [ "recover_stamina" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_eshaper_shardspray", + "name": "Scroll of Shardspray", + "description": "This spell projects a wide spray of sharp metal shards, cutting into your foes and friends alike.", + "use_action": { "type": "learn_spell", "spells": [ "eshaper_shardspray" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_eshaper_piercing_bolt", + "name": "Scroll of Piercing Bolt", + "description": "This spell projects a piercing rod of conjured iron at those that dare oppose you.", + "use_action": { "type": "learn_spell", "spells": [ "eshaper_piercing_bolt" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_eshaper_shardstorm", + "name": "Scroll of Shardstorm", + "description": "Creates an omnidirectional spray of razor sharp metal shards all around you.", + "use_action": { "type": "learn_spell", "spells": [ "eshaper_shardstorm" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_eshaper_rockbolt", + "name": "Scroll of Rockbolt", + "description": "Fires a conjured stone projectile at high velocity.", + "use_action": { "type": "learn_spell", "spells": [ "eshaper_rockbolt" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_point_flare", + "name": "Scroll of Point Flare", + "description": "Causes an intense heat at the location, damaging the target.", + "use_action": { "type": "learn_spell", "spells": [ "point_flare" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_create_lighter", + "name": "Scroll of Finger Firelighter", + "description": "Summons a small flame that does not burn you, but you can use it to light things on fire. It seems to need you to have some intent to light things on fire, because you are able to put it in your pocket with no issue.", + "use_action": { "type": "learn_spell", "spells": [ "create_lighter" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_ice_spike", + "name": "Scroll of Ice Spike", + "description": "Causes jagged icicles to form in the air above the target, falling and damaging it.", + "use_action": { "type": "learn_spell", "spells": [ "ice_spike" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_fireball", + "name": "Scroll of Fireball", + "description": "You hurl a pea-sized glowing orb that when reaches its target or an obstacle produces a pressure-less blast of searing heat.", + "use_action": { "type": "learn_spell", "spells": [ "fireball" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_cone_cold", + "name": "Scroll of Cone of Cold", + "description": "You blast a cone of frigid air toward the target.", + "use_action": { "type": "learn_spell", "spells": [ "cone_cold" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_burning_hands", + "name": "Scroll of Burning Hands", + "description": "You're pretty sure you saw this in a game somewhere. You fire a short-range cone of fire.", + "use_action": { "type": "learn_spell", "spells": [ "burning_hands" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_frost_spray", + "name": "Scroll of Frost Spray", + "description": "You're pretty sure you saw this in a game somewhere. You fire a short-range cone of ice and cold.", + "use_action": { "type": "learn_spell", "spells": [ "frost_spray" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_chilling_touch", + "name": "Scroll of Chilling Touch", + "description": "Freezes the touched target with intense cold.", + "use_action": { "type": "learn_spell", "spells": [ "chilling_touch" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_glide_ice", + "name": "Scroll of Glide on Ice", + "description": "Encases your feet in a magical coating of ice, allowing you to glide along smooth surfaces faster.", + "use_action": { "type": "learn_spell", "spells": [ "glide_ice" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_hoary_blast", + "name": "Scroll of Hoary Blast", + "description": "You project a glowing white crystal of ice and it explodes on impact into a blossom of shattering cold.", + "use_action": { "type": "learn_spell", "spells": [ "hoary_blast" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_ice_shield", + "name": "Scroll of Ice Shield", + "description": "Creates a magical shield of ice on your arm, you can defend yourself with it in melee combat and use it to bash.", + "use_action": { "type": "learn_spell", "spells": [ "ice_shield" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_frost_armor", + "name": "Scroll of Frost Armor", + "description": "Covers you in a thin layer of magical ice to protect you from harm.", + "use_action": { "type": "learn_spell", "spells": [ "frost_armor" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_magic_missile", + "name": "Scroll of Magic Missile", + "description": "I cast Magic Missile at the darkness!", + "use_action": { "type": "learn_spell", "spells": [ "magic_missile" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_phase_door", + "name": "Scroll of Phase Door", + "description": "Teleports you in a random direction a short distance.", + "use_action": { "type": "learn_spell", "spells": [ "phase_door" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_gravity_well", + "name": "Scroll of Gravity Well", + "description": "Summons a well of gravity with the epicenter at the location. Deals bashing damage to all creatures in the affected area.", + "use_action": { "type": "learn_spell", "spells": [ "gravity_well" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_magus_mana_blast", + "name": "Scroll of Mana Blast", + "description": "A blast of concentrated magical power that obliterates a large area.", + "use_action": { "type": "learn_spell", "spells": [ "magus_mana_blast" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_magus_mana_bolt", + "name": "Scroll of Mana Bolt", + "description": "A bolt of magical power that only damages your foes.", + "use_action": { "type": "learn_spell", "spells": [ "magus_mana_bolt" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_magus_haste", + "name": "Scroll of Haste", + "description": "This spell gives you an enormous boost of speed lasting a short period of time.", + "use_action": { "type": "learn_spell", "spells": [ "magus_haste" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_magus_mana_beam", + "name": "Scroll of Mana Beam", + "description": "A beam of focused magical power that damages any foes in its path.", + "use_action": { "type": "learn_spell", "spells": [ "magus_mana_beam" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_magus_escape", + "name": "Scroll of Escape", + "description": "Teleports you in a random direction a medium distance, to help escape your foes in dangerous situations.", + "use_action": { "type": "learn_spell", "spells": [ "magus_escape" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_cats_grace", + "name": "Scroll of Cat's Grace", + "description": "You become more graceful, agile, and coordinated.", + "use_action": { "type": "learn_spell", "spells": [ "cats_grace" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_eagles_sight", + "name": "Scroll of Eagle's Sight", + "description": "You gain the perception of an eagle.", + "use_action": { "type": "learn_spell", "spells": [ "eagles_sight" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_ogres_strength", + "name": "Scroll of Ogre's Strength", + "description": "You gain the strength of an ogre.", + "use_action": { "type": "learn_spell", "spells": [ "ogres_strength" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_foxs_cunning", + "name": "Scroll of Fox's Cunning", + "description": "You become wily like a fox.", + "use_action": { "type": "learn_spell", "spells": [ "foxs_cunning" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_jolt", + "name": "Scroll of Jolt", + "description": "A short-ranged fan of electricity shoots from your fingers.", + "use_action": { "type": "learn_spell", "spells": [ "jolt" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_lightning_bolt", + "name": "Scroll of Lightning Bolt", + "description": "The goto spell for many Stormshapers, this iconic spell does just what you expect: you shoot lightning from your fingertips. However, this lightning is more directed than most lightning, and travels in a line through most non-solid targets.", + "use_action": { "type": "learn_spell", "spells": [ "lightning_bolt" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_windstrike", + "name": "Scroll of Windstrike", + "description": "A powerful blast of wind slams into anything in front of your outstretched hand.", + "use_action": { "type": "learn_spell", "spells": [ "windstrike" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_windrun", + "name": "Scroll of Windrunning", + "description": "A magical wind pushes you forward as you move, easing your movements and increasing speed.", + "use_action": { "type": "learn_spell", "spells": [ "windstrike" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_storm_hammer", + "name": "Scroll of Call Stormhammer", + "description": "Creates a crackling magical warhammer full of lightning to smite your foes with, and of course, smash things to bits!", + "use_action": { "type": "learn_spell", "spells": [ "storm_hammer" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_bless", + "name": "Scroll of Bless", + "description": "A spell of blessing that gives you energy and boosts your abilities.", + "use_action": { "type": "learn_spell", "spells": [ "bless" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_holy_blade", + "name": "Scroll of Holy Blade", + "description": "This blade of light will cut through any evil it makes contact with!", + "use_action": { "type": "learn_spell", "spells": [ "holy_blade" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_spirit_armor", + "name": "Scroll of Spiritual Armor", + "description": "Evil will not make it through your defenses if your faith is strong enough!", + "use_action": { "type": "learn_spell", "spells": [ "spirit_armor" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_create_atomic_lamp", + "name": "Scroll of Lamp", + "description": "Creates a magical lamp.", + "use_action": { "type": "learn_spell", "spells": [ "create_atomic_lamp" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_recover_bionic_power", + "name": "Scroll of Manatricity", + "description": "You have found a way to convert your spiritual energy into power you can use for your bionics.", + "use_action": { "type": "learn_spell", "spells": [ "recover_bionic_power" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_taze", + "name": "Scroll of Taze", + "description": "This spell creates a very short range bolt of electricity to shock your foes.", + "use_action": { "type": "learn_spell", "spells": [ "taze" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_quantum_tunnel_lesser", + "name": "Scroll of Lesser Quantum Tunnel", + "description": "This spell manipulates some quantum something or other to tunnel you through a short distance of space, and even matter, unfortunately there's that whole uncertainty thing as to where you come out. It leaves you a little dazed on the other side as you reorient yourself.", + "use_action": { "type": "learn_spell", "spells": [ "quantum_tunnel_lesser" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_synaptic_stimulation", + "name": "Scroll of Laze", + "description": "This spell stimulates the synapses in your brain beyond normal processing speeds, giving you a large boost in mental processing capability, including enhancing your reflexes, speed, and raw intellectual power. Use responsibly!", + "use_action": { "type": "learn_spell", "spells": [ "synaptic_stimulation" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_laze", + "name": "Scroll of Laze", + "description": "You concentrate and release a focused beam of photons at a target, also known as a laser.", + "use_action": { "type": "learn_spell", "spells": [ "laze" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_animated_blade", + "name": "Scroll of Animated Blade", + "description": "This spell conjures flying animated blades that will cut your enemies down to size. Into small pieces that is.", + "use_action": { "type": "learn_spell", "spells": [ "animated_blade" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_mirror_image", + "name": "Scroll of Mirror Image", + "description": "This spell manipulates light into barely tangible duplicates of a living being, a magical hologram in short.", + "use_action": { "type": "learn_spell", "spells": [ "mirror_image" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_lightning_blast", + "name": "Scroll of Lightning Blast", + "description": "You fire a small concentrated ball of lightning at the target. The electricity diffuses quickly, so it doesn't do much damage, but you're able to fire off several quick ones in a row.", + "use_action": { "type": "learn_spell", "spells": [ "lightning_blast" ] } + }, + { + "type": "GENERIC", + "copy-from": "spell_scroll", + "id": "spell_scroll_necrotic_gaze", + "name": "Scroll of Necrotic Gaze", + "description": "You use the power of your own blood to imbue necrotic energy into your gaze, damaging the target you look at.", + "use_action": { "type": "learn_spell", "spells": [ "necrotic_gaze" ] } + } +] diff --git a/data/mods/Magiclysm/monsters/demon_spider.json b/data/mods/Magiclysm/monsters/demon_spider.json index 1a855c5edc631..8f579eff60c9d 100644 --- a/data/mods/Magiclysm/monsters/demon_spider.json +++ b/data/mods/Magiclysm/monsters/demon_spider.json @@ -11,7 +11,7 @@ ] }, { - "id": "demon_spider", + "id": "demon_spider_queen", "type": "harvest", "//": "separate harvest entry for future special alchemical items", "entries": [ @@ -22,6 +22,11 @@ { "drop": "demon_spider_fang", "base_num": [ 0, 0 ], "scale_num": [ 0.1, 0.6 ], "max": 2, "type": "flesh" } ] }, + { + "type": "MONSTER_FACTION", + "name": "demon_spider", + "base_faction": "spider" + }, { "type": "material", "ident": "demon_chitin", diff --git a/data/mods/Magiclysm/monsters/monsters.json b/data/mods/Magiclysm/monsters/monsters.json index 3ec73759cfb91..a48cea4b341cd 100644 --- a/data/mods/Magiclysm/monsters/monsters.json +++ b/data/mods/Magiclysm/monsters/monsters.json @@ -32,6 +32,44 @@ "special_attacks": [ { "type": "bite", "cooldown": 10 }, [ "GRAB", 7 ], [ "scratch", 7 ] ], "flags": [ "SEES", "HEARS", "SMELLS", "KEENNOSE", "PATH_AVOID_DANGER_1", "WARM", "GRABS", "SWARMS" ] }, + { + "id": "mon_nothic", + "name": "nothic", + "description": "A baleful eye peers out from the darkness, its gleam hinting at a weird intelligence and unnerving malevolence.", + "symbol": "N", + "color": "dark_gray", + "type": "MONSTER", + "flags": [ "SEES", "HEARS", "SMELLS", "KEENNOSE", "PATH_AVOID_DANGER_1", "WARM" ], + "harvest": "human", + "material": [ "hflesh" ], + "bodytype": "human", + "default_faction": "magical_beast", + "species": [ "MAGICAL_BEAST", "HUMAN" ], + "volume": "62500 ml", + "weight": 81500, + "hp": 60, + "speed": 100, + "aggression": 30, + "morale": 100, + "melee_skill": 3, + "melee_cut": 20, + "melee_dice": 3, + "melee_dice_sides": 10, + "dodge": 6, + "vision_night": 30, + "vision_day": 30, + "path_settings": { "max_dist": 35 }, + "death_function": [ "NORMAL" ], + "anger_triggers": [ "HURT", "PLAYER_CLOSE", "PLAYER_WEAK" ], + "special_attacks": [ + { + "id": "scratch", + "cooldown": 1, + "damage_max_instance": [ { "damage_type": "cut", "amount": 25, "armor_multiplier": 1.2 } ] + }, + { "type": "spell", "spell_id": "necrotic_gaze", "cooldown": 5 } + ] + }, { "id": "mon_owlbear_cub", "type": "MONSTER", diff --git a/data/mods/Magiclysm/professions.json b/data/mods/Magiclysm/professions.json index b200bde75e76e..b7db797c37997 100644 --- a/data/mods/Magiclysm/professions.json +++ b/data/mods/Magiclysm/professions.json @@ -41,10 +41,11 @@ "name": "Priest", "description": "When the apocalypse struck you did everything you could to protect your parish faithful, but it appears that prayers were not enough. Now that they are all dead, you should probably find something more tangible to protect you.", "points": 0, + "flags": [ "SCEN_ONLY" ], "skills": [ { "level": 3, "name": "speech" } ], "items": { "both": { - "items": [ "pants", "longshirt", "socks", "cassock", "dress_shoes", "holy_symbol", "holybook_bible1", "priest_beginner", "pyro" ], + "items": [ "pants", "longshirt", "socks", "cassock", "dress_shoes", "holy_symbol", "holybook_bible1", "priest_beginner" ], "entries": [ { "group": "charged_cell_phone" } ] }, "male": [ "briefs" ], @@ -57,10 +58,11 @@ "name": "Kannushi", "description": "You were one of the maintainers of a Shinto shrine, performing rituals and sacred tasks. You preferred it when only the spirits of the dead inhabited your shrine, and not their rotting corpses.", "points": 0, + "flags": [ "SCEN_ONLY" ], "skills": [ { "level": 1, "name": "fabrication" }, { "level": 1, "name": "tailor" } ], "items": { "both": { - "items": [ "kariginu", "eboshi", "pants", "tabi_dress", "geta", "holy_symbol", "holybook_kojiki", "priest_beginner", "pyro" ], + "items": [ "kariginu", "eboshi", "pants", "tabi_dress", "geta", "holy_symbol", "holybook_kojiki", "priest_beginner" ], "entries": [ { "group": "charged_cell_phone" } ] }, "male": [ "briefs" ], @@ -73,11 +75,12 @@ "name": { "male": "Imam", "female": "Mourchida" }, "description": "You spent much of your time prior to the apocalypse at the local mosque, studying the words of the Prophet and the Quran and guiding your community in prayer. Back then they came from far and wide to listen to you, now they come to eat your brains.", "points": 0, + "flags": [ "SCEN_ONLY" ], "//": "No knife, fire, or decent storage/armor. Skill points are countered.", "skills": [ { "level": 2, "name": "speech" }, { "level": 1, "name": "barter" } ], "items": { "both": { - "items": [ "pants", "tshirt", "socks", "kufi", "thawb", "lowtops", "holybook_quran", "priest_beginner", "pyro" ], + "items": [ "pants", "tshirt", "socks", "kufi", "thawb", "lowtops", "holybook_quran", "priest_beginner" ], "entries": [ { "group": "charged_cell_phone" } ] }, "male": [ "briefs" ], @@ -90,6 +93,7 @@ "name": "Rabbi", "description": "You were celebrating with your flock in the temple when the cataclysm struck. You sure could use a messiah right now!", "points": 0, + "flags": [ "SCEN_ONLY" ], "skills": [ { "level": 2, "name": "speech" }, { "level": 1, "name": "firstaid" } ], "items": { "both": { @@ -104,8 +108,7 @@ "holy_symbol", "holybook_talmud", "holybook_tanakh", - "priest_beginner", - "pyro" + "priest_beginner" ], "entries": [ { "group": "charged_cell_phone" } ] }, @@ -119,6 +122,7 @@ "name": "Guru", "description": "You spent many years traveling the world, becoming wise and learned. Normally, you can answer any question, but even you are not quite sure what to do about the ravenous undead.", "points": 2, + "flags": [ "SCEN_ONLY" ], "//": "1.5 pts skills, cutting implement, and plenty of storage, so lack of fire only goes so far.", "skills": [ { "level": 2, "name": "speech" }, { "level": 1, "name": "survival" } ], "items": { @@ -135,8 +139,7 @@ "leathersandals", "holy_symbol", "wristwatch", - "priest_beginner", - "pyro" + "priest_beginner" ], "entries": [ { "group": "charged_cell_phone" } ] }, @@ -150,6 +153,7 @@ "name": "Preacher", "description": "You devoted your life to spreading the good word; always on the road, traveling from town to town. Now everything has gone to hell, you can't host your daily podcast, and the undead listening to your sermons don't seem particularly moved.", "points": 2, + "flags": [ "SCEN_ONLY" ], "//": "Storage + 2 points in skills, - no knife or fire.", "skills": [ { "level": 2, "name": "speech" }, { "level": 1, "name": "driving" }, { "level": 1, "name": "computer" } ], "items": { @@ -166,8 +170,7 @@ "flyer", "flyer", "holy_symbol", - "priest_beginner", - "pyro" + "priest_beginner" ], "entries": [ { "group": "charged_cell_phone" } ] }, @@ -180,7 +183,7 @@ "ident": "novice_necromancer", "name": "Novice Necromancer", "description": "You always had to hide your magic, as it was generally not an acceptable type of magic in the wizarding world, but with the cataclysm you need to pull every trick out of the book.", - "spells": [ { "id": "summon_zombie", "level": 5 } ], + "spells": [ { "id": "summon_zombie", "level": 5 }, { "id": "necrotic_gaze", "level": 1 } ], "points": 2, "skills": [ { "level": 1, "name": "spellcraft" } ], "items": { @@ -189,5 +192,47 @@ "female": [ "bra", "panties" ] }, "traits": [ "ANIMIST" ] + }, + { + "type": "profession", + "ident": "novice_stormshaper", + "name": "Novice Stormshaper", + "description": "The son of a weatherman, you were always interested in the weather. Recently you found it was possible to manipulate the weather yourself through arcane means! Unfortunately you did not have long to enjoy your power, as events unfolded...", + "spells": [ { "id": "lightning_bolt", "level": 1 }, { "id": "windrun", "level": 1 } ], + "points": 0, + "items": { + "both": [ "jeans", "tshirt", "hat_ball", "sneakers", "socks", "hoodie" ], + "male": [ "boxer_briefs" ], + "female": [ "bra", "panties" ] + }, + "traits": [ "STORMSHAPER" ] + }, + { + "type": "profession", + "ident": "novice_earthshaper", + "name": "Novice Earthshaper", + "description": "You always were tough in a fight. Your coach managed to teach you a trick or two to make you a little tougher.", + "spells": [ { "id": "stonefist", "level": 1 }, { "id": "seismic_stomp", "level": 1 } ], + "points": 0, + "items": { + "both": [ "jeans", "tshirt", "boxing_gloves", "hat_ball", "sneakers", "socks", "hoodie" ], + "male": [ "boxer_briefs" ], + "female": [ "bra", "panties" ] + }, + "traits": [ "EARTHSHAPER" ] + }, + { + "type": "profession", + "ident": "novice_technomancer", + "name": "Novice Technomancer", + "description": "With the recent Cataclysm, the way you found to cheat on tests may have to find some other use.", + "spells": [ { "id": "synaptic_stimulation", "level": 4 } ], + "points": 0, + "items": { + "both": [ "jeans", "tshirt", "gloves_light", "hat_ball", "sneakers", "socks", "hoodie" ], + "male": [ "boxer_briefs" ], + "female": [ "bra", "panties" ] + }, + "traits": [ "TECHNOMANCER" ] } ] diff --git a/data/mods/Magiclysm/scenarios.json b/data/mods/Magiclysm/scenarios.json index 4bdbc66bc91a7..38a0ab3b4ca8a 100644 --- a/data/mods/Magiclysm/scenarios.json +++ b/data/mods/Magiclysm/scenarios.json @@ -19,6 +19,6 @@ "description": "You have been an apprentice to a wizard for some time now, working on a prototype device that could perhaps be used to escape from the Cataclysm. Something went wrong with the maiden voyage though, and your teacher teleported his insides into his outsides. Now you have to figure out how to survive on your own. At least he left his notebook...", "start_name": "Wizard's Secret Basement Study", "allowed_locs": [ "magic_basement" ], - "professions": [ "wizard_novice", "novice_necromancer" ] + "professions": [ "wizard_novice", "novice_necromancer", "novice_earthshaper", "novice_technomancer", "novice_stormshaper" ] } ] diff --git a/data/mods/Magiclysm/worldgen/overmap_terrain.json b/data/mods/Magiclysm/worldgen/overmap_terrain.json index 31118f230ea57..e72d01bc35712 100644 --- a/data/mods/Magiclysm/worldgen/overmap_terrain.json +++ b/data/mods/Magiclysm/worldgen/overmap_terrain.json @@ -62,6 +62,28 @@ "color": "light_gray", "see_cost": 5 }, + { + "type": "overmap_terrain", + "id": "used_bookstore", + "name": "used bookstore", + "sym": "B", + "color": "white", + "see_cost": 5, + "extras": "build", + "mondensity": 1, + "flags": [ "SIDEWALK" ] + }, + { + "type": "overmap_terrain", + "id": "used_bookstore_roof", + "name": "used bookstore", + "sym": "B", + "color": "white", + "see_cost": 5, + "extras": "build", + "mondensity": 1, + "flags": [ "SIDEWALK" ] + }, { "type": "overmap_terrain", "id": "magic_basement", diff --git a/data/mods/Magiclysm/worldgen/regional_overlay.json b/data/mods/Magiclysm/worldgen/regional_overlay.json index e3694685e37f7..40dfa330b3ffe 100644 --- a/data/mods/Magiclysm/worldgen/regional_overlay.json +++ b/data/mods/Magiclysm/worldgen/regional_overlay.json @@ -3,6 +3,6 @@ "type": "region_overlay", "id": "magiclysm_buildings_overlay", "regions": [ "all" ], - "city": { "shops": { "magic_shop": 100 }, "basements": { "magic_basement": 50 } } + "city": { "shops": { "magic_shop": 100, "used_bookstore": 225 }, "basements": { "magic_basement": 50 } } } ] diff --git a/data/mods/Magiclysm/worldgen/used_bookstore.json b/data/mods/Magiclysm/worldgen/used_bookstore.json new file mode 100644 index 0000000000000..a7d01fd1c3f44 --- /dev/null +++ b/data/mods/Magiclysm/worldgen/used_bookstore.json @@ -0,0 +1,93 @@ +[ + { + "type": "mapgen", + "method": "json", + "om_terrain": "used_bookstore", + "object": { + "rows": [ + ",.................******", + ",#WWW#+###WWW##W##******", + ",W cC N cc NN N#******", + ",W cC N TT NN N#******", + ",W CCC N cc NN N#******", + ",# N#******", + ",#N NNN NNN N#******", + ",#NNN NNNN N N#******", + ",#NNN NN####D##W##******", + ",W #Mz #dd********", + ",W NNN#MOz #dd********", + ",#N ####MMMM#dd********", + ",#N +s&######,*********", + ",########,,,,,,********,", + ",**********************,", + ",**********************,", + ",**********************,", + ",**********************,", + ",**********************,", + ",**********************,", + ",**********************,", + ",,,,,,,,,,,,,,,,,,,,,,,,", + ",,,,,,,,,,,,,,,,,,,,,,,,", + ",,,,,,,,,,,,,,,,,,,,,,,," + ], + "place_vehicles": [ { "vehicle": "car", "x": 4, "y": 17, "chance": 100, "fuel": 20, "status": 33, "rotation": 180 } ], + "fill_ter": "t_carpet_yellow", + "terrain": { + ".": "t_sidewalk", + "W": "t_window_domestic", + "+": "t_door_white_c", + "#": "t_wall_w", + "D": "t_door_metal_c", + "*": "t_pavement", + ",": [ "t_dirt", [ "t_grass", 4 ] ] + }, + "furniture": { + "c": "f_chair", + "N": "f_bookcase", + "M": "f_bookcase", + "T": "f_table", + "O": "f_magic_circle", + "d": "f_dumpster", + "C": "f_counter" + }, + "monster": { "O": { "monster": "mon_nothic", "chance": 100 }, "z": { "monster": "mon_nothic", "chance": 33 } }, + "toilets": { "&": { } }, + "items": { "M": { "item": "spellbook_loot_1", "chance": 60 }, "N": { "item": "novels", "chance": 60, "repeat": [ 1, 3 ] } } + } + }, + { + "type": "mapgen", + "method": "json", + "om_terrain": "used_bookstore_roof", + "object": { + "fill_ter": "t_flat_roof", + "rows": [ + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " |2222222 ", + " |......5#2223 ", + " |....&......3 ", + " |...........3 ", + " |...........3 ", + " |...........52223 ", + " |...............3 ", + " |...............3 ", + " |......A........3 ", + " |...............3 ", + " |...............3 ", + " |...............3 ", + " 52222222222222225 ", + " " + ], + "palettes": [ "roof_palette" ] + } + } +] diff --git a/doc/JSON_FLAGS.md b/doc/JSON_FLAGS.md index f206faf421058..8908e75c90948 100644 --- a/doc/JSON_FLAGS.md +++ b/doc/JSON_FLAGS.md @@ -1016,7 +1016,7 @@ Also see `monster_attacks.json` for more special attacks, for example, impale an - ```SHRIEK_STUN``` "a stunning shriek!", causes a small bash, can cause a stun. - ```SHRIEK``` "a terrible shriek!" - ```SLIMESPRING``` Can provide a morale boost to the player, and cure bite and bleed effects. -- ```SMASH``` Smashes the target for massive damage, sending it flying. +- ```SMASH``` Smashes the target for massive damage, sending it flying for a number of tiles equal to `("melee_dice" * "melee_dice_sides" * 3) / 10`. - ```SMG``` SMG turret fires. - ```SPIT_SAP``` Spit sap. - ```STARE``` Stare at the player and inflict teleglow. diff --git a/doc/MAGIC.md b/doc/MAGIC.md index 6b1965847400a..63116d2e29787 100644 --- a/doc/MAGIC.md +++ b/doc/MAGIC.md @@ -39,7 +39,13 @@ In `data/mods/Magiclysm` there is a template spell, copied here for your perusal "duration_increment": 4, "min_pierce": 0, // how much of the spell pierces armor (currently not implemented) "max_pierce": 1, - "pierce_increment": 0.1 + "pierce_increment": 0.1, + "field_id": "fd_blood", // the string id of the field (currently hardcoded) + "field_chance": 100, // one_in( field_chance ) chance of spawning a field per tile in aoe + "min_field_intensity": 10, // field intensity of fields generated + "max_field_intensity": 10, + "field_intensity_increment": 1, + "field_intensity_variance": 0.1 // the field can range in intensity from -variance as a percent to +variance as a percent i.e. this spell would be 9-11 } ``` Most of the default values for the above are either 0 or "NONE", so you may leave out most of the values if they do not pertain to your spell. diff --git a/src/action.cpp b/src/action.cpp index a08b6e563069a..c3e842c911e35 100644 --- a/src/action.cpp +++ b/src/action.cpp @@ -458,8 +458,8 @@ action_id look_up_action( const std::string &ident ) } // ^^ Temporarily for the interface with the input manager! for( int i = 0; i < NUM_ACTIONS; i++ ) { - if( action_ident( action_id( i ) ) == ident ) { - return action_id( i ); + if( action_ident( static_cast( i ) ) == ident ) { + return static_cast( i ); } } return ACTION_NULL; diff --git a/src/activity_handlers.cpp b/src/activity_handlers.cpp index 6f8501c5a5aa2..2df5b5756764e 100644 --- a/src/activity_handlers.cpp +++ b/src/activity_handlers.cpp @@ -2668,7 +2668,6 @@ void activity_handlers::drive_do_turn( player_activity *act, player *p ) void activity_handlers::travel_do_turn( player_activity *act, player *p ) { - const activity_id act_travel = activity_id( "ACT_TRAVELLING" ); if( !p->omt_path.empty() ) { p->omt_path.pop_back(); if( p->omt_path.empty() ) { @@ -2691,6 +2690,7 @@ void activity_handlers::travel_do_turn( player_activity *act, player *p ) const auto route_to = g->m.route( p->pos(), centre_sub, p->get_pathfinding_settings(), p->get_path_avoid() ); if( !route_to.empty() ) { + const activity_id act_travel = activity_id( "ACT_TRAVELLING" ); p->set_destination( route_to, player_activity( act_travel ) ); } else { p->add_msg_if_player( _( "You cannot reach that destination" ) ); diff --git a/src/activity_item_handling.cpp b/src/activity_item_handling.cpp index b6547537aad1a..eb8f27bd3b90e 100644 --- a/src/activity_item_handling.cpp +++ b/src/activity_item_handling.cpp @@ -1163,7 +1163,7 @@ void activity_on_turn_blueprint_move( player_activity &, player &p ) // If we got here without restarting the activity, it means we're done. if( p.is_npc() ) { npc *guy = dynamic_cast( &p ); - guy->current_activity = ""; + guy->current_activity.clear(); guy->revert_after_activity(); } } diff --git a/src/animation.cpp b/src/animation.cpp index c88605e4306c3..bceaa1fb3653d 100644 --- a/src/animation.cpp +++ b/src/animation.cpp @@ -40,7 +40,7 @@ class basic_animation { public: basic_animation( const int scale ) : - delay{ 0, get_option( "ANIMATION_DELAY" ) * scale * 1000000l } { + delay{ 0, get_option( "ANIMATION_DELAY" ) * scale * 1000000L } { } void draw() const { diff --git a/src/armor_layers.cpp b/src/armor_layers.cpp index 86e740fa7ad5e..66569201873ac 100644 --- a/src/armor_layers.cpp +++ b/src/armor_layers.cpp @@ -123,7 +123,7 @@ std::string body_part_names( const std::vector &parts ) names.reserve( parts.size() ); for( size_t i = 0; i < parts.size(); ++i ) { const body_part part = parts[i]; - if( i + 1 < parts.size() && parts[i + 1] == body_part( bp_aiOther[part] ) ) { + if( i + 1 < parts.size() && parts[i + 1] == static_cast( bp_aiOther[part] ) ) { // Can combine two body parts (e.g. arms) names.push_back( body_part_name_accusative( part, 2 ) ); ++i; diff --git a/src/artifact.cpp b/src/artifact.cpp index cb9b222541949..dc2f8bb4a1fa1 100644 --- a/src/artifact.cpp +++ b/src/artifact.cpp @@ -782,14 +782,14 @@ std::string new_artifact() def.tool->def_charges = def.tool->max_charges; // If we have charges, pick a recharge mechanism if( def.tool->max_charges > 0 ) { - def.artifact->charge_type = art_charge( rng( ARTC_NULL + 1, NUM_ARTCS - 1 ) ); + def.artifact->charge_type = static_cast( rng( ARTC_NULL + 1, NUM_ARTCS - 1 ) ); } if( one_in( 8 ) && num_bad + num_good >= 4 ) { def.artifact->charge_type = ARTC_NULL; // 1 in 8 chance that it can't recharge! } // Maybe pick an extra recharge requirement if( one_in( std::max( 1, 6 - num_good ) ) && def.artifact->charge_type != ARTC_NULL ) { - def.artifact->charge_req = art_charge_req( rng( ACR_NULL + 1, NUM_ACRS - 1 ) ); + def.artifact->charge_req = static_cast( rng( ACR_NULL + 1, NUM_ACRS - 1 ) ); } // Assign dream data (stored individually so they can be overridden in json) def.artifact->dream_msg_unmet = artifact_dream_data[static_cast @@ -923,8 +923,8 @@ std::string new_natural_artifact( artifact_natural_property prop ) const artifact_shape_datum &shape_data = random_entry_ref( artifact_shape_data ); // Pick a property const artifact_natural_property property = ( prop > ARTPROP_NULL ? prop : - artifact_natural_property( rng( ARTPROP_NULL + 1, - ARTPROP_MAX - 1 ) ) ); + static_cast( rng( ARTPROP_NULL + 1, + ARTPROP_MAX - 1 ) ) ); const artifact_property_datum &property_data = artifact_property_data[property]; def.sym = ":"; @@ -968,25 +968,25 @@ std::string new_natural_artifact( artifact_natural_property prop ) if( good_passive ) { aep_good = random_entry_ref( property_data.passive_good ); if( aep_good == AEP_NULL || one_in( 4 ) ) { - aep_good = art_effect_passive( rng( AEP_NULL + 1, AEP_SPLIT - 1 ) ); + aep_good = static_cast( rng( AEP_NULL + 1, AEP_SPLIT - 1 ) ); } } if( bad_passive ) { aep_bad = random_entry_ref( property_data.passive_bad ); if( aep_bad == AEP_NULL || one_in( 4 ) ) { - aep_bad = art_effect_passive( rng( AEP_SPLIT + 1, NUM_AEAS - 1 ) ); + aep_bad = static_cast( rng( AEP_SPLIT + 1, NUM_AEAS - 1 ) ); } } if( good_active ) { aea_good = random_entry_ref( property_data.active_good ); if( aea_good == AEA_NULL || one_in( 4 ) ) { - aea_good = art_effect_active( rng( AEA_NULL + 1, AEA_SPLIT - 1 ) ); + aea_good = static_cast( rng( AEA_NULL + 1, AEA_SPLIT - 1 ) ); } } if( bad_active ) { aea_bad = random_entry_ref( property_data.active_bad ); if( aea_bad == AEA_NULL || one_in( 4 ) ) { - aea_bad = art_effect_active( rng( AEA_SPLIT + 1, NUM_AEAS - 1 ) ); + aea_bad = static_cast( rng( AEA_SPLIT + 1, NUM_AEAS - 1 ) ); } } @@ -1012,10 +1012,10 @@ std::string new_natural_artifact( artifact_natural_property prop ) // (When "implanting" them in a mundane item, this ability may be lost if( !def.artifact->effects_activated.empty() ) { def.tool->def_charges = def.tool->max_charges = rng( 1, 4 ); - def.artifact->charge_type = art_charge( rng( ARTC_NULL + 1, NUM_ARTCS - 1 ) ); + def.artifact->charge_type = static_cast( rng( ARTC_NULL + 1, NUM_ARTCS - 1 ) ); //Maybe pick an extra recharge requirement if( one_in( 6 ) ) { - def.artifact->charge_req = art_charge_req( rng( ACR_NULL + 1, NUM_ACRS - 1 ) ); + def.artifact->charge_req = static_cast( rng( ACR_NULL + 1, NUM_ACRS - 1 ) ); } } // Assign dream data (stored individually so they can be overridden in json) @@ -1062,7 +1062,7 @@ std::vector fill_good_passive() { std::vector ret; for( int i = AEP_NULL + 1; i < AEP_SPLIT; i++ ) { - ret.push_back( art_effect_passive( i ) ); + ret.push_back( static_cast( i ) ); } return ret; } @@ -1071,7 +1071,7 @@ std::vector fill_bad_passive() { std::vector ret; for( int i = AEP_SPLIT + 1; i < NUM_AEPS; i++ ) { - ret.push_back( art_effect_passive( i ) ); + ret.push_back( static_cast( i ) ); } return ret; } @@ -1080,7 +1080,7 @@ std::vector fill_good_active() { std::vector ret; for( int i = AEA_NULL + 1; i < AEA_SPLIT; i++ ) { - ret.push_back( art_effect_active( i ) ); + ret.push_back( static_cast( i ) ); } return ret; } @@ -1089,7 +1089,7 @@ std::vector fill_bad_active() { std::vector ret; for( int i = AEA_SPLIT + 1; i < NUM_AEAS; i++ ) { - ret.push_back( art_effect_active( i ) ); + ret.push_back( static_cast( i ) ); } return ret; } diff --git a/src/avatar.cpp b/src/avatar.cpp index ce9aae7339012..cde4bc302dfab 100644 --- a/src/avatar.cpp +++ b/src/avatar.cpp @@ -89,8 +89,6 @@ static const trait_id trait_CHITIN2( "CHITIN2" ); static const trait_id trait_CHITIN3( "CHITIN3" ); static const trait_id trait_CHITIN_FUR3( "CHITIN_FUR3" ); static const trait_id trait_COMPOUND_EYES( "COMPOUND_EYES" ); -static const trait_id trait_FORGETFUL( "FORGETFUL" ); -static const trait_id trait_GOODMEMORY( "GOODMEMORY" ); static const trait_id trait_HYPEROPIC( "HYPEROPIC" ); static const trait_id trait_INSECT_ARMS( "INSECT_ARMS" ); static const trait_id trait_INSECT_ARMS_OK( "INSECT_ARMS_OK" ); @@ -378,15 +376,12 @@ size_t avatar::max_memorized_tiles() const // Only check traits once a turn since this is called a huge number of times. if( current_map_memory_turn != calendar::turn ) { current_map_memory_turn = calendar::turn; + float map_memory_capacity_multiplier = + mutation_value( "map_memory_capacity_multiplier" ); if( has_active_bionic( bio_memory ) ) { - current_map_memory_capacity = SEEX * SEEY * 20000; // 5000 overmap tiles - } else if( has_trait( trait_FORGETFUL ) ) { - current_map_memory_capacity = SEEX * SEEY * 200; // 50 overmap tiles - } else if( has_trait( trait_GOODMEMORY ) ) { - current_map_memory_capacity = SEEX * SEEY * 800; // 200 overmap tiles - } else { - current_map_memory_capacity = SEEX * SEEY * 400; // 100 overmap tiles + map_memory_capacity_multiplier = 50; } + current_map_memory_capacity = 2 * SEEX * 2 * SEEY * 100 * map_memory_capacity_multiplier; } return current_map_memory_capacity; } @@ -1357,13 +1352,14 @@ void avatar::reset_stats() set_fake_effect_dur( effect_stim_overdose, 1_turns * ( stim - 30 ) ); } // Starvation - if( get_starvation() >= 200 ) { - // We die at 6000 - const int dex_mod = -( get_starvation() + 300 ) / 1000; - add_miss_reason( _( "You're weak from hunger." ), static_cast( -dex_mod ) ); - mod_str_bonus( -( get_starvation() + 300 ) / 500 ); - mod_dex_bonus( dex_mod ); - mod_int_bonus( -( get_starvation() + 300 ) / 1000 ); + const float bmi = get_bmi(); + if( bmi < character_weight_category::underweight ) { + const int str_penalty = floor( ( 1.0f - ( bmi - 13.0f ) / 3.0f ) * get_str_base() ) + 0.5f; + add_miss_reason( _( "You're weak from hunger." ), + static_cast( ( get_starvation() + 300 ) / 1000 ) ); + mod_str_bonus( -str_penalty ); + mod_dex_bonus( -( str_penalty / 2 ) ); + mod_int_bonus( -( str_penalty / 2 ) ); } // Thirst if( get_thirst() >= 200 ) { diff --git a/src/calendar.cpp b/src/calendar.cpp index 7cc39e90eb50e..afb20ce9598c3 100644 --- a/src/calendar.cpp +++ b/src/calendar.cpp @@ -12,6 +12,8 @@ // Divided by 100 to prevent overflowing when converted to moves const int calendar::INDEFINITELY_LONG( std::numeric_limits::max() / 100 ); +const time_duration calendar::INDEFINITELY_LONG_DURATION( + time_duration::from_turns( std::numeric_limits::max() ) ); bool calendar::is_eternal_season = false; int calendar::cur_season_length = 1; @@ -366,8 +368,7 @@ static std::string to_string_clipped( const int num, const clipped_unit type, std::pair clipped_time( const time_duration &d ) { - // TODO: change INDEFINITELY_LONG to time_duration - if( to_turns( d ) >= calendar::INDEFINITELY_LONG ) { + if( d >= calendar::INDEFINITELY_LONG_DURATION ) { return { 0, clipped_unit::forever }; } @@ -410,7 +411,7 @@ std::string to_string_clipped( const time_duration &d, std::string to_string( const time_duration &d ) { - if( d >= time_duration::from_turns( calendar::INDEFINITELY_LONG ) ) { + if( d >= calendar::INDEFINITELY_LONG_DURATION ) { return _( "forever" ); } @@ -580,7 +581,7 @@ void calendar::sync() // mid-game, the result could be the wrong season! season = initial_season; } else { - season = season_type( turn_number / DAYS( sl ) % 4 ); + season = static_cast( turn_number / DAYS( sl ) % 4 ); } day = turn_number / DAYS( 1 ) % sl; diff --git a/src/calendar.h b/src/calendar.h index 1fdad3ffb0244..81fa2d7c09e5d 100644 --- a/src/calendar.h +++ b/src/calendar.h @@ -204,15 +204,24 @@ class calendar public: /** - * The expected duration of the cataclysm - * - * Large number that can be used to approximate infinite amounts of time. + * A number that represents the longest possible action. * * This number should be regarded as a number of turns, and can safely be converted to a * number of seconds or moves (movement points) without integer overflow. If used to * represent a larger unit (hours/days/years), then this will result in integer overflow. */ static const int INDEFINITELY_LONG; + + /** + * The expected duration of the cataclysm + * + * Large duration that can be used to approximate infinite amounts of time. + * + * This number can't be safely converted to a number of moves without causing + * an integer overflow. + */ + static const time_duration INDEFINITELY_LONG_DURATION; + /// @returns Whether the eternal season is enabled. static bool eternal_season(); static void set_eternal_season( bool is_eternal_season ); diff --git a/src/character.cpp b/src/character.cpp index aa38e50fc4690..33ebdfc3cd897 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -898,6 +898,7 @@ item &Character::i_add( item it, bool should_stack ) } auto &item_in_inv = inv.add_item( it, keep_invlet, true, should_stack ); item_in_inv.on_pickup( *this ); + cached_info.erase( "reloadables" ); return item_in_inv; } @@ -3135,7 +3136,9 @@ mutation_value_map = { { "hearing_modifier", calc_mutation_value_multiplicative<&mutation_branch::hearing_modifier> }, { "noise_modifier", calc_mutation_value_multiplicative<&mutation_branch::noise_modifier> }, { "overmap_sight", calc_mutation_value_multiplicative<&mutation_branch::overmap_sight> }, - { "overmap_multiplier", calc_mutation_value_multiplicative<&mutation_branch::overmap_multiplier> } + { "overmap_multiplier", calc_mutation_value_multiplicative<&mutation_branch::overmap_multiplier> }, + { "map_memory_capacity_multiplier", calc_mutation_value_multiplicative<&mutation_branch::map_memory_capacity_multiplier> }, + { "skill_rust_multiplier", calc_mutation_value_multiplicative<&mutation_branch::skill_rust_multiplier> } }; float Character::mutation_value( const std::string &val ) const diff --git a/src/character.h b/src/character.h index 8d4de3c763e75..11d4549856a72 100644 --- a/src/character.h +++ b/src/character.h @@ -676,7 +676,7 @@ class Character : public Creature, public visitable void drop_invalid_inventory(); - bool has_artifact_with( const art_effect_passive effect ) const; + virtual bool has_artifact_with( const art_effect_passive effect ) const; // --------------- Clothing Stuff --------------- /** Returns true if the player is wearing the item. */ @@ -906,6 +906,7 @@ class Character : public Creature, public visitable float activity_level = NO_EXERCISE; std::array encumbrance_cache; + mutable std::map cached_info; /** * Traits / mutations of the character. Key is the mutation id (it's also a valid diff --git a/src/computer.cpp b/src/computer.cpp index 2897ecc887b56..4a69fedc84e31 100644 --- a/src/computer.cpp +++ b/src/computer.cpp @@ -332,7 +332,8 @@ void computer::load_data( const std::string &data ) dump >> tmpname >> tmpaction >> tmpsec; - add_option( string_replace( tmpname, "_", " " ), computer_action( tmpaction ), tmpsec ); + add_option( string_replace( tmpname, "_", " " ), static_cast( tmpaction ), + tmpsec ); } // Pull in failures @@ -341,7 +342,7 @@ void computer::load_data( const std::string &data ) for( int n = 0; n < failsize; n++ ) { int tmpfail; dump >> tmpfail; - add_failure( computer_failure_type( tmpfail ) ); + add_failure( static_cast( tmpfail ) ); } } @@ -592,10 +593,11 @@ void computer::activate_function( computer_action action ) const tripoint center = g->u.global_omt_location(); for( int i = -60; i <= 60; i++ ) { for( int j = -60; j <= 60; j++ ) { - const oter_id &oter = overmap_buffer.ter( center.x + i, center.y + j, center.z ); + point offset( i, j ); + const oter_id &oter = overmap_buffer.ter( center + offset ); if( is_ot_match( "sewer", oter, ot_match_type::type ) || is_ot_match( "sewage", oter, ot_match_type::prefix ) ) { - overmap_buffer.set_seen( center.x + i, center.y + j, center.z, true ); + overmap_buffer.set_seen( center + offset, true ); } } } @@ -609,10 +611,11 @@ void computer::activate_function( computer_action action ) const tripoint center = g->u.global_omt_location(); for( int i = -60; i <= 60; i++ ) { for( int j = -60; j <= 60; j++ ) { - const oter_id &oter = overmap_buffer.ter( center.x + i, center.y + j, center.z ); + point offset( i, j ); + const oter_id &oter = overmap_buffer.ter( center + offset ); if( is_ot_match( "subway", oter, ot_match_type::type ) || is_ot_match( "lab_train_depot", oter, ot_match_type::contains ) ) { - overmap_buffer.set_seen( center.x + i, center.y + j, center.z, true ); + overmap_buffer.set_seen( center + offset, true ); } } } @@ -628,6 +631,10 @@ void computer::activate_function( computer_action action ) add_msg( m_info, _( "Target acquisition canceled." ) ); return; } + + // TODO: Z + target.z = 0; + if( query_yn( _( "Confirm nuclear missile launch." ) ) ) { add_msg( m_info, _( "Nuclear missile launched!" ) ); //Remove the option to fire another missile. @@ -666,7 +673,7 @@ void computer::activate_function( computer_action action ) tmpmap.save(); } - const oter_id oter = overmap_buffer.ter( target.x, target.y, 0 ); + const oter_id oter = overmap_buffer.ter( target ); //~ %s is terrain name g->u.add_memorial_log( pgettext( "memorial_male", "Launched a nuke at a %s." ), pgettext( "memorial_female", "Launched a nuke at a %s." ), diff --git a/src/crash.cpp b/src/crash.cpp index 0cec7e79582c2..bdef8ae81ed60 100644 --- a/src/crash.cpp +++ b/src/crash.cpp @@ -270,10 +270,7 @@ extern "C" { static void signal_handler( int sig ) { - // supress clang-tidy warning about a cast IN SIG_DFL -#ifndef __clang_analyzer__ signal( sig, SIG_DFL ); -#endif const char *msg; switch( sig ) { case SIGSEGV: @@ -292,10 +289,7 @@ extern "C" { return; } log_crash( "Signal", msg ); - // supress clang-tidy warning about a cast IN SIG_DFL -#ifndef __clang_analyzer__ std::signal( SIGABRT, SIG_DFL ); -#endif abort(); } diff --git a/src/debug_menu.cpp b/src/debug_menu.cpp index 069e714bc94f1..9578925a772c1 100644 --- a/src/debug_menu.cpp +++ b/src/debug_menu.cpp @@ -1182,7 +1182,7 @@ void debug() case DEBUG_SPAWN_ARTIFACT: if( const cata::optional center = g->look_around() ) { - artifact_natural_property prop = artifact_natural_property( rng( ARTPROP_NULL + 1, + artifact_natural_property prop = static_cast( rng( ARTPROP_NULL + 1, ARTPROP_MAX - 1 ) ); m.create_anomaly( *center, prop ); m.spawn_natural_artifact( *center, prop ); diff --git a/src/defense.cpp b/src/defense.cpp index 56f8d4e78e82b..5576e53409ffe 100644 --- a/src/defense.cpp +++ b/src/defense.cpp @@ -213,7 +213,7 @@ void defense_game::init_constructions() void defense_game::init_map() { - auto &starting_om = overmap_buffer.get( 0, 0 ); + auto &starting_om = overmap_buffer.get( point_zero ); for( int x = 0; x < OMAPX; x++ ) { for( int y = 0; y < OMAPY; y++ ) { starting_om.ter( x, y, 0 ) = oter_id( "field" ); @@ -538,17 +538,17 @@ void defense_game::setup() switch( selection ) { case 1: // Scenario selection if( action == "RIGHT" ) { - if( style == defense_style( NUM_DEFENSE_STYLES - 1 ) ) { - style = defense_style( 1 ); + if( style == static_cast( NUM_DEFENSE_STYLES - 1 ) ) { + style = static_cast( 1 ); } else { - style = defense_style( style + 1 ); + style = static_cast( style + 1 ); } } if( action == "LEFT" ) { - if( style == defense_style( 1 ) ) { - style = defense_style( NUM_DEFENSE_STYLES - 1 ); + if( style == static_cast( 1 ) ) { + style = static_cast( NUM_DEFENSE_STYLES - 1 ); } else { - style = defense_style( style - 1 ); + style = static_cast( style - 1 ); } } init_to_style( style ); @@ -556,17 +556,17 @@ void defense_game::setup() case 2: // Location selection if( action == "RIGHT" ) { - if( location == defense_location( NUM_DEFENSE_LOCATIONS - 1 ) ) { - location = defense_location( 1 ); + if( location == static_cast( NUM_DEFENSE_LOCATIONS - 1 ) ) { + location = static_cast( 1 ); } else { - location = defense_location( location + 1 ); + location = static_cast( location + 1 ); } } if( action == "LEFT" ) { - if( location == defense_location( 1 ) ) { - location = defense_location( NUM_DEFENSE_LOCATIONS - 1 ); + if( location == static_cast( 1 ) ) { + location = static_cast( NUM_DEFENSE_LOCATIONS - 1 ); } else { - location = defense_location( location - 1 ); + location = static_cast( location - 1 ); } } mvwprintz( w, 5, 2, c_black, "\ @@ -900,7 +900,7 @@ void defense_game::caravan() // Init the items for each category for( int i = 0; i < NUM_CARAVAN_CATEGORIES; i++ ) { - items[i] = caravan_items( caravan_category( i ) ); + items[i] = caravan_items( static_cast( i ) ); for( std::vector::iterator it = items[i].begin(); it != items[i].end(); ) { if( current_wave == 0 || !one_in( 4 ) ) { @@ -1286,7 +1286,7 @@ void draw_caravan_categories( const catacurses::window &w, int category_selected for( int i = 0; i < NUM_CARAVAN_CATEGORIES; i++ ) { mvwprintz( w, i + 3, 1, ( i == category_selected ? h_white : c_white ), - caravan_category_name( caravan_category( i ) ) ); + caravan_category_name( static_cast( i ) ) ); } wrefresh( w ); } diff --git a/src/editmap.cpp b/src/editmap.cpp index 26667aed6b0fe..df3b2e922c1df 100644 --- a/src/editmap.cpp +++ b/src/editmap.cpp @@ -52,12 +52,9 @@ #define dbg(x) DebugLog((DebugLevel)(x),D_GAME) << __FILE__ << ":" << __LINE__ << ": " static constexpr tripoint editmap_boundary_min( 0, 0, -OVERMAP_DEPTH ); -static constexpr tripoint editmap_boundary_max( MAPSIZE_X, MAPSIZE_Y, OVERMAP_HEIGHT ); -static constexpr tripoint editmap_clearance_min( tripoint_zero ); -static constexpr tripoint editmap_clearance_max( 1, 1, 0 ); +static constexpr tripoint editmap_boundary_max( MAPSIZE_X, MAPSIZE_Y, OVERMAP_HEIGHT + 1 ); static constexpr box editmap_boundaries( editmap_boundary_min, editmap_boundary_max ); -static constexpr box editmap_clearance( editmap_clearance_min, editmap_clearance_max ); static const ter_id undefined_ter_id( -1 ); static const furn_id undefined_furn_id( -1 ); @@ -1429,7 +1426,7 @@ tripoint editmap::recalc_target( shapetype shape ) for( int y = origin.y - radius; y <= origin.y + radius; y++ ) { const tripoint p( x, y, z ); if( rl_dist( p, origin ) <= radius ) { - if( generic_inbounds( p, editmap_boundaries, editmap_clearance ) ) { + if( editmap_boundaries.contains_half_open( p ) ) { target_list.push_back( p ); } } @@ -1461,7 +1458,7 @@ tripoint editmap::recalc_target( shapetype shape ) for( int y = sy; y <= ey; y++ ) { if( shape == editmap_rect_filled || x == sx || x == ex || y == sy || y == ey ) { const tripoint p( x, y, z ); - if( generic_inbounds( p, editmap_boundaries, editmap_clearance ) ) { + if( editmap_boundaries.contains_half_open( p ) ) { target_list.push_back( p ); } } @@ -1647,7 +1644,7 @@ int editmap::mapgen_preview( const real_coords &tc, uilist &gmenu ) // Coordinates of the overmap terrain that should be generated. const point omt_pos = ms_to_omt_copy( tc.abs_pos ); - oter_id &omt_ref = overmap_buffer.ter( omt_pos.x, omt_pos.y, target.z ); + oter_id &omt_ref = overmap_buffer.ter( tripoint( omt_pos, target.z ) ); // Copy to store the original value, to restore it upon canceling const oter_id orig_oters = omt_ref; omt_ref = oter_id( gmenu.ret ); @@ -1871,9 +1868,8 @@ int editmap::mapgen_retarget() if( const cata::optional vec = ctxt.get_direction( action ) ) { tripoint ptarget = tripoint( target.x + ( vec->x * SEEX * 2 ), target.y + ( vec->y * SEEY * 2 ), target.z ); - if( generic_inbounds( ptarget, editmap_boundaries, editmap_clearance ) && - generic_inbounds( { ptarget.x + SEEX, ptarget.y + SEEY, ptarget.z }, - editmap_boundaries, editmap_clearance ) ) { + if( editmap_boundaries.contains_half_open( ptarget ) && + editmap_boundaries.contains_half_open( ptarget + point( SEEX, SEEY ) ) ) { target = ptarget; target_list.clear(); diff --git a/src/event.cpp b/src/event.cpp index c459f57c4873d..87dd8e175e934 100644 --- a/src/event.cpp +++ b/src/event.cpp @@ -53,8 +53,8 @@ void event::actualize() g->u.add_memorial_log( pgettext( "memorial_male", "Became wanted by the police!" ), pgettext( "memorial_female", "Became wanted by the police!" ) ); - int robx = ( u_pos.x > map_point.x ? 0 - SEEX * 2 : SEEX * 4 ); - int roby = ( u_pos.y > map_point.y ? 0 - SEEY * 2 : SEEY * 4 ); + int robx = u_pos.x > map_point.x ? 0 - SEEX * 2 : SEEX * 4; + int roby = u_pos.y > map_point.y ? 0 - SEEY * 2 : SEEY * 4; g->summon_mon( robot_type, tripoint( robx, roby, g->u.posz() ) ); } } diff --git a/src/explosion.cpp b/src/explosion.cpp index 6179c7ca9cdf7..4b756c9a7b1b7 100644 --- a/src/explosion.cpp +++ b/src/explosion.cpp @@ -19,6 +19,7 @@ #include "calendar.h" #include "cata_utility.h" #include "color.h" +#include "coordinate_conversions.h" #include "creature.h" #include "damage.h" #include "debug.h" @@ -822,10 +823,9 @@ void nuke( const tripoint &p ) { // TODO: nukes hit above surface, not critter = 0 // TODO: Z - int x = p.x; - int y = p.y; + tripoint p_surface( p.xy(), 0 ); tinymap tmpmap; - tmpmap.load( x * 2, y * 2, 0, false ); + tmpmap.load( omt_to_sm_copy( p_surface ), false ); tripoint dest( 0, 0, p.z ); int &i = dest.x; int &j = dest.y; @@ -841,9 +841,9 @@ void nuke( const tripoint &p ) } } tmpmap.save(); - overmap_buffer.ter( x, y, 0 ) = oter_id( "crater" ); + overmap_buffer.ter( p_surface ) = oter_id( "crater" ); // Kill any npcs on that omap location. - for( const auto &npc : overmap_buffer.get_npcs_near_omt( x, y, 0, 0 ) ) { + for( const auto &npc : overmap_buffer.get_npcs_near_omt( p_surface, 0 ) ) { npc->marked_for_death = true; } } diff --git a/src/faction.cpp b/src/faction.cpp index 9f24f30025094..e4a4e67b134e3 100644 --- a/src/faction.cpp +++ b/src/faction.cpp @@ -372,8 +372,7 @@ void basecamp::faction_display( const catacurses::window &fac_w, const int width tripoint camp_pos = camp_omt_pos(); std::string direction = direction_name( direction_from( player_abspos, camp_pos ) ); mvwprintz( fac_w, ++y, width, c_light_gray, _( "Press enter to rename this camp" ) ); - const std::string centerstring = "center"; - if( ( !direction.compare( centerstring ) ) == 0 ) { + if( direction != "center" ) { mvwprintz( fac_w, ++y, width, c_light_gray, _( "Direction : to the " ) + direction ); } mvwprintz( fac_w, ++y, width, col, _( "Location : (%d, %d)" ), camp_pos.x, camp_pos.y ); @@ -405,7 +404,7 @@ int npc::faction_display( const catacurses::window &fac_w, const int width ) con cata::optional dest = get_mission_destination(); if( dest ) { basecamp *dest_camp; - cata::optional temp_camp = overmap_buffer.find_camp( dest->x, dest->y ); + cata::optional temp_camp = overmap_buffer.find_camp( dest->xy() ); if( temp_camp ) { dest_camp = *temp_camp; dest_string = _( "travelling to : " ) + dest_camp->camp_name(); @@ -423,7 +422,7 @@ int npc::faction_display( const catacurses::window &fac_w, const int width ) con tripoint guy_abspos = global_omt_location(); basecamp *stationed_at; bool is_stationed = false; - cata::optional p = overmap_buffer.find_camp( guy_abspos.x, guy_abspos.y ); + cata::optional p = overmap_buffer.find_camp( guy_abspos.xy() ); if( p ) { is_stationed = true; stationed_at = *p; @@ -431,8 +430,7 @@ int npc::faction_display( const catacurses::window &fac_w, const int width ) con stationed_at = nullptr; } std::string direction = direction_name( direction_from( player_abspos, guy_abspos ) ); - std::string centerstring = "center"; - if( ( !direction.compare( centerstring ) ) == 0 ) { + if( direction != "center" ) { mvwprintz( fac_w, ++y, width, col, _( "Direction : to the " ) + direction ); } else { mvwprintz( fac_w, ++y, width, col, _( "Direction : Nearby" ) ); @@ -467,10 +465,10 @@ int npc::faction_display( const catacurses::window &fac_w, const int width ) con } } // if camp that player is at, has a radio tower - cata::optional player_camp = overmap_buffer.find_camp( g->u.global_omt_location().x, - g->u.global_omt_location().y ); + cata::optional player_camp = + overmap_buffer.find_camp( g->u.global_omt_location().xy() ); if( const cata::optional player_camp = overmap_buffer.find_camp( - g->u.global_omt_location().x, g->u.global_omt_location().y ) ) { + g->u.global_omt_location().xy() ) ) { if( ( *player_camp )->has_provides( "radio_tower" ) ) { max_range *= 5; } @@ -607,7 +605,7 @@ void new_faction_manager::display() const // create a list of faction camps std::vector camps; for( auto elem : g->u.camps ) { - cata::optional p = overmap_buffer.find_camp( elem.x, elem.y ); + cata::optional p = overmap_buffer.find_camp( elem.xy() ); if( !p ) { continue; } diff --git a/src/faction_camp.cpp b/src/faction_camp.cpp index 0125c587d51d8..a86b32e97aaae 100644 --- a/src/faction_camp.cpp +++ b/src/faction_camp.cpp @@ -431,12 +431,12 @@ static cata::optional get_basecamp( npc &p, const std::string &camp_ { tripoint omt_pos = p.global_omt_location(); - cata::optional bcp = overmap_buffer.find_camp( omt_pos.x, omt_pos.y ); + cata::optional bcp = overmap_buffer.find_camp( omt_pos.xy() ); if( bcp ) { return bcp; } g->m.add_camp( p.pos(), "faction_camp" ); - bcp = overmap_buffer.find_camp( omt_pos.x, omt_pos.y ); + bcp = overmap_buffer.find_camp( omt_pos.xy() ); if( !bcp ) { return cata::nullopt; } @@ -2569,7 +2569,7 @@ std::string talk_function::name_mission_tabs( const tripoint &omt_pos, const std if( role_id != base_camps::id ) { return cur_title; } - cata::optional temp_camp = overmap_buffer.find_camp( omt_pos.x, omt_pos.y ); + cata::optional temp_camp = overmap_buffer.find_camp( omt_pos.xy() ); if( !temp_camp ) { return cur_title; } @@ -2907,7 +2907,7 @@ tripoint om_target_tile( const tripoint &omt_pos, int min_range, int range, oter_id &omt_ref = overmap_buffer.ter( omt_tgt ); - if( must_see && !overmap_buffer.seen( omt_tgt.x, omt_tgt.y, omt_tgt.z ) ) { + if( must_see && !overmap_buffer.seen( omt_tgt ) ) { errors = true; popup( _( "You must be able to see the target that you select." ) ); } diff --git a/src/field.h b/src/field.h index f4030540621e8..9e4e0dd225bd5 100644 --- a/src/field.h +++ b/src/field.h @@ -69,6 +69,10 @@ class field_entry return is_alive; } + time_duration get_underwater_age_speedup() const { + return type.obj().underwater_age_speedup; + } + bool decays_on_actualize() const { return type.obj().accelerated_decay; } diff --git a/src/field_type.cpp b/src/field_type.cpp index b1710852f9bcc..9ed47d6fa9d13 100644 --- a/src/field_type.cpp +++ b/src/field_type.cpp @@ -84,6 +84,7 @@ void field_type::load( JsonObject &jo, const std::string & ) fallback_intensity_level.move_cost ); intensity_levels.emplace_back( intensity_level ); } + optional( jo, was_loaded, "underwater_age_speedup", underwater_age_speedup, 0_turns ); optional( jo, was_loaded, "priority", priority, 0 ); optional( jo, was_loaded, "half_life", half_life, 0_turns ); if( jo.has_member( "phase" ) ) { diff --git a/src/field_type.h b/src/field_type.h index c0038d3e22ecb..e756fdc854ae4 100644 --- a/src/field_type.h +++ b/src/field_type.h @@ -44,8 +44,10 @@ struct field_type { std::vector intensity_levels; + time_duration underwater_age_speedup = 0_turns; + int priority = 0; - time_duration half_life = 0_days; + time_duration half_life = 0_turns; phase_id phase = PNULL; bool accelerated_decay = false; bool display_items = true; diff --git a/src/game.cpp b/src/game.cpp index 57db149796d25..41201948728dc 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1715,6 +1715,17 @@ void game::set_critter_died() critter_died = true; } +static int maptile_field_intensity( maptile &mt, field_type_id fld ) +{ + auto field_ptr = mt.find_field( fld ); + + return ( field_ptr == nullptr ? 0 : field_ptr->get_field_intensity() ); +} +static bool maptile_trap_eq( maptile &mt, const trap_id &id ) +{ + return mt.get_trap() == id; +} + int get_heat_radiation( const tripoint &location, bool direct ) { // Direct heat from fire sources @@ -1725,10 +1736,12 @@ int get_heat_radiation( const tripoint &location, bool direct ) for( const tripoint &dest : g->m.points_in_radius( location, 6 ) ) { int heat_intensity = 0; - int ffire = g->m.get_field_intensity( dest, fd_fire ); + maptile mt = g->m.maptile_at( dest ); + + int ffire = maptile_field_intensity( mt, fd_fire ); if( ffire > 0 ) { heat_intensity = ffire; - } else if( g->m.tr_at( dest ).loadid == tr_lava ) { + } else if( maptile_trap_eq( mt, tr_lava ) ) { heat_intensity = 3; } if( heat_intensity == 0 ) { @@ -1760,29 +1773,29 @@ int get_convection_temperature( const tripoint &location ) { // Heat from hot air (fields) int temp_mod = 0; - const trap &trap_at_pos = g->m.tr_at( location ); + maptile mt = g->m.maptile_at( location ); // directly on fire/lava tiles - int tile_intensity = g->m.get_field_intensity( location, fd_fire ); - if( tile_intensity > 0 || trap_at_pos.loadid == tr_lava ) { + int tile_intensity = maptile_field_intensity( mt, fd_fire ); + if( tile_intensity > 0 || maptile_trap_eq( mt, tr_lava ) ) { temp_mod += 300; } // hot air of a fire/lava - auto tile_intensity_mod = []( const tripoint & loc, field_type_id fld, int case_1, int case_2, + auto tile_intensity_mod = []( maptile & mt, field_type_id fld, int case_1, int case_2, int case_3 ) { - int field_intensity = g->m.get_field_intensity( loc, fld ); + int field_intensity = maptile_field_intensity( mt, fld ); int cases[3] = { case_1, case_2, case_3 }; return ( field_intensity > 0 && field_intensity < 4 ) ? cases[ field_intensity - 1 ] : 0; }; // TODO: Jsonize - temp_mod += tile_intensity_mod( location, fd_hot_air1, 2, 6, 10 ); - temp_mod += tile_intensity_mod( location, fd_hot_air2, 6, 16, 20 ); - temp_mod += tile_intensity_mod( location, fd_hot_air3, 16, 40, 70 ); - temp_mod += tile_intensity_mod( location, fd_hot_air4, 70, 100, 160 ); - temp_mod -= tile_intensity_mod( location, fd_cold_air1, 2, 6, 10 ); - temp_mod -= tile_intensity_mod( location, fd_cold_air2, 6, 16, 20 ); - temp_mod -= tile_intensity_mod( location, fd_cold_air3, 16, 40, 70 ); - temp_mod -= tile_intensity_mod( location, fd_cold_air4, 70, 100, 160 ); + temp_mod += tile_intensity_mod( mt, fd_hot_air1, 2, 6, 10 ); + temp_mod += tile_intensity_mod( mt, fd_hot_air2, 6, 16, 20 ); + temp_mod += tile_intensity_mod( mt, fd_hot_air3, 16, 40, 70 ); + temp_mod += tile_intensity_mod( mt, fd_hot_air4, 70, 100, 160 ); + temp_mod -= tile_intensity_mod( mt, fd_cold_air1, 2, 6, 10 ); + temp_mod -= tile_intensity_mod( mt, fd_cold_air2, 6, 16, 20 ); + temp_mod -= tile_intensity_mod( mt, fd_cold_air3, 16, 40, 70 ); + temp_mod -= tile_intensity_mod( mt, fd_cold_air4, 70, 100, 160 ); return temp_mod; } @@ -3431,12 +3444,13 @@ void game::draw_minimap() const int omx = cursx + i; const int omy = cursy + j; nc_color ter_color; + tripoint omp( omx, omy, get_levz() ); std::string ter_sym; - const bool seen = overmap_buffer.seen( omx, omy, get_levz() ); - const bool vehicle_here = overmap_buffer.has_vehicle( omx, omy, get_levz() ); - if( overmap_buffer.has_note( omx, omy, get_levz() ) ) { + const bool seen = overmap_buffer.seen( omp ); + const bool vehicle_here = overmap_buffer.has_vehicle( omp ); + if( overmap_buffer.has_note( omp ) ) { - const std::string ¬e_text = overmap_buffer.note( omx, omy, get_levz() ); + const std::string ¬e_text = overmap_buffer.note( omp ); ter_color = c_yellow; ter_sym = "N"; @@ -3529,15 +3543,15 @@ void game::draw_minimap() ter_color = c_cyan; ter_sym = "c"; } else { - const oter_id &cur_ter = overmap_buffer.ter( omx, omy, get_levz() ); + const oter_id &cur_ter = overmap_buffer.ter( omp ); ter_sym = cur_ter->get_symbol(); - if( overmap_buffer.is_explored( omx, omy, get_levz() ) ) { + if( overmap_buffer.is_explored( omp ) ) { ter_color = c_dark_gray; } else { ter_color = cur_ter->get_color(); } } - if( !drew_mission && targ.x == omx && targ.y == omy ) { + if( !drew_mission && targ.xy() == omp.xy() ) { // If there is a mission target, and it's not on the same // overmap terrain as the player character, mark it. // TODO: Inform player if the mission is above or below @@ -3606,14 +3620,15 @@ void game::draw_minimap() } const int omx = cursx + i; const int omy = cursy + j; - if( overmap_buffer.get_horde_size( omx, omy, get_levz() ) >= HORDE_VISIBILITY_SIZE ) { + tripoint omp( omx, omy, get_levz() ); + if( overmap_buffer.get_horde_size( omp ) >= HORDE_VISIBILITY_SIZE ) { const tripoint cur_pos { omx, omy, get_levz() }; - if( overmap_buffer.seen( omx, omy, get_levz() ) + if( overmap_buffer.seen( omp ) && g->u.overmap_los( cur_pos, sight_points ) ) { mvwputch( w_minimap, j + 3, i + 3, c_green, - overmap_buffer.get_horde_size( omx, omy, get_levz() ) > HORDE_VISIBILITY_SIZE * 2 ? 'Z' : 'z' ); + overmap_buffer.get_horde_size( omp ) > HORDE_VISIBILITY_SIZE * 2 ? 'Z' : 'z' ); } } } @@ -3755,7 +3770,7 @@ std::unordered_set game::get_fishable_locations( int distance, const t } // This point is out of bounds, so bail. - if( !generic_inbounds( current_point, fishing_boundaries ) ) { + if( !fishing_boundaries.contains_inclusive( current_point ) ) { continue; } @@ -10363,23 +10378,26 @@ void game::vertical_notes( int z_before, int z_after ) for( int y = -REVEAL_RADIUS; y <= REVEAL_RADIUS; y++ ) { const int cursx = gpos.x + x; const int cursy = gpos.y + y; - if( !overmap_buffer.seen( cursx, cursy, z_before ) ) { + const tripoint cursp_before( cursx, cursy, z_before ); + const tripoint cursp_after( cursx, cursy, z_after ); + + if( !overmap_buffer.seen( cursp_before ) ) { continue; } - if( overmap_buffer.has_note( cursx, cursy, z_after ) ) { + if( overmap_buffer.has_note( cursp_before ) ) { // Already has a note -> never add an AUTO-note continue; } - const oter_id &ter = overmap_buffer.ter( cursx, cursy, z_before ); - const oter_id &ter2 = overmap_buffer.ter( cursx, cursy, z_after ); + const oter_id &ter = overmap_buffer.ter( cursp_before ); + const oter_id &ter2 = overmap_buffer.ter( cursp_after ); if( z_after > z_before && ter->has_flag( known_up ) && !ter2->has_flag( known_down ) ) { - overmap_buffer.set_seen( cursx, cursy, z_after, true ); - overmap_buffer.add_note( cursx, cursy, z_after, string_format( ">:W;%s", _( "AUTO: goes down" ) ) ); + overmap_buffer.set_seen( cursp_after, true ); + overmap_buffer.add_note( cursp_after, string_format( ">:W;%s", _( "AUTO: goes down" ) ) ); } else if( z_after < z_before && ter->has_flag( known_down ) && !ter2->has_flag( known_up ) ) { - overmap_buffer.set_seen( cursx, cursy, z_after, true ); - overmap_buffer.add_note( cursx, cursy, z_after, string_format( "<:W;%s", _( "AUTO: goes up" ) ) ); + overmap_buffer.set_seen( cursp_after, true ); + overmap_buffer.add_note( cursp_after, string_format( "<:W;%s", _( "AUTO: goes up" ) ) ); } } } @@ -10474,30 +10492,30 @@ void game::update_overmap_seen() const int dist = u.overmap_sight_range( light_level( u.posz() ) ); const int dist_squared = dist * dist; // We can always see where we're standing - overmap_buffer.set_seen( ompos.x, ompos.y, ompos.z, true ); + overmap_buffer.set_seen( ompos, true ); for( int dx = -dist; dx <= dist; dx++ ) { for( int dy = -dist; dy <= dist; dy++ ) { const int h_squared = dx * dx + dy * dy; if( trigdist && h_squared > dist_squared ) { continue; } - int x = ompos.x + dx; - int y = ompos.y + dy; + const tripoint p = ompos + point( dx, dy ); // If circular distances are enabled, scale overmap distances by the diagonality of the sight line. const float multiplier = trigdist ? std::sqrt( h_squared ) / std::max( std::abs( dx ), std::abs( dy ) ) : 1; - const std::vector line = line_to( ompos.x, ompos.y, x, y, 0 ); + const std::vector line = line_to( ompos, p, 0 ); float sight_points = dist; for( auto it = line.begin(); it != line.end() && sight_points >= 0; ++it ) { - const oter_id &ter = overmap_buffer.ter( it->x, it->y, ompos.z ); + const oter_id &ter = overmap_buffer.ter( *it ); sight_points -= static_cast( ter->get_see_cost() ) * multiplier; } if( sight_points >= 0 ) { - overmap_buffer.set_seen( x, y, ompos.z, true ); - for( int z = ompos.z - 1; z >= 0; z-- ) { - overmap_buffer.set_seen( x, y, z, true ); - } + tripoint seen( p ); + do { + overmap_buffer.set_seen( seen, true ); + --seen.z; + } while( seen.z >= 0 ); } } } @@ -11626,7 +11644,7 @@ overmap &game::get_cur_om() const // The player is located in the middle submap of the map. const tripoint sm = m.get_abs_sub() + tripoint( HALF_MAPSIZE, HALF_MAPSIZE, 0 ); const tripoint pos_om = sm_to_om_copy( sm ); - return overmap_buffer.get( pos_om.x, pos_om.y ); + return overmap_buffer.get( pos_om.xy() ); } std::vector game::allies() diff --git a/src/grab.cpp b/src/grab.cpp index 3fb1c046b1042..00ab4a19149ae 100644 --- a/src/grab.cpp +++ b/src/grab.cpp @@ -62,8 +62,8 @@ bool game::grabbed_veh_move( const tripoint &dp ) next_grab.x != 0 && next_grab.y != 0 ) { // Zig-zag (or semi-zig-zag) pull: player is diagonal to vehicle // and moves away from it, but not directly away - dp_veh.x = ( dp.x == -dp_veh.x ) ? 0 : dp_veh.x; - dp_veh.y = ( dp.y == -dp_veh.y ) ? 0 : dp_veh.y; + dp_veh.x = dp.x == -dp_veh.x ? 0 : dp_veh.x; + dp_veh.y = dp.y == -dp_veh.y ? 0 : dp_veh.y; next_grab = -dp_veh; zigzag = true; diff --git a/src/handle_action.cpp b/src/handle_action.cpp index 0559a2c94423e..6c7a80eb2cc8e 100644 --- a/src/handle_action.cpp +++ b/src/handle_action.cpp @@ -75,7 +75,6 @@ static const bionic_id bio_remote( "bio_remote" ); static const trait_id trait_HIBERNATE( "HIBERNATE" ); static const trait_id trait_SHELL2( "SHELL2" ); -static const trait_id trait_DEBUG_HS( "DEBUG_HS" ); const skill_id skill_driving( "driving" ); const skill_id skill_melee( "melee" ); diff --git a/src/handle_liquid.cpp b/src/handle_liquid.cpp index 024df401acfd6..2fbebb661ef54 100644 --- a/src/handle_liquid.cpp +++ b/src/handle_liquid.cpp @@ -91,7 +91,7 @@ namespace liquid_handler { void handle_all_liquid( item liquid, const int radius ) { - while( liquid.charges > 0l ) { + while( liquid.charges > 0 ) { // handle_liquid allows to pour onto the ground, which will handle all the liquid and // set charges to 0. This allows terminating the loop. // The result of handle_liquid is ignored, the player *has* to handle all the liquid. diff --git a/src/help.cpp b/src/help.cpp index bfb6c69239c7f..c9b9cbc6688b0 100644 --- a/src/help.cpp +++ b/src/help.cpp @@ -107,7 +107,7 @@ Press ESC to return to the game." ) ) + 1; second_column = std::max( second_column, utf8_width( cat_name ) + 4 ); } - shortcut_print( win, y + i % half_size, ( i < half_size ? 1 : second_column ), + shortcut_print( win, y + i % half_size, i < half_size ? 1 : second_column, c_white, c_light_blue, cat_name ); } diff --git a/src/iexamine.cpp b/src/iexamine.cpp index 23a72a7623be9..15ff22ed76245 100644 --- a/src/iexamine.cpp +++ b/src/iexamine.cpp @@ -1286,8 +1286,7 @@ void iexamine::bulletin_board( player &p, const tripoint &examp ) { g->validate_camps(); point omt = ms_to_omt_copy( g->m.getabs( examp.x, examp.y ) ); - tripoint omt_tri = tripoint( omt.x, omt.y, p.pos().z ); - cata::optional bcp = overmap_buffer.find_camp( omt_tri.x, omt_tri.y ); + cata::optional bcp = overmap_buffer.find_camp( omt ); if( bcp ) { basecamp *temp_camp = *bcp; temp_camp->validate_assignees(); @@ -3846,7 +3845,7 @@ void iexamine::pay_gas( player &p, const tripoint &examp ) // Okay, we have a cash card. Now we need to know what's left in the pump. const cata::optional pGasPump = getGasPumpByNumber( examp, uistate.ags_pay_gas_selected_pump ); - int amount = pGasPump ? fromPumpFuel( pTank, *pGasPump ) : 0l; + int amount = pGasPump ? fromPumpFuel( pTank, *pGasPump ) : 0; if( amount >= 0 ) { sounds::sound( p.pos(), 6, sounds::sound_t::activity, _( "Glug Glug Glug" ), true, "tool", "gaspump" ); diff --git a/src/inventory.cpp b/src/inventory.cpp index 37315aebb3124..85583b2179a52 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -839,47 +839,6 @@ item *inventory::most_appropriate_painkiller( int pain ) return ret; } -item *inventory::best_for_melee( player &p, double &best ) -{ - item *ret = &null_item_reference(); - for( auto &elem : items ) { - auto score = p.melee_value( elem.front() ); - if( score > best ) { - best = score; - ret = &( elem.front() ); - } - } - - return ret; -} - -item *inventory::most_loaded_gun() -{ - item *ret = &null_item_reference(); - int max = 0; - for( auto &elem : items ) { - item &gun = elem.front(); - if( !gun.is_gun() ) { - continue; - } - - const auto required = gun.ammo_required(); - int cur = 0; - if( required <= 0 ) { - // Arbitrary - cur = 5; - } else { - cur = gun.ammo_remaining() / required; - } - - if( cur > max ) { - ret = &gun; - max = cur; - } - } - return ret; -} - void inventory::rust_iron_items() { for( auto &elem : items ) { diff --git a/src/inventory.h b/src/inventory.h index f17c320d1697d..e3259a88b6a41 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -177,8 +177,6 @@ class inventory : public visitable int worst_item_value( npc *p ) const; bool has_enough_painkiller( int pain ) const; item *most_appropriate_painkiller( int pain ); - item *best_for_melee( player &p, double &best ); - item *most_loaded_gun(); void rust_iron_items(); diff --git a/src/inventory_ui.cpp b/src/inventory_ui.cpp index 2195586f44a6e..e515b70d1f776 100644 --- a/src/inventory_ui.cpp +++ b/src/inventory_ui.cpp @@ -1826,7 +1826,10 @@ item_location inventory_pick_selector::execute() } else if( input.action == "QUIT" ) { return item_location(); } else if( input.action == "CONFIRM" ) { - return get_active_column().get_selected().any_item(); + const inventory_entry &selected = get_active_column().get_selected(); + if( selected ) { + return selected.any_item(); + } } else if( input.action == "INVENTORY_FILTER" ) { set_filter(); } else { diff --git a/src/item.cpp b/src/item.cpp index ade66029a6f10..f70b2c379cc38 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -161,6 +161,29 @@ item &null_item_reference() return result; } +namespace item_internal +{ +bool goes_bad_temp_cache = false; +bool goes_bad_temp_cache_set = false; +inline bool goes_bad_cache_fetch() +{ + return goes_bad_temp_cache; +} +inline void goes_bad_cache_set( bool v ) +{ + goes_bad_temp_cache = v; + goes_bad_temp_cache_set = true; +} +inline void goes_bad_cache_unset() +{ + goes_bad_temp_cache_set = goes_bad_temp_cache = false; +} +inline bool goes_bad_cache_is_set() +{ + return goes_bad_temp_cache_set; +} +} // namespace item_internal + const int item::INFINITE_CHARGES = INT_MAX; item::item() : bday( calendar::start ) @@ -1240,7 +1263,7 @@ std::string item::info( std::vector &info, const iteminfo_query *parts return ( g->u.vitamin_rate( v.first ) > 0_turns && v.second != 0 ) // only display vitamins that we actually require ? string_format( "%s (%i%%)", v.first.obj().name(), - int( v.second * g->u.vitamin_rate( v.first ) / 1_days * 100 ) ) + static_cast( v.second * g->u.vitamin_rate( v.first ) / 1_days * 100 ) ) : std::string(); } ); if( !required_vits.empty() && parts->test( iteminfo_parts::FOOD_VITAMINS ) ) { @@ -4005,6 +4028,9 @@ std::set item::get_techniques() const bool item::goes_bad() const { + if( item_internal::goes_bad_cache_is_set() ) { + return item_internal::goes_bad_cache_fetch(); + } if( has_flag( "PROCESSING" ) ) { return false; } @@ -4420,7 +4446,7 @@ bool item::ready_to_revive( const tripoint &pos ) const return false; } int age_in_hours = to_hours( age() ); - age_in_hours -= int( static_cast( burnt ) / ( volume() / 250_ml ) ); + age_in_hours -= static_cast( static_cast( burnt ) / ( volume() / 250_ml ) ); if( damage_level( 4 ) > 0 ) { age_in_hours /= ( damage_level( 4 ) + 1 ); } @@ -5057,7 +5083,7 @@ bool item::is_med_container() const bool item::is_corpse() const { - return typeId() == "corpse" && corpse != nullptr; + return corpse != nullptr && typeId() == "corpse"; } const mtype *item::get_mtype() const @@ -5935,7 +5961,7 @@ const itype *item::ammo_data() const auto mods = is_gun() ? gunmods() : toolmods(); for( const auto e : mods ) { - if( !e->type->mod->ammo_modifier.empty() && + if( !e->type->mod->ammo_modifier.empty() && e->ammo_current() != "null" && item_controller->has_template( e->ammo_current() ) ) { return item_controller->find_template( e->ammo_current() ); } @@ -7443,6 +7469,7 @@ void item::process_temperature_rot( float insulation, const tripoint &pos, } time_point time; + item_internal::goes_bad_cache_set( goes_bad() ); if( goes_bad() ) { time = std::min( { last_rot_check, last_temp_check } ); } else { @@ -7479,8 +7506,8 @@ void item::process_temperature_rot( float insulation, const tripoint &pos, //Use weather if above ground, use map temp if below double env_temperature = 0; if( pos.z >= 0 ) { - w_point weather = wgen.get_weather( pos, time, seed ); - env_temperature = weather.temperature + enviroment_mod + local_mod; + double weather_temperature = wgen.get_weather_temperature( pos, time, seed ); + env_temperature = weather_temperature + enviroment_mod + local_mod; } else { env_temperature = AVERAGE_ANNUAL_TEMPERATURE + enviroment_mod + local_mod; } @@ -7521,6 +7548,7 @@ void item::process_temperature_rot( float insulation, const tripoint &pos, if( has_rotten_away() || ( is_corpse() && rot > 10_days ) ) { // No need to track item that will be gone + item_internal::goes_bad_cache_unset(); return; } } @@ -7532,6 +7560,7 @@ void item::process_temperature_rot( float insulation, const tripoint &pos, if( now - time > smallest_interval ) { calc_temp( temp, insulation, now ); calc_rot( now, temp ); + item_internal::goes_bad_cache_unset(); return; } @@ -7539,6 +7568,7 @@ void item::process_temperature_rot( float insulation, const tripoint &pos, if( specific_energy < 0 ) { set_item_temperature( temp_to_kelvin( temp ) ); } + item_internal::goes_bad_cache_unset(); } void item::calc_temp( const int temp, const float insulation, const time_point &time ) diff --git a/src/item_stack.cpp b/src/item_stack.cpp index 0d4bcc6cc509b..910a86fb4fdde 100644 --- a/src/item_stack.cpp +++ b/src/item_stack.cpp @@ -111,7 +111,7 @@ int item_stack::amount_can_fit( const item &it ) const const item *here = it.count_by_charges() ? stacks_with( it ) : nullptr; if( violates_count && !here ) { - return 0l; + return 0; } // Call max because a tile may have been overfilled to begin with (e.g. #14115) const int ret = std::max( 0, it.charges_per_volume( free_volume() ) ); diff --git a/src/iuse.cpp b/src/iuse.cpp index e53f06188cc74..41d9d0fc31de8 100644 --- a/src/iuse.cpp +++ b/src/iuse.cpp @@ -6567,7 +6567,7 @@ static std::string colorized_trap_name_at( const tripoint &point ) std::string name; if( !trap.is_null() && trap.get_visibility() <= 1 ) { name = colorize( trap.name(), trap.color ) + _( " on " ); - }; + } return name; } @@ -7015,6 +7015,9 @@ static extended_photo_def photo_def_for_camera_point( const tripoint &aim_point, } if( guy ) { + if( guy->is_hallucination() ) { + continue; // do not include hallucinations + } if( guy->movement_mode_is( PMM_CROUCH ) ) { pose = _( "sits" ); } else { @@ -7443,7 +7446,9 @@ int iuse::camera( player *p, item *it, bool, const tripoint & ) p->add_msg_if_player( _( "Strange... there's nothing in the center of picture?" ) ); } } else if( guy ) { - if( !aim_bounds.is_point_inside( trajectory_point ) ) { + if( trajectory_point == aim_point && guy->is_hallucination() ) { + p->add_msg_if_player( _( "Strange... %s's not visible on the picture?" ), guy->name ); + } else if( !aim_bounds.is_point_inside( trajectory_point ) ) { // take a photo of the monster that's in the way p->add_msg_if_player( m_warning, _( "%s got in the way of your photo." ), guy->name ); incorrect_focus = true; diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index 585b1517ce046..13c4ac71e8b1a 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -635,8 +635,8 @@ void consume_drug_iuse::info( const item &, std::vector &dump ) const if( rate <= 0_turns ) { return std::string(); } - const int lo = int( v.second.first * rate / 1_days * 100 ); - const int hi = int( v.second.second * rate / 1_days * 100 ); + const int lo = static_cast( v.second.first * rate / 1_days * 100 ); + const int hi = static_cast( v.second.second * rate / 1_days * 100 ); return string_format( lo == hi ? "%s (%i%%)" : "%s (%i-%i%%)", v.first.obj().name(), lo, hi ); @@ -1078,16 +1078,28 @@ void reveal_map_actor::load( JsonObject &obj ) radius = obj.get_int( "radius" ); message = obj.get_string( "message" ); JsonArray jarr = obj.get_array( "terrain" ); + std::string ter; + ot_match_type ter_match_type; while( jarr.has_more() ) { - omt_types.push_back( jarr.next_string() ); + if( jarr.test_string() ) { + ter = jarr.next_string(); + ter_match_type = ot_match_type::contains; + } else { + JsonObject jo = jarr.next_object(); + ter = jo.get_string( "om_terrain" ); + ter_match_type = jo.get_enum_value( jo.get_string( "om_terrain_match_type", + "CONTAINS" ), ot_match_type::contains ); + } + omt_types.push_back( std::make_pair( ter, ter_match_type ) ); } } -void reveal_map_actor::reveal_targets( const tripoint ¢er, const std::string &target, +void reveal_map_actor::reveal_targets( const tripoint ¢er, + const std::pair &target, int reveal_distance ) const { - const auto places = overmap_buffer.find_all( center, target, radius, false, - ot_match_type::contains ); + const auto places = overmap_buffer.find_all( center, target.first, radius, false, + target.second ); for( auto &place : places ) { overmap_buffer.reveal( place, reveal_distance ); } @@ -3561,7 +3573,7 @@ hp_part heal_actor::use_healing_item( player &healer, player &patient, item &it, int highest_damage = 0; for( int i = 0; i < num_hp_parts; i++ ) { int damage = 0; - const body_part i_bp = player::hp_to_bp( hp_part( i ) ); + const body_part i_bp = player::hp_to_bp( static_cast( i ) ); if( !patient.has_effect( effect_bandaged, i_bp ) ) { damage += patient.hp_max[i] - patient.hp_cur[i]; } @@ -3570,7 +3582,7 @@ hp_part heal_actor::use_healing_item( player &healer, player &patient, item &it, damage += infect * patient.get_effect_dur( effect_infected, i_bp ) / 10_minutes; if( damage > highest_damage ) { highest_damage = damage; - healed = hp_part( i ); + healed = static_cast( i ); } } } else if( patient.is_player() ) { diff --git a/src/iuse_actor.h b/src/iuse_actor.h index fb498a7a33aa2..b0ce6e2dba9c9 100644 --- a/src/iuse_actor.h +++ b/src/iuse_actor.h @@ -11,6 +11,7 @@ #include "calendar.h" #include "color.h" +#include "enums.h" #include "explosion.h" #include "game_constants.h" #include "iuse.h" @@ -382,13 +383,14 @@ class reveal_map_actor : public iuse_actor /** * Overmap terrain types that get revealed. */ - std::vector omt_types; + std::vector> omt_types; /** * The message displayed after revealing. */ std::string message; - void reveal_targets( const tripoint ¢er, const std::string &target, int reveal_distance ) const; + void reveal_targets( const tripoint ¢er, const std::pair &target, + int reveal_distance ) const; reveal_map_actor( const std::string &type = "reveal_map" ) : iuse_actor( type ) {} diff --git a/src/iuse_software_snake.cpp b/src/iuse_software_snake.cpp index 02b478d8ab3ce..bb74998b71a5d 100644 --- a/src/iuse_software_snake.cpp +++ b/src/iuse_software_snake.cpp @@ -86,8 +86,8 @@ int snake_game::start_game() std::vector > vSnakeBody; std::map > mSnakeBody; - int iOffsetX = ( TERMX > FULL_SCREEN_WIDTH ) ? ( TERMX - FULL_SCREEN_WIDTH ) / 2 : 0; - int iOffsetY = ( TERMY > FULL_SCREEN_HEIGHT ) ? ( TERMY - FULL_SCREEN_HEIGHT ) / 2 : 0; + int iOffsetX = TERMX > FULL_SCREEN_WIDTH ? ( TERMX - FULL_SCREEN_WIDTH ) / 2 : 0; + int iOffsetY = TERMY > FULL_SCREEN_HEIGHT ? ( TERMY - FULL_SCREEN_HEIGHT ) / 2 : 0; catacurses::window w_snake = catacurses::newwin( FULL_SCREEN_HEIGHT, FULL_SCREEN_WIDTH, iOffsetY, iOffsetX ); diff --git a/src/lightmap.cpp b/src/lightmap.cpp index 9ffca4026ee79..00a6868e70cf1 100644 --- a/src/lightmap.cpp +++ b/src/lightmap.cpp @@ -12,6 +12,7 @@ #include "avatar.h" #include "fragment_cloud.h" // IWYU pragma: keep #include "game.h" +#include "math_defines.h" #include "map.h" #include "map_iterator.h" #include "mapdata.h" @@ -42,19 +43,12 @@ static constexpr point lightmap_boundary_min( point_zero ); static constexpr point lightmap_boundary_max( LIGHTMAP_CACHE_X, LIGHTMAP_CACHE_Y ); -static constexpr point lightmap_clearance_min( point_zero ); -static constexpr point lightmap_clearance_max( 1, 1 ); const rectangle lightmap_boundaries( lightmap_boundary_min, lightmap_boundary_max ); -const rectangle lightmap_clearance( lightmap_clearance_min, lightmap_clearance_max ); const efftype_id effect_onfire( "onfire" ); const efftype_id effect_haslight( "haslight" ); -constexpr double PI = 3.14159265358979323846; -constexpr double HALFPI = 1.57079632679489661923; -constexpr double SQRT_2 = 1.41421356237309504880; - std::string four_quadrants::to_string() const { return string_format( "(%.2f,%.2f,%.2f,%.2f)", @@ -324,9 +318,9 @@ void map::generate_lightmap( const int zlev ) if( !outside_cache[p.x][p.y] ) { // Apply light sources for external/internal divide for( int i = 0; i < 4; ++i ) { - if( generic_inbounds( { p.x + dir_x[i], p.y + dir_y[i] }, - lightmap_boundaries, lightmap_clearance - ) && outside_cache[p.x + dir_x[i]][p.y + dir_y[i]] + point neighbour = p.xy() + point( dir_x[i], dir_y[i] ); + if( lightmap_boundaries.contains_half_open( neighbour ) + && outside_cache[neighbour.x][neighbour.y] ) { if( light_transparency( p ) > LIGHT_TRANSPARENCY_SOLID ) { update_light_quadrants( @@ -454,18 +448,18 @@ void map::generate_lightmap( const int zlev ) if( vp.has_flag( VPFLAG_CONE_LIGHT ) ) { if( veh_luminance > LL_LIT ) { - add_light_source( src, SQRT_2 ); // Add a little surrounding light + add_light_source( src, M_SQRT2 ); // Add a little surrounding light apply_light_arc( src, v->face.dir() + pt->direction, veh_luminance, 45 ); } } else if( vp.has_flag( VPFLAG_WIDE_CONE_LIGHT ) ) { if( veh_luminance > LL_LIT ) { - add_light_source( src, SQRT_2 ); // Add a little surrounding light + add_light_source( src, M_SQRT2 ); // Add a little surrounding light apply_light_arc( src, v->face.dir() + pt->direction, veh_luminance, 90 ); } } else if( vp.has_flag( VPFLAG_HALF_CIRCLE_LIGHT ) ) { - add_light_source( src, SQRT_2 ); // Add a little surrounding light + add_light_source( src, M_SQRT2 ); // Add a little surrounding light apply_light_arc( src, v->face.dir() + pt->direction, vp.bonus, 180 ); } else if( vp.has_flag( VPFLAG_CIRCLE_LIGHT ) ) { @@ -606,17 +600,16 @@ map::apparent_light_info map::apparent_light_helper( const level_cache &map_cach four_quadrants seen_from( 0 ); for( const offset_and_quadrants &oq : adjacent_offsets ) { - const int neighbour_x = p.x + oq.offset.x; - const int neighbour_y = p.y + oq.offset.y; + const point neighbour = p.xy() + oq.offset; - if( !generic_inbounds( { neighbour_x, neighbour_y }, lightmap_boundaries, lightmap_clearance ) ) { + if( !lightmap_boundaries.contains_half_open( neighbour ) ) { continue; } - if( is_opaque( neighbour_x, neighbour_y ) ) { + if( is_opaque( neighbour.x, neighbour.y ) ) { continue; } - if( map_cache.seen_cache[neighbour_x][neighbour_y] == 0 && - map_cache.camera_cache[neighbour_x][neighbour_y] == 0 ) { + if( map_cache.seen_cache[neighbour.x][neighbour.y] == 0 && + map_cache.camera_cache[neighbour.x][neighbour.y] == 0 ) { continue; } // This is a non-opaque visible neighbour, so count visibility from the relevant @@ -1421,7 +1414,7 @@ void map::apply_light_arc( const tripoint &p, int angle, float luminance, int wi int nangle = angle % 360; tripoint end; - double rad = PI * static_cast( nangle ) / 180; + double rad = M_PI * static_cast( nangle ) / 180; int range = LIGHT_RANGE( luminance ); calc_ray_end( nangle, range, p, end ); apply_light_ray( lit, p, end, luminance ); @@ -1435,13 +1428,13 @@ void map::apply_light_arc( const tripoint &p, int angle, float luminance, int wi } // attempt to determine beam intensity required to cover all squares - const double wstep = ( wangle / ( wdist * SQRT_2 ) ); + const double wstep = ( wangle / ( wdist * M_SQRT2 ) ); // NOLINTNEXTLINE(clang-analyzer-security.FloatLoopCounter) for( double ao = wstep; ao <= wangle; ao += wstep ) { if( trigdist ) { - double fdist = ( ao * HALFPI ) / wangle; - double orad = ( PI * ao / 180.0 ); + double fdist = ( ao * M_PI_2 ) / wangle; + double orad = ( M_PI * ao / 180.0 ); end.x = static_cast( p.x + ( static_cast( range ) - fdist * 2.0 ) * cos( rad + orad ) ); end.y = static_cast( p.y + ( static_cast( range ) - fdist * 2.0 ) * sin( @@ -1499,7 +1492,7 @@ void map::apply_light_ray( bool lit[LIGHTMAP_CACHE_X][LIGHTMAP_CACHE_Y], t += ay; // TODO: clamp coordinates to map bounds before this method is called. - if( generic_inbounds( { x, y }, lightmap_boundaries, lightmap_clearance ) ) { + if( lightmap_boundaries.contains_half_open( point( x, y ) ) ) { float current_transparency = transparency_cache[x][y]; bool is_opaque = ( current_transparency == LIGHT_TRANSPARENCY_SOLID ); if( !lit[x][y] ) { @@ -1531,7 +1524,7 @@ void map::apply_light_ray( bool lit[LIGHTMAP_CACHE_X][LIGHTMAP_CACHE_Y], y += dy; t += ax; - if( generic_inbounds( { x, y }, lightmap_boundaries, lightmap_clearance ) ) { + if( lightmap_boundaries.contains_half_open( point( x, y ) ) ) { float current_transparency = transparency_cache[x][y]; bool is_opaque = ( current_transparency == LIGHT_TRANSPARENCY_SOLID ); if( !lit[x][y] ) { diff --git a/src/line.cpp b/src/line.cpp index c3d57688e383c..62b6745f69d45 100644 --- a/src/line.cpp +++ b/src/line.cpp @@ -8,6 +8,7 @@ #include #include +#include "math_defines.h" #include "translations.h" #include "string_formatter.h" #include "output.h" @@ -250,7 +251,14 @@ float trig_dist( const tripoint &loc1, const tripoint &loc2 ) int square_dist( const int x1, const int y1, const int x2, const int y2 ) { - return square_dist( tripoint( x1, y1, 0 ), tripoint( x2, y2, 0 ) ); + return square_dist( point( x1, y1 ), point( x2, y2 ) ); +} + +int square_dist( const point &loc1, const point &loc2 ) +{ + const int dx = abs( loc1.x - loc2.x ); + const int dy = abs( loc1.y - loc2.y ); + return dx > dy ? dx : dy; } int square_dist( const tripoint &loc1, const tripoint &loc2 ) @@ -283,7 +291,7 @@ int rl_dist( const tripoint &loc1, const tripoint &loc2 ) // This more general version of this function gives correct values for larger values. unsigned make_xyz( const int x, const int y, const int z ) { - static const double sixteenth_arc = 0.392699082; + static constexpr double sixteenth_arc = M_PI / 8; int vertical_position = ( ( z > 0 ) ? 2u : ( z < 0 ) ? 1u : 0u ) * 9u; if( x == 0 && y == 0 ) { return vertical_position; diff --git a/src/line.h b/src/line.h index 464fa219378a3..5aacee74574cb 100644 --- a/src/line.h +++ b/src/line.h @@ -113,6 +113,7 @@ float trig_dist( int x1, int y1, int x2, int y2 ); float trig_dist( const tripoint &loc1, const tripoint &loc2 ); // Roguelike distance; minimum of dX and dY int square_dist( int x1, int y1, int x2, int y2 ); +int square_dist( const point &loc1, const point &loc2 ); int square_dist( const tripoint &loc1, const tripoint &loc2 ); int rl_dist( int x1, int y1, int x2, int y2 ); int rl_dist( const tripoint &loc1, const tripoint &loc2 ); diff --git a/src/list.h b/src/list.h new file mode 100644 index 0000000000000..3672bd1ce5010 --- /dev/null +++ b/src/list.h @@ -0,0 +1,2476 @@ +// This software is a modified version of the original. +// The original license is as follows: + +// Copyright (c) 2019, Matthew Bentley (mattreecebentley@gmail.com) www.plflib.org + +// zLib license (https://www.zlib.net/zlib_license.html): +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgement in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. + +#pragma once +#ifndef LIST_H +#define LIST_H + +#define LIST_BLOCK_MIN static_cast((sizeof(node) * 8 > (sizeof(*this) + sizeof(group)) * 2) ? 8 : (((sizeof(*this) + sizeof(group)) * 2) / sizeof(node)) + 1) +#define LIST_BLOCK_MAX 2048 + +#define LIST_CONSTEXPR +#define LIST_NOEXCEPT_SWAP(the_allocator) noexcept +#define LIST_NOEXCEPT_MOVE_ASSIGNMENT(the_allocator) noexcept + +// TODO: Switch to these when we move to C++17 +// #define LIST_CONSTEXPR constexpr +// #define LIST_NOEXCEPT_SWAP(the_allocator) noexcept(std::allocator_traits::propagate_on_container_swap::value) +// #define LIST_NOEXCEPT_MOVE_ASSIGNMENT(the_allocator) noexcept(std::allocator_traits::is_always_equal::value) + +// Note: GCC creates faster code without forcing inline +#if defined(_MSC_VER) +#define LIST_FORCE_INLINE __forceinline +#else +#define LIST_FORCE_INLINE +#endif + +// TODO: get rid of these defines +#define LIST_CONSTRUCT(the_allocator, allocator_instance, location, ...) std::allocator_traits::construct(allocator_instance, location, __VA_ARGS__) +#define LIST_DESTROY(the_allocator, allocator_instance, location) std::allocator_traits::destroy(allocator_instance, location) +#define LIST_ALLOCATE(the_allocator, allocator_instance, size, hint) std::allocator_traits::allocate(allocator_instance, size, hint) +#define LIST_ALLOCATE_INITIALIZATION(the_allocator, size, hint) std::allocator_traits::allocate(*this, size, hint) +#define LIST_DEALLOCATE(the_allocator, allocator_instance, location, size) std::allocator_traits::deallocate(allocator_instance, location, size) + +#include // std::sort +#include // assert +#include // memmove, memcpy +#include +#include // std::bidirectional_iterator_tag +#include // std::numeric_limits +#include // std::uninitialized_copy, std::allocator +#include // std::is_trivially_destructible, etc +#include // std::move + +namespace cata +{ + +template > class + list : private element_allocator_type +{ + public: + // Standard container typedefs: + using value_type = element_type; + using allocator_type = element_allocator_type; + using group_size_type = unsigned short; + + using size_type = typename std::allocator_traits::size_type; + using difference_type = typename std::allocator_traits::difference_type; + using reference = element_type&; + using const_reference = const element_type&; + using pointer = typename std::allocator_traits::pointer; + using const_pointer = typename std::allocator_traits::const_pointer; + + // Iterator declarations: + template class list_iterator; + using iterator = list_iterator; + using const_iterator = list_iterator; + friend class list_iterator; // Using 'iterator' typedef name here is illegal under C++03 + friend class list_iterator; + + template class list_reverse_iterator; + using reverse_iterator = list_reverse_iterator; + using const_reverse_iterator = list_reverse_iterator; + friend class list_reverse_iterator; + friend class list_reverse_iterator; + + private: + struct group; // forward declarations for typedefs below + struct node; + + using group_allocator_type = typename std::allocator_traits::template + rebind_alloc; + using node_allocator_type = typename std::allocator_traits::template + rebind_alloc; + using group_pointer_type = typename std::allocator_traits::pointer; + using node_pointer_type = typename std::allocator_traits::pointer; + using node_pointer_allocator_type = typename std::allocator_traits::template + rebind_alloc; + + struct node_base { + node_pointer_type next, previous; + + node_base() + {} + + node_base( const node_pointer_type &n, const node_pointer_type &p ): + next( n ), + previous( p ) + {} + + + node_base( node_pointer_type &&n, node_pointer_type &&p ) noexcept: + next( std::move( n ) ), + previous( std::move( p ) ) + {} + }; + + struct node : public node_base { + element_type element; + + node( const node_pointer_type next, const node_pointer_type previous, const element_type &source ): + node_base( next, previous ), + element( source ) + {} + + node( node_pointer_type &&next, node_pointer_type &&previous, element_type &&source ) noexcept: + node_base( std::move( next ), std::move( previous ) ), + element( std::move( source ) ) + {} + + template + node( node_pointer_type const next, node_pointer_type const previous, arguments &&... parameters ): + node_base( next, previous ), + element( std::forward( parameters ) ... ) + {} + }; + + struct group : public node_allocator_type { + node_pointer_type nodes; + node_pointer_type free_list_head; + node_pointer_type beyond_end; + group_size_type number_of_elements; + + group() noexcept: + nodes( NULL ), + free_list_head( NULL ), + beyond_end( NULL ), + number_of_elements( 0 ) + {} + + group( const group_size_type group_size, node_pointer_type const previous = NULL ): + nodes( LIST_ALLOCATE_INITIALIZATION( node_allocator_type, group_size, previous ) ), + free_list_head( NULL ), + beyond_end( nodes + group_size ), + number_of_elements( 0 ) + {} + + // Actually a move operator, used by c++03 in group_vector's remove, expand_capacity and append + group &operator=( const group &source ) noexcept { + nodes = source.nodes; + free_list_head = source.free_list_head; + beyond_end = source.beyond_end; + number_of_elements = source.number_of_elements; + return *this; + } + + group( group &&source ) noexcept: + node_allocator_type( source ), + nodes( std::move( source.nodes ) ), + free_list_head( std::move( source.free_list_head ) ), + beyond_end( std::move( source.beyond_end ) ), + number_of_elements( source.number_of_elements ) { + source.nodes = NULL; + source.beyond_end = NULL; + } + + group &operator=( group &&source ) noexcept { + nodes = std::move( source.nodes ); + free_list_head = std::move( source.free_list_head ); + beyond_end = std::move( source.beyond_end ); + number_of_elements = std::move( source.number_of_elements ); + source.nodes = NULL; + source.beyond_end = NULL; + return *this; + } + + ~group() noexcept { + LIST_DEALLOCATE( node_allocator_type, ( *this ), nodes, + static_cast( beyond_end - nodes ) ); + } + }; + + class group_vector : private node_pointer_allocator_type + { + public: + // last_endpoint_group is the last -active- group in the block. Other -inactive- (previously used, now empty of elements) groups may be stored after this group for future usage (to reduce deallocation/reallocation of nodes). block_pointer + size - 1 == the last group in the block, regardless of whether or not the group is active. + group_pointer_type last_endpoint_group, block_pointer, last_searched_group; + size_type size; + + struct ebco_pair2 : allocator_type { // empty-base-class optimisation + size_type capacity; // Total element capacity of all initialized groups + explicit ebco_pair2( const size_type number_of_elements ) noexcept: capacity( + number_of_elements ) {} + } element_allocator_pair; + + struct ebco_pair : group_allocator_type { + size_type capacity; // Total group capacity + explicit ebco_pair( const size_type number_of_groups ) noexcept: capacity( number_of_groups ) {} + } group_allocator_pair; + + group_vector() noexcept: + node_pointer_allocator_type( node_pointer_allocator_type() ), + last_endpoint_group( NULL ), + block_pointer( NULL ), + last_searched_group( NULL ), + size( 0 ), + element_allocator_pair( 0 ), + group_allocator_pair( 0 ) + {} + + inline LIST_FORCE_INLINE void blank() noexcept { + if LIST_CONSTEXPR( std::is_trivial::value ) { + std::memset( static_cast( this ), 0, sizeof( group_vector ) ); + } else { + last_endpoint_group = NULL; + block_pointer = NULL; + last_searched_group = NULL; + size = 0; + element_allocator_pair.capacity = 0; + group_allocator_pair.capacity = 0; + } + } + + group_vector( group_vector &&source ) noexcept: + last_endpoint_group( std::move( source.last_endpoint_group ) ), + block_pointer( std::move( source.block_pointer ) ), + last_searched_group( std::move( source.last_searched_group ) ), + size( source.size ), + element_allocator_pair( source.element_allocator_pair.capacity ), + group_allocator_pair( source.group_allocator_pair.capacity ) { + source.blank(); + } + + group_vector &operator = ( group_vector &&source ) noexcept { + if LIST_CONSTEXPR( std::is_trivial::value ) { + std::memcpy( static_cast( this ), &source, sizeof( group_vector ) ); + } else { + last_endpoint_group = std::move( source.last_endpoint_group ); + block_pointer = std::move( source.block_pointer ); + last_searched_group = std::move( source.last_searched_group ); + size = source.size; + element_allocator_pair.capacity = source.element_allocator_pair.capacity; + group_allocator_pair.capacity = source.group_allocator_pair.capacity; + } + + source.blank(); + return *this; + } + + ~group_vector() noexcept + {} + + void destroy_all_data( const node_pointer_type last_endpoint_node ) noexcept { + if( block_pointer == NULL ) { + return; + } + + if LIST_CONSTEXPR( !std::is_trivially_destructible::value || + !std::is_trivially_destructible::value ) { + clear( last_endpoint_node ); // If clear has already been called, last_endpoint_node will already be == block_pointer->nodes, so no work will occur + } + + const group_pointer_type end_group = block_pointer + size; + for( group_pointer_type current_group = block_pointer; current_group != end_group; + ++current_group ) { + LIST_DESTROY( group_allocator_type, group_allocator_pair, current_group ); + } + + LIST_DEALLOCATE( group_allocator_type, group_allocator_pair, block_pointer, + group_allocator_pair.capacity ); + blank(); + } + + void clear( const node_pointer_type last_endpoint_node ) noexcept { + for( group_pointer_type current_group = block_pointer; current_group != last_endpoint_group; + ++current_group ) { + if LIST_CONSTEXPR( !std::is_trivially_destructible::value || + !std::is_trivially_destructible::value ) { + const node_pointer_type end = current_group->beyond_end; + + if( ( end - current_group->nodes ) != current_group->number_of_elements ) { + // If there are erased nodes present in the group + for( node_pointer_type current_node = current_group->nodes; current_node != end; ++current_node ) { + if LIST_CONSTEXPR( !std::is_trivially_destructible::value ) { + if( current_node->next != NULL ) { // ie. is not part of free list + LIST_DESTROY( element_allocator_type, element_allocator_pair, &( current_node->element ) ); + } + } + + if LIST_CONSTEXPR( !std::is_trivially_destructible::value ) { + LIST_DESTROY( node_pointer_allocator_type, ( *this ), &( current_node->next ) ); + LIST_DESTROY( node_pointer_allocator_type, ( *this ), &( current_node->previous ) ); + } + } + } else { + for( node_pointer_type current_node = current_group->nodes; current_node != end; ++current_node ) { + if LIST_CONSTEXPR( !std::is_trivially_destructible::value ) { + LIST_DESTROY( element_allocator_type, element_allocator_pair, &( current_node->element ) ); + } + + if LIST_CONSTEXPR( !std::is_trivially_destructible::value ) { + LIST_DESTROY( node_pointer_allocator_type, ( *this ), &( current_node->next ) ); + LIST_DESTROY( node_pointer_allocator_type, ( *this ), &( current_node->previous ) ); + } + } + } + } + + current_group->free_list_head = NULL; + current_group->number_of_elements = 0; + } + + if LIST_CONSTEXPR( !std::is_trivially_destructible::value || + !std::is_trivially_destructible::value ) { + if( ( last_endpoint_node - last_endpoint_group->nodes ) != + last_endpoint_group->number_of_elements ) { + // If there are erased nodes present in the group + for( node_pointer_type current_node = last_endpoint_group->nodes; + current_node != last_endpoint_node; ++current_node ) { + if LIST_CONSTEXPR( !std::is_trivially_destructible::value ) { + if( current_node->next != NULL ) { + // is not part of free list ie. element has not already had it's destructor called + LIST_DESTROY( element_allocator_type, element_allocator_pair, &( current_node->element ) ); + } + } + + if LIST_CONSTEXPR( !std::is_trivially_destructible::value ) { + LIST_DESTROY( node_pointer_allocator_type, ( *this ), &( current_node->next ) ); + LIST_DESTROY( node_pointer_allocator_type, ( *this ), &( current_node->previous ) ); + } + } + } else { + for( node_pointer_type current_node = last_endpoint_group->nodes; + current_node != last_endpoint_node; ++current_node ) { + if LIST_CONSTEXPR( !std::is_trivially_destructible::value ) { + LIST_DESTROY( element_allocator_type, element_allocator_pair, &( current_node->element ) ); + } + + if LIST_CONSTEXPR( !std::is_trivially_destructible::value ) { + LIST_DESTROY( node_pointer_allocator_type, ( *this ), &( current_node->next ) ); + LIST_DESTROY( node_pointer_allocator_type, ( *this ), &( current_node->previous ) ); + } + } + } + } + + last_endpoint_group->free_list_head = NULL; + last_endpoint_group->number_of_elements = 0; + last_searched_group = last_endpoint_group = block_pointer; + } + + void expand_capacity( const size_type new_capacity ) { // used by add_new and append + group_pointer_type const old_block = block_pointer; + block_pointer = LIST_ALLOCATE( group_allocator_type, group_allocator_pair, new_capacity, 0 ); + + if LIST_CONSTEXPR( std::is_trivially_copyable::value && + std::is_trivially_destructible::value ) { + // Dereferencing here in order to deal with smart pointer situations ie. obtaining the raw pointer from the smart pointer + // reinterpret_cast necessary to deal with GCC 8 warnings + std::memcpy( static_cast( &*block_pointer ), static_cast( &*old_block ), + sizeof( group ) * size ); + } else if LIST_CONSTEXPR( std::is_move_constructible::value ) { + std::uninitialized_copy( std::make_move_iterator( old_block ), + std::make_move_iterator( old_block + size ), block_pointer ); + } else { + // If allocator supplies non-trivial pointers it becomes necessary to destroy the group. uninitialized_copy will not work in this context as the copy constructor for "group" is overriden in C++03/98. The = operator for "group" has been overriden to make the following work: + const group_pointer_type beyond_end = old_block + size; + group_pointer_type current_new_group = block_pointer; + + for( group_pointer_type current_group = old_block; current_group != beyond_end; ++current_group ) { + *( current_new_group++ ) = *( current_group ); + + current_group->nodes = NULL; + current_group->beyond_end = NULL; + LIST_DESTROY( group_allocator_type, group_allocator_pair, current_group ); + } + } + + // correct pointer post-reallocation + last_searched_group = block_pointer + ( last_searched_group - old_block ); + LIST_DEALLOCATE( group_allocator_type, group_allocator_pair, old_block, + group_allocator_pair.capacity ); + group_allocator_pair.capacity = new_capacity; + } + + void add_new( const group_size_type group_size ) { + if( group_allocator_pair.capacity == size ) { + expand_capacity( group_allocator_pair.capacity * 2 ); + } + + last_endpoint_group = block_pointer + size - 1; + + LIST_CONSTRUCT( group_allocator_type, group_allocator_pair, last_endpoint_group + 1, group_size, + last_endpoint_group->nodes ); + + ++last_endpoint_group; // Doing this here instead of pre-construct to avoid need for a try-catch block + element_allocator_pair.capacity += group_size; + ++size; + } + + // For adding first group *only* when group vector is completely empty and block_pointer is NULL + void initialize( const group_size_type group_size ) { + last_endpoint_group = block_pointer = last_searched_group = LIST_ALLOCATE( group_allocator_type, + group_allocator_pair, 1, 0 ); + group_allocator_pair.capacity = 1; + + LIST_CONSTRUCT( group_allocator_type, group_allocator_pair, last_endpoint_group, group_size ); + + size = 1; // Doing these here instead of pre-construct to avoid need for a try-catch block + element_allocator_pair.capacity = group_size; + } + + void remove( group_pointer_type const group_to_erase ) noexcept { + if( last_searched_group >= group_to_erase && last_searched_group != block_pointer ) { + --last_searched_group; + } + + element_allocator_pair.capacity -= static_cast( group_to_erase->beyond_end - + group_to_erase->nodes ); + + LIST_DESTROY( group_allocator_type, group_allocator_pair, group_to_erase ); + + if LIST_CONSTEXPR( std::is_trivially_copyable::value && + std::is_trivially_destructible::value ) { + // Dereferencing here in order to deal with smart pointer situations ie. obtaining the raw pointer from the smart pointer + std::memmove( static_cast( &*group_to_erase ), static_cast( &*group_to_erase + 1 ), + sizeof( group ) * ( --size - static_cast( &*group_to_erase - &*block_pointer ) ) ); + } else if LIST_CONSTEXPR( std::is_move_constructible::value ) { + std::move( group_to_erase + 1, block_pointer + size--, group_to_erase ); + } else { + group_pointer_type back = block_pointer + size--; + std::copy( group_to_erase + 1, back--, group_to_erase ); + + back->nodes = NULL; + back->beyond_end = NULL; + LIST_DESTROY( group_allocator_type, group_allocator_pair, back ); + } + } + + void move_to_back( group_pointer_type const group_to_erase ) { + if( last_searched_group >= group_to_erase && last_searched_group != block_pointer ) { + --last_searched_group; + } + + group *temp_group = LIST_ALLOCATE( group_allocator_type, group_allocator_pair, 1, NULL ); + + if LIST_CONSTEXPR( std::is_trivially_copyable::value && + std::is_trivially_destructible::value ) { + std::memcpy( static_cast( &*temp_group ), static_cast( &*group_to_erase ), + sizeof( group ) ); + std::memmove( static_cast( &*group_to_erase ), static_cast( &*group_to_erase + 1 ), + sizeof( group ) * ( ( size - 1 ) - static_cast( &*group_to_erase - &*block_pointer ) ) ); + std::memcpy( static_cast( &*( block_pointer + size - 1 ) ), + static_cast( &*temp_group ), sizeof( group ) ); + } else if LIST_CONSTEXPR( std::is_move_constructible::value ) { + LIST_CONSTRUCT( group_allocator_type, group_allocator_pair, temp_group, + std::move( *group_to_erase ) ); + std::move( group_to_erase + 1, block_pointer + size, group_to_erase ); + *( block_pointer + size - 1 ) = std::move( *temp_group ); + + if LIST_CONSTEXPR( !std::is_trivially_destructible::value ) { + LIST_DESTROY( group_allocator_type, group_allocator_pair, temp_group ); + } + } else { + LIST_CONSTRUCT( group_allocator_type, group_allocator_pair, temp_group, group() ); + + *temp_group = *group_to_erase; + std::copy( group_to_erase + 1, block_pointer + size, group_to_erase ); + *( block_pointer + --size ) = *temp_group; + + temp_group->nodes = NULL; + LIST_DESTROY( group_allocator_type, group_allocator_pair, temp_group ); + } + + LIST_DEALLOCATE( group_allocator_type, group_allocator_pair, temp_group, 1 ); + } + + // In working implementation this cannot throw + group_pointer_type get_nearest_freelist_group( const node_pointer_type location_node ) noexcept { + const group_pointer_type beyond_end_group = last_endpoint_group + 1; + group_pointer_type left = last_searched_group - 1, right = last_searched_group + 1, + freelist_group = NULL; + bool right_not_beyond_back = ( right < beyond_end_group ); + bool left_not_beyond_front = ( left >= block_pointer ); + + + // ie. location is within last_search_group + if( location_node >= last_searched_group->nodes && + location_node < last_searched_group->beyond_end ) { + // if last_searched_group has previously-erased nodes + if( last_searched_group->free_list_head != NULL ) { + return last_searched_group; + } + } else { // search for the node group which location_node is located within, using last_searched_group as a starting point and searching left and right. Try and find the closest node group with reusable erased-element locations along the way: + group_pointer_type closest_freelist_left = ( last_searched_group->free_list_head == NULL ) ? NULL : + last_searched_group, closest_freelist_right = ( last_searched_group->free_list_head == NULL ) ? + NULL : last_searched_group; + + while( true ) { + if( right_not_beyond_back ) { + // location_node's group is found + if( ( location_node < right->beyond_end ) && ( location_node >= right->nodes ) ) { + // group has erased nodes, reuse them: + if( right->free_list_head != NULL ) { + last_searched_group = right; + return right; + } + + difference_type left_distance; + + if( closest_freelist_right != NULL ) { + last_searched_group = right; + left_distance = right - closest_freelist_right; + + if( left_distance <= 2 ) { // ie. this group is close enough to location_node's group + return closest_freelist_right; + } + + freelist_group = closest_freelist_right; + } else { + last_searched_group = right; + left_distance = right - left; + } + + + // Otherwise find closest group with freelist - check an equal distance on the right to the distance we've checked on the left: + const group_pointer_type end_group = ( ( ( right + left_distance ) > beyond_end_group ) ? + beyond_end_group : ( right + left_distance - 1 ) ); + + while( ++right != end_group ) { + if( right->free_list_head != NULL ) { + return right; + } + } + + if( freelist_group != NULL ) { + return freelist_group; + } + + right_not_beyond_back = ( right < beyond_end_group ); + break; // group with reusable erased nodes not found yet, continue searching in loop below + } + + // location_node's group not found, but a reusable location found + if( right->free_list_head != NULL ) { + if( ( closest_freelist_right == NULL ) & ( closest_freelist_left == NULL ) ) { + closest_freelist_left = right; + } + + closest_freelist_right = right; + } + + right_not_beyond_back = ( ++right < beyond_end_group ); + } + + if( left_not_beyond_front ) { + if( ( location_node >= left->nodes ) && ( location_node < left->beyond_end ) ) { + if( left->free_list_head != NULL ) { + last_searched_group = left; + return left; + } + + difference_type right_distance; + + if( closest_freelist_left != NULL ) { + last_searched_group = left; + right_distance = closest_freelist_left - left; + + if( right_distance <= 2 ) { + return closest_freelist_left; + } + + freelist_group = closest_freelist_left; + } else { + last_searched_group = left; + right_distance = right - left; + } + + // Otherwise find closest group with freelist: + const group_pointer_type end_group = ( ( ( left - right_distance ) < block_pointer ) ? block_pointer + - 1 : ( left - right_distance ) + 1 ); + + while( --left != end_group ) { + if( left->free_list_head != NULL ) { + return left; + } + } + + if( freelist_group != NULL ) { + return freelist_group; + } + + left_not_beyond_front = ( left >= block_pointer ); + break; + } + + if( left->free_list_head != NULL ) { + if( ( closest_freelist_left == NULL ) & ( closest_freelist_right == NULL ) ) { + closest_freelist_right = left; + } + + closest_freelist_left = left; + } + + left_not_beyond_front = ( --left >= block_pointer ); + } + } + } + + // The node group which location_node is located within, is known at this point. Continue searching outwards from this group until a group is found with a reusable location: + while( true ) { + if( right_not_beyond_back ) { + if( right->free_list_head != NULL ) { + return right; + } + + right_not_beyond_back = ( ++right < beyond_end_group ); + } + + if( left_not_beyond_front ) { + if( left->free_list_head != NULL ) { + return left; + } + + left_not_beyond_front = ( --left >= block_pointer ); + } + } + + // Will never reach here on functioning implementations + } + + void swap( group_vector &source ) LIST_NOEXCEPT_SWAP( group_allocator_type ) { + if LIST_CONSTEXPR( + std::is_trivial::value ) { // if all pointer types are trivial we can just copy using memcpy - faster - avoids constructors/destructors etc + char temp[sizeof( group_vector )]; + std::memcpy( static_cast( &temp ), static_cast( this ), sizeof( group_vector ) ); + std::memcpy( static_cast( this ), static_cast( &source ), sizeof( group_vector ) ); + std::memcpy( static_cast( &source ), static_cast( &temp ), sizeof( group_vector ) ); + } else { + const group_pointer_type swap_last_endpoint_group = last_endpoint_group, + swap_block_pointer = block_pointer, swap_last_searched_group = last_searched_group; + const size_type swap_size = size, swap_element_capacity = element_allocator_pair.capacity, + swap_capacity = group_allocator_pair.capacity; + + last_endpoint_group = source.last_endpoint_group; + block_pointer = source.block_pointer; + last_searched_group = source.last_searched_group; + size = source.size; + element_allocator_pair.capacity = source.element_allocator_pair.capacity; + group_allocator_pair.capacity = source.group_allocator_pair.capacity; + + source.last_endpoint_group = swap_last_endpoint_group; + source.block_pointer = swap_block_pointer; + source.last_searched_group = swap_last_searched_group; + source.size = swap_size; + source.element_allocator_pair.capacity = swap_element_capacity; + source.group_allocator_pair.capacity = swap_capacity; + } + } + + void trim_trailing_groups() noexcept { + const group_pointer_type beyond_last = block_pointer + size; + + for( group_pointer_type current_group = last_endpoint_group + 1; current_group != beyond_last; + ++current_group ) { + element_allocator_pair.capacity -= static_cast( current_group->beyond_end - + current_group->nodes ); + LIST_DESTROY( group_allocator_type, group_allocator_pair, current_group ); + } + + size -= static_cast( beyond_last - ( last_endpoint_group + 1 ) ); + } + + void append( group_vector &source ) { + source.trim_trailing_groups(); + trim_trailing_groups(); + + if( size + source.size > group_allocator_pair.capacity ) { + expand_capacity( size + source.size ); + } + + if LIST_CONSTEXPR( std::is_trivially_copyable::value && + std::is_trivially_destructible::value ) { + // &* in order to deal with smart pointer situations ie. obtaining the raw pointer from the smart pointer + std::memcpy( static_cast( &*block_pointer + size ), + static_cast( &*source.block_pointer ), sizeof( group ) * source.size ); + } else if LIST_CONSTEXPR( std::is_move_constructible::value ) { + std::uninitialized_copy( std::make_move_iterator( source.block_pointer ), + std::make_move_iterator( source.block_pointer + source.size ), block_pointer + size ); + } else { + group_pointer_type current_new_group = block_pointer + size; + const group_pointer_type beyond_end_source = source.block_pointer + source.size; + + for( group_pointer_type current_group = source.block_pointer; current_group != beyond_end_source; + ++current_group ) { + *( current_new_group++ ) = *( current_group ); + + current_group->nodes = NULL; + current_group->beyond_end = NULL; + LIST_DESTROY( group_allocator_type, source.group_allocator_pair, current_group ); + } + } + + LIST_DEALLOCATE( group_allocator_type, source.group_allocator_pair, source.block_pointer, + source.group_allocator_pair.capacity ); + size += source.size; + last_endpoint_group = block_pointer + size - 1; + element_allocator_pair.capacity += source.element_allocator_pair.capacity; + source.blank(); + } + }; + + // Implement const/non-const iterator switching pattern: + template struct choose; + + template struct choose { + using type = IsTrue; + }; + + template struct choose { + using type = IsFalse; + }; + + public: + + template class list_iterator + { + private: + node_pointer_type node_pointer; + + public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = typename list::value_type; + using difference_type = typename list::difference_type; + using pointer = typename + choose::type; + using reference = typename + choose::type; + + friend class list; + + inline LIST_FORCE_INLINE bool operator==( const list_iterator rh ) const noexcept { + return ( node_pointer == rh.node_pointer ); + } + + inline LIST_FORCE_INLINE bool operator==( const list_iterator < !is_const > rh ) const + noexcept { + return ( node_pointer == rh.node_pointer ); + } + + inline LIST_FORCE_INLINE bool operator!=( const list_iterator rh ) const noexcept { + return ( node_pointer != rh.node_pointer ); + } + + inline LIST_FORCE_INLINE bool operator!=( const list_iterator < !is_const > rh ) const + noexcept { + return ( node_pointer != rh.node_pointer ); + } + + inline LIST_FORCE_INLINE reference operator*() const { + return node_pointer->element; + } + + inline LIST_FORCE_INLINE pointer operator->() const { + return &( node_pointer->element ); + } + + inline LIST_FORCE_INLINE list_iterator &operator++() noexcept { + assert( node_pointer != NULL ); // covers uninitialised list_iterator + node_pointer = node_pointer->next; + return *this; + } + + inline list_iterator operator++( int ) noexcept { + const list_iterator copy( *this ); + ++*this; + return copy; + } + + inline LIST_FORCE_INLINE list_iterator &operator--() noexcept { + assert( node_pointer != NULL ); // covers uninitialised list_iterator + node_pointer = node_pointer->previous; + return *this; + } + + inline list_iterator operator--( int ) noexcept { + const list_iterator copy( *this ); + --*this; + return copy; + } + + inline list_iterator &operator=( const list_iterator &rh ) noexcept { + node_pointer = rh.node_pointer; + return *this; + } + + inline list_iterator &operator=( const list_iterator < !is_const > &rh ) noexcept { + node_pointer = rh.node_pointer; + return *this; + } + + inline list_iterator &operator=( const list_iterator &&rh ) noexcept { + node_pointer = std::move( rh.node_pointer ); + return *this; + } + + inline list_iterator &operator=( const list_iterator < !is_const > &&rh ) noexcept { + node_pointer = std::move( rh.node_pointer ); + return *this; + } + + list_iterator() noexcept: node_pointer( NULL ) {} + + list_iterator( const list_iterator &source ) noexcept: node_pointer( source.node_pointer ) {} + + list_iterator( const list_iterator < !is_const > &source ) noexcept: node_pointer( + source.node_pointer ) {} + + list_iterator( const list_iterator &&source ) noexcept: node_pointer( std::move( + source.node_pointer ) ) {} + + list_iterator( const list_iterator < !is_const > && + source ) noexcept: node_pointer( std::move( source.node_pointer ) ) {} + + private: + + list_iterator( const node_pointer_type node_p ) noexcept: node_pointer( node_p ) {} + }; + + template class list_reverse_iterator + { + private: + node_pointer_type node_pointer; + + public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = typename list::value_type; + using difference_type = typename list::difference_type; + using pointer = typename + choose::type; + using reference = typename + choose::type; + + friend class list; + + inline LIST_FORCE_INLINE bool operator==( const list_reverse_iterator rh ) const noexcept { + return ( node_pointer == rh.node_pointer ); + } + + inline LIST_FORCE_INLINE bool operator==( const list_reverse_iterator < !is_const > rh ) const + noexcept { + return ( node_pointer == rh.node_pointer ); + } + + inline LIST_FORCE_INLINE bool operator!=( const list_reverse_iterator rh ) const noexcept { + return ( node_pointer != rh.node_pointer ); + } + + inline LIST_FORCE_INLINE bool operator!=( const list_reverse_iterator < !is_const > rh ) const + noexcept { + return ( node_pointer != rh.node_pointer ); + } + + inline LIST_FORCE_INLINE reference operator*() const { + return node_pointer->element; + } + + inline LIST_FORCE_INLINE pointer operator->() const { + return &( node_pointer->element ); + } + + inline LIST_FORCE_INLINE list_reverse_iterator &operator++() noexcept { + assert( node_pointer != NULL ); // covers uninitialised list_reverse_iterator + node_pointer = node_pointer->previous; + return *this; + } + + inline list_reverse_iterator operator++( int ) noexcept { + const list_reverse_iterator copy( *this ); + ++*this; + return copy; + } + + inline LIST_FORCE_INLINE list_reverse_iterator &operator--() noexcept { + assert( node_pointer != NULL ); + node_pointer = node_pointer->next; + return *this; + } + + inline list_reverse_iterator operator--( int ) noexcept { + const list_reverse_iterator copy( *this ); + --*this; + return copy; + } + + inline list_reverse_iterator &operator=( const list_reverse_iterator &rh ) noexcept { + node_pointer = rh.node_pointer; + return *this; + } + + inline list_reverse_iterator &operator=( const list_reverse_iterator < !is_const > &rh ) + noexcept { + node_pointer = rh.node_pointer; + return *this; + } + + inline list_reverse_iterator &operator=( const list_reverse_iterator &&rh ) noexcept { + node_pointer = std::move( rh.node_pointer ); + return *this; + } + + inline list_reverse_iterator &operator=( const list_reverse_iterator < !is_const > && + rh ) noexcept { + node_pointer = std::move( rh.node_pointer ); + return *this; + } + + inline typename list::iterator base() const noexcept { + return typename list::iterator( node_pointer->next ); + } + + list_reverse_iterator() noexcept: node_pointer( NULL ) {} + + list_reverse_iterator( const list_reverse_iterator &source ) noexcept: node_pointer( + source.node_pointer ) {} + + list_reverse_iterator( const list_reverse_iterator &&source ) noexcept: node_pointer( std::move( + source.node_pointer ) ) {} + + private: + + list_reverse_iterator( const node_pointer_type node_p ) noexcept: node_pointer( node_p ) {} + }; + + private: + + // Used by range-insert and range-constructor to prevent fill-insert and fill-constructor function calls mistakenly resolving to the range insert/constructor + template + struct plf_enable_if_c { + using type = T; + }; + + template + struct plf_enable_if_c { + }; + + group_vector groups; + node_base end_node; + node_pointer_type + last_endpoint; // last_endpoint being NULL means no elements have been constructed, but there may still be groups available due to clear() or reservee() + iterator end_iterator; // end_iterator is always the last entry point in last group in list (or one past the end of group) + iterator begin_iterator; + + // Packaging the group allocator with least-used member variables, for empty-base-class optimisation + struct ebco_pair1 : node_pointer_allocator_type { + size_type total_number_of_elements; + explicit ebco_pair1( const size_type total_num_elements ) noexcept: total_number_of_elements( + total_num_elements ) {} + } node_pointer_allocator_pair; + + struct ebco_pair2 : node_allocator_type { + size_type number_of_erased_nodes; + explicit ebco_pair2( const size_type num_erased_nodes ) noexcept: number_of_erased_nodes( + num_erased_nodes ) {} + } node_allocator_pair; + + public: + + // Default constructor: + list() noexcept: + element_allocator_type( element_allocator_type() ), + end_node( reinterpret_cast( &end_node ), + reinterpret_cast( &end_node ) ), + last_endpoint( NULL ), + end_iterator( reinterpret_cast( &end_node ) ), + begin_iterator( reinterpret_cast( &end_node ) ), + node_pointer_allocator_pair( 0 ), + node_allocator_pair( 0 ) + {} + + // Allocator-extended constructor: + explicit list( const element_allocator_type &alloc ): + element_allocator_type( alloc ), + end_node( reinterpret_cast( &end_node ), + reinterpret_cast( &end_node ) ), + last_endpoint( NULL ), + end_iterator( reinterpret_cast( &end_node ) ), + begin_iterator( reinterpret_cast( &end_node ) ), + node_pointer_allocator_pair( 0 ), + node_allocator_pair( 0 ) + {} + + // Copy constructor: + list( const list &source ): + element_allocator_type( source ), + end_node( reinterpret_cast( &end_node ), + reinterpret_cast( &end_node ) ), + last_endpoint( NULL ), + end_iterator( reinterpret_cast( &end_node ) ), + begin_iterator( reinterpret_cast( &end_node ) ), + node_pointer_allocator_pair( 0 ), + node_allocator_pair( 0 ) { + reserve( source.node_pointer_allocator_pair.total_number_of_elements ); + insert( end_iterator, source.begin_iterator, source.end_iterator ); + } + + // Allocator-extended copy constructor: + list( const list &source, const allocator_type &alloc ): + element_allocator_type( alloc ), + end_node( reinterpret_cast( &end_node ), + reinterpret_cast( &end_node ) ), + last_endpoint( NULL ), + end_iterator( reinterpret_cast( &end_node ) ), + begin_iterator( reinterpret_cast( &end_node ) ), + node_pointer_allocator_pair( 0 ), + node_allocator_pair( 0 ) { + reserve( source.node_pointer_allocator_pair.total_number_of_elements ); + insert( end_iterator, source.begin_iterator, source.end_iterator ); + } + + // Move constructor: + list( list &&source ) noexcept: + element_allocator_type( source ), + groups( std::move( source.groups ) ), + end_node( std::move( source.end_node ) ), + last_endpoint( std::move( source.last_endpoint ) ), + end_iterator( reinterpret_cast( &end_node ) ), + begin_iterator( ( source.begin_iterator.node_pointer == source.end_iterator.node_pointer ) ? + reinterpret_cast( &end_node ) : std::move( source.begin_iterator ) ), + node_pointer_allocator_pair( source.node_pointer_allocator_pair.total_number_of_elements ), + node_allocator_pair( source.node_allocator_pair.number_of_erased_nodes ) { + end_node.previous->next = begin_iterator.node_pointer->previous = end_iterator.node_pointer; + source.groups.blank(); + source.reset(); + } + + // Allocator-extended move constructor: + list( list &&source, const allocator_type &alloc ): + element_allocator_type( alloc ), + groups( std::move( source.groups ) ), + end_node( std::move( source.end_node ) ), + last_endpoint( std::move( source.last_endpoint ) ), + end_iterator( reinterpret_cast( &end_node ) ), + begin_iterator( ( source.begin_iterator.node_pointer == source.end_iterator.node_pointer ) ? + reinterpret_cast( &end_node ) : std::move( source.begin_iterator ) ), + node_pointer_allocator_pair( source.node_pointer_allocator_pair.total_number_of_elements ), + node_allocator_pair( source.node_allocator_pair.number_of_erased_nodes ) { + end_node.previous->next = begin_iterator.node_pointer->previous = end_iterator.node_pointer; + source.groups.blank(); + source.reset(); + } + + // Fill constructor: + list( const size_type fill_number, const element_type &element, + const element_allocator_type &alloc = element_allocator_type() ): + element_allocator_type( alloc ), + end_node( reinterpret_cast( &end_node ), + reinterpret_cast( &end_node ) ), + last_endpoint( NULL ), + end_iterator( reinterpret_cast( &end_node ) ), + begin_iterator( reinterpret_cast( &end_node ) ), + node_pointer_allocator_pair( 0 ), + node_allocator_pair( 0 ) { + reserve( fill_number ); + insert( end_iterator, fill_number, element ); + } + + // Range constructor: + template + list( const typename plf_enable_if_c < !std::numeric_limits::is_integer, + iterator_type >::type &first, const iterator_type &last, + const element_allocator_type &alloc = element_allocator_type() ): + element_allocator_type( alloc ), + end_node( reinterpret_cast( &end_node ), + reinterpret_cast( &end_node ) ), + last_endpoint( NULL ), + end_iterator( reinterpret_cast( &end_node ) ), + begin_iterator( reinterpret_cast( &end_node ) ), + node_pointer_allocator_pair( 0 ), + node_allocator_pair( 0 ) { + insert( end_iterator, first, last ); + } + + // Initializer-list constructor: + list( const std::initializer_list &element_list, + const element_allocator_type &alloc = element_allocator_type() ): + element_allocator_type( alloc ), + end_node( reinterpret_cast( &end_node ), + reinterpret_cast( &end_node ) ), + last_endpoint( NULL ), + end_iterator( reinterpret_cast( &end_node ) ), + begin_iterator( reinterpret_cast( &end_node ) ), + node_pointer_allocator_pair( 0 ), + node_allocator_pair( 0 ) { + reserve( element_list.size() ); + insert( end_iterator, element_list ); + } + + ~list() noexcept { + groups.destroy_all_data( last_endpoint ); + } + + inline iterator begin() noexcept { + return begin_iterator; + } + + inline const_iterator begin() const noexcept { + return begin_iterator; + } + + inline iterator end() noexcept { + return end_iterator; + } + + inline const_iterator end() const noexcept { + return end_iterator; + } + + inline const_iterator cbegin() const noexcept { + return const_iterator( begin_iterator.node_pointer ); + } + + inline const_iterator cend() const noexcept { + return const_iterator( end_iterator.node_pointer ); + } + + inline reverse_iterator rbegin() const noexcept { + return reverse_iterator( end_node.previous ); + } + + inline reverse_iterator rend() const noexcept { + return reverse_iterator( end_iterator.node_pointer ); + } + + inline const_reverse_iterator crbegin() const noexcept { + return const_reverse_iterator( end_node.previous ); + } + + inline const_reverse_iterator crend() const noexcept { + return const_reverse_iterator( end_iterator.node_pointer ); + } + + inline reference front() { + assert( begin_iterator.node_pointer != &end_node ); + return begin_iterator.node_pointer->element; + } + + inline const_reference front() const { + assert( begin_iterator.node_pointer != &end_node ); + return begin_iterator.node_pointer->element; + } + + inline reference back() { + assert( end_node.previous != &end_node ); + return end_node.previous->element; + } + + inline const_reference back() const { + assert( end_node.previous != &end_node ); + return end_node.previous->element; + } + + void clear() noexcept { + if( last_endpoint == NULL ) { + return; + } + + if( node_pointer_allocator_pair.total_number_of_elements != 0 ) { + groups.clear( last_endpoint ); + } + + end_node.next = reinterpret_cast( &end_node ); + end_node.previous = reinterpret_cast( &end_node ); + last_endpoint = groups.block_pointer->nodes; + begin_iterator.node_pointer = end_iterator.node_pointer; + node_pointer_allocator_pair.total_number_of_elements = 0; + node_allocator_pair.number_of_erased_nodes = 0; + } + + private: + + void reset() noexcept { + groups.destroy_all_data( last_endpoint ); + last_endpoint = NULL; + end_node.next = reinterpret_cast( &end_node ); + end_node.previous = reinterpret_cast( &end_node ); + begin_iterator.node_pointer = end_iterator.node_pointer; + node_pointer_allocator_pair.total_number_of_elements = 0; + node_allocator_pair.number_of_erased_nodes = 0; + } + + public: + + iterator insert( const iterator it, const element_type &element ) { + // ie. list is not empty + if( last_endpoint != NULL ) { + // No erased nodes available for reuse + if( node_allocator_pair.number_of_erased_nodes == 0 ) { + // last_endpoint is beyond the end of a group + if( last_endpoint == groups.last_endpoint_group->beyond_end ) { + // ie. there are no reusable groups available at the back of group vector + if( static_cast( groups.last_endpoint_group - groups.block_pointer ) == groups.size - + 1 ) { + groups.add_new( ( node_pointer_allocator_pair.total_number_of_elements < LIST_BLOCK_MAX ) ? + static_cast( node_pointer_allocator_pair.total_number_of_elements ) : + LIST_BLOCK_MAX ); + } else { + ++groups.last_endpoint_group; + } + + last_endpoint = groups.last_endpoint_group->nodes; + } + + LIST_CONSTRUCT( node_allocator_type, node_allocator_pair, last_endpoint, it.node_pointer, + it.node_pointer->previous, element ); + + ++( groups.last_endpoint_group->number_of_elements ); + ++node_pointer_allocator_pair.total_number_of_elements; + + if( it.node_pointer == begin_iterator.node_pointer ) { + begin_iterator.node_pointer = last_endpoint; + } + + it.node_pointer->previous->next = last_endpoint; + it.node_pointer->previous = last_endpoint; + + return iterator( last_endpoint++ ); + } else { + group_pointer_type const node_group = groups.get_nearest_freelist_group( ( + it.node_pointer != end_iterator.node_pointer ) ? it.node_pointer : end_node.previous ); + node_pointer_type const selected_node = node_group->free_list_head; + const node_pointer_type previous = node_group->free_list_head->previous; + + LIST_CONSTRUCT( node_allocator_type, node_allocator_pair, selected_node, it.node_pointer, + it.node_pointer->previous, element ); + + node_group->free_list_head = previous; + ++( node_group->number_of_elements ); + ++node_pointer_allocator_pair.total_number_of_elements; + --node_allocator_pair.number_of_erased_nodes; + + it.node_pointer->previous->next = selected_node; + it.node_pointer->previous = selected_node; + + if( it.node_pointer == begin_iterator.node_pointer ) { + begin_iterator.node_pointer = selected_node; + } + + return iterator( selected_node ); + } + } else { // list is empty + // In case of prior reserve/clear call as opposed to being uninitialized + if( groups.block_pointer == NULL ) { + groups.initialize( LIST_BLOCK_MIN ); + } + + groups.last_endpoint_group->number_of_elements = 1; + end_node.next = end_node.previous = last_endpoint = begin_iterator.node_pointer = + groups.last_endpoint_group->nodes; + node_pointer_allocator_pair.total_number_of_elements = 1; + + if LIST_CONSTEXPR( + std::is_nothrow_copy_constructible::value ) { // Avoid try-catch code generation + LIST_CONSTRUCT( node_allocator_type, node_allocator_pair, last_endpoint++, + end_iterator.node_pointer, end_iterator.node_pointer, element ); + } else { + try { + LIST_CONSTRUCT( node_allocator_type, node_allocator_pair, last_endpoint++, + end_iterator.node_pointer, end_iterator.node_pointer, element ); + } catch( ... ) { + reset(); + throw; + } + } + + return begin_iterator; + } + } + + inline LIST_FORCE_INLINE void push_back( const element_type &element ) { + insert( end_iterator, element ); + } + + inline LIST_FORCE_INLINE void push_front( const element_type &element ) { + insert( begin_iterator, element ); + } + + // This is almost identical to the insert implementation above with the only change being std::move of the element + iterator insert( const iterator it, element_type &&element ) { + if( last_endpoint != NULL ) { + if( node_allocator_pair.number_of_erased_nodes == 0 ) { + if( last_endpoint == groups.last_endpoint_group->beyond_end ) { + if( static_cast( groups.last_endpoint_group - groups.block_pointer ) == groups.size - + 1 ) { + groups.add_new( ( node_pointer_allocator_pair.total_number_of_elements < LIST_BLOCK_MAX ) ? + static_cast( node_pointer_allocator_pair.total_number_of_elements ) : + LIST_BLOCK_MAX ); + } else { + ++groups.last_endpoint_group; + } + + last_endpoint = groups.last_endpoint_group->nodes; + } + + LIST_CONSTRUCT( node_allocator_type, node_allocator_pair, last_endpoint, it.node_pointer, + it.node_pointer->previous, std::move( element ) ); + + ++( groups.last_endpoint_group->number_of_elements ); + ++node_pointer_allocator_pair.total_number_of_elements; + + if( it.node_pointer == begin_iterator.node_pointer ) { + begin_iterator.node_pointer = last_endpoint; + } + + it.node_pointer->previous->next = last_endpoint; + it.node_pointer->previous = last_endpoint; + + return iterator( last_endpoint++ ); + } else { + group_pointer_type const node_group = groups.get_nearest_freelist_group( ( + it.node_pointer != end_iterator.node_pointer ) ? it.node_pointer : end_node.previous ); + node_pointer_type const selected_node = node_group->free_list_head; + const node_pointer_type previous = node_group->free_list_head->previous; + + LIST_CONSTRUCT( node_allocator_type, node_allocator_pair, selected_node, it.node_pointer, + it.node_pointer->previous, std::move( element ) ); + + node_group->free_list_head = previous; + ++( node_group->number_of_elements ); + ++node_pointer_allocator_pair.total_number_of_elements; + --node_allocator_pair.number_of_erased_nodes; + + it.node_pointer->previous->next = selected_node; + it.node_pointer->previous = selected_node; + + if( it.node_pointer == begin_iterator.node_pointer ) { + begin_iterator.node_pointer = selected_node; + } + + return iterator( selected_node ); + } + } else { + if( groups.block_pointer == NULL ) { + groups.initialize( LIST_BLOCK_MIN ); + } + + groups.last_endpoint_group->number_of_elements = 1; + end_node.next = end_node.previous = last_endpoint = begin_iterator.node_pointer = + groups.last_endpoint_group->nodes; + node_pointer_allocator_pair.total_number_of_elements = 1; + + if LIST_CONSTEXPR( std::is_nothrow_move_constructible::value ) { + LIST_CONSTRUCT( node_allocator_type, node_allocator_pair, last_endpoint++, + end_iterator.node_pointer, end_iterator.node_pointer, std::move( element ) ); + + } else { + try { + LIST_CONSTRUCT( node_allocator_type, node_allocator_pair, last_endpoint++, + end_iterator.node_pointer, end_iterator.node_pointer, std::move( element ) ); + } catch( ... ) { + reset(); + throw; + } + } + + return begin_iterator; + } + } + + inline LIST_FORCE_INLINE void push_back( element_type &&element ) { + insert( end_iterator, std::move( element ) ); + } + + inline LIST_FORCE_INLINE void push_front( element_type &&element ) { + insert( begin_iterator, std::move( element ) ); + } + + // This is almost identical to the insert implementations above with the only changes being std::forward of element parameters + template + iterator emplace( const iterator it, arguments &&... parameters ) { + if( last_endpoint != NULL ) { + if( node_allocator_pair.number_of_erased_nodes == 0 ) { + if( last_endpoint == groups.last_endpoint_group->beyond_end ) { + if( static_cast( groups.last_endpoint_group - groups.block_pointer ) == groups.size - + 1 ) { + groups.add_new( ( node_pointer_allocator_pair.total_number_of_elements < LIST_BLOCK_MAX ) ? + static_cast( node_pointer_allocator_pair.total_number_of_elements ) : + LIST_BLOCK_MAX ); + } else { + ++groups.last_endpoint_group; + } + + last_endpoint = groups.last_endpoint_group->nodes; + } + + LIST_CONSTRUCT( node_allocator_type, node_allocator_pair, last_endpoint, it.node_pointer, + it.node_pointer->previous, std::forward( parameters )... ); + + ++( groups.last_endpoint_group->number_of_elements ); + ++node_pointer_allocator_pair.total_number_of_elements; + + if( it.node_pointer == begin_iterator.node_pointer ) { + begin_iterator.node_pointer = last_endpoint; + } + + it.node_pointer->previous->next = last_endpoint; + it.node_pointer->previous = last_endpoint; + + return iterator( last_endpoint++ ); + } else { + group_pointer_type const node_group = groups.get_nearest_freelist_group( ( + it.node_pointer != end_iterator.node_pointer ) ? it.node_pointer : end_node.previous ); + node_pointer_type const selected_node = node_group->free_list_head; + const node_pointer_type previous = node_group->free_list_head->previous; + + LIST_CONSTRUCT( node_allocator_type, node_allocator_pair, selected_node, it.node_pointer, + it.node_pointer->previous, std::forward( parameters )... ); + + node_group->free_list_head = previous; + ++( node_group->number_of_elements ); + ++node_pointer_allocator_pair.total_number_of_elements; + --node_allocator_pair.number_of_erased_nodes; + + it.node_pointer->previous->next = selected_node; + it.node_pointer->previous = selected_node; + + if( it.node_pointer == begin_iterator.node_pointer ) { + begin_iterator.node_pointer = selected_node; + } + + return iterator( selected_node ); + } + } else { + if( groups.block_pointer == NULL ) { + groups.initialize( LIST_BLOCK_MIN ); + } + + groups.last_endpoint_group->number_of_elements = 1; + end_node.next = end_node.previous = last_endpoint = begin_iterator.node_pointer = + groups.last_endpoint_group->nodes; + node_pointer_allocator_pair.total_number_of_elements = 1; + + if LIST_CONSTEXPR( std::is_nothrow_constructible::value ) { + LIST_CONSTRUCT( node_allocator_type, node_allocator_pair, last_endpoint++, + end_iterator.node_pointer, end_iterator.node_pointer, std::forward( parameters )... ); + } else { + try { + LIST_CONSTRUCT( node_allocator_type, node_allocator_pair, last_endpoint++, + end_iterator.node_pointer, end_iterator.node_pointer, std::forward( parameters )... ); + } catch( ... ) { + reset(); + throw; + } + } + + return begin_iterator; + } + } + + template + inline LIST_FORCE_INLINE reference emplace_back( arguments &&... parameters ) { + return ( emplace( end_iterator, std::forward( parameters )... ) ).node_pointer->element; + } + + template + inline LIST_FORCE_INLINE reference emplace_front( arguments &&... parameters ) { + return ( emplace( begin_iterator, + std::forward( parameters )... ) ).node_pointer->element; + } + + private: + + void group_fill_position( const element_type &element, group_size_type number_of_elements, + node_pointer_type const position ) { + position->previous->next = last_endpoint; + groups.last_endpoint_group->number_of_elements += number_of_elements; + node_pointer_type previous = position->previous; + + do { + if LIST_CONSTEXPR( std::is_nothrow_copy_constructible::value ) { + LIST_CONSTRUCT( node_allocator_type, node_allocator_pair, last_endpoint, last_endpoint + 1, + previous, element ); + } else { + try { + LIST_CONSTRUCT( node_allocator_type, node_allocator_pair, last_endpoint, last_endpoint + 1, + previous, element ); + } catch( ... ) { + previous->next = position; + position->previous = --previous; + groups.last_endpoint_group->number_of_elements -= static_cast + ( number_of_elements - ( last_endpoint - position ) ); + throw; + } + } + + previous = last_endpoint++; + } while( --number_of_elements != 0 ); + + previous->next = position; + position->previous = previous; + } + + public: + + // Fill insert + iterator insert( iterator position, const size_type number_of_elements, + const element_type &element ) { + if( number_of_elements == 0 ) { + return end_iterator; + } else if( number_of_elements == 1 ) { + return insert( position, element ); + } + + if( node_pointer_allocator_pair.total_number_of_elements == 0 && last_endpoint != NULL && + ( static_cast( groups.block_pointer->beyond_end - groups.block_pointer->nodes ) < + number_of_elements ) && + ( static_cast( groups.block_pointer->beyond_end - groups.block_pointer->nodes ) < + LIST_BLOCK_MAX ) ) { + reset(); + } + + if( groups.block_pointer == NULL ) { // ie. Uninitialized list + if( number_of_elements > LIST_BLOCK_MAX ) { + size_type multiples = number_of_elements / LIST_BLOCK_MAX; + const group_size_type remainder = static_cast( number_of_elements - + ( multiples++ * LIST_BLOCK_MAX ) ); // ++ to aid while loop below + + // Create and fill first group: + if( remainder != 0 ) { // make sure smallest block is first + if( remainder >= LIST_BLOCK_MIN ) { + groups.initialize( remainder ); + end_node.next = end_node.previous = last_endpoint = begin_iterator.node_pointer = + groups.last_endpoint_group->nodes; + group_fill_position( element, remainder, end_iterator.node_pointer ); + } else { + // Create first group as BLOCK_MIN size then subtract difference between BLOCK_MIN and remainder from next group: + groups.initialize( LIST_BLOCK_MIN ); + end_node.next = end_node.previous = last_endpoint = begin_iterator.node_pointer = + groups.last_endpoint_group->nodes; + group_fill_position( element, LIST_BLOCK_MIN, end_iterator.node_pointer ); + + groups.add_new( LIST_BLOCK_MAX - ( LIST_BLOCK_MIN - remainder ) ); + end_node.previous = last_endpoint = groups.last_endpoint_group->nodes; + group_fill_position( element, LIST_BLOCK_MAX - ( LIST_BLOCK_MIN - remainder ), + end_iterator.node_pointer ); + --multiples; + } + } else { + groups.initialize( LIST_BLOCK_MAX ); + end_node.next = end_node.previous = last_endpoint = begin_iterator.node_pointer = + groups.last_endpoint_group->nodes; + group_fill_position( element, LIST_BLOCK_MAX, end_iterator.node_pointer ); + --multiples; + } + + while( --multiples != 0 ) { + groups.add_new( LIST_BLOCK_MAX ); + end_node.previous = last_endpoint = groups.last_endpoint_group->nodes; + group_fill_position( element, LIST_BLOCK_MAX, end_iterator.node_pointer ); + } + + } else { + groups.initialize( ( number_of_elements < LIST_BLOCK_MIN ) ? LIST_BLOCK_MIN : + static_cast( number_of_elements ) ); // Construct first group + end_node.next = end_node.previous = last_endpoint = begin_iterator.node_pointer = + groups.last_endpoint_group->nodes; + group_fill_position( element, static_cast( number_of_elements ), + end_iterator.node_pointer ); + } + + node_pointer_allocator_pair.total_number_of_elements = number_of_elements; + return begin_iterator; + } else { + // Insert first element, then use up any erased nodes: + size_type remainder = number_of_elements - 1; + const iterator return_iterator = insert( position, element ); + + while( node_allocator_pair.number_of_erased_nodes != 0 ) { + insert( position, element ); + --node_allocator_pair.number_of_erased_nodes; + + if( --remainder == 0 ) { + return return_iterator; + } + } + + node_pointer_allocator_pair.total_number_of_elements += remainder; + + // then use up remainder of last_endpoint_group: + const group_size_type remaining_nodes_in_group = static_cast + ( groups.last_endpoint_group->beyond_end - last_endpoint ); + + if( remaining_nodes_in_group != 0 ) { + if( remaining_nodes_in_group < remainder ) { + group_fill_position( element, remaining_nodes_in_group, position.node_pointer ); + remainder -= remaining_nodes_in_group; + } else { + group_fill_position( element, static_cast( remainder ), position.node_pointer ); + return return_iterator; + } + } + + // use up trailing groups: + while( ( groups.last_endpoint_group != ( groups.block_pointer + groups.size - 1 ) ) & + ( remainder != 0 ) ) { + last_endpoint = ( ++groups.last_endpoint_group )->nodes; + const group_size_type group_size = static_cast + ( groups.last_endpoint_group->beyond_end - groups.last_endpoint_group->nodes ); + + if( group_size < remainder ) { + group_fill_position( element, group_size, position.node_pointer ); + remainder -= group_size; + } else { + group_fill_position( element, static_cast( remainder ), position.node_pointer ); + return return_iterator; + } + } + + size_type multiples = remainder / static_cast( LIST_BLOCK_MAX ); + remainder -= multiples * LIST_BLOCK_MAX; + + while( multiples-- != 0 ) { + groups.add_new( LIST_BLOCK_MAX ); + last_endpoint = groups.last_endpoint_group->nodes; + group_fill_position( element, LIST_BLOCK_MAX, position.node_pointer ); + } + + if( remainder != + 0 ) { // Bit annoying to create a large block to house a lower number of elements, but beats the alternatives + groups.add_new( LIST_BLOCK_MAX ); + last_endpoint = groups.last_endpoint_group->nodes; + group_fill_position( element, static_cast( remainder ), position.node_pointer ); + } + + return return_iterator; + } + } + + // Range insert + template + iterator insert( const iterator it, + typename plf_enable_if_c < !std::numeric_limits::is_integer, + iterator_type >::type first, const iterator_type last ) { + if( first == last ) { + return end_iterator; + } + + const iterator return_iterator = insert( it, *first ); + + while( ++first != last ) { + insert( it, *first ); + } + + return return_iterator; + } + + // Initializer-list insert + inline iterator insert( const iterator it, + const std::initializer_list &element_list ) { + // use range insert: + return insert( it, element_list.begin(), element_list.end() ); + } + + private: + + inline LIST_FORCE_INLINE void destroy_all_node_pointers( group_pointer_type const + group_to_process, const node_pointer_type beyond_end_node ) noexcept { + for( node_pointer_type current_node = group_to_process->nodes; current_node != beyond_end_node; + ++current_node ) { + LIST_DESTROY( node_pointer_allocator_type, node_pointer_allocator_pair, + &( current_node->next ) ); // Destruct element + LIST_DESTROY( node_pointer_allocator_type, node_pointer_allocator_pair, + &( current_node->previous ) ); // Destruct element + } + } + + public: + + // Single erase: + // if uninitialized/invalid iterator supplied, function could generate an exception, hence no noexcept + iterator erase( const const_iterator it ) { + assert( node_pointer_allocator_pair.total_number_of_elements != 0 ); + assert( it.node_pointer != NULL ); + assert( it.node_pointer != end_iterator.node_pointer ); + + if LIST_CONSTEXPR( !( std::is_trivially_destructible::value ) ) { + LIST_DESTROY( element_allocator_type, ( *this ), + &( it.node_pointer->element ) ); // Destruct element + } + + --node_pointer_allocator_pair.total_number_of_elements; + ++node_allocator_pair.number_of_erased_nodes; + + group_pointer_type node_group = groups.last_searched_group; + + // find nearest group with reusable (erased element) memory location: + if( ( it.node_pointer < node_group->nodes ) || ( it.node_pointer >= node_group->beyond_end ) ) { + // Search left and right: + const group_pointer_type beyond_end_group = groups.last_endpoint_group + 1; + group_pointer_type left = node_group - 1; + bool right_not_beyond_back = ( ++node_group < beyond_end_group ); + bool left_not_beyond_front = ( left >= groups.block_pointer ); + + while( true ) { + if( right_not_beyond_back ) { + if( ( it.node_pointer < node_group->beyond_end ) && + ( it.node_pointer >= node_group->nodes ) ) { // usable location found + break; + } + + right_not_beyond_back = ( ++node_group < beyond_end_group ); + } + + if( left_not_beyond_front ) { + if( ( it.node_pointer >= left->nodes ) && + ( it.node_pointer < left->beyond_end ) ) { // usable location found + node_group = left; + break; + } + + left_not_beyond_front = ( --left >= groups.block_pointer ); + } + } + + groups.last_searched_group = node_group; + } + + it.node_pointer->next->previous = it.node_pointer->previous; + it.node_pointer->previous->next = it.node_pointer->next; + + if( it.node_pointer == begin_iterator.node_pointer ) { + begin_iterator.node_pointer = it.node_pointer->next; + } + + const iterator return_iterator( it.node_pointer->next ); + + // ie. group is not empty yet, add node to free list + if( --( node_group->number_of_elements ) != 0 ) { + // next == NULL so that destructor can detect the free list item as opposed to non-free-list item + it.node_pointer->next = NULL; + it.node_pointer->previous = node_group->free_list_head; + node_group->free_list_head = it.node_pointer; + return return_iterator; + } else if( node_group != groups.last_endpoint_group-- ) { + // remove group (and decrement active back group) + const group_size_type group_size = static_cast( node_group->beyond_end - + node_group->nodes ); + node_allocator_pair.number_of_erased_nodes -= group_size; + + if LIST_CONSTEXPR( !( std::is_trivially_destructible::value ) ) { + destroy_all_node_pointers( node_group, node_group->beyond_end ); + } + + node_group->free_list_head = NULL; + + // Preserve only last (active) group or second/third-to-last group - seems to be best for performance under high-modification benchmarks + if( ( group_size == LIST_BLOCK_MAX ) | ( node_group >= groups.last_endpoint_group - 1 ) ) { + groups.move_to_back( node_group ); + } else { + groups.remove( node_group ); + } + + return return_iterator; + } else { // clear back group, leave trailing + if LIST_CONSTEXPR( !( std::is_trivially_destructible::value ) ) { + destroy_all_node_pointers( node_group, last_endpoint ); + } + + node_group->free_list_head = NULL; + + if( node_pointer_allocator_pair.total_number_of_elements != 0 ) { + node_allocator_pair.number_of_erased_nodes -= static_cast + ( last_endpoint - node_group->nodes ); + last_endpoint = groups.last_endpoint_group->beyond_end; + } else { + // If number of elements is zero, it indicates that this was the first group in the vector. In which case the last_endpoint_group would be invalid at this point due to the decrement in the above else-if statement. So it needs to be reset, as it will not be reset in the function call below. + groups.last_endpoint_group = groups.block_pointer; + clear(); + } + + return return_iterator; + } + } + + // Range-erase: + // if uninitialized/invalid iterator supplied, function could generate an exception + inline void erase( const const_iterator iterator1, const const_iterator iterator2 ) { + for( const_iterator current = iterator1; current != iterator2; ) { + current = erase( current ); + } + } + + inline void pop_back() { // Exception will occur on empty list + erase( iterator( end_node.previous ) ); + } + + inline void pop_front() { // Exception will occur on empty list + erase( begin_iterator ); + } + + inline list &operator=( const list &source ) { + assert( &source != this ); + + clear(); + reserve( source.node_pointer_allocator_pair.total_number_of_elements ); + insert( end_iterator, source.begin_iterator, source.end_iterator ); + + return *this; + } + + // Move assignment + list &operator=( list &&source ) LIST_NOEXCEPT_MOVE_ASSIGNMENT( allocator_type ) { + assert( &source != this ); + + // Move source values across: + groups.destroy_all_data( last_endpoint ); + + groups = std::move( source.groups ); + end_node = std::move( source.end_node ); + last_endpoint = std::move( source.last_endpoint ); + begin_iterator.node_pointer = ( source.begin_iterator.node_pointer == + source.end_iterator.node_pointer ) ? end_iterator.node_pointer : std::move( + source.begin_iterator.node_pointer ); + node_pointer_allocator_pair.total_number_of_elements = + source.node_pointer_allocator_pair.total_number_of_elements; + node_allocator_pair.number_of_erased_nodes = source.node_allocator_pair.number_of_erased_nodes; + + end_node.previous->next = begin_iterator.node_pointer->previous = end_iterator.node_pointer; + + source.groups.blank(); + source.reset(); + return *this; + } + + bool operator==( const list &rh ) const noexcept { + assert( this != &rh ); + + if( node_pointer_allocator_pair.total_number_of_elements != + rh.node_pointer_allocator_pair.total_number_of_elements ) { + return false; + } + + iterator rh_iterator = rh.begin_iterator; + + for( iterator lh_iterator = begin_iterator; lh_iterator != end_iterator; ) { + if( *rh_iterator++ != *lh_iterator++ ) { + return false; + } + } + + return true; + } + + inline bool operator!=( const list &rh ) const noexcept { + return !( *this == rh ); + } + + inline bool empty() const noexcept { + return node_pointer_allocator_pair.total_number_of_elements == 0; + } + + inline size_type size() const noexcept { + return node_pointer_allocator_pair.total_number_of_elements; + } + + inline size_type max_size() const noexcept { + return std::allocator_traits::max_size( *this ); + } + + inline size_type capacity() const noexcept { + return groups.element_allocator_pair.capacity; + } + + inline size_type approximate_memory_use() const noexcept { + return static_cast( sizeof( *this ) + ( groups.element_allocator_pair.capacity * sizeof( + node ) ) + ( sizeof( group ) * groups.group_allocator_pair.capacity ) ); + } + + private: + + struct less { + inline bool operator()( const element_type &a, const element_type &b ) const noexcept { + return a < b; + } + }; + + // Function-object to redirect the sort function to sort pointers by the elements they point to, not the pointer value + template + struct sort_dereferencer { + comparison_function stored_instance; + + explicit sort_dereferencer( const comparison_function &function_instance ): + stored_instance( function_instance ) + {} + + sort_dereferencer() noexcept + {} + + inline bool operator()( const node_pointer_type first, const node_pointer_type second ) { + return stored_instance( first->element, second->element ); + } + }; + + public: + + template + void sort( comparison_function compare ) { + if( node_pointer_allocator_pair.total_number_of_elements < 2 ) { + return; + } + + node_pointer_type *const node_pointers = LIST_ALLOCATE( node_pointer_allocator_type, + node_pointer_allocator_pair, node_pointer_allocator_pair.total_number_of_elements, NULL ); + node_pointer_type *node_pointer = node_pointers; + + // According to the C++ standard, construction of a pointer (of any type) may not trigger an exception - hence, no try-catch blocks are necessary for constructing the pointers: + for( group_pointer_type current_group = groups.block_pointer; + current_group != groups.last_endpoint_group; ++current_group ) { + const node_pointer_type end = current_group->beyond_end; + + if( ( end - current_group->nodes ) != + current_group->number_of_elements ) { // If there are erased nodes present in the group + for( node_pointer_type current_node = current_group->nodes; current_node != end; ++current_node ) { + if( current_node->next != NULL ) { // is not free list node + LIST_CONSTRUCT( node_pointer_allocator_type, node_pointer_allocator_pair, node_pointer++, + current_node ); + } + } + } else { + for( node_pointer_type current_node = current_group->nodes; current_node != end; ++current_node ) { + LIST_CONSTRUCT( node_pointer_allocator_type, node_pointer_allocator_pair, node_pointer++, + current_node ); + } + } + } + + if( ( last_endpoint - groups.last_endpoint_group->nodes ) != + groups.last_endpoint_group->number_of_elements ) { // If there are erased nodes present in the group + for( node_pointer_type current_node = groups.last_endpoint_group->nodes; + current_node != last_endpoint; ++current_node ) { + if( current_node->next != NULL ) { + LIST_CONSTRUCT( node_pointer_allocator_type, node_pointer_allocator_pair, node_pointer++, + current_node ); + } + } + } else { + for( node_pointer_type current_node = groups.last_endpoint_group->nodes; + current_node != last_endpoint; ++current_node ) { + LIST_CONSTRUCT( node_pointer_allocator_type, node_pointer_allocator_pair, node_pointer++, + current_node ); + } + } + + std::sort( node_pointers, node_pointers + node_pointer_allocator_pair.total_number_of_elements, + sort_dereferencer( compare ) ); + + begin_iterator.node_pointer = node_pointers[0]; + begin_iterator.node_pointer->next = node_pointers[1]; + begin_iterator.node_pointer->previous = end_iterator.node_pointer; + + end_node.next = node_pointers[0]; + end_node.previous = node_pointers[node_pointer_allocator_pair.total_number_of_elements - 1]; + end_node.previous->next = end_iterator.node_pointer; + end_node.previous->previous = node_pointers[node_pointer_allocator_pair.total_number_of_elements - + 2]; + + node_pointer_type *const back = node_pointers + + node_pointer_allocator_pair.total_number_of_elements - 1; + + for( node_pointer = node_pointers + 1; node_pointer != back; ++node_pointer ) { + ( *node_pointer )->next = *( node_pointer + 1 ); + ( *node_pointer )->previous = *( node_pointer - 1 ); + + if LIST_CONSTEXPR( !std::is_trivially_destructible::value ) { + LIST_DESTROY( node_pointer_allocator_type, node_pointer_allocator_pair, node_pointer - 1 ); + } + } + + if LIST_CONSTEXPR( !std::is_trivially_destructible::value ) { + LIST_DESTROY( node_pointer_allocator_type, node_pointer_allocator_pair, back ); + } + + LIST_DEALLOCATE( node_pointer_allocator_type, node_pointer_allocator_pair, node_pointers, + node_pointer_allocator_pair.total_number_of_elements ); + } + + inline void sort() { + sort( less() ); + } + + void reorder( const iterator position, const iterator first, const iterator last ) noexcept { + last.node_pointer->next->previous = first.node_pointer->previous; + first.node_pointer->previous->next = last.node_pointer->next; + + last.node_pointer->next = position.node_pointer; + first.node_pointer->previous = position.node_pointer->previous; + + position.node_pointer->previous->next = first.node_pointer; + position.node_pointer->previous = last.node_pointer; + + if( begin_iterator == position ) { + begin_iterator = first; + } + } + + inline void reorder( const iterator position, const iterator location ) noexcept { + reorder( position, location, location ); + } + + void reserve( size_type reserve_amount ) { + if( reserve_amount == 0 || reserve_amount <= groups.element_allocator_pair.capacity ) { + return; + } else if( reserve_amount < LIST_BLOCK_MIN ) { + reserve_amount = LIST_BLOCK_MIN; + } else if( reserve_amount > max_size() ) { + reserve_amount = max_size(); + } + + if( groups.block_pointer != NULL && node_pointer_allocator_pair.total_number_of_elements == 0 ) { + // edge case: has been filled with elements then clear()'d - some groups may be smaller than would be desired, should be replaced + group_size_type end_group_size = static_cast( ( groups.block_pointer + groups.size + - 1 )->beyond_end - ( groups.block_pointer + groups.size - 1 )->nodes ); + + // if last group isn't large enough, remove all groups + if( reserve_amount > end_group_size && end_group_size != LIST_BLOCK_MAX ) { + reset(); + } else { + size_type number_of_full_groups_needed = reserve_amount / LIST_BLOCK_MAX; + group_size_type remainder = static_cast( reserve_amount - + ( number_of_full_groups_needed * LIST_BLOCK_MAX ) ); + + // Remove any max_size groups which're not needed and any groups that're smaller than remainder: + for( group_pointer_type current_group = groups.block_pointer; + current_group < groups.block_pointer + groups.size; ) { + const group_size_type current_group_size = static_cast + ( groups.block_pointer->beyond_end - groups.block_pointer->nodes ); + + if( number_of_full_groups_needed != 0 && current_group_size == LIST_BLOCK_MAX ) { + --number_of_full_groups_needed; + ++current_group; + } else if( remainder != 0 && current_group_size >= remainder ) { + remainder = 0; + ++current_group; + } else { + groups.remove( current_group ); + } + } + + last_endpoint = groups.block_pointer->nodes; + } + } + + reserve_amount -= groups.element_allocator_pair.capacity; + + // To correct from possible reallocation caused by add_new: + const difference_type last_endpoint_group_number = groups.last_endpoint_group - + groups.block_pointer; + + size_type number_of_full_groups = ( reserve_amount / LIST_BLOCK_MAX ); + reserve_amount -= ( number_of_full_groups++ * LIST_BLOCK_MAX ); // ++ to aid while loop below + + // Previously uninitialized list or reset in above if statement; most common scenario + if( groups.block_pointer == NULL ) { + if( reserve_amount != 0 ) { + groups.initialize( static_cast( ( ( reserve_amount < LIST_BLOCK_MIN ) ? + LIST_BLOCK_MIN : reserve_amount ) ) ); + } else { + groups.initialize( LIST_BLOCK_MAX ); + --number_of_full_groups; + } + } else if( reserve_amount != 0 ) { + // Create a group at least as large as the last group - may allocate more than necessary, but better solution than creating a veyr small group in the middle of the group vector, I think: + const group_size_type last_endpoint_group_capacity = static_cast + ( groups.last_endpoint_group->beyond_end - groups.last_endpoint_group->nodes ); + groups.add_new( static_cast( ( reserve_amount < last_endpoint_group_capacity ) ? + last_endpoint_group_capacity : reserve_amount ) ); + } + + while( --number_of_full_groups != 0 ) { + groups.add_new( LIST_BLOCK_MAX ); + } + + groups.last_endpoint_group = groups.block_pointer + last_endpoint_group_number; + } + + inline LIST_FORCE_INLINE void free_unused_memory() noexcept { + groups.trim_trailing_groups(); + } + + void shrink_to_fit() { + if( ( last_endpoint == NULL ) | ( node_pointer_allocator_pair.total_number_of_elements == + groups.element_allocator_pair.capacity ) ) { // uninitialized list or full + return; + } else if( node_pointer_allocator_pair.total_number_of_elements == 0 ) { // Edge case + reset(); + return; + } else if( node_allocator_pair.number_of_erased_nodes == 0 && + last_endpoint == + groups.last_endpoint_group->beyond_end ) { //edge case - currently no waste except for possible trailing groups + groups.trim_trailing_groups(); + return; + } + + list temp; + temp.reserve( node_pointer_allocator_pair.total_number_of_elements ); + + if LIST_CONSTEXPR( std::is_move_assignable::value && + std::is_move_constructible::value ) { // move elements if possible, otherwise copy them + temp.insert( temp.end_iterator, std::make_move_iterator( begin_iterator ), + std::make_move_iterator( end_iterator ) ); + } else { + temp.insert( temp.end_iterator, begin_iterator, end_iterator ); + } + + *this = std::move( temp ); + } + + private: + + void append_process( list &source ) { // used by merge and splice + if( last_endpoint != groups.last_endpoint_group->beyond_end ) { + // Add unused nodes to group's free list + const node_pointer_type back_node = last_endpoint - 1; + for( node_pointer_type current_node = groups.last_endpoint_group->beyond_end - 1; + current_node != back_node; --current_node ) { + current_node->next = NULL; + current_node->previous = groups.last_endpoint_group->free_list_head; + groups.last_endpoint_group->free_list_head = current_node; + } + + node_allocator_pair.number_of_erased_nodes += static_cast + ( groups.last_endpoint_group->beyond_end - last_endpoint ); + } + + groups.append( source.groups ); + last_endpoint = source.last_endpoint; + node_pointer_allocator_pair.total_number_of_elements += + source.node_pointer_allocator_pair.total_number_of_elements; + source.reset(); + } + + public: + + void splice( iterator position, list &source ) { + assert( &source != this ); + + if( source.node_pointer_allocator_pair.total_number_of_elements == 0 ) { + return; + } else if( node_pointer_allocator_pair.total_number_of_elements == 0 ) { + *this = std::move( source ); + return; + } + + if( position.node_pointer == + begin_iterator.node_pointer ) { // put source groups at front rather than back + swap( source ); + position.node_pointer = end_iterator.node_pointer; + } + + position.node_pointer->previous->next = source.begin_iterator.node_pointer; + source.begin_iterator.node_pointer->previous = position.node_pointer->previous; + position.node_pointer->previous = source.end_node.previous; + source.end_node.previous->next = position.node_pointer; + + append_process( source ); + } + + template + void merge( list &source, comparison_function compare ) { + assert( &source != this ); + splice( ( source.node_pointer_allocator_pair.total_number_of_elements >= + node_pointer_allocator_pair.total_number_of_elements ) ? end_iterator : begin_iterator, source ); + sort( compare ); + } + + void merge( list &source ) { + assert( &source != this ); + + if( source.node_pointer_allocator_pair.total_number_of_elements == 0 ) { + return; + } else if( node_pointer_allocator_pair.total_number_of_elements == 0 ) { + *this = std::move( source ); + return; + } + + node_pointer_type current1 = begin_iterator.node_pointer->next, + current2 = source.begin_iterator.node_pointer->next; + node_pointer_type previous = source.begin_iterator.node_pointer; + const node_pointer_type source_end = source.end_iterator.node_pointer, + this_end = end_iterator.node_pointer; + + begin_iterator.node_pointer->next = source.begin_iterator.node_pointer; + source.begin_iterator.node_pointer->previous = begin_iterator.node_pointer; + + + while( ( current1 != this_end ) & ( current2 != source_end ) ) { + previous->next = current1; + current1->previous = previous; + previous = current1; + current1 = current1->next; + + previous->next = current2; + current2->previous = previous; + previous = current2; + current2 = current2->next; + } + + if( current1 != this_end ) { + previous->next = current1; + current1->previous = previous; + } else { + end_node.previous = source.end_node.previous; + source.end_node.previous->next = end_iterator.node_pointer; + } + + append_process( source ); + } + + void reverse() noexcept { + if( node_pointer_allocator_pair.total_number_of_elements > 1 ) { + for( group_pointer_type current_group = groups.block_pointer; + current_group != groups.last_endpoint_group; ++current_group ) { + const node_pointer_type end = current_group->beyond_end; + + for( node_pointer_type current_node = current_group->nodes; current_node != end; ++current_node ) { + if( current_node->next != NULL ) { // is not free list node + // swap the pointers: + const node_pointer_type temp = current_node->next; + current_node->next = current_node->previous; + current_node->previous = temp; + } + } + } + + for( node_pointer_type current_node = groups.last_endpoint_group->nodes; + current_node != last_endpoint; ++current_node ) { + if( current_node->next != NULL ) { + const node_pointer_type temp = current_node->next; + current_node->next = current_node->previous; + current_node->previous = temp; + } + } + + const node_pointer_type temp = end_node.previous; + end_node.previous = begin_iterator.node_pointer; + begin_iterator.node_pointer = temp; + + end_node.previous->next = end_iterator.node_pointer; + begin_iterator.node_pointer->previous = end_iterator.node_pointer; + } + } + + private: + + // Used by unique() + struct eq { + inline bool operator()( const element_type &a, const element_type &b ) const noexcept { + return a == b; + } + }; + + // Used by remove() + struct eq_to { + const element_type value; + + explicit eq_to( const element_type store_value ): + value( store_value ) + {} + + eq_to() noexcept + {} + + inline bool operator()( const element_type compare_value ) const noexcept { + return value == compare_value; + } + }; + + public: + + template + size_type unique( comparison_function compare ) { + const size_type original_number_of_elements = node_pointer_allocator_pair.total_number_of_elements; + + if( original_number_of_elements > 1 ) { + element_type *previous = &( begin_iterator.node_pointer->element ); + + for( iterator current = ++iterator( begin_iterator ); current != end_iterator; ) { + if( compare( *current, *previous ) ) { + current = erase( current ); + } else { + previous = &( current++.node_pointer->element ); + } + } + } + + return original_number_of_elements - node_pointer_allocator_pair.total_number_of_elements; + } + + inline size_type unique() { + return unique( eq() ); + } + + template + size_type remove_if( predicate_function predicate ) { + const size_type original_number_of_elements = node_pointer_allocator_pair.total_number_of_elements; + + if( original_number_of_elements != 0 ) { + for( group_pointer_type current_group = groups.block_pointer; + current_group != groups.last_endpoint_group; ++current_group ) { + group_size_type num_elements = current_group->number_of_elements; + const node_pointer_type end = current_group->beyond_end; + + // If there are erased nodes present in the group + if( end - current_group->nodes != num_elements ) { + for( node_pointer_type current_node = current_group->nodes; current_node != end; ++current_node ) { + // is not free list node and validates predicate + if( current_node->next != NULL && predicate( current_node->element ) ) { + erase( current_node ); + + // ie. group will be empty (and removed) now - nothing left to iterate over + if( --num_elements == 0 ) { + // As current group has been removed, subsequent groups have already shifted back by one, hence, the ++ to the current group in the for loop is unnecessary, and negated here + --current_group; + break; + } + } + } + } else { // No erased nodes in group + for( node_pointer_type current_node = current_group->nodes; current_node != end; ++current_node ) { + if( predicate( current_node->element ) ) { + erase( current_node ); + + if( --num_elements == 0 ) { + --current_group; + break; + } + } + } + } + } + + group_size_type num_elements = groups.last_endpoint_group->number_of_elements; + + // If there are erased nodes present in the group + if( last_endpoint - groups.last_endpoint_group->nodes != num_elements ) { + for( node_pointer_type current_node = groups.last_endpoint_group->nodes; + current_node != last_endpoint; ++current_node ) { + if( current_node->next != NULL && predicate( current_node->element ) ) { + erase( current_node ); + + if( --num_elements == 0 ) { + break; + } + } + } + } else { + for( node_pointer_type current_node = groups.last_endpoint_group->nodes; + current_node != last_endpoint; ++current_node ) { + if( predicate( current_node->element ) ) { + erase( current_node ); + + if( --num_elements == 0 ) { + break; + } + } + } + } + } + + return original_number_of_elements - node_pointer_allocator_pair.total_number_of_elements; + } + + inline size_type remove( const element_type &value ) { + return remove_if( eq_to( value ) ); + } + + void resize( const size_type number_of_elements, const element_type &value = element_type() ) { + if( node_pointer_allocator_pair.total_number_of_elements == number_of_elements ) { + return; + } else if( number_of_elements == 0 ) { + clear(); + return; + } else if( node_pointer_allocator_pair.total_number_of_elements < number_of_elements ) { + insert( end_iterator, number_of_elements - node_pointer_allocator_pair.total_number_of_elements, + value ); + } else { // ie. node_pointer_allocator_pair.total_number_of_elements > number_of_elements + iterator current( end_node.previous ); + + for( size_type number_to_remove = node_pointer_allocator_pair.total_number_of_elements - + number_of_elements; number_to_remove != 0; --number_to_remove ) { + const node_pointer_type temp = current.node_pointer->previous; + erase( current ); + current.node_pointer = temp; + } + } + } + + // Range assign: + template + inline void assign( const typename plf_enable_if_c < + !std::numeric_limits::is_integer, iterator_type >::type first, + const iterator_type last ) { + clear(); + insert( end_iterator, first, last ); + groups.trim_trailing_groups(); + } + + // Fill assign: + inline void assign( const size_type number_of_elements, const element_type &value ) { + clear(); + reserve( number_of_elements ); // Will return anyway if capacity already > number_of_elements + insert( end_iterator, number_of_elements, value ); + } + + // Initializer-list assign: + inline void assign( const std::initializer_list &element_list ) { + clear(); + reserve( element_list.size() ); + insert( end_iterator, element_list ); + } + + inline allocator_type get_allocator() const noexcept { + return element_allocator_type(); + } + + void swap( list &source ) LIST_NOEXCEPT_SWAP( allocator_type ) { + list temp( std::move( source ) ); + source = std::move( *this ); + *this = std::move( temp ); + } +}; + +template +inline void swap( list &a, + list &b ) LIST_NOEXCEPT_SWAP( + swap_element_allocator_type ) +{ + a.swap( b ); +} + +} // namespace cata + +#undef LIST_BLOCK_MAX +#undef LIST_BLOCK_MIN + +#undef LIST_FORCE_INLINE + +#undef LIST_CONSTEXPR +#undef LIST_NOEXCEPT_SWAP +#undef LIST_NOEXCEPT_MOVE_ASSIGNMENT + +#undef LIST_CONSTRUCT +#undef LIST_DESTROY +#undef LIST_ALLOCATE +#undef LIST_ALLOCATE_INITIALIZATION +#undef LIST_DEALLOCATE + +#endif // LIST_H diff --git a/src/magic.cpp b/src/magic.cpp index ebdf47b301bb8..18cd75a252471 100644 --- a/src/magic.cpp +++ b/src/magic.cpp @@ -13,9 +13,11 @@ #include "calendar.h" #include "color.h" #include "damage.h" +#include "field.h" #include "game.h" #include "generic_factory.h" #include "json.h" +#include "map.h" #include "messages.h" #include "monster.h" #include "mutation.h" @@ -200,6 +202,17 @@ void spell_type::load( JsonObject &jo, const std::string & ) optional( jo, was_loaded, "effect_str", effect_str, "" ); + std::string field_input; + optional( jo, was_loaded, "field_id", field_input, "none" ); + if( field_input != "none" ) { + field = field_type_id( field_input ); + } + optional( jo, was_loaded, "field_chance", field_chance, 1 ); + optional( jo, was_loaded, "min_field_intensity", min_field_intensity, 0 ); + optional( jo, was_loaded, "max_field_intensity", max_field_intensity, 0 ); + optional( jo, was_loaded, "field_intensity_increment", field_intensity_increment, 0.0f ); + optional( jo, was_loaded, "field_intensity_variance", field_intensity_variance, 0.0f ); + optional( jo, was_loaded, "min_damage", min_damage, 0 ); optional( jo, was_loaded, "damage_increment", damage_increment, 0.0f ); optional( jo, was_loaded, "max_damage", max_damage, 0 ); @@ -302,6 +315,19 @@ void spell_type::check_consistency() if( spell_infinite_loop_check( spell_effect_list, sp_t.id ) ) { debugmsg( "ERROR: %s has infinite loop in extra_effects", sp_t.id.c_str() ); } + if( sp_t.field ) { + if( sp_t.field_chance <= 0 ) { + debugmsg( "ERROR: %s must have a positive field chance.", sp_t.id.c_str() ); + } + if( sp_t.field_intensity_increment > 0 && sp_t.max_field_intensity < sp_t.min_field_intensity ) { + debugmsg( "ERROR: max_field_intensity must be greater than min_field_intensity with positive increment: %s", + sp_t.id.c_str() ); + } else if( sp_t.field_intensity_increment < 0 && + sp_t.max_field_intensity > sp_t.min_field_intensity ) { + debugmsg( "ERROR: min_field_intensity must be greater than max_field_intensity with negative increment.", + sp_t.id.c_str() ); + } + } } } @@ -340,6 +366,13 @@ trait_id spell::spell_class() const return type->spell_class; } +int spell::field_intensity() const +{ + return std::min( type->max_field_intensity, + static_cast( type->min_field_intensity + round( get_level() * + type->field_intensity_increment ) ) ); +} + int spell::damage() const { if( type->min_damage >= 0 ) { @@ -639,6 +672,26 @@ bool spell::bp_is_affected( body_part bp ) const return type->affected_bps[bp]; } +void spell::create_field( const tripoint &at ) const +{ + if( !type->field ) { + return; + } + const int intensity = field_intensity() + rng( -type->field_intensity_variance * field_intensity(), + type->field_intensity_variance * field_intensity() ); + if( intensity <= 0 ) { + return; + } + if( one_in( type->field_chance ) ) { + field_entry *field = g->m.get_field( at, *type->field ); + if( field ) { + field->set_field_intensity( field->get_field_intensity() + intensity ); + } else { + g->m.add_field( at, *type->field, intensity ); + } + } +} + void spell::make_sound( const tripoint &target ) const { if( !has_flag( spell_flag::SILENT ) ) { @@ -863,7 +916,7 @@ int spell::heal( const tripoint &target ) const return -1; } -bool spell::cast_spell_effect( const Creature &source, const tripoint &target ) +bool spell::cast_spell_effect( const Creature &source, const tripoint &target ) const { // figure out which function is the effect (maybe change this into how iuse or activity_handlers does it) // TODO: refactor these so make_sound can be called inside each of these functions @@ -902,7 +955,7 @@ bool spell::cast_spell_effect( const Creature &source, const tripoint &target ) return true; } -bool spell::cast_all_effects( const Creature &source, const tripoint &target ) +bool spell::cast_all_effects( const Creature &source, const tripoint &target ) const { // first call the effect of the main spell bool success = cast_spell_effect( source, target ); diff --git a/src/magic.h b/src/magic.h index fbb8e7e9a8a33..154792629e7f2 100644 --- a/src/magic.h +++ b/src/magic.h @@ -78,7 +78,7 @@ struct fake_spell { bool self; fake_spell( const spell_id &sp_id, bool hit_self = false, const cata::optional &max_level = cata::nullopt ) : id( sp_id ), - max_level( max_level ), self( hit_self ) {}; + max_level( max_level ), self( hit_self ) {} }; class spell_type @@ -101,6 +101,19 @@ class spell_type // list of additional "spell effects" std::vector additional_spells; + // if the spell has a field name defined, this is where it is + cata::optional field; + // the chance one_in( field_chance ) that the field spawns at a tripoint in the area of the spell + int field_chance; + // field intensity at spell level 0 + int min_field_intensity; + // increment of field intensity per level + float field_intensity_increment; + // maximum field intensity allowed + int max_field_intensity; + // field intensity added to the map is +- ( 1 + field_intensity_variance ) * field_intensity + float field_intensity_variance; + // minimum damage this spell can cause int min_damage; // amount of damage increase per spell level @@ -235,6 +248,8 @@ class spell // what is the max level of the spell int get_max_level() const; + // what is the intensity of the field the spell generates ( 0 if no field ) + int field_intensity() const; // how much damage does the spell do int damage() const; dealt_damage_instance get_dealt_damage_instance() const; @@ -298,15 +313,18 @@ class spell // difficulty of the level int get_difficulty() const; + // tries to create a field at the location specified + void create_field( const tripoint &at ) const; + // makes a spell sound at the location void make_sound( const tripoint &target ) const; // heals the critter at the location, returns amount healed (player heals each body part) int heal( const tripoint &target ) const; // casts the spell effect. returns true if successful - bool cast_spell_effect( const Creature &source, const tripoint &target ); + bool cast_spell_effect( const Creature &source, const tripoint &target ) const; // goes through the spell effect and all of its internal spells - bool cast_all_effects( const Creature &source, const tripoint &target ); + bool cast_all_effects( const Creature &source, const tripoint &target ) const; // is the target valid for this spell? bool is_valid_target( const Creature &caster, const tripoint &p ) const; @@ -398,10 +416,10 @@ std::set spell_effect_line( const spell &, const tripoint &source, const tripoint &target, const int aoe_radius, const bool ignore_walls ); -void spawn_ethereal_item( spell &sp ); -void recover_energy( spell &sp, const tripoint &target ); +void spawn_ethereal_item( const spell &sp ); +void recover_energy( const spell &sp, const tripoint &target ); void spawn_summoned_monster( const spell &sp, const Creature &caster, const tripoint &target ); -void translocate( spell &sp, const Creature &caster, const tripoint &target, +void translocate( const spell &sp, const Creature &caster, const tripoint &target, teleporter_list &tp_list ); } // namespace spell_effect diff --git a/src/magic_spell_effect.cpp b/src/magic_spell_effect.cpp index b4fac687677b9..0cd1af09f617b 100644 --- a/src/magic_spell_effect.cpp +++ b/src/magic_spell_effect.cpp @@ -320,6 +320,7 @@ static void damage_targets( const spell &sp, const Creature &caster, continue; } sp.make_sound( target ); + sp.create_field( target ); Creature *const cr = g->critter_at( target ); if( !cr ) { continue; @@ -383,7 +384,7 @@ void spell_effect::line_attack( const spell &sp, const Creature &caster, sp.has_flag( spell_flag::IGNORE_WALLS ) ) ); } -void spell_effect::spawn_ethereal_item( spell &sp ) +void spell_effect::spawn_ethereal_item( const spell &sp ) { item granted( sp.effect_data(), calendar::turn ); if( !granted.is_comestible() && !( sp.has_flag( spell_flag::PERMANENT ) && sp.is_max_level() ) ) { @@ -408,7 +409,7 @@ void spell_effect::spawn_ethereal_item( spell &sp ) } } -void spell_effect::recover_energy( spell &sp, const tripoint &target ) +void spell_effect::recover_energy( const spell &sp, const tripoint &target ) { // this spell is not appropriate for healing const int healing = sp.damage(); @@ -416,6 +417,9 @@ void spell_effect::recover_energy( spell &sp, const tripoint &target ) // TODO: Change to Character // current limitation is that Character does not have stamina or power_level members player *p = g->critter_at( target ); + if( !p ) { + return; + } if( energy_source == "MANA" ) { p->magic.mod_mana( *p, healing ); @@ -490,7 +494,7 @@ void spell_effect::spawn_summoned_monster( const spell &sp, const Creature &cast } } -void spell_effect::translocate( spell &sp, const Creature &caster, +void spell_effect::translocate( const spell &sp, const Creature &caster, const tripoint &target, teleporter_list &tp_list ) { tp_list.translocate( spell_effect_area( sp, target, spell_effect_blast, caster, true ) ); diff --git a/src/main_menu.cpp b/src/main_menu.cpp index 299c713222782..f85834f4b688c 100644 --- a/src/main_menu.cpp +++ b/src/main_menu.cpp @@ -509,7 +509,7 @@ bool main_menu::opening_screen() int yoffset = iMenuOffsetY - 2; int xlen = 0; for( int i = 1; i < NUM_SPECIAL_GAMES; i++ ) { - std::string spec_name = special_game_name( special_game_id( i ) ); + std::string spec_name = special_game_name( static_cast( i ) ); special_names.push_back( spec_name ); xlen += spec_name.size() + 2; } @@ -538,9 +538,9 @@ bool main_menu::opening_screen() } if( action == "UP" || action == "CONFIRM" ) { if( sel2 >= 0 && sel2 < NUM_SPECIAL_GAMES - 1 ) { - g->gamemode = get_special_game( special_game_id( sel2 + 1 ) ); + g->gamemode = get_special_game( static_cast( sel2 + 1 ) ); // check world - WORLDPTR world = world_generator->make_new_world( special_game_id( sel2 + 1 ) ); + WORLDPTR world = world_generator->make_new_world( static_cast( sel2 + 1 ) ); if( world == nullptr ) { continue; } diff --git a/src/map.cpp b/src/map.cpp index 1dc1437874f72..7f31c4ed2611c 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -5566,7 +5566,7 @@ void map::update_visibility_cache( const int zlev ) const tripoint sm( gridx, gridy, 0 ); const auto abs_sm = map::abs_sub + sm; const auto abs_omt = sm_to_omt_copy( abs_sm ); - overmap_buffer.set_seen( abs_omt.x, abs_omt.y, abs_omt.z, true ); + overmap_buffer.set_seen( abs_omt, true ); } } } @@ -6706,9 +6706,9 @@ void map::loadn( const int gridx, const int gridy, const bool update_vehicles ) // Optimized mapgen function that only works properly for very simple overmap types // Does not create or require a temporary map and does its own saving -static void generate_uniform( const int x, const int y, const int z, const ter_id &terrain_type ) +static void generate_uniform( const tripoint &p, const ter_id &terrain_type ) { - dbg( D_INFO ) << "generate_uniform x: " << x << " y: " << y << " abs_z: " << z + dbg( D_INFO ) << "generate_uniform p: " << p << " terrain_type: " << terrain_type.id().str(); constexpr size_t block_size = SEEX * SEEY; @@ -6718,7 +6718,7 @@ static void generate_uniform( const int x, const int y, const int z, const ter_i sm->is_uniform = true; std::uninitialized_fill_n( &sm->ter[0][0], block_size, terrain_type ); sm->last_touched = calendar::turn; - MAPBUFFER.add_submap( x + xd, y + yd, z, sm ); + MAPBUFFER.add_submap( p + point(xd, yd), sm ); } } } @@ -6733,47 +6733,42 @@ void map::loadn( const int gridx, const int gridy, const int gridz, const bool u << "], worldy[" << abs_sub.y << "], gridx[" << gridx << "], gridy[" << gridy << "], gridz[" << gridz << "])"; - const int absx = abs_sub.x + gridx, - absy = abs_sub.y + gridy; + const tripoint grid_abs_sub(abs_sub.x + gridx, abs_sub.y + gridy, gridz ); const size_t gridn = get_nonant( { gridx, gridy, gridz } ); - dbg( D_INFO ) << "map::loadn absx: " << absx << " absy: " << absy - << " gridn: " << gridn; + dbg( D_INFO ) << "map::loadn grid_abs_sub: " << grid_abs_sub << " gridn: " << gridn; const int old_abs_z = abs_sub.z; // Ugly, but necessary at the moment abs_sub.z = gridz; - submap *tmpsub = MAPBUFFER.lookup_submap( absx, absy, gridz ); + submap *tmpsub = MAPBUFFER.lookup_submap( grid_abs_sub ); if( tmpsub == nullptr ) { // It doesn't exist; we must generate it! dbg( D_INFO | D_WARNING ) << "map::loadn: Missing mapbuffer data. Regenerating."; // Each overmap square is two nonants; to prevent overlap, generate only at // squares divisible by 2. - const int newmapx = absx - ( abs( absx ) % 2 ); - const int newmapy = absy - ( abs( absy ) % 2 ); - // Short-circuit if the map tile is uniform - int overx = newmapx; - int overy = newmapy; - sm_to_omt( overx, overy ); + const tripoint grid_abs_omt = sm_to_omt_copy( grid_abs_sub ); + const tripoint grid_abs_sub_rounded = omt_to_sm_copy( grid_abs_omt ); - const oter_id terrain_type = overmap_buffer.ter( overx, overy, gridz ); + const oter_id terrain_type = overmap_buffer.ter( grid_abs_omt ); + // Short-circuit if the map tile is uniform // TODO: Replace with json mapgen functions. if( terrain_type == air ) { - generate_uniform( newmapx, newmapy, gridz, t_open_air ); + generate_uniform( grid_abs_sub_rounded, t_open_air ); } else if( terrain_type == rock ) { - generate_uniform( newmapx, newmapy, gridz, t_rock ); + generate_uniform( grid_abs_sub_rounded, t_rock ); } else { tinymap tmp_map; - tmp_map.generate( newmapx, newmapy, gridz, calendar::turn ); + tmp_map.generate( grid_abs_sub_rounded, calendar::turn ); } // This is the same call to MAPBUFFER as above! - tmpsub = MAPBUFFER.lookup_submap( absx, absy, gridz ); + tmpsub = MAPBUFFER.lookup_submap( grid_abs_sub ); if( tmpsub == nullptr ) { - dbg( D_ERROR ) << "failed to generate a submap at " << absx << absy << abs_sub.z; - debugmsg( "failed to generate a submap at %d,%d,%d", absx, absy, abs_sub.z ); + dbg( D_ERROR ) << "failed to generate a submap at " << grid_abs_sub; + debugmsg( "failed to generate a submap at %s", grid_abs_sub.to_string() ); return; } } @@ -6785,7 +6780,7 @@ void map::loadn( const int gridx, const int gridy, const int gridz, const bool u set_pathfinding_cache_dirty( gridz ); setsubmap( gridn, tmpsub ); if( !tmpsub->active_items.empty() ) { - submaps_with_active_items.emplace( absx, absy, gridz ); + submaps_with_active_items.emplace( grid_abs_sub ); } if( tmpsub->field_count > 0 ) { get_cache( gridz ).field_cache.set( gridx + ( gridy * MAPSIZE ) ); @@ -7418,10 +7413,10 @@ void map::spawn_monsters_submap_group( const tripoint &gp, mongroup &group, bool void map::spawn_monsters_submap( const tripoint &gp, bool ignore_sight ) { // Load unloaded monsters - overmap_buffer.spawn_monster( abs_sub.x + gp.x, abs_sub.y + gp.y, gp.z ); + overmap_buffer.spawn_monster( gp + abs_sub.xy() ); // Only spawn new monsters after existing monsters are loaded. - auto groups = overmap_buffer.groups_at( abs_sub.x + gp.x, abs_sub.y + gp.y, gp.z ); + auto groups = overmap_buffer.groups_at( gp + abs_sub.xy() ); for( auto &mgp : groups ) { spawn_monsters_submap_group( gp, *mgp, ignore_sight ); } @@ -7521,27 +7516,21 @@ const std::vector &map::trap_locations( const trap_id &type ) const bool map::inbounds( const tripoint &p ) const { static constexpr tripoint map_boundary_min( 0, 0, -OVERMAP_DEPTH ); - static constexpr tripoint map_boundary_max( MAPSIZE_Y, MAPSIZE_X, OVERMAP_HEIGHT ); - static constexpr tripoint map_clearance_min( tripoint_zero ); - static constexpr tripoint map_clearance_max( 1, 1, 0 ); + static constexpr tripoint map_boundary_max( MAPSIZE_Y, MAPSIZE_X, OVERMAP_HEIGHT + 1 ); static constexpr box map_boundaries( map_boundary_min, map_boundary_max ); - static constexpr box map_clearance( map_clearance_min, map_clearance_max ); - return generic_inbounds( p, map_boundaries, map_clearance ); + return map_boundaries.contains_half_open( p ); } bool tinymap::inbounds( const tripoint &p ) const { constexpr tripoint map_boundary_min( 0, 0, -OVERMAP_DEPTH ); - constexpr tripoint map_boundary_max( SEEY * 2, SEEX * 2, OVERMAP_HEIGHT ); - constexpr tripoint map_clearance_min( tripoint_zero ); - constexpr tripoint map_clearance_max( 1, 1, 0 ); + constexpr tripoint map_boundary_max( SEEY * 2, SEEX * 2, OVERMAP_HEIGHT + 1 ); constexpr box map_boundaries( map_boundary_min, map_boundary_max ); - constexpr box map_clearance( map_clearance_min, map_clearance_max ); - return generic_inbounds( p, map_boundaries, map_clearance ); + return map_boundaries.contains_half_open( p ); } // set up a map just long enough scribble on it @@ -7552,18 +7541,19 @@ bool tinymap::fake_load( const furn_id &fur_type, const ter_id &ter_type, const set_abs_sub( 0, 0, 0 ); for( int gridx = 0; gridx < my_MAPSIZE; gridx++ ) { for( int gridy = 0; gridy < my_MAPSIZE; gridy++ ) { - submap *tmpsub = MAPBUFFER.lookup_submap( gridx, gridy, 0 ); + const tripoint gridp( gridx, gridy, 0 ); + submap *tmpsub = MAPBUFFER.lookup_submap( gridp ); if( tmpsub == nullptr ) { - generate_uniform( gridx, gridy, 0, ter_type ); + generate_uniform( gridp, ter_type ); do_terset = false; - tmpsub = MAPBUFFER.lookup_submap( gridx, gridy, 0 ); + tmpsub = MAPBUFFER.lookup_submap( gridp ); if( tmpsub == nullptr ) { dbg( D_ERROR ) << "failed to generate a fake submap at 0, 0, 0 "; debugmsg( "failed to generate a fake submap at 0,0,0" ); return false; } } - const size_t gridn = get_nonant( { gridx, gridy, 0 } ); + const size_t gridn = get_nonant( gridp ); setsubmap( gridn, tmpsub ); } @@ -8392,19 +8382,21 @@ std::list map::get_active_items_in_radius( const tripoint ¢er const point maxg( std::min( maxp.x / SEEX, my_MAPSIZE - 1 ), std::min( maxp.y / SEEY, my_MAPSIZE - 1 ) ); - for( int gx = ming.x; gx <= maxg.x; ++gx ) { - for( int gy = ming.y; gy <= maxg.y; ++gy ) { - const point sm_offset( gx * SEEX, gy * SEEY ); - - for( const auto &elem : get_submap_at_grid( { gx, gy, center.z } )->active_items.get() ) { - const tripoint pos( sm_offset + elem.location, center.z ); + for( const tripoint &submap_loc : submaps_with_active_items ) { + if( submap_loc.x < ming.x || submap_loc.y < ming.y || + submap_loc.x > maxg.x || submap_loc.y > maxg.y ) { + continue; + } + const point sm_offset( submap_loc.x * SEEX, submap_loc.y * SEEY ); - if( rl_dist( pos, center ) > radius ) { - continue; - } + for( const auto &elem : get_submap_at_grid( submap_loc )->active_items.get() ) { + const tripoint pos( sm_offset + elem.location, submap_loc.z ); - result.emplace_back( map_cursor( pos ), elem.item_ref.get() ); + if( rl_dist( pos, center ) > radius ) { + continue; } + + result.emplace_back( map_cursor( pos ), elem.item_ref.get() ); } } diff --git a/src/map.h b/src/map.h index 1033c72f6ea20..328c7356b3ae3 100644 --- a/src/map.h +++ b/src/map.h @@ -348,6 +348,9 @@ class map * @param update_vehicles If true, add vehicles to the vehicle cache. */ void load( const int wx, const int wy, const int wz, const bool update_vehicles ); + void load( const tripoint &p, const bool update_vehicles ) { + load( p.x, p.y, p.z, update_vehicles ); + } /** * Shift the map along the vector (sx,sy). * This is like loading the map with coordinates derived from the current @@ -1222,6 +1225,9 @@ class map // mapgen.cpp functions void generate( const int x, const int y, const int z, const time_point &when ); + void generate( const tripoint &p, const time_point &when ) { + generate( p.x, p.y, p.z, when ); + } void place_spawns( const mongroup_id &group, const int chance, const int x1, const int y1, const int x2, const int y2, const float intensity, const bool individual = false, const bool friendly = false ); diff --git a/src/map_extras.cpp b/src/map_extras.cpp index 5162a27e925e4..b8aab0e720b77 100644 --- a/src/map_extras.cpp +++ b/src/map_extras.cpp @@ -420,28 +420,16 @@ static void mx_collegekids( map &m, const tripoint & ) static void mx_roadblock( map &m, const tripoint &abs_sub ) { - std::string north = overmap_buffer.ter( abs_sub.x / 2, abs_sub.y / 2 - 1, abs_sub.z ).id().c_str(); - std::string south = overmap_buffer.ter( abs_sub.x / 2, abs_sub.y / 2 + 1, abs_sub.z ).id().c_str(); - std::string west = overmap_buffer.ter( abs_sub.x / 2 - 1, abs_sub.y / 2, abs_sub.z ).id().c_str(); - std::string east = overmap_buffer.ter( abs_sub.x / 2 + 1, abs_sub.y / 2, abs_sub.z ).id().c_str(); + const tripoint abs_omt = sm_to_omt_copy( abs_sub ); + const oter_id &north = overmap_buffer.ter( abs_omt + point( 0, -1 ) ); + const oter_id &south = overmap_buffer.ter( abs_omt + point( 0, 1 ) ); + const oter_id &west = overmap_buffer.ter( abs_omt + point( -1, 0 ) ); + const oter_id &east = overmap_buffer.ter( abs_omt + point( 1, 0 ) ); - bool northroad = false; - bool eastroad = false; - bool southroad = false; - bool westroad = false; - - if( north.find( "road_" ) == 0 ) { - northroad = true; - } - if( east.find( "road_" ) == 0 ) { - eastroad = true; - } - if( south.find( "road_" ) == 0 ) { - southroad = true; - } - if( west.find( "road_" ) == 0 ) { - westroad = true; - } + const bool road_at_north = is_ot_match( "road", north, ot_match_type::type ); + const bool road_at_south = is_ot_match( "road", south, ot_match_type::type ); + const bool road_at_west = is_ot_match( "road", west, ot_match_type::type ); + const bool road_at_east = is_ot_match( "road", east, ot_match_type::type ); const auto spawn_turret = [&]( int x, int y ) { if( one_in( 2 ) ) { @@ -457,36 +445,36 @@ static void mx_roadblock( map &m, const tripoint &abs_sub ) if( mil ) { //Military doesn't joke around with their barricades! if( one_in( 2 ) ) { - if( northroad ) { + if( road_at_north ) { line( &m, t_fence_barbed, 4, 3, 10, 3 ); line( &m, t_fence_barbed, 13, 3, 19, 3 ); } - if( eastroad ) { + if( road_at_east ) { line( &m, t_fence_barbed, SEEX * 2 - 3, 4, SEEX * 2 - 3, 10 ); line( &m, t_fence_barbed, SEEX * 2 - 3, 13, SEEX * 2 - 3, 19 ); } - if( southroad ) { + if( road_at_south ) { line( &m, t_fence_barbed, 4, SEEY * 2 - 3, 10, SEEY * 2 - 3 ); line( &m, t_fence_barbed, 13, SEEY * 2 - 3, 19, SEEY * 2 - 3 ); } - if( eastroad ) { + if( road_at_east ) { line( &m, t_fence_barbed, 3, 4, 3, 10 ); line( &m, t_fence_barbed, 3, 13, 3, 19 ); } } else { - if( northroad ) { + if( road_at_north ) { line_furn( &m, f_sandbag_half, 4, 3, 10, 3 ); line_furn( &m, f_sandbag_half, 13, 3, 19, 3 ); } - if( eastroad ) { + if( road_at_east ) { line_furn( &m, f_sandbag_half, SEEX * 2 - 3, 4, SEEX * 2 - 3, 10 ); line_furn( &m, f_sandbag_half, SEEX * 2 - 3, 13, SEEX * 2 - 3, 19 ); } - if( southroad ) { + if( road_at_south ) { line_furn( &m, f_sandbag_half, 4, SEEY * 2 - 3, 10, SEEY * 2 - 3 ); line_furn( &m, f_sandbag_half, 13, SEEY * 2 - 3, 19, SEEY * 2 - 3 ); } - if( eastroad ) { + if( road_at_east ) { line_furn( &m, f_sandbag_half, 3, 4, 3, 10 ); line_furn( &m, f_sandbag_half, 3, 13, 3, 19 ); } @@ -503,16 +491,16 @@ static void mx_roadblock( map &m, const tripoint &abs_sub ) } else { // Vehicle & turrets m.add_vehicle( vgroup_id( "military_vehicles" ), tripoint( 12, SEEY * 2 - 10, abs_sub.z ), 0, 70, -1 ); - if( northroad ) { + if( road_at_north ) { spawn_turret( 12, 6 ); } - if( eastroad ) { + if( road_at_east ) { spawn_turret( 18, 12 ); } - if( southroad ) { + if( road_at_south ) { spawn_turret( 12, 18 ); } - if( westroad ) { + if( road_at_west ) { spawn_turret( 6, 12 ); } } @@ -532,22 +520,22 @@ static void mx_roadblock( map &m, const tripoint &abs_sub ) } } else { // Police roadblock - if( northroad ) { + if( road_at_north ) { line_furn( &m, f_barricade_road, 4, 3, 10, 3 ); line_furn( &m, f_barricade_road, 13, 3, 19, 3 ); m.add_spawn( mon_turret, 1, 12, 1 ); } - if( eastroad ) { + if( road_at_east ) { line_furn( &m, f_barricade_road, SEEX * 2 - 3, 4, SEEX * 2 - 3, 10 ); line_furn( &m, f_barricade_road, SEEX * 2 - 3, 13, SEEX * 2 - 3, 19 ); m.add_spawn( mon_turret, 1, SEEX * 2 - 1, 12 ); } - if( southroad ) { + if( road_at_south ) { line_furn( &m, f_barricade_road, 4, SEEY * 2 - 3, 10, SEEY * 2 - 3 ); line_furn( &m, f_barricade_road, 13, SEEY * 2 - 3, 19, SEEY * 2 - 3 ); m.add_spawn( mon_turret, 1, 12, SEEY * 2 - 1 ); } - if( westroad ) { + if( road_at_west ) { line_furn( &m, f_barricade_road, 3, 4, 3, 10 ); line_furn( &m, f_barricade_road, 3, 13, 3, 19 ); m.add_spawn( mon_turret, 1, 1, 12 ); @@ -590,10 +578,11 @@ static void mx_marloss_pilgrimage( map &m, const tripoint &abs_sub ) static void mx_bandits_block( map &m, const tripoint &abs_sub ) { - const oter_id &north = overmap_buffer.ter( abs_sub.x, abs_sub.y - 1, abs_sub.z ); - const oter_id &south = overmap_buffer.ter( abs_sub.x, abs_sub.y + 1, abs_sub.z ); - const oter_id &west = overmap_buffer.ter( abs_sub.x - 1, abs_sub.y, abs_sub.z ); - const oter_id &east = overmap_buffer.ter( abs_sub.x + 1, abs_sub.y, abs_sub.z ); + const tripoint abs_omt = sm_to_omt_copy( abs_sub ); + const oter_id &north = overmap_buffer.ter( abs_omt + point( 0, -1 ) ); + const oter_id &south = overmap_buffer.ter( abs_omt + point( 0, 1 ) ); + const oter_id &west = overmap_buffer.ter( abs_omt + point( -1, 0 ) ); + const oter_id &east = overmap_buffer.ter( abs_omt + point( 1, 0 ) ); const bool forest_at_north = is_ot_match( "forest", north, ot_match_type::prefix ); const bool forest_at_south = is_ot_match( "forest", south, ot_match_type::prefix ); @@ -819,11 +808,12 @@ static void mx_portal( map &m, const tripoint &abs_sub ) static void mx_minefield( map &m, const tripoint &abs_sub ) { - const oter_id ¢er = overmap_buffer.ter( abs_sub.x, abs_sub.y, abs_sub.z ); - const oter_id &north = overmap_buffer.ter( abs_sub.x, abs_sub.y - 1, abs_sub.z ); - const oter_id &south = overmap_buffer.ter( abs_sub.x, abs_sub.y + 1, abs_sub.z ); - const oter_id &west = overmap_buffer.ter( abs_sub.x - 1, abs_sub.y, abs_sub.z ); - const oter_id &east = overmap_buffer.ter( abs_sub.x + 1, abs_sub.y, abs_sub.z ); + const tripoint abs_omt = sm_to_omt_copy( abs_sub ); + const oter_id ¢er = overmap_buffer.ter( abs_omt ); + const oter_id &north = overmap_buffer.ter( abs_omt + point( 0, -1 ) ); + const oter_id &south = overmap_buffer.ter( abs_omt + point( 0, 1 ) ); + const oter_id &west = overmap_buffer.ter( abs_omt + point( -1, 0 ) ); + const oter_id &east = overmap_buffer.ter( abs_omt + point( 1, 0 ) ); const bool bridge_at_center = is_ot_match( "bridge", center, ot_match_type::type ); const bool bridge_at_north = is_ot_match( "bridge", north, ot_match_type::type ); @@ -1428,7 +1418,7 @@ static void mx_anomaly( map &m, const tripoint &abs_sub ) { tripoint center( rng( 6, SEEX * 2 - 7 ), rng( 6, SEEY * 2 - 7 ), abs_sub.z ); artifact_natural_property prop = - artifact_natural_property( rng( ARTPROP_NULL + 1, ARTPROP_MAX - 1 ) ); + static_cast( rng( ARTPROP_NULL + 1, ARTPROP_MAX - 1 ) ); m.create_anomaly( center, prop ); m.spawn_natural_artifact( center, prop ); } @@ -1909,10 +1899,11 @@ static void mx_roadworks( map &m, const tripoint &abs_sub ) // equipment in a box // (curved roads & intersections excluded, perhaps TODO) - const oter_id &north = overmap_buffer.ter( abs_sub.x, abs_sub.y - 1, abs_sub.z ); - const oter_id &south = overmap_buffer.ter( abs_sub.x, abs_sub.y + 1, abs_sub.z ); - const oter_id &west = overmap_buffer.ter( abs_sub.x - 1, abs_sub.y, abs_sub.z ); - const oter_id &east = overmap_buffer.ter( abs_sub.x + 1, abs_sub.y, abs_sub.z ); + const tripoint abs_omt = sm_to_omt_copy( abs_sub ); + const oter_id &north = overmap_buffer.ter( abs_omt + point( 0, -1 ) ); + const oter_id &south = overmap_buffer.ter( abs_omt + point( 0, 1 ) ); + const oter_id &west = overmap_buffer.ter( abs_omt + point( -1, 0 ) ); + const oter_id &east = overmap_buffer.ter( abs_omt + point( 1, 0 ) ); const bool road_at_north = is_ot_match( "road", north, ot_match_type::type ); const bool road_at_south = is_ot_match( "road", south, ot_match_type::type ); @@ -2313,8 +2304,8 @@ static void mx_casings( map &m, const tripoint &abs_sub ) //Spawn blood and bloody rag and sometimes trail of blood if( one_in( 2 ) ) { m.add_field( location, fd_blood, rng( 1, 3 ) ); - const tripoint bloody_rag_loc = random_entry( m.points_in_radius( location, 3 ) ); if( one_in( 2 ) ) { + const tripoint bloody_rag_loc = random_entry( m.points_in_radius( location, 3 ) ); m.spawn_item( bloody_rag_loc, "rag_bloody" ); } if( one_in( 2 ) ) { @@ -2343,11 +2334,11 @@ static void mx_casings( map &m, const tripoint &abs_sub ) m.spawn_items( trash_loc, trash ); } //Spawn blood and bloody rag in random place - const tripoint random_place = random_entry( m.points_in_radius( location, rng( 1, 10 ) ) ); if( one_in( 2 ) ) { + const tripoint random_place = random_entry( m.points_in_radius( location, rng( 1, 10 ) ) ); m.add_field( random_place, fd_blood, rng( 1, 3 ) ); - const tripoint bloody_rag_loc = random_entry( m.points_in_radius( random_place, 3 ) ); if( one_in( 2 ) ) { + const tripoint bloody_rag_loc = random_entry( m.points_in_radius( random_place, 3 ) ); m.spawn_item( bloody_rag_loc, "rag_bloody" ); } } @@ -2376,8 +2367,8 @@ static void mx_casings( map &m, const tripoint &abs_sub ) //Spawn blood and bloody rag at the destination if( one_in( 2 ) ) { m.add_field( from, fd_blood, rng( 1, 3 ) ); - const tripoint bloody_rag_loc = random_entry( m.points_in_radius( to, 3 ) ); if( one_in( 2 ) ) { + const tripoint bloody_rag_loc = random_entry( m.points_in_radius( to, 3 ) ); m.spawn_item( bloody_rag_loc, "rag_bloody" ); } } @@ -2409,8 +2400,8 @@ static void mx_casings( map &m, const tripoint &abs_sub ) //Spawn blood and bloody rag at the first location, sometimes trail of blood if( one_in( 2 ) ) { m.add_field( first_loc, fd_blood, rng( 1, 3 ) ); - const tripoint bloody_rag_loc = random_entry( m.points_in_radius( first_loc, 3 ) ); if( one_in( 2 ) ) { + const tripoint bloody_rag_loc = random_entry( m.points_in_radius( first_loc, 3 ) ); m.spawn_item( bloody_rag_loc, "rag_bloody" ); } if( one_in( 2 ) ) { @@ -2421,8 +2412,8 @@ static void mx_casings( map &m, const tripoint &abs_sub ) //Spawn blood and bloody rag at the second location, sometimes trail of blood if( one_in( 2 ) ) { m.add_field( second_loc, fd_blood, rng( 1, 3 ) ); - const tripoint bloody_rag_loc = random_entry( m.points_in_radius( second_loc, 3 ) ); if( one_in( 2 ) ) { + const tripoint bloody_rag_loc = random_entry( m.points_in_radius( second_loc, 3 ) ); m.spawn_item( bloody_rag_loc, "rag_bloody" ); } if( one_in( 2 ) ) { diff --git a/src/map_field.cpp b/src/map_field.cpp index de973c76b42e7..9fef4bbf5044f 100644 --- a/src/map_field.cpp +++ b/src/map_field.cpp @@ -83,9 +83,9 @@ void map::create_burnproducts( const tripoint &p, const item &fuel, const units: if( all_mats.empty() ) { return; } - //Items that are multiple materials are assumed to be equal parts each. - units::mass by_weight = burned_mass / all_mats.size(); - for( auto &mat : all_mats ) { + // Items that are multiple materials are assumed to be equal parts each. + const units::mass by_weight = burned_mass / all_mats.size(); + for( material_id &mat : all_mats ) { for( auto &bp : mat->burn_products() ) { itype_id id = bp.first; // Spawning the same item as the one that was just burned is pointless @@ -93,8 +93,8 @@ void map::create_burnproducts( const tripoint &p, const item &fuel, const units: if( fuel.typeId() == id ) { continue; } - float eff = bp.second; - int n = floor( eff * ( by_weight / item::find_type( id )->weight ) ); + const float eff = bp.second; + const int n = floor( eff * ( by_weight / item::find_type( id )->weight ) ); if( n <= 0 ) { continue; @@ -112,7 +112,7 @@ int map::burn_body_part( player &u, field_entry &cur, body_part bp, const int sc const int damage = rng( 1, scale + intensity ); // A bit ugly, but better than being annoyed by acid when in hazmat if( u.get_armor_type( DT_ACID, bp ) < damage ) { - auto ddi = u.deal_damage( nullptr, bp, damage_instance( DT_ACID, damage ) ); + const dealt_damage_instance ddi = u.deal_damage( nullptr, bp, damage_instance( DT_ACID, damage ) ); total_damage += ddi.total_damage(); } // Represents acid seeping in rather than being splashed on @@ -131,7 +131,7 @@ bool map::process_fields() auto &field_cache = get_cache( z ).field_cache; for( int x = 0; x < my_MAPSIZE; x++ ) { for( int y = 0; y < my_MAPSIZE; y++ ) { - if( field_cache[ x + ( y * MAPSIZE ) ] ) { + if( field_cache[ x + y * MAPSIZE ] ) { submap *const current_submap = get_submap_at_grid( { x, y, z } ); const bool cur_dirty = process_fields_in_submap( current_submap, x, y, z ); zlev_dirty |= cur_dirty; @@ -207,8 +207,8 @@ std::array map::get_neighbors( const tripoint &p ) bool map::gas_can_spread_to( field_entry &cur, const maptile &dst ) { const field_entry *tmpfld = dst.get_field().find_field( cur.get_field_type() ); - const auto &ter = dst.get_ter_t(); - const auto &frn = dst.get_furn_t(); + const ter_t &ter = dst.get_ter_t(); + const furn_t &frn = dst.get_furn_t(); // Candidates are existing weaker fields or navigable/flagged tiles with no field. return ( ter_furn_movecost( ter, frn ) > 0 || ter_furn_has_flag( ter, frn, TFLAG_PERMEABLE ) ) && ( tmpfld == nullptr || tmpfld->get_field_intensity() < cur.get_field_intensity() ); @@ -216,9 +216,9 @@ bool map::gas_can_spread_to( field_entry &cur, const maptile &dst ) void map::gas_spread_to( field_entry &cur, maptile &dst ) { - auto current_type = cur.get_field_type(); - auto current_age = cur.get_field_age(); - auto current_intensity = cur.get_field_intensity(); + const field_type_id current_type = cur.get_field_type(); + const time_duration current_age = cur.get_field_age(); + const int current_intensity = cur.get_field_intensity(); field_entry *candidate_field = dst.find_field( current_type ); // Nearby gas grows thicker, and ages are shared. const time_duration age_fraction = current_age / current_intensity ; @@ -239,32 +239,31 @@ void map::spread_gas( field_entry &cur, const tripoint &p, int percent_spread, const time_duration &outdoor_age_speedup, scent_block &sblk ) { const oter_id &cur_om_ter = overmap_buffer.ter( ms_to_omt_copy( g->m.getabs( p ) ) ); - bool sheltered = g->is_sheltered( p ); - int winddirection = g->weather.winddirection; - int windpower = get_local_windpower( g->weather.windspeed, cur_om_ter, p, winddirection, - sheltered ); + const bool sheltered = g->is_sheltered( p ); + const int winddirection = g->weather.winddirection; + const int windpower = get_local_windpower( g->weather.windspeed, cur_om_ter, p, winddirection, + sheltered ); // Reset nearby scents to zero for( const tripoint &tmp : points_in_radius( p, 1 ) ) { sblk.apply_gas( tmp ); } const int current_intensity = cur.get_field_intensity(); - const time_duration current_age = cur.get_field_age(); // Dissipate faster outdoors. if( is_outside( p ) ) { + const time_duration current_age = cur.get_field_age(); cur.set_field_age( current_age + outdoor_age_speedup ); } // Bail out if we don't meet the spread chance or required intensity. - if( current_intensity <= 1 || rng( 1, ( 100 - windpower ) ) > percent_spread ) { + if( current_intensity <= 1 || rng( 1, 100 - windpower ) > percent_spread ) { return; } - // First check if we can fall // TODO: Make fall and rise chances parameters to enable heavy/light gas if( zlevels && p.z > -OVERMAP_DEPTH ) { - tripoint down{p.x, p.y, p.z - 1}; + const tripoint down{ p.x, p.y, p.z - 1 }; maptile down_tile = maptile_at_internal( down ); if( gas_can_spread_to( cur, down_tile ) && valid_move( p, down, true, true ) ) { gas_spread_to( cur, down_tile ); @@ -290,10 +289,10 @@ void map::spread_gas( field_entry &cur, const tripoint &p, int percent_spread, } } auto maptiles = get_wind_blockers( winddirection, p ); - maptile remove_tile = std::get<0>( maptiles ); - maptile remove_tile2 = std::get<1>( maptiles ); - maptile remove_tile3 = std::get<2>( maptiles ); - // three map tiles that are facing th wind direction. + // Three map tiles that are facing the wind direction. + const maptile remove_tile = std::get<0>( maptiles ); + const maptile remove_tile2 = std::get<1>( maptiles ); + const maptile remove_tile3 = std::get<2>( maptiles ); if( !zlevels || one_in( spread.size() ) ) { // Construct the destination from offset and p if( g->is_sheltered( p ) || windpower < 5 ) { @@ -318,7 +317,7 @@ void map::spread_gas( field_entry &cur, const tripoint &p, int percent_spread, } } } else if( zlevels && p.z < OVERMAP_HEIGHT ) { - tripoint up{p.x, p.y, p.z + 1}; + const tripoint up{ p.x, p.y, p.z + 1 }; maptile up_tile = maptile_at_internal( up ); if( gas_can_spread_to( cur, up_tile ) && valid_move( p, up, true, true ) ) { gas_spread_to( cur, up_tile ); @@ -371,7 +370,7 @@ bool map::process_fields_in_submap( submap *const current_submap, // More correctly: not just when the field is opaque, but when it changes state // to a more/less transparent one, or creates a non-transparent field nearby bool dirty_transparency_cache = false; - //Holds m.field_at(x,y).find_field(fd_some_field) type returns. + // Holds m.field_at(x,y).find_field(fd_some_field) type returns. // Just to avoid typing that long string for a temp value. field_entry *tmpfld = nullptr; @@ -382,7 +381,7 @@ bool map::process_fields_in_submap( submap *const current_submap, maptile map_tile( current_submap, 0, 0 ); size_t &locx = map_tile.x; size_t &locy = map_tile.y; - //Loop through all tiles in this submap indicated by current_submap + // Loop through all tiles in this submap indicated by current_submap for( locx = 0; locx < SEEX; locx++ ) { for( locy = 0; locy < SEEY; locy++ ) { // This is a translation from local coordinates to submap coordinates. @@ -395,7 +394,7 @@ bool map::process_fields_in_submap( submap *const current_submap, // contains all the pointers to the real field effects. field &curfield = current_submap->fld[locx][locy]; for( auto it = curfield.begin(); it != curfield.end(); ) { - //Iterating through all field effects in the submap's field. + // Iterating through all field effects in the submap's field. field_entry &cur = it->second; // The field might have been killed by processing a neighbor field if( !cur.is_field_alive() ) { @@ -407,7 +406,7 @@ bool map::process_fields_in_submap( submap *const current_submap, continue; } - //Holds cur.get_field_type() as that is what the old system used before rewrite. + // Holds cur.get_field_type() as that is what the old system used before rewrite. field_type_id curtype = cur.get_field_type(); // Again, legacy support in the event someone Mods set_field_intensity to allow more values. if( cur.get_field_intensity() > 3 || cur.get_field_intensity() < 1 ) { @@ -420,25 +419,12 @@ bool map::process_fields_in_submap( submap *const current_submap, } int part; - if( curtype == fd_blood || - curtype == fd_blood_veggy || - curtype == fd_blood_insect || - curtype == fd_blood_invertebrate || - curtype == fd_bile || - curtype == fd_gibs_flesh || - curtype == fd_gibs_veggy || - curtype == fd_gibs_insect || - curtype == fd_gibs_invertebrate ) { - // Dissipate faster in water - if( map_tile.get_ter_t().has_flag( TFLAG_SWIMMABLE ) ) { - cur.set_field_age( cur.get_field_age() + 25_minutes ); - } + const ter_t &ter = map_tile.get_ter_t(); + // Dissipate faster in water + if( ter.has_flag( TFLAG_SWIMMABLE ) ) { + cur.mod_field_age( cur.get_underwater_age_speedup() ); } if( curtype == fd_acid ) { - const auto &ter = map_tile.get_ter_t(); - if( ter.has_flag( TFLAG_SWIMMABLE ) ) { // Dissipate faster in water - cur.set_field_age( cur.get_field_age() + 2_minutes ); - } // Try to fall by a z-level if( !zlevels || p.z <= -OVERMAP_DEPTH ) { break; @@ -482,8 +468,8 @@ bool map::process_fields_in_submap( submap *const current_submap, int winddirection = g->weather.winddirection; int windpower = get_local_windpower( g->weather.windspeed, cur_om_ter, p, winddirection, sheltered ); - const auto &ter = map_tile.get_ter_t(); - const auto &frn = map_tile.get_furn_t(); + const ter_t &ter = map_tile.get_ter_t(); + const furn_t &frn = map_tile.get_furn_t(); // We've got ter/furn cached, so let's use that const bool is_sealed = ter_furn_has_flag( ter, frn, TFLAG_SEALED ) && @@ -502,7 +488,7 @@ bool map::process_fields_in_submap( submap *const current_submap, // The huge indent below should probably be somehow moved away from here // without forcing the function to use i_at( p ) for fires without items if( !is_sealed && map_tile.get_item_count() > 0 ) { - auto items_here = i_at( p ); + map_stack items_here = i_at( p ); std::vector new_content; for( auto explosive = items_here.begin(); explosive != items_here.end(); ) { if( explosive->will_explode_in_fire() ) { @@ -556,11 +542,11 @@ bool map::process_fields_in_submap( submap *const current_submap, time_added = 1_turns * roll_remainder( frd.fuel_produced ); } - //Get the part of the vehicle in the fire. - vehicle *veh = veh_at_internal( p, part ); // _internal skips the boundary check + // Get the part of the vehicle in the fire (_internal skips the boundary check) + vehicle *veh = veh_at_internal( p, part ); if( veh != nullptr ) { veh->damage( part, cur.get_field_intensity() * 10, DT_HEAT, true ); - //Damage the vehicle in the fire. + // Damage the vehicle in the fire. } if( can_burn ) { if( ter.has_flag( TFLAG_SWIMMABLE ) ) { @@ -598,7 +584,7 @@ bool map::process_fields_in_submap( submap *const current_submap, if( cur.get_field_intensity() > 1 && one_in( 200 - cur.get_field_intensity() * 50 ) ) { if( p.z > 0 ) { - // we're in the air + // We're in the air ter_set( p, t_open_air ); } else { ter_set( p, t_dirt ); @@ -659,7 +645,7 @@ bool map::process_fields_in_submap( submap *const current_submap, // Below we will access our nearest 8 neighbors, so let's cache them now // This should probably be done more globally, because large fires will re-do it a lot auto neighs = get_neighbors( p ); - // get the neighbours that are allowed due to wind direction + // Get the neighbours that are allowed due to wind direction auto maptiles = get_wind_blockers( winddirection, p ); maptile remove_tile = std::get<0>( maptiles ); maptile remove_tile2 = std::get<1>( maptiles ); @@ -721,7 +707,7 @@ bool map::process_fields_in_submap( submap *const current_submap, count != neighbour_vec.size() && cur.get_field_age() < 0_turns; i = ( i + 1 ) % neighbour_vec.size(), count++ ) { maptile &dst = neighbour_vec[i]; - auto dstfld = dst.find_field( fd_fire ); + field_entry *dstfld = dst.find_field( fd_fire ); // If the fire exists and is weaker than ours, boost it if( dstfld != nullptr && ( dstfld->get_field_intensity() <= cur.get_field_intensity() || @@ -827,8 +813,8 @@ bool map::process_fields_in_submap( submap *const current_submap, spread_chance = 50 + spread_chance / 2; } - const auto &dster = dst.get_ter_t(); - const auto &dsfrn = dst.get_furn_t(); + const ter_t &dster = dst.get_ter_t(); + const furn_t &dsfrn = dst.get_furn_t(); // Allow weaker fires to spread occasionally const int power = cur.get_field_intensity() + one_in( 5 ); if( can_spread && rng( 1, 100 ) < spread_chance && @@ -843,7 +829,8 @@ bool map::process_fields_in_submap( submap *const current_submap, flammable_items_at( p + eight_horizontal_neighbors[i] ) && one_in( 5 ) ) ) ) { - dst.add_field( fd_fire, 1, 0_turns ); // Nearby open flammable ground? Set it on fire. + // Nearby open flammable ground? Set it on fire. + dst.add_field( fd_fire, 1, 0_turns ); tmpfld = dst.find_field( fd_fire ); if( tmpfld != nullptr ) { // Make the new fire quite weak, so that it doesn't start jumping around instantly @@ -888,11 +875,11 @@ bool map::process_fields_in_submap( submap *const current_submap, spread_chance = 50 + spread_chance / 2; } - const auto &dster = dst.get_ter_t(); - const auto &dsfrn = dst.get_furn_t(); + const ter_t &dster = dst.get_ter_t(); + const furn_t &dsfrn = dst.get_furn_t(); // Allow weaker fires to spread occasionally const int power = cur.get_field_intensity() + one_in( 5 ); - if( can_spread && rng( 1, ( 100 - windpower ) ) < spread_chance && + if( can_spread && rng( 1, 100 - windpower ) < spread_chance && ( dster.is_flammable() || dsfrn.is_flammable() ) && ( in_pit == ( dster.id.id() == t_pit ) ) && ( @@ -904,7 +891,8 @@ bool map::process_fields_in_submap( submap *const current_submap, flammable_items_at( p + eight_horizontal_neighbors[i] ) && one_in( 5 ) ) ) ) { - dst.add_field( fd_fire, 1, 0_turns ); // Nearby open flammable ground? Set it on fire. + // Nearby open flammable ground? Set it on fire. + dst.add_field( fd_fire, 1, 0_turns ); tmpfld = dst.find_field( fd_fire ); if( tmpfld != nullptr ) { // Make the new fire quite weak, so that it doesn't start jumping around instantly @@ -920,13 +908,13 @@ bool map::process_fields_in_submap( submap *const current_submap, } // Create smoke once - above us if possible, at us otherwise if( !ter_furn_has_flag( ter, frn, TFLAG_SUPPRESS_SMOKE ) && - rng( 0, ( 100 - windpower ) ) <= smoke && + rng( 0, 100 - windpower ) <= smoke && rng( 3, 35 ) < cur.get_field_intensity() * 10 ) { bool smoke_up = zlevels && p.z < OVERMAP_HEIGHT; if( smoke_up ) { tripoint up{p.x, p.y, p.z + 1}; maptile dst = maptile_at_internal( up ); - const auto &dst_ter = dst.get_ter_t(); + const ter_t &dst_ter = dst.get_ter_t(); if( dst_ter.has_flag( TFLAG_NO_FLOOR ) ) { dst.add_field( fd_smoke, rng( 1, cur.get_field_intensity() ), 0_turns ); } else { @@ -941,7 +929,8 @@ bool map::process_fields_in_submap( submap *const current_submap, dst.add_field( fd_smoke, cur.get_field_intensity(), 0_turns ); } - dirty_transparency_cache = true; // Smoke affects transparency + // Smoke affects transparency + dirty_transparency_cache = true; } // Hot air is a load on the CPU @@ -1016,8 +1005,8 @@ bool map::process_fields_in_submap( submap *const current_submap, } if( curtype == fd_cold_air1 || curtype == fd_cold_air2 || curtype == fd_cold_air3 || curtype == fd_cold_air4 || - curtype == fd_hot_air1 || curtype == fd_hot_air3 || - curtype == fd_hot_air3 || curtype == fd_cold_air3 ) { + curtype == fd_hot_air1 || curtype == fd_hot_air2 || + curtype == fd_hot_air3 || curtype == fd_hot_air4 ) { // No transparency cache wrecking here! spread_gas( cur, p, 100, 100_minutes, sblk ); } @@ -1070,9 +1059,11 @@ bool map::process_fields_in_submap( submap *const current_submap, } } if( curtype == fd_electricity ) { - if( !one_in( 5 ) ) { // 4 in 5 chance to spread + // 4 in 5 chance to spread + if( !one_in( 5 ) ) { std::vector valid; - if( impassable( p ) && cur.get_field_intensity() > 1 ) { // We're grounded + // We're grounded + if( impassable( p ) && cur.get_field_intensity() > 1 ) { int tries = 0; tripoint pnt; pnt.z = p.z; @@ -1087,13 +1078,16 @@ bool map::process_fields_in_submap( submap *const current_submap, tries++; } } - } else { // We're not grounded; attempt to ground + // We're not grounded; attempt to ground + } else { for( const tripoint &dst : points_in_radius( p, 1 ) ) { - if( impassable( dst ) ) { // Grounded tiles first + // Grounded tiles first + if( impassable( dst ) ) { valid.push_back( dst ); } } - if( valid.empty() ) { // Spread to adjacent space, then + // Spread to adjacent space, then + if( valid.empty() ) { tripoint dst( p.x + rng( -1, 1 ), p.y + rng( -1, 1 ), p.z ); field_entry *elec = get_field( dst ).find_field( fd_electricity ); if( passable( dst ) && elec != nullptr && @@ -1123,13 +1117,14 @@ bool map::process_fields_in_submap( submap *const current_submap, }; if( cur.get_field_intensity() < 3 && calendar::once_every( 6_hours ) && one_in( 10 ) ) { cur.set_field_intensity( cur.get_field_intensity() + 1 ); - } else if( cur.get_field_intensity() == 3 && one_in( 600 ) ) { // Spawn nether creature! + // Spawn nether creature! + } else if( cur.get_field_intensity() == 3 && one_in( 600 ) ) { g->summon_mon( random_entry( monids ), p ); } } if( curtype == fd_push_items ) { - auto items = i_at( p ); + map_stack items = i_at( p ); for( auto pushee = items.begin(); pushee != items.end(); ) { if( pushee->typeId() != "rock" || pushee->age() < 1_turns ) { @@ -1225,7 +1220,7 @@ bool map::process_fields_in_submap( submap *const current_submap, for( const tripoint &t : points_in_radius( p, 5 ) ) { const field_entry *acid = get_field( t, fd_acid ); if( acid != nullptr && acid->get_field_intensity() == 0 ) { - int new_intensity = 3 - ( rl_dist( p, t ) / 2 ) + ( one_in( 3 ) ? 1 : 0 ); + int new_intensity = 3 - rl_dist( p, t ) / 2 + ( one_in( 3 ) ? 1 : 0 ); if( new_intensity > 3 ) { new_intensity = 3; } @@ -1270,7 +1265,7 @@ bool map::process_fields_in_submap( submap *const current_submap, std::vector candidate_positions = squares_in_direction( p.x, p.y, g->u.posx(), g->u.posy() ); - for( auto &candidate_position : candidate_positions ) { + for( point &candidate_position : candidate_positions ) { field &target_field = get_field( tripoint( candidate_position, p.z ) ); // Only shift if there are no bees already there. @@ -1290,7 +1285,7 @@ bool map::process_fields_in_submap( submap *const current_submap, } } if( curtype == fd_incendiary ) { - //Needed for variable scope + // Needed for variable scope dirty_transparency_cache = true; tripoint dst( p.x + rng( -1, 1 ), p.y + rng( -1, 1 ), p.z ); if( has_flag( TFLAG_FLAMMABLE, dst ) || @@ -1299,7 +1294,7 @@ bool map::process_fields_in_submap( submap *const current_submap, add_field( dst, fd_fire, 1 ); } - //check piles for flammable items and set those on fire + // Check piles for flammable items and set those on fire if( flammable_items_at( dst ) ) { add_field( dst, fd_fire, 1 ); } @@ -1308,15 +1303,15 @@ bool map::process_fields_in_submap( submap *const current_submap, create_hot_air( p, cur.get_field_intensity() ); } if( curtype == fd_rubble ) { - //Legacy Stuff + // Legacy Stuff make_rubble( p ); } if( curtype == fd_fungicidal_gas ) { dirty_transparency_cache = true; spread_gas( cur, p, 120, 1_minutes, sblk ); - //check the terrain and replace it accordingly to simulate the fungus dieing off - const auto &ter = map_tile.get_ter_t(); - const auto &frn = map_tile.get_furn_t(); + // Check the terrain and replace it accordingly to simulate the fungus dieing off + const ter_t &ter = map_tile.get_ter_t(); + const furn_t &frn = map_tile.get_furn_t(); const int intensity = cur.get_field_intensity(); if( ter.has_flag( "FUNGUS" ) && one_in( 10 / intensity ) ) { ter_set( p, t_dirt ); @@ -1349,9 +1344,9 @@ bool map::process_fields_in_submap( submap *const current_submap, for( int y = std::max( submap_y - 1, 0 ); y <= std::min( submap_y + 1, MAPSIZE - 1 ); ++y ) { for( int x = std::max( submap_x - 1, 0 ); x <= std::min( submap_x + 1, MAPSIZE - 1 ); ++x ) { if( get_submap_at_grid( { x, y, z } )->field_count > 0 ) { - field_cache.set( x + ( y * MAPSIZE ) ); + field_cache.set( x + y * MAPSIZE ); } else { - field_cache.reset( x + ( y * MAPSIZE ) ); + field_cache.reset( x + y * MAPSIZE ); } } } @@ -1360,7 +1355,7 @@ bool map::process_fields_in_submap( submap *const current_submap, return dirty_transparency_cache; } -//This entire function makes very little sense. Why are the rules the way they are? Why does walking into some things destroy them but not others? +// This entire function makes very little sense. Why are the rules the way they are? Why does walking into some things destroy them but not others? /* Function: step_in_field @@ -1372,11 +1367,9 @@ void map::player_in_field( player &u ) { // A copy of the current field for reference. Do not add fields to it, use map::add_field field &curfield = get_field( u.pos() ); - bool inside = false; // Are we inside? - //to modify power of a field based on... whatever is relevant for the effect. - int adjusted_intensity; - - //If we are in a vehicle figure out if we are inside (reduces effects usually) + // Are we inside? + bool inside = false; + // If we are in a vehicle figure out if we are inside (reduces effects usually) // and what part of the vehicle we need to deal with. if( u.in_vehicle ) { if( const optional_vpart_position vp = veh_at( u.pos() ) ) { @@ -1393,18 +1386,19 @@ void map::player_in_field( player &u ) continue; } - //Do things based on what field effect we are currently in. - const auto ft = cur.get_field_type(); + // Do things based on what field effect we are currently in. + const field_type_id ft = cur.get_field_type(); if( ft == fd_web ) { - //If we are in a web, can't walk in webs or are in a vehicle, get webbed maybe. - //Moving through multiple webs stacks the effect. + // If we are in a web, can't walk in webs or are in a vehicle, get webbed maybe. + // Moving through multiple webs stacks the effect. if( !u.has_trait( trait_id( "WEB_WALKER" ) ) && !u.in_vehicle ) { - //between 5 and 15 minus your current web level. + // Between 5 and 15 minus your current web level. u.add_effect( effect_webbed, 1_turns, num_bp, true, cur.get_field_intensity() ); - cur.set_field_intensity( 0 ); //Its spent. + // It is spent. + cur.set_field_intensity( 0 ); continue; - //If you are in a vehicle destroy the web. - //It should of been destroyed when you ran over it anyway. + // If you are in a vehicle destroy the web. + // It should of been destroyed when you ran over it anyway. } else if( u.in_vehicle ) { cur.set_field_intensity( 0 ); continue; @@ -1459,18 +1453,20 @@ void map::player_in_field( player &u ) u.check_dead_state(); } if( ft == fd_sap ) { - //Sap causes the player to get sap disease, slowing them down. + // Sap causes the player to get sap disease, slowing them down. if( u.in_vehicle ) { - break; //sap does nothing to cars. + // Sap does nothing to cars. + break; } u.add_msg_player_or_npc( m_bad, _( "The sap sticks to you!" ), _( "The sap sticks to !" ) ); u.add_effect( effect_sap, cur.get_field_intensity() * 2_turns ); - cur.set_field_intensity( cur.get_field_intensity() - 1 ); //Use up sap. + // Use up sap. + cur.set_field_intensity( cur.get_field_intensity() - 1 ); } if( ft == fd_sludge ) { - //sludge is on the ground, but you are above the ground when boarded on a vehicle + // Sludge is on the ground, but you are above the ground when boarded on a vehicle if( !u.in_vehicle ) { u.add_msg_if_player( m_bad, _( "The sludge is thick and sticky. You struggle to pull free." ) ); u.moves -= cur.get_field_intensity() * 300; @@ -1479,11 +1475,12 @@ void map::player_in_field( player &u ) } if( ft == fd_fire ) { if( u.has_active_bionic( bionic_id( "bio_heatsink" ) ) || u.is_wearing( "rm13_armor_on" ) ) { - //heatsink or suit prevents ALL fire damage. + // Heatsink or suit prevents ALL fire damage. break; } - //Burn the player. Less so if you are in a car or ON a car. - adjusted_intensity = cur.get_field_intensity(); + // To modify power of a field based on... whatever is relevant for the effect. + int adjusted_intensity = cur.get_field_intensity(); + // Burn the player. Less so if you are in a car or ON a car. if( u.in_vehicle ) { if( inside ) { adjusted_intensity -= 2; @@ -1519,8 +1516,8 @@ void map::player_in_field( player &u ) } }; - int burn_min = adjusted_intensity; - int burn_max = 3 * adjusted_intensity + 3; + const int burn_min = adjusted_intensity; + const int burn_max = 3 * adjusted_intensity + 3; std::list parts_burned; int msg_num = adjusted_intensity - 1; if( !u.is_on_ground() ) { @@ -1547,9 +1544,9 @@ void map::player_in_field( player &u ) } int total_damage = 0; - for( auto part_burned : parts_burned ) { - const auto dealt = u.deal_damage( nullptr, part_burned, - damage_instance( DT_HEAT, rng( burn_min, burn_max ) ) ); + for( body_part part_burned : parts_burned ) { + const dealt_damage_instance dealt = u.deal_damage( nullptr, part_burned, + damage_instance( DT_HEAT, rng( burn_min, burn_max ) ) ); total_damage += dealt.type_damage( DT_HEAT ); } if( total_damage > 0 ) { @@ -1563,25 +1560,28 @@ void map::player_in_field( player &u ) } if( ft == fd_smoke ) { if( !inside ) { - //Get smoke disease from standing in smoke. - int intensity = cur.get_field_intensity(); - int coughStr; - time_duration coughDur = 0_turns; - if( intensity >= 3 ) { // thick smoke - coughStr = 4; - coughDur = 15_turns; - } else if( intensity == 2 ) { // smoke - coughStr = 2; - coughDur = 7_turns; - } else { // intensity 1, thin smoke - coughStr = 1; - coughDur = 2_turns; + // Get smoke disease from standing in smoke. + const int intensity = cur.get_field_intensity(); + int cough_strength; + time_duration cough_duration = 0_turns; + // Thick smoke + if( intensity >= 3 ) { + cough_strength = 4; + cough_duration = 15_turns; + // Smoke + } else if( intensity == 2 ) { + cough_strength = 2; + cough_duration = 7_turns; + // Intensity 1, thin smoke + } else { + cough_strength = 1; + cough_duration = 2_turns; } - u.add_env_effect( effect_smoke, bp_mouth, coughStr, coughDur ); + u.add_env_effect( effect_smoke, bp_mouth, cough_strength, cough_duration ); } } if( ft == fd_tear_gas ) { - //Tear gas will both give you teargas disease and/or blind you. + // Tear gas will both give you teargas disease and/or blind you. if( ( cur.get_field_intensity() > 1 || !one_in( 3 ) ) && ( !inside || one_in( 3 ) ) ) { u.add_env_effect( effect_teargas, bp_mouth, 5, 2_minutes ); } @@ -1617,11 +1617,11 @@ void map::player_in_field( player &u ) inhaled = u.add_env_effect( effect_poison, bp_mouth, 5, 3_minutes ); } else if( cur.get_field_intensity() == 3 && !inside ) { inhaled = u.add_env_effect( effect_badpoison, bp_mouth, 5, 3_minutes ); - } else if( cur.get_field_intensity() == 1 && ( !inside ) ) { + } else if( cur.get_field_intensity() == 1 && !inside ) { inhaled = u.add_env_effect( effect_poison, bp_mouth, 2, 2_minutes ); } if( inhaled ) { - // player does not know how the npc feels, so no message. + // Player does not know how the npc feels, so no message. u.add_msg_if_player( m_bad, _( "You feel sick from inhaling the %s" ), cur.name() ); } } @@ -1629,9 +1629,9 @@ void map::player_in_field( player &u ) if( ft == fd_nuke_gas ) { // Get irradiated by the nuclear fallout. // Changed to min of intensity, not 0. - float rads = rng( cur.get_field_intensity(), - cur.get_field_intensity() * ( cur.get_field_intensity() + 1 ) ); - bool rad_proof = !u.irradiate( rads ); + const float rads = rng( cur.get_field_intensity(), + cur.get_field_intensity() * ( cur.get_field_intensity() + 1 ) ); + const bool rad_proof = !u.irradiate( rads ); // TODO: Reduce damage for rad resistant? if( cur.get_field_intensity() == 3 && !rad_proof ) { u.add_msg_if_player( m_bad, _( "This radioactive gas burns!" ) ); @@ -1639,12 +1639,14 @@ void map::player_in_field( player &u ) } } if( ft == fd_flame_burst ) { - //A burst of flame? Only hits the legs and torso. + // A burst of flame? Only hits the legs and torso. if( inside ) { - break; //fireballs can't touch you inside a car. + // Fireballs can't touch you inside a car. + break; } + // Heatsink or suit stops fire. if( !u.has_active_bionic( bionic_id( "bio_heatsink" ) ) && - !u.is_wearing( "rm13_armor_on" ) ) { //heatsink or suit stops fire. + !u.is_wearing( "rm13_armor_on" ) ) { u.add_msg_player_or_npc( m_bad, _( "You're torched by flames!" ), _( " is torched by flames!" ) ); u.deal_damage( nullptr, bp_leg_l, damage_instance( DT_HEAT, rng( 2, 6 ) ) ); @@ -1682,7 +1684,7 @@ void map::player_in_field( player &u ) } } if( ft == fd_fatigue ) { - //Teleports you... somewhere. + // Teleports you... somewhere. if( rng( 0, 2 ) < cur.get_field_intensity() && u.is_player() ) { // TODO: allow teleporting for npcs add_msg( m_bad, _( "You're violently teleported!" ) ); @@ -1699,7 +1701,7 @@ void map::player_in_field( player &u ) // Player is immune to bees while underwater. if( !u.is_underwater() ) { int times_stung = 0; - int intensity = cur.get_field_intensity(); + const int intensity = cur.get_field_intensity(); // If the bees can get at you, they cause steadily increasing pain. // TODO: Specific stinging messages. times_stung += one_in( 4 ) && @@ -1757,29 +1759,27 @@ void map::player_in_field( player &u ) u.hurtall( rng( 2, 6 ), nullptr ); } } - if( ft == fd_fungicidal_gas ) { // Fungicidal gas is unhealthy and becomes deadly if you cross a related threshold. - { - // The gas won't harm you inside a vehicle. - if( inside ) { - break; - } - // Full body suits protect you from the effects of the gas. - if( u.worn_with_flag( "GAS_PROOF" ) && u.get_env_resist( bp_mouth ) >= 15 && - u.get_env_resist( bp_eyes ) >= 15 ) { - break; - } - bool inhaled = false; - const int intensity = cur.get_field_intensity(); - inhaled = u.add_env_effect( effect_poison, bp_mouth, 5, intensity * 1_minutes ); - if( u.has_trait( trait_id( "THRESH_MYCUS" ) ) || u.has_trait( trait_id( "THRESH_MARLOSS" ) ) ) { - inhaled |= u.add_env_effect( effect_badpoison, bp_mouth, 5, intensity * 1_minutes ); - u.hurtall( rng( intensity, intensity * 2 ), nullptr ); - u.add_msg_if_player( m_bad, _( "The %s burns your skin." ), cur.name() ); - } + // Fungicidal gas is unhealthy and becomes deadly if you cross a related threshold. + if( ft == fd_fungicidal_gas ) { + // The gas won't harm you inside a vehicle. + if( inside ) { + break; + } + // Full body suits protect you from the effects of the gas. + if( u.worn_with_flag( "GAS_PROOF" ) && u.get_env_resist( bp_mouth ) >= 15 && + u.get_env_resist( bp_eyes ) >= 15 ) { + break; + } + const int intensity = cur.get_field_intensity(); + bool inhaled = u.add_env_effect( effect_poison, bp_mouth, 5, intensity * 1_minutes ); + if( u.has_trait( trait_id( "THRESH_MYCUS" ) ) || u.has_trait( trait_id( "THRESH_MARLOSS" ) ) ) { + inhaled |= u.add_env_effect( effect_badpoison, bp_mouth, 5, intensity * 1_minutes ); + u.hurtall( rng( intensity, intensity * 2 ), nullptr ); + u.add_msg_if_player( m_bad, _( "The %s burns your skin." ), cur.name() ); + } - if( inhaled ) { - u.add_msg_if_player( m_bad, _( "The %s makes you feel sick." ), cur.name() ); - } + if( inhaled ) { + u.add_msg_if_player( m_bad, _( "The %s makes you feel sick." ), cur.name() ); } } } @@ -1797,7 +1797,8 @@ void map::creature_in_field( Creature &critter ) void map::monster_in_field( monster &z ) { if( z.digging() ) { - return; // Digging monsters are immune to fields + // Digging monsters are immune to fields + return; } field &curfield = get_field( z.pos() ); @@ -1810,7 +1811,7 @@ void map::monster_in_field( monster &z ) if( !cur.is_field_alive() ) { continue; } - const auto cur_field_type = cur.get_field_type(); + const field_type_id cur_field_type = cur.get_field_type(); if( cur_field_type == fd_web ) { if( !z.has_flag( MF_WEBWALK ) ) { z.add_effect( effect_webbed, 1_turns, num_bp, true, cur.get_field_intensity() ); @@ -1884,7 +1885,8 @@ void map::monster_in_field( monster &z ) if( cur.get_field_intensity() == 3 ) { z.moves -= rng( 10, 20 ); } - if( z.made_of( material_id( "veggy" ) ) ) { // Plants suffer from smoke even worse + // Plants suffer from smoke even worse + if( z.made_of( material_id( "veggy" ) ) ) { z.moves -= rng( 1, cur.get_field_intensity() * 12 ); } } @@ -2101,9 +2103,9 @@ void map::emit_field( const tripoint &pos, const emit_id &src, float mul ) return; } - float chance = src->chance() * mul; + const float chance = src->chance() * mul; if( src.is_valid() && x_in_y( chance, 100 ) ) { - int qty = chance > 100.0f ? roll_remainder( src->qty() * chance / 100.0f ) : src->qty(); + const int qty = chance > 100.0f ? roll_remainder( src->qty() * chance / 100.0f ) : src->qty(); propagate_field( pos, src->field(), qty, src->intensity() ); } } @@ -2127,7 +2129,7 @@ void map::propagate_field( const tripoint ¢er, const field_type_id type, int // All points with equal gas intensity should propagate at the same time std::list gas_front; gas_front.push_back( open.top() ); - int cur_intensity = get_field_intensity( open.top().second, type ); + const int cur_intensity = get_field_intensity( open.top().second, type ); open.pop(); while( !open.empty() && get_field_intensity( open.top().second, type ) == cur_intensity ) { if( closed.count( open.top().second ) == 0 ) { @@ -2140,11 +2142,11 @@ void map::propagate_field( const tripoint ¢er, const field_type_id type, int int increment = std::max( 1, amount / gas_front.size() ); while( amount > 0 && !gas_front.empty() ) { - auto gp = random_entry_removed( gas_front ); + gas_blast gp = random_entry_removed( gas_front ); closed.insert( gp.second ); - int cur_intensity = get_field_intensity( gp.second, type ); + const int cur_intensity = get_field_intensity( gp.second, type ); if( cur_intensity < max_intensity ) { - int bonus = std::min( max_intensity - cur_intensity, increment ); + const int bonus = std::min( max_intensity - cur_intensity, increment ); mod_field_intensity( gp.second, type, bonus ); amount -= bonus; } else { diff --git a/src/mapgen.cpp b/src/mapgen.cpp index 8207fb3f89e78..6c64ee072de39 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -120,28 +120,26 @@ void map::generate( const int x, const int y, const int z, const time_point &whe } } // x, and y are submap coordinates, convert to overmap terrain coordinates - int overx = x; - int overy = y; - sm_to_omt( overx, overy ); - const regional_settings *rsettings = &overmap_buffer.get_settings( overx, overy, z ); - oter_id terrain_type = overmap_buffer.ter( overx, overy, z ); - oter_id t_above = overmap_buffer.ter( overx, overy, z + 1 ); - oter_id t_below = overmap_buffer.ter( overx, overy, z - 1 ); - oter_id t_north = overmap_buffer.ter( overx, overy - 1, z ); - oter_id t_neast = overmap_buffer.ter( overx + 1, overy - 1, z ); - oter_id t_east = overmap_buffer.ter( overx + 1, overy, z ); - oter_id t_seast = overmap_buffer.ter( overx + 1, overy + 1, z ); - oter_id t_south = overmap_buffer.ter( overx, overy + 1, z ); - oter_id t_swest = overmap_buffer.ter( overx - 1, overy + 1, z ); - oter_id t_west = overmap_buffer.ter( overx - 1, overy, z ); - oter_id t_nwest = overmap_buffer.ter( overx - 1, overy - 1, z ); + tripoint abs_omt = sm_to_omt_copy( tripoint( x, y, z ) ); + const regional_settings *rsettings = &overmap_buffer.get_settings( abs_omt ); + oter_id terrain_type = overmap_buffer.ter( abs_omt ); + oter_id t_above = overmap_buffer.ter( abs_omt + tripoint( 0, 0, 1 ) ); + oter_id t_below = overmap_buffer.ter( abs_omt + tripoint( 0, 0, -1 ) ); + oter_id t_north = overmap_buffer.ter( abs_omt + tripoint( 0, -1, 0 ) ); + oter_id t_neast = overmap_buffer.ter( abs_omt + tripoint( 1, -1, 0 ) ); + oter_id t_east = overmap_buffer.ter( abs_omt + tripoint( 1, 0, 0 ) ); + oter_id t_seast = overmap_buffer.ter( abs_omt + tripoint( 1, 1, 0 ) ); + oter_id t_south = overmap_buffer.ter( abs_omt + tripoint( 0, 1, 0 ) ); + oter_id t_swest = overmap_buffer.ter( abs_omt + tripoint( -1, 1, 0 ) ); + oter_id t_west = overmap_buffer.ter( abs_omt + tripoint( -1, 0, 0 ) ); + oter_id t_nwest = overmap_buffer.ter( abs_omt + tripoint( -1, -1, 0 ) ); // This attempts to scale density of zombies inversely with distance from the nearest city. // In other words, make city centers dense and perimeters sparse. float density = 0.0; - for( int i = overx - MON_RADIUS; i <= overx + MON_RADIUS; i++ ) { - for( int j = overy - MON_RADIUS; j <= overy + MON_RADIUS; j++ ) { - density += overmap_buffer.ter( i, j, z )->get_mondensity(); + for( int i = -MON_RADIUS; i <= MON_RADIUS; i++ ) { + for( int j = -MON_RADIUS; j <= MON_RADIUS; j++ ) { + density += overmap_buffer.ter( abs_omt + point( i, j ) )->get_mondensity(); } } density = density / 100; @@ -690,7 +688,7 @@ void mapgen_function_json_base::setup_setmap( JsonArray &parray ) pjo.read( "fuel", tmp_fuel ); pjo.read( "status", tmp_status ); jmapgen_setmap tmp( tmp_x, tmp_y, tmp_x2, tmp_y2, - jmapgen_setmap_op( tmpop + setmap_optype ), tmp_i, + static_cast( tmpop + setmap_optype ), tmp_i, tmp_chance, tmp_repeat, tmp_rotation, tmp_fuel, tmp_status ); setmap_points.push_back( tmp ); @@ -4171,12 +4169,10 @@ void map::draw_lab( const oter_id &terrain_type, mapgendata &dat, const time_poi furn_set( SEEX - 1, SEEY, f_table ); furn_set( SEEX, SEEY, f_table ); if( loot_variant <= 67 ) { - spawn_item( SEEX - 1, SEEY - 1, "laser_pack", dice( 4, 3 ) ); spawn_item( SEEX, SEEY - 1, "UPS_off" ); spawn_item( SEEX, SEEY - 1, "battery", dice( 4, 3 ) ); spawn_item( SEEX - 1, SEEY, "v29" ); spawn_item( SEEX - 1, SEEY, "laser_rifle", dice( 1, 0 ) ); - spawn_item( SEEX, SEEY, "ftk93" ); spawn_item( SEEX - 1, SEEY, "recipe_atomic_battery" ); spawn_item( SEEX, SEEY - 1, "solar_panel_v3" ); } else if( loot_variant > 67 && loot_variant < 89 ) { @@ -4967,7 +4963,7 @@ void map::draw_mine( const oter_id &terrain_type, mapgendata &dat, const time_po } } if( okay ) { - room_type type = room_type( rng( room_mine_office, room_mine_housing ) ); + room_type type = static_cast( rng( room_mine_office, room_mine_housing ) ); build_mine_room( this, type, x1, y1, x2, y2, dat ); tries = 0; } else { @@ -6846,8 +6842,8 @@ void map::place_spawns( const mongroup_id &group, const int chance, const bool individual, const bool friendly ) { if( !group.is_valid() ) { - const point omt = sm_to_omt_copy( get_abs_sub().x, get_abs_sub().y ); - const oter_id &oid = overmap_buffer.ter( omt.x, omt.y, get_abs_sub().z ); + const tripoint omt = sm_to_omt_copy( get_abs_sub() ); + const oter_id &oid = overmap_buffer.ter( omt ); debugmsg( "place_spawns: invalid mongroup '%s', om_terrain = '%s' (%s)", group.c_str(), oid.id().c_str(), oid->get_mapgen_id().c_str() ); return; @@ -6960,7 +6956,6 @@ void map::apply_faction_ownership( const int x1, const int y1, const int x2, con vehicle *source_veh = veh_pointer_or_null( veh_at( p ) ); if( source_veh ) { if( !source_veh->has_owner() ) { - source_veh->base_name = source_veh->name; source_veh->set_owner( fac ); } } @@ -6989,8 +6984,8 @@ std::vector map::place_items( const items_location &loc, int chance, int return res; } if( !item_group::group_is_defined( loc ) ) { - const point omt = sm_to_omt_copy( get_abs_sub().x, get_abs_sub().y ); - const oter_id &oid = overmap_buffer.ter( omt.x, omt.y, get_abs_sub().z ); + const tripoint omt = sm_to_omt_copy( get_abs_sub() ); + const oter_id &oid = overmap_buffer.ter( omt ); debugmsg( "place_items: invalid item group '%s', om_terrain = '%s' (%s)", loc.c_str(), oid.id().c_str(), oid->get_mapgen_id().c_str() ); return res; @@ -7283,8 +7278,7 @@ void map::rotate( int turns ) // TODO: This radius can be smaller - how small? const int radius = HALF_MAPSIZE + 3; // uses submap coordinates - const std::vector> npcs = overmap_buffer.get_npcs_near( abs_sub.x, abs_sub.y, - abs_sub.z, radius ); + const std::vector> npcs = overmap_buffer.get_npcs_near( abs_sub, radius ); for( const std::shared_ptr &i : npcs ) { npc &np = *i; const tripoint sq = np.global_square_location(); @@ -8262,13 +8256,13 @@ void map::create_anomaly( const tripoint &cp, artifact_natural_property prop, bo case ARTPROP_FRACTAL: create_anomaly( cx - 4, cy - 4, - artifact_natural_property( rng( ARTPROP_NULL + 1, ARTPROP_MAX - 1 ) ) ); + static_cast( rng( ARTPROP_NULL + 1, ARTPROP_MAX - 1 ) ) ); create_anomaly( cx + 4, cy - 4, - artifact_natural_property( rng( ARTPROP_NULL + 1, ARTPROP_MAX - 1 ) ) ); + static_cast( rng( ARTPROP_NULL + 1, ARTPROP_MAX - 1 ) ) ); create_anomaly( cx - 4, cy + 4, - artifact_natural_property( rng( ARTPROP_NULL + 1, ARTPROP_MAX - 1 ) ) ); + static_cast( rng( ARTPROP_NULL + 1, ARTPROP_MAX - 1 ) ) ); create_anomaly( cx + 4, cy - 4, - artifact_natural_property( rng( ARTPROP_NULL + 1, ARTPROP_MAX - 1 ) ) ); + static_cast( rng( ARTPROP_NULL + 1, ARTPROP_MAX - 1 ) ) ); break; default: break; @@ -8360,9 +8354,9 @@ bool update_mapgen_function_json::update_map( const tripoint &omt_pos, int offse mission *miss, bool verify ) const { tinymap update_tmap; - const regional_settings &rsettings = overmap_buffer.get_settings( omt_pos.x, omt_pos.y, - omt_pos.z ); - update_tmap.load( omt_pos.x * 2, omt_pos.y * 2, omt_pos.z, false ); + const regional_settings &rsettings = overmap_buffer.get_settings( omt_pos ); + const tripoint sm_pos = omt_to_sm_copy( omt_pos ); + update_tmap.load( sm_pos, false ); const std::string map_id = overmap_buffer.ter( omt_pos ).id().c_str(); oter_id north = overmap_buffer.ter( omt_pos + tripoint( 0, -1, 0 ) ); oter_id south = overmap_buffer.ter( omt_pos + tripoint( 0, 1, 0 ) ); diff --git a/src/mapgen_functions.cpp b/src/mapgen_functions.cpp index d8cb7d34d415f..0703939408993 100644 --- a/src/mapgen_functions.cpp +++ b/src/mapgen_functions.cpp @@ -4282,10 +4282,10 @@ void mapgen_lake_shore( map *m, oter_id, mapgendata dat, const time_point &turn, const int sector_length = SEEX * 2 / 3; // Define the corners of the map. These won't change. - const point nw_corner( 0, 0 ); - const point ne_corner( SEEX * 2 - 1, 0 ); - const point se_corner( SEEX * 2 - 1, SEEY * 2 - 1 ); - const point sw_corner( 0, SEEY * 2 - 1 ); + static constexpr point nw_corner( 0, 0 ); + static constexpr point ne_corner( SEEX * 2 - 1, 0 ); + static constexpr point se_corner( SEEX * 2 - 1, SEEY * 2 - 1 ); + static constexpr point sw_corner( 0, SEEY * 2 - 1 ); // Define the four points that make up our polygon that we'll later pull line segments from for // the actual shoreline. @@ -4443,7 +4443,7 @@ void mapgen_lake_shore( map *m, oter_id, mapgendata dat, const time_point &turn, line_segments.push_back( { sw, nw } ); } - const rectangle map_boundaries( nw_corner, se_corner ); + static constexpr rectangle map_boundaries( nw_corner, se_corner ); // This will draw our shallow water coastline from the "from" point to the "to" point. // It buffers the points a bit for a thicker line. It also clears any furniture that might @@ -4453,7 +4453,7 @@ void mapgen_lake_shore( map *m, oter_id, mapgendata dat, const time_point &turn, for( auto &p : points ) { std::vector buffered_points = closest_points_first( 1, p.x, p.y ); for( const point &bp : buffered_points ) { - if( !generic_inbounds( bp, map_boundaries ) ) { + if( !map_boundaries.contains_inclusive( bp ) ) { continue; } // Use t_null for now instead of t_water_sh, because sometimes our extended terrain @@ -4504,7 +4504,7 @@ void mapgen_lake_shore( map *m, oter_id, mapgendata dat, const time_point &turn, visited.emplace( current_point ); - if( !generic_inbounds( current_point, map_boundaries ) ) { + if( !map_boundaries.contains_inclusive( current_point ) ) { continue; } diff --git a/src/martialarts.cpp b/src/martialarts.cpp index 50902268b1b38..ff92150da1d4b 100644 --- a/src/martialarts.cpp +++ b/src/martialarts.cpp @@ -203,12 +203,18 @@ class ma_buff_reader : public generic_typed_reader void martialart::load( JsonObject &jo, const std::string & ) { - JsonArray jsarr; - mandatory( jo, was_loaded, "name", name ); mandatory( jo, was_loaded, "description", description ); mandatory( jo, was_loaded, "initiate", initiate ); - optional( jo, was_loaded, "autolearn", autolearn_skills ); + JsonArray jsarr = jo.get_array( "autolearn" ); + while( jsarr.has_more() ) { + JsonArray skillArray = jsarr.next_array(); + std::string skill_name = skillArray.get_string( 0 ); + int skill_level = 0; + std::string skill_level_string = skillArray.get_string( 1 ); + skill_level = stoi( skill_level_string ); + autolearn_skills.emplace_back( skill_name, skill_level ); + } optional( jo, was_loaded, "primary_skill", primary_skill, skill_id( "unarmed" ) ); optional( jo, was_loaded, "learn_difficulty", learn_difficulty ); @@ -264,6 +270,18 @@ std::vector all_martialart_types() return result; } +std::vector autolearn_martialart_types() +{ + std::vector result; + for( const auto &ma : martialarts.get_all() ) { + if( ma.autolearn_skills.empty() ) { + continue; + } + result.push_back( ma.id ); + } + return result; +} + static void check( const ma_requirements &req, const std::string &display_text ) { for( auto &r : req.req_buffs ) { @@ -480,7 +498,7 @@ bool ma_technique::is_valid_player( const player &u ) const } ma_buff::ma_buff() - : buff_duration( 2_turns ) + : buff_duration( 1_turns ) { max_stacks = 1; // total number of stacks this buff can have @@ -1033,10 +1051,10 @@ bool player::can_autolearn( const matype_id &ma_id ) const return false; } - const std::vector> skills = ma_id.obj().autolearn_skills; - for( auto &elem : skills ) { - const skill_id skill_req( elem[0] ); - const int required_level = std::stoi( elem[1] ); + + for( const std::pair &elem : ma_id.obj().autolearn_skills ) { + const skill_id skill_req( elem.first ); + const int required_level = elem.second; if( required_level > get_skill_level( skill_req ) ) { return false; diff --git a/src/martialarts.h b/src/martialarts.h index b9b3842cb1886..780e563c2f1e6 100644 --- a/src/martialarts.h +++ b/src/martialarts.h @@ -230,7 +230,7 @@ class martialart std::string name; std::string description; std::vector initiate; - std::vector> autolearn_skills; + std::vector> autolearn_skills; skill_id primary_skill; int learn_difficulty = 0; int arm_block; @@ -279,5 +279,6 @@ void finialize_martial_arts(); const std::string martialart_difficulty( matype_id mstyle ); std::vector all_martialart_types(); +std::vector autolearn_martialart_types(); #endif diff --git a/src/math_defines.h b/src/math_defines.h index 8e28c6f27987c..d3844756ba353 100644 --- a/src/math_defines.h +++ b/src/math_defines.h @@ -16,9 +16,17 @@ #include // And on mingw even that doesn't work, so we are forced to have our own -// fallback definition. +// fallback definitions. #ifndef M_PI #define M_PI 3.14159265358979323846 #endif +#ifndef M_PI_2 +#define M_PI_2 1.57079632679489661923 +#endif + +#ifndef M_SQRT2 +#define M_SQRT2 1.41421356237309504880 +#endif + #endif // CATA_MATH_DEFINES_H diff --git a/src/mattack_actors.cpp b/src/mattack_actors.cpp index 910adb7a94a71..141192e5fb02a 100644 --- a/src/mattack_actors.cpp +++ b/src/mattack_actors.cpp @@ -180,20 +180,8 @@ bool mon_spellcasting_actor::call( monster &mon ) const return false; } - if( fx == "target_attack" ) { - spell_effect::target_attack( spell_data, mon, target ); - } else if( fx == "projectile_attack" ) { - spell_effect::projectile_attack( spell_data, mon, target ); - } else if( fx == "cone_attack" ) { - spell_effect::cone_attack( spell_data, mon, target ); - } else if( fx == "line_attack" ) { - spell_effect::line_attack( spell_data, mon, target ); - } else if( fx == "summon" ) { - spell_effect::spawn_summoned_monster( spell_data, mon, target ); - } else { - debugmsg( "ERROR: %s spell attack effect not implemented" ); - return false; - } + spell_data.cast_all_effects( mon, target ); + return true; } diff --git a/src/melee.cpp b/src/melee.cpp index 5bee0a3e68e8f..71f9d85bf4432 100644 --- a/src/melee.cpp +++ b/src/melee.cpp @@ -1988,6 +1988,12 @@ int player::attack_speed( const item &weap ) const double player::weapon_value( const item &weap, int ammo ) const { + if( &weapon == &weap ) { + auto cached_value = cached_info.find( "weapon_value" ); + if( cached_value != cached_info.end() ) { + return cached_value->second; + } + } const double val_gun = gun_value( weap, ammo ); const double val_melee = melee_value( weap ); const double more = std::max( val_gun, val_melee ); @@ -1996,6 +2002,9 @@ double player::weapon_value( const item &weap, int ammo ) const // A small bonus for guns you can also use to hit stuff with (bayonets etc.) const double my_val = more + ( less / 2.0 ); add_msg( m_debug, "%s (%ld ammo) sum value: %.1f", weap.type->get_id(), ammo, my_val ); + if( &weapon == &weap ) { + cached_info.emplace( "weapon_value", my_val ); + } return my_val; } diff --git a/src/mission.cpp b/src/mission.cpp index 327283a4a7853..e0173e3bc846b 100644 --- a/src/mission.cpp +++ b/src/mission.cpp @@ -339,7 +339,7 @@ bool mission::is_complete( const int _npc_id ) const tmp_inv.dump( items ); Group_tag grp_type = type->group_id; itype_id container = type->container_id; - bool specific_container_required = container.compare( "null" ) != 0; + bool specific_container_required = container != "null"; std::map matches = std::map(); get_all_item_group_matches( @@ -621,7 +621,7 @@ void mission::load_info( std::istream &data ) deadline = time_point::from_turn( deadline_ ); target.z = 0; follow_up = mission_type::from_legacy( tmpfollow ); - reward.type = npc_favor_type( reward_id ); + reward.type = static_cast( reward_id ); reward.item_id = itype_id( rew_item ); reward.skill = Skill::from_legacy_int( rew_skill ); item_id = itype_id( itemid ); diff --git a/src/mission_companion.cpp b/src/mission_companion.cpp index dc78ffe468d74..f93447fd4edc4 100644 --- a/src/mission_companion.cpp +++ b/src/mission_companion.cpp @@ -1482,7 +1482,7 @@ bool talk_function::companion_om_combat_check( const std::vector &group for( int y = 0; y < 2; y++ ) { point sm( ( om_tgt.x * 2 ) + x, ( om_tgt.x * 2 ) + y ); const point omp = sm_to_om_remain( sm ); - overmap &omi = overmap_buffer.get( omp.x, omp.y ); + overmap &omi = overmap_buffer.get( omp ); const tripoint current_submap_loc( om_tgt.x * 2 + x, om_tgt.y * 2 + y, om_tgt.z ); auto monster_bucket = omi.monster_map.equal_range( current_submap_loc ); @@ -1881,8 +1881,7 @@ std::vector talk_function::companion_rank( const std::vector npc_ptr talk_function::companion_choose( const std::string &skill_tested, int skill_level ) { std::vector available; - cata::optional bcp = overmap_buffer.find_camp( g->u.global_omt_location().x, - g->u.global_omt_location().y ); + cata::optional bcp = overmap_buffer.find_camp( g->u.global_omt_location().xy() ); for( auto &elem : g->get_follower_list() ) { npc_ptr guy = overmap_buffer.find_npc( elem ); @@ -1905,8 +1904,7 @@ npc_ptr talk_function::companion_choose( const std::string &skill_tested, int sk } } else { const tripoint &guy_omt_pos = guy->global_omt_location(); - cata::optional guy_camp = overmap_buffer.find_camp( guy_omt_pos.x, - guy_omt_pos.y ); + cata::optional guy_camp = overmap_buffer.find_camp( guy_omt_pos.xy() ); if( guy_camp ) { // get NPCs assigned to guard a remote base basecamp *temp_camp = *guy_camp; @@ -2090,7 +2088,7 @@ void talk_function::loot_building( const tripoint &site ) } } bay.save(); - overmap_buffer.ter( site.x, site.y, site.z ) = oter_id( "looted_building" ); + overmap_buffer.ter( site ) = oter_id( "looted_building" ); } void mission_data::add( const std::string &id, const std::string &name_display, diff --git a/src/mission_start.cpp b/src/mission_start.cpp index 38c3e7c96dc02..50b1b69c1bd8e 100644 --- a/src/mission_start.cpp +++ b/src/mission_start.cpp @@ -204,7 +204,7 @@ void mission_start::place_npc_software( mission *miss ) compmap.load( place.x * 2, place.y * 2, place.z, false ); tripoint comppoint; - oter_id oter = overmap_buffer.ter( place.x, place.y, place.z ); + oter_id oter = overmap_buffer.ter( place ); if( is_ot_match( "house", oter, ot_match_type::prefix ) || is_ot_match( "s_pharm", oter, ot_match_type::type ) || oter == "" ) { comppoint = find_potential_computer_point( compmap, place.z ); diff --git a/src/mission_util.cpp b/src/mission_util.cpp index e685b7a2c987a..9b905b76e3d77 100644 --- a/src/mission_util.cpp +++ b/src/mission_util.cpp @@ -120,8 +120,9 @@ static tripoint random_house_in_city( const city_reference &cref ) int endy = city_center_omt.y + size; for( int x = startx; x <= endx; x++ ) { for( int y = starty; y <= endy; y++ ) { - if( overmap_buffer.check_ot( "house", ot_match_type::type, x, y, z ) ) { - valid.push_back( tripoint( x, y, z ) ); + tripoint p( x, y, z ); + if( overmap_buffer.check_ot( "house", ot_match_type::type, p ) ) { + valid.push_back( p ); } } } diff --git a/src/monster.cpp b/src/monster.cpp index 47625d6972acd..412d2f5fb20fc 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -2075,13 +2075,13 @@ void monster::die( Creature *nkiller ) if( !is_hallucination() && has_flag( MF_QUEEN ) ) { // The submap coordinates of this monster, monster groups coordinates are // submap coordinates. - const point abssub = ms_to_sm_copy( g->m.getabs( posx(), posy() ) ); + const tripoint abssub = ms_to_sm_copy( g->m.getabs( pos() ) ); // Do it for overmap above/below too for( int z = 1; z >= -1; --z ) { for( int x = -HALF_MAPSIZE; x <= HALF_MAPSIZE; x++ ) { for( int y = -HALF_MAPSIZE; y <= HALF_MAPSIZE; y++ ) { - std::vector groups = overmap_buffer.groups_at( abssub.x + x, abssub.y + y, - g->get_levz() + z ); + tripoint offset( x, y, z ); + std::vector groups = overmap_buffer.groups_at( abssub + offset ); for( auto &mgp : groups ) { if( MonsterGroupManager::IsMonsterInGroup( mgp->type, type->id ) ) { mgp->dying = true; diff --git a/src/mutation.cpp b/src/mutation.cpp index 9d833b778e72d..d6ede9fec2ebe 100644 --- a/src/mutation.cpp +++ b/src/mutation.cpp @@ -532,7 +532,8 @@ void player::mutate() const mutation_branch &base_mdata = traits_iter; bool thresh_save = base_mdata.threshold; bool prof_save = base_mdata.profession; - bool purify_save = base_mdata.purifiable; + // are we unpurifiable? (saved from mutating away) + bool purify_save = !base_mdata.purifiable; // ...that we have... if( has_trait( base_mutation ) ) { @@ -684,17 +685,7 @@ void player::mutate_category( const std::string &cat ) } } - // if we can't mutate in the category do nothing - if( valid.empty() ) { - return; - } - - if( mutate_towards( random_entry( valid ) ) ) { - return; - } else { - // if mutation failed (errors, post-threshold pick), try again once. - mutate_towards( random_entry( valid ) ); - } + mutate_towards( valid, 2 ); } static std::vector get_all_mutation_prereqs( const trait_id &id ) @@ -713,6 +704,22 @@ static std::vector get_all_mutation_prereqs( const trait_id &id ) return ret; } +bool player::mutate_towards( std::vector muts, int num_tries ) +{ + while( !muts.empty() && num_tries > 0 ) { + int i = rng( 0, muts.size() - 1 ); + + if( mutate_towards( muts[i] ) ) { + return true; + } + + muts.erase( muts.begin() + i ); + --num_tries; + } + + return false; +} + bool player::mutate_towards( const trait_id &mut ) { if( has_child_flag( mut ) ) { @@ -780,9 +787,9 @@ bool player::mutate_towards( const trait_id &mut ) if( !has_prereqs && ( !prereq.empty() || !prereqs2.empty() ) ) { if( !prereq1 && !prereq.empty() ) { - return mutate_towards( random_entry( prereq ) ); + return mutate_towards( prereq ); } else if( !prereq2 && !prereqs2.empty() ) { - return mutate_towards( random_entry( prereqs2 ) ); + return mutate_towards( prereqs2 ); } } diff --git a/src/mutation.h b/src/mutation.h index 831b5d19fc8cf..2bdb829acaa4c 100644 --- a/src/mutation.h +++ b/src/mutation.h @@ -158,6 +158,12 @@ struct mutation_branch { // Multiplier for sight range, defaulting to 1. float overmap_multiplier = 1.0f; + // Multiplier for map memory capacity, defaulting to 1. + float map_memory_capacity_multiplier = 1.0f; + + // Multiplier for skill rust, defaulting to 1. + float skill_rust_multiplier = 1.0f; + // Bonus or penalty to social checks (additive). 50 adds 50% to success, -25 subtracts 25% social_modifiers social_mods; diff --git a/src/mutation_data.cpp b/src/mutation_data.cpp index 864afad68474f..c87b320fda3db 100644 --- a/src/mutation_data.cpp +++ b/src/mutation_data.cpp @@ -350,6 +350,8 @@ void mutation_branch::load( JsonObject &jo, const std::string & ) optional( jo, was_loaded, "stamina_regen_modifier", stamina_regen_modifier, 0.0f ); optional( jo, was_loaded, "overmap_sight", overmap_sight, 0.0f ); optional( jo, was_loaded, "overmap_multiplier", overmap_multiplier, 1.0f ); + optional( jo, was_loaded, "map_memory_capacity_multiplier", map_memory_capacity_multiplier, 1.0f ); + optional( jo, was_loaded, "skill_rust_multiplier", skill_rust_multiplier, 1.0f ); optional( jo, was_loaded, "mana_modifier", mana_modifier, 0 ); optional( jo, was_loaded, "mana_multiplier", mana_multiplier, 1.0f ); @@ -748,7 +750,7 @@ void mutation_branch::add_entry( Trait_group &tg, JsonObject &obj ) std::shared_ptr mutation_branch::get_group( const trait_group::Trait_group_tag &gid ) { auto found = trait_groups.find( gid ); - return ( found != trait_groups.end() ) ? found->second : nullptr; + return found != trait_groups.end() ? found->second : nullptr; } std::vector mutation_branch::get_all_group_names() diff --git a/src/mutation_ui.cpp b/src/mutation_ui.cpp index f56ea5e7cc556..61deee6f1f59a 100644 --- a/src/mutation_ui.cpp +++ b/src/mutation_ui.cpp @@ -145,7 +145,7 @@ void player::power_mutations() // drawing the mutation starts with mutation[scroll_position] const int list_start_y = HEADER_LINE_Y + 2 - scroll_position; int max_scroll_position = HEADER_LINE_Y + 2 + mutations_count - - ( ( menu_mode == "examining" ) ? DESCRIPTION_LINE_Y : ( HEIGHT - 1 ) ); + ( menu_mode == "examining" ? DESCRIPTION_LINE_Y : HEIGHT - 1 ); if( redraw ) { redraw = false; @@ -175,7 +175,7 @@ void player::power_mutations() ( menu_mode == "examining" ? DESCRIPTION_LINE_Y : HEIGHT - 1 ) ) { break; } - type = ( has_base_trait( passive[i] ) ? c_cyan : c_light_cyan ); + type = has_base_trait( passive[i] ) ? c_cyan : c_light_cyan; mvwprintz( wBio, list_start_y + i, 2, type, "%c %s", td.key, md.name() ); } } @@ -191,9 +191,9 @@ void player::power_mutations() break; } if( td.powered ) { - type = ( has_base_trait( active[i] ) ? c_green : c_light_green ); + type = has_base_trait( active[i] ) ? c_green : c_light_green; } else { - type = ( has_base_trait( active[i] ) ? c_red : c_light_red ); + type = has_base_trait( active[i] ) ? c_red : c_light_red; } // TODO: track resource(s) used and specify mvwputch( wBio, list_start_y + i, second_column, type, td.key ); diff --git a/src/npc.cpp b/src/npc.cpp index 3686e2e4480dc..424cdef817eb0 100644 --- a/src/npc.cpp +++ b/src/npc.cpp @@ -213,8 +213,8 @@ void npc_template::load( JsonObject &jsobj ) guy.myclass = npc_class_id( jsobj.get_string( "class" ) ); } - guy.set_attitude( npc_attitude( jsobj.get_int( "attitude" ) ) ); - guy.mission = npc_mission( jsobj.get_int( "mission" ) ); + guy.set_attitude( static_cast( jsobj.get_int( "attitude" ) ) ); + guy.mission = static_cast( jsobj.get_int( "mission" ) ); guy.chatbin.first_topic = jsobj.get_string( "chat" ); if( jsobj.has_string( "mission_offered" ) ) { guy.miss_ids.emplace_back( mission_type_id( jsobj.get_string( "mission_offered" ) ) ); @@ -643,8 +643,8 @@ void npc::setpos( const tripoint &pos ) submap_coords.y = g->get_levy() + pos.y / SEEY; const point pos_om_new = sm_to_om_copy( submap_coords ); if( !is_fake() && pos_om_old != pos_om_new ) { - overmap &om_old = overmap_buffer.get( pos_om_old.x, pos_om_old.y ); - overmap &om_new = overmap_buffer.get( pos_om_new.x, pos_om_new.y ); + overmap &om_old = overmap_buffer.get( pos_om_old ); + overmap &om_new = overmap_buffer.get( pos_om_new ); if( const auto ptr = om_old.erase_npc( getID() ) ) { om_new.insert_npc( ptr ); } else { @@ -664,8 +664,8 @@ void npc::travel_overmap( const tripoint &pos ) reach_omt_destination(); } if( !is_fake() && pos_om_old != pos_om_new ) { - overmap &om_old = overmap_buffer.get( pos_om_old.x, pos_om_old.y ); - overmap &om_new = overmap_buffer.get( pos_om_new.x, pos_om_new.y ); + overmap &om_old = overmap_buffer.get( pos_om_old ); + overmap &om_new = overmap_buffer.get( pos_om_new ); if( const auto ptr = om_old.erase_npc( getID() ) ) { om_new.insert_npc( ptr ); } else { @@ -827,7 +827,7 @@ bool npc::wear_if_wanted( const item &it ) if( splint ) { splint = false; for( int i = 0; i < num_hp_parts; i++ ) { - hp_part hpp = hp_part( i ); + hp_part hpp = static_cast( i ); body_part bp = player::hp_to_bp( hpp ); if( hp_cur[i] <= 0 && it.covers( bp ) ) { splint = true; @@ -924,6 +924,7 @@ void npc::stow_item( item &it ) bool npc::wield( item &it ) { + cached_info.erase( "weapon_value" ); if( is_armed() ) { stow_item( weapon ); } @@ -1100,7 +1101,8 @@ float npc::vehicle_danger( int radius ) const int size = std::max( last_part.mount.x, last_part.mount.y ); double normal = sqrt( static_cast( ( bx - ax ) * ( bx - ax ) + ( by - ay ) * ( by - ay ) ) ); - int closest = int( abs( ( posx() - ax ) * ( by - ay ) - ( posy() - ay ) * ( bx - ax ) ) / normal ); + int closest = static_cast( abs( ( posx() - ax ) * ( by - ay ) - ( posy() - ay ) * + ( bx - ax ) ) / normal ); if( size > closest ) { danger = i; @@ -1221,12 +1223,12 @@ void npc::decide_needs() if( needrank[i] < 20 ) { for( j = 0; j < needs.size(); j++ ) { if( needrank[i] < needrank[needs[j]] ) { - needs.insert( needs.begin() + j, npc_need( i ) ); + needs.insert( needs.begin() + j, static_cast( i ) ); j = needs.size() + 1; } } if( j == needs.size() ) { - needs.push_back( npc_need( i ) ); + needs.push_back( static_cast( i ) ); } } } @@ -1654,8 +1656,7 @@ bool npc::is_leader() const bool npc::is_assigned_to_camp() const { - cata::optional bcp = overmap_buffer.find_camp( global_omt_location().x, - global_omt_location().y ); + cata::optional bcp = overmap_buffer.find_camp( global_omt_location().xy() ); if( !bcp ) { return false; } diff --git a/src/npc.h b/src/npc.h index 33042a47d8197..479e0e49deac5 100644 --- a/src/npc.h +++ b/src/npc.h @@ -869,6 +869,9 @@ class npc : public player bool has_identified( const std::string & ) const override { return true; } + bool has_artifact_with( const art_effect_passive ) const override { + return false; + } /** Is the item safe or does the NPC trust you enough? */ bool will_accept_from_player( const item &it ) const; diff --git a/src/npcmove.cpp b/src/npcmove.cpp index 1913e12dcfd7b..8659a8d4f709d 100644 --- a/src/npcmove.cpp +++ b/src/npcmove.cpp @@ -401,6 +401,7 @@ void npc::assess_danger() cur_threat_map[ threat_dir ] = 0.25f * ai_cache.threat_map[ threat_dir ]; } // first, check if we're about to be consumed by fire + // TODO: Use the field cache for( const tripoint &pt : g->m.points_in_radius( pos(), 6 ) ) { if( pt == pos() || g->m.has_flag( TFLAG_FIRE_CONTAINER, pt ) ) { continue; @@ -418,13 +419,13 @@ void npc::assess_danger() // find our Character friends and enemies std::vector> hostile_guys; for( const npc &guy : g->all_npcs() ) { - if( &guy == this || !guy.is_active() || !sees( guy.pos() ) ) { + if( &guy == this || !guy.is_active() ) { continue; } if( has_faction_relationship( guy, npc_factions::watch_your_back ) ) { ai_cache.friends.emplace_back( g->shared_from( guy ) ); - } else if( attitude_to( guy ) != A_NEUTRAL ) { + } else if( attitude_to( guy ) != A_NEUTRAL && sees( guy.pos() ) ) { hostile_guys.emplace_back( g->shared_from( guy ) ); } } @@ -437,14 +438,17 @@ void npc::assess_danger() } for( const monster &critter : g->all_monsters() ) { - if( !sees( critter ) ) { - continue; - } auto att = critter.attitude_to( *this ); if( att == A_FRIENDLY ) { ai_cache.friends.emplace_back( g->shared_from( critter ) ); continue; } + if( att != A_HOSTILE && ( critter.friendly || !is_enemy() ) ) { + continue; + } + if( !sees( critter ) ) { + continue; + } float critter_threat = evaluate_enemy( critter ); // warn and consider the odds for distant enemies if( ( is_enemy() || !critter.friendly ) ) { @@ -454,9 +458,6 @@ void npc::assess_danger() } } - if( att != A_HOSTILE ) { - continue; - } int dist = rl_dist( pos(), critter.pos() ); float scaled_distance = std::max( 1.0f, dist / critter.speed_rating() ); float hp_percent = 1.0f - static_cast( critter.get_hp() ) / critter.get_hp_max(); @@ -484,6 +485,10 @@ void npc::assess_danger() } } + if( assessment == 0.0 && hostile_guys.empty() ) { + ai_cache.danger_assessment = assessment; + return; + } const auto handle_hostile = [&]( const player & foe, float foe_threat, const std::string & bogey, const std::string & warning ) { if( foe_threat > ( 8.0f + personality.bravery + rng( 0, 5 ) ) ) { @@ -1342,7 +1347,6 @@ npc_action npc::method_of_attack() bool npc::need_heal( const player &n ) { - // if there are no healing items, there's nothing to do if( !( ai_cache.can_heal.bandage || ai_cache.can_heal.bleed || ai_cache.can_heal.bite || ai_cache.can_heal.infect ) ) { @@ -1359,7 +1363,7 @@ bool npc::need_heal( const player &n ) } for( int i = 0; i < num_hp_parts; i++ ) { - const hp_part part = hp_part( i ); + const hp_part part = static_cast( i ); const body_part bp_wounded = hp_to_bp( part ); if( ai_cache.can_heal.bleed && n.has_effect( effect_bleed, bp_wounded ) ) { @@ -1431,6 +1435,10 @@ static bool wants_to_reload_with( const item &weap, const item &ammo ) item &npc::find_reloadable() { + auto cached_value = cached_info.find( "reloadables" ); + if( cached_value != cached_info.end() ) { + return null_item_reference(); + } // Check wielded gun, non-wielded guns, mags and tools // TODO: Build a proper gun->mag->ammo DAG (Directed Acyclic Graph) // to avoid checking same properties over and over @@ -1455,6 +1463,7 @@ item &npc::find_reloadable() return *reloadable; } + cached_info.emplace( "reloadables", 0.0 ); return null_item_reference(); } @@ -1465,7 +1474,7 @@ const item &npc::find_reloadable() const bool npc::can_reload_current() { - if( !weapon.is_gun() ) { + if( !weapon.is_gun() || !wants_to_reload( *this, weapon ) ) { return false; } @@ -1683,18 +1692,20 @@ npc_action npc::address_needs( float danger ) deactivate_bionic_by_id( bio_nanobots ); } - if( is_player_ally() && need_heal( g->u ) ) { - ai_cache.ally = g->shared_from( g->u ); - return npc_heal_player; - } + // If there are no healing items, there's nothing to do + if( ai_cache.can_heal.bandage || ai_cache.can_heal.bleed || ai_cache.can_heal.bite || + ai_cache.can_heal.infect ) { + if( is_player_ally() && need_heal( g->u ) ) { + ai_cache.ally = g->shared_from( g->u ); + return npc_heal_player; + } - const std::vector allies = g->get_npcs_if( [&]( const npc & guy ) { - return guy.getID() != getID() && guy.is_ally( *this ) && posz() == guy.posz() && - sees( guy.pos() ) && rl_dist( pos(), guy.pos() ) <= SEEX * 2; - } ); + const std::vector hurt_allies = g->get_npcs_if( [&]( const npc & guy ) { + return guy.getID() != getID() && guy.is_ally( *this ) && posz() == guy.posz() && + need_heal( guy ) && rl_dist( pos(), guy.pos() ) <= SEEX * 2 && sees( guy.pos() ); + } ); - for( npc *guy : allies ) { - if( need_heal( *guy ) ) { + for( npc *guy : hurt_allies ) { ai_cache.ally = g->shared_from( *guy ); return npc_heal_player; } @@ -1710,29 +1721,44 @@ npc_action npc::address_needs( float danger ) return npc_noop; } - if( ( danger <= NPC_DANGER_VERY_LOW && - ( get_stored_kcal() + stomach.get_calories() < get_healthy_kcal() * 0.95 || get_thirst() > 40 ) ) || - get_thirst() > 80 || get_stored_kcal() + stomach.get_calories() < get_healthy_kcal() * 0.75 ) { + // Extreme thirst or hunger, bypass safety check. + if( get_thirst() > 80 || + get_stored_kcal() + stomach.get_calories() < get_healthy_kcal() * 0.75 ) { if( consume_food() ) { return npc_noop; } } - - if( danger <= NPC_DANGER_VERY_LOW ) { - if( wants_to_recharge_cbm() && recharge_cbm() ) { - return npc_noop; + //Does the hallucination needs to disappear ? + if( g->u.sees( *this ) && is_hallucination() ) { + if( !g->u.has_effect( effect_hallu ) ) { + die( nullptr ); } + } - if( find_corpse_to_pulp() ) { - if( !do_pulp() ) { - move_to_next(); - } + if( danger > NPC_DANGER_VERY_LOW ) { + return npc_undecided; + } + + if( get_thirst() > 40 || + get_stored_kcal() + stomach.get_calories() < get_healthy_kcal() * 0.95 ) { + if( consume_food() ) { return npc_noop; } + } - if( adjust_worn() ) { - return npc_noop; + if( wants_to_recharge_cbm() && recharge_cbm() ) { + return npc_noop; + } + + if( find_corpse_to_pulp() ) { + if( !do_pulp() ) { + move_to_next(); } + return npc_noop; + } + + if( adjust_worn() ) { + return npc_noop; } const auto could_sleep = [&]() { @@ -1760,13 +1786,6 @@ npc_action npc::address_needs( float danger ) return npc_sleep; } } - //Does the hallucination needs to disappear ? - if( g->u.sees( *this ) && is_hallucination() ) { - if( !g->u.has_effect( effect_hallu ) ) { - die( nullptr ); - } - } - // TODO: Mutation & trait related needs // e.g. finding glasses; getting out of sunlight if we're an albino; etc. @@ -2946,11 +2965,11 @@ bool npc::find_corpse_to_pulp() if( corpse == nullptr ) { // If we're following the player, don't wander off to pulp corpses const tripoint &around = is_walking_with() ? g->u.pos() : pos(); - for( const tripoint &p : closest_tripoints_first( range, around ) ) { - corpse = check_tile( p ); + for( const item_location &location : g->m.get_active_items_in_radius( around, range ) ) { + corpse = check_tile( location.position() ); if( corpse != nullptr ) { - pulp_location.emplace( p ); + pulp_location.emplace( location.position() ); break; } @@ -4112,7 +4131,7 @@ bool npc::adjust_worn() const auto covers_broken = [this]( const item & it, side s ) { const auto covered = it.get_covered_body_parts( s ); for( size_t i = 0; i < num_hp_parts; i++ ) { - if( hp_cur[ i ] <= 0 && covered.test( hp_to_bp( hp_part( i ) ) ) ) { + if( hp_cur[ i ] <= 0 && covered.test( hp_to_bp( static_cast( i ) ) ) ) { return true; } } diff --git a/src/npctalk.cpp b/src/npctalk.cpp index a559b297258c9..27e96b4ca2328 100644 --- a/src/npctalk.cpp +++ b/src/npctalk.cpp @@ -2802,7 +2802,7 @@ void conditional_t::set_at_om_location( JsonObject &jo, const std::string &membe oter_id &omt_ref = overmap_buffer.ter( omt_pos ); if( location == "FACTION_CAMP_ANY" ) { - cata::optional bcp = overmap_buffer.find_camp( omt_pos.x, omt_pos.y ); + cata::optional bcp = overmap_buffer.find_camp( omt_pos.xy() ); if( bcp ) { return true; } diff --git a/src/npctalk_funcs.cpp b/src/npctalk_funcs.cpp index 4d4ceb09fcee0..3bd6d45595848 100644 --- a/src/npctalk_funcs.cpp +++ b/src/npctalk_funcs.cpp @@ -229,7 +229,7 @@ void talk_function::goto_location( npc &p ) if( elem == p.global_omt_location() ) { continue; } - cata::optional camp = overmap_buffer.find_camp( elem.x, elem.y ); + cata::optional camp = overmap_buffer.find_camp( elem.xy() ); if( !camp ) { continue; } @@ -282,8 +282,7 @@ void talk_function::assign_guard( npc &p ) p.set_mission( NPC_MISSION_GUARD_ALLY ); p.chatbin.first_topic = "TALK_FRIEND_GUARD"; p.set_omt_destination(); - cata::optional bcp = overmap_buffer.find_camp( p.global_omt_location().x, - p.global_omt_location().y ); + cata::optional bcp = overmap_buffer.find_camp( p.global_omt_location().xy() ); if( bcp ) { basecamp *temp_camp = *bcp; temp_camp->validate_assignees(); @@ -319,8 +318,7 @@ void talk_function::stop_guard( npc &p ) p.chatbin.first_topic = "TALK_FRIEND"; p.goal = npc::no_goal_point; p.guard_pos = npc::no_goal_point; - cata::optional bcp = overmap_buffer.find_camp( p.global_omt_location().x, - p.global_omt_location().y ); + cata::optional bcp = overmap_buffer.find_camp( p.global_omt_location().xy() ); if( bcp ) { basecamp *temp_camp = *bcp; temp_camp->validate_assignees(); @@ -491,8 +489,8 @@ void talk_function::give_aid( npc &p ) { p.add_effect( effect_currently_busy, 30_minutes ); for( int i = 0; i < num_hp_parts; i++ ) { - const body_part bp_healed = player::hp_to_bp( hp_part( i ) ); - g->u.heal( hp_part( i ), 5 * rng( 2, 5 ) ); + const body_part bp_healed = player::hp_to_bp( static_cast( i ) ); + g->u.heal( static_cast( i ), 5 * rng( 2, 5 ) ); if( g->u.has_effect( effect_bite, bp_healed ) ) { g->u.remove_effect( effect_bite, bp_healed ); } @@ -514,8 +512,8 @@ void talk_function::give_all_aid( npc &p ) for( npc &guy : g->all_npcs() ) { if( guy.is_walking_with() && rl_dist( guy.pos(), g->u.pos() ) < PICKUP_RANGE ) { for( int i = 0; i < num_hp_parts; i++ ) { - const body_part bp_healed = player::hp_to_bp( hp_part( i ) ); - guy.heal( hp_part( i ), 5 * rng( 2, 5 ) ); + const body_part bp_healed = player::hp_to_bp( static_cast( i ) ); + guy.heal( static_cast( i ), 5 * rng( 2, 5 ) ); if( guy.has_effect( effect_bite, bp_healed ) ) { guy.remove_effect( effect_bite, bp_healed ); } diff --git a/src/overmap.cpp b/src/overmap.cpp index 48a252f90a1b7..5a1b3230da9d3 100644 --- a/src/overmap.cpp +++ b/src/overmap.cpp @@ -116,6 +116,9 @@ const size_t invalid = 0; constexpr size_t rotate( size_t line, om_direction::type dir ) { + if( dir == om_direction::type::invalid ) { + return line; + } // Bitwise rotation to the left. return ( ( ( line << static_cast( dir ) ) | ( line >> ( om_direction::size - static_cast( dir ) ) ) ) & om_direction::bits ); @@ -123,6 +126,9 @@ constexpr size_t rotate( size_t line, om_direction::type dir ) constexpr size_t set_segment( size_t line, om_direction::type dir ) { + if( dir == om_direction::type::invalid ) { + return line; + } return line | 1 << static_cast( dir ); } @@ -312,7 +318,7 @@ const string_id &int_id::id() const bool operator==( const int_id &lhs, const char *rhs ) { - return lhs.id().str().compare( rhs ) == 0; + return lhs.id().str() == rhs; } bool operator!=( const int_id &lhs, const char *rhs ) @@ -1186,6 +1192,14 @@ bool &overmap::seen( int x, int y, int z ) return layer[z + OVERMAP_DEPTH].visible[x][y]; } +bool overmap::seen( int x, int y, int z ) const +{ + if( !inbounds( tripoint( x, y, z ) ) ) { + return false; + } + return layer[z + OVERMAP_DEPTH].visible[x][y]; +} + bool &overmap::explored( int x, int y, int z ) { if( !inbounds( tripoint( x, y, z ) ) ) { @@ -1403,15 +1417,14 @@ std::vector overmap::find_extras( const int z, const std::string &text ) bool overmap::inbounds( const tripoint &p, int clearance ) { - const tripoint overmap_boundary_min( 0, 0, -OVERMAP_DEPTH ); - const tripoint overmap_boundary_max( OMAPX, OMAPY, OVERMAP_HEIGHT ); - const tripoint overmap_clearance_min( 0 + clearance, 0 + clearance, 0 ); - const tripoint overmap_clearance_max( 1 + clearance, 1 + clearance, 0 ); + static constexpr tripoint overmap_boundary_min( 0, 0, -OVERMAP_DEPTH ); + static constexpr tripoint overmap_boundary_max( OMAPX, OMAPY, OVERMAP_HEIGHT + 1 ); - const box overmap_boundaries( overmap_boundary_min, overmap_boundary_max ); - const box overmap_clearance( overmap_clearance_min, overmap_clearance_max ); + static constexpr box overmap_boundaries( overmap_boundary_min, overmap_boundary_max ); + box stricter_boundaries = overmap_boundaries; + stricter_boundaries.shrink( tripoint( clearance, clearance, 0 ) ); - return generic_inbounds( p, overmap_boundaries, overmap_clearance ); + return stricter_boundaries.contains_half_open( p ); } const scent_trace &overmap::scent_at( const tripoint &loc ) const @@ -4087,7 +4100,7 @@ void overmap::place_specials( overmap_special_batch &enabled_specials ) // Since this starts at enabled_specials::origin, it will only place new overmaps // in the 5x5 area surrounding the initial overmap, bounding the amount of work we will do. for( point candidate_addr : closest_points_first( 2, custom_overmap_specials.get_origin() ) ) { - if( !overmap_buffer.has( candidate_addr.x, candidate_addr.y ) ) { + if( !overmap_buffer.has( candidate_addr ) ) { int current_distance = square_dist( pos().x, pos().y, candidate_addr.x, candidate_addr.y ); if( nearest_candidates.empty() || current_distance == previous_distance ) { @@ -4101,7 +4114,7 @@ void overmap::place_specials( overmap_special_batch &enabled_specials ) if( !nearest_candidates.empty() ) { std::shuffle( nearest_candidates.begin(), nearest_candidates.end(), rng_get_engine() ); point new_om_addr = nearest_candidates.front(); - overmap_buffer.create_custom_overmap( new_om_addr.x, new_om_addr.y, custom_overmap_specials ); + overmap_buffer.create_custom_overmap( new_om_addr, custom_overmap_specials ); } else { add_msg( _( "Unable to place all configured specials, some missions may fail to initialize." ) ); } @@ -4259,21 +4272,21 @@ void overmap::place_radios() void overmap::open( overmap_special_batch &enabled_specials ) { - const std::string terfilename = overmapbuffer::terrain_filename( loc.x, loc.y ); + const std::string terfilename = overmapbuffer::terrain_filename( loc ); using namespace std::placeholders; if( read_from_file_optional( terfilename, std::bind( &overmap::unserialize, this, _1 ) ) ) { - const std::string plrfilename = overmapbuffer::player_filename( loc.x, loc.y ); + const std::string plrfilename = overmapbuffer::player_filename( loc ); read_from_file_optional( plrfilename, std::bind( &overmap::unserialize_view, this, _1 ) ); } else { // No map exists! Prepare neighbors, and generate one. std::vector pointers; // Fetch south and north for( int i = -1; i <= 1; i += 2 ) { - pointers.push_back( overmap_buffer.get_existing( loc.x, loc.y + i ) ); + pointers.push_back( overmap_buffer.get_existing( loc + point( 0, i ) ) ); } // Fetch east and west for( int i = -1; i <= 1; i += 2 ) { - pointers.push_back( overmap_buffer.get_existing( loc.x + i, loc.y ) ); + pointers.push_back( overmap_buffer.get_existing( loc + point( i, 0 ) ) ); } // pointers looks like (north, south, west, east) @@ -4284,8 +4297,8 @@ void overmap::open( overmap_special_batch &enabled_specials ) // Note: this may throw io errors from std::ofstream void overmap::save() const { - const std::string plrfilename = overmapbuffer::player_filename( loc.x, loc.y ); - const std::string terfilename = overmapbuffer::terrain_filename( loc.x, loc.y ); + const std::string plrfilename = overmapbuffer::player_filename( loc ); + const std::string terfilename = overmapbuffer::terrain_filename( loc ); ofstream_wrapper fout_player( plrfilename ); serialize_view( fout_player ); diff --git a/src/overmap.h b/src/overmap.h index db9c16defd4a5..01c72dbe9bdcf 100644 --- a/src/overmap.h +++ b/src/overmap.h @@ -70,8 +70,7 @@ struct om_map_extra { }; struct om_vehicle { - int x; // overmap x coordinate of tracked vehicle - int y; // overmap y coordinate + point p; // overmap coordinates of tracked vehicle std::string name; }; @@ -163,6 +162,7 @@ class overmap overmap( const overmap & ) = default; overmap( overmap && ) = default; overmap( int x, int y ); + overmap( const point &p ) : overmap( p.x, p.y ) {} ~overmap(); overmap &operator=( const overmap & ) = default; @@ -197,19 +197,56 @@ class overmap oter_id &ter( const tripoint &p ); const oter_id get_ter( const int x, const int y, const int z ) const; const oter_id get_ter( const tripoint &p ) const; - bool &seen( int x, int y, int z ); - bool &explored( int x, int y, int z ); + bool &seen( int x, int y, int z ); + bool &seen( const tripoint &p ) { + return seen( p.x, p.y, p.z ); + } + bool seen( int x, int y, int z ) const; + bool seen( const tripoint &p ) const { + return seen( p.x, p.y, p.z ); + } + bool &explored( int x, int y, int z ); + bool &explored( const tripoint &p ) { + return explored( p.x, p.y, p.z ); + } bool is_explored( const int x, const int y, const int z ) const; + bool is_explored( const tripoint &p ) const { + return is_explored( p.x, p.y, p.z ); + } bool has_note( int x, int y, int z ) const; + bool has_note( const tripoint &p ) const { + return has_note( p.x, p.y, p.z ); + } const std::string ¬e( int x, int y, int z ) const; + const std::string ¬e( const tripoint &p ) const { + return note( p.x, p.y, p.z ); + } void add_note( int x, int y, int z, std::string message ); + void add_note( const tripoint &p, std::string message ) { + add_note( p.x, p.y, p.z, message ); + } void delete_note( int x, int y, int z ); + void delete_note( const tripoint &p ) { + delete_note( p.x, p.y, p.z ); + } bool has_extra( int x, int y, int z ) const; + bool has_extra( const tripoint &p ) const { + return has_extra( p.x, p.y, p.z ); + } const string_id &extra( int x, int y, int z ) const; + const string_id &extra( const tripoint &p ) const { + return extra( p.x, p.y, p.z ); + } void add_extra( int x, int y, int z, const string_id &id ); + void add_extra( const tripoint &p, const string_id &id ) { + add_extra( p.x, p.y, p.z, id ); + } void delete_extra( int x, int y, int z ); + void delete_extra( const tripoint &p ) { + delete_extra( p.x, p.y, p.z ); + } /** * Getter for overmap scents. @@ -278,6 +315,9 @@ class overmap std::vector cities; std::vector roads_out; cata::optional find_camp( const int x, const int y ); + cata::optional find_camp( const point &p ) { + return find_camp( p.x, p.y ); + } /// Adds the npc to the contained list of npcs ( @ref npcs ). void insert_npc( std::shared_ptr who ); /// Removes the npc and returns it ( or returns nullptr if not found ). @@ -398,6 +438,9 @@ class overmap const overmap_connection &connection ); // Polishing bool check_ot( const std::string &otype, ot_match_type match_type, int x, int y, int z ) const; + bool check_ot( const std::string &otype, ot_match_type match_type, const tripoint &p ) const { + return check_ot( otype, match_type, p.x, p.y, p.z ); + } bool check_overmap_special_type( const overmap_special_id &id, const tripoint &location ) const; void chip_rock( int x, int y, int z ); diff --git a/src/overmap_ui.cpp b/src/overmap_ui.cpp index a6569c26353d9..a3a381b20a228 100644 --- a/src/overmap_ui.cpp +++ b/src/overmap_ui.cpp @@ -124,7 +124,7 @@ static std::array, npm_width *npm_height> get_o for( const tripoint &dest : tripoint_range( current - shift, current + shift ) ) { nc_color ter_color = c_black; std::string ter_sym = " "; - const bool see = has_debug_vision || overmap_buffer.seen( dest.x, dest.y, dest.z ); + const bool see = has_debug_vision || overmap_buffer.seen( dest ); if( see ) { // Only load terrain if we can actually see it oter_id cur_ter = overmap_buffer.ter( dest ); @@ -192,8 +192,8 @@ static weather_type get_weather_at_point( const tripoint &pos ) } auto iter = weather_cache.find( pos ); if( iter == weather_cache.end() ) { - const auto abs_ms_pos = tripoint( pos.x * SEEX * 2, pos.y * SEEY * 2, pos.z ); - const auto &wgen = overmap_buffer.get_settings( pos.x, pos.y, pos.z ).weather; + const tripoint abs_ms_pos = sm_to_ms_copy( omt_to_sm_copy( pos ) ); + const auto &wgen = overmap_buffer.get_settings( pos ).weather; const auto weather = wgen.get_weather_conditions( abs_ms_pos, calendar::turn, g->get_seed() ); iter = weather_cache.insert( std::make_pair( pos, weather ) ).first; } @@ -256,7 +256,7 @@ static void draw_city_labels( const catacurses::window &w, const tripoint ¢e continue; // right under the cursor. } - if( !overmap_buffer.seen( city_pos.x, city_pos.y, center.z ) ) { + if( !overmap_buffer.seen( tripoint( city_pos, center.z ) ) ) { continue; // haven't seen it. } @@ -294,7 +294,7 @@ static void draw_camp_labels( const catacurses::window &w, const tripoint ¢e continue; // right under the cursor. } - if( !overmap_buffer.seen( camp_pos.x, camp_pos.y, center.z ) ) { + if( !overmap_buffer.seen( tripoint( camp_pos, center.z ) ) ) { continue; // haven't seen it. } @@ -396,7 +396,8 @@ static point draw_notes( const tripoint &origin ) const int distance_player = rl_dist( p_player, p_omt ); const point sm_pos = omt_to_sm_copy( p_omt ); const point p_om = omt_to_om_remain( p_omt ); - const std::string location_desc = overmap_buffer.get_description_at( sm_pos, origin.z ); + const std::string location_desc = + overmap_buffer.get_description_at( tripoint( sm_pos, origin.z ) ); nmenu.addentry_desc( string_format( _( "[%s] %s" ), colorize( note_symbol, note_color ), note_text ), string_format( @@ -423,14 +424,11 @@ void draw( const catacurses::window &w, const catacurses::window &wbar, const tr const tripoint &orig, bool blink, bool show_explored, bool fast_scroll, input_context *inp_ctxt, const draw_data_t &data ) { - const int z = center.z; - const int cursx = center.x; - const int cursy = center.y; const int om_map_width = OVERMAP_WINDOW_WIDTH; const int om_map_height = OVERMAP_WINDOW_HEIGHT; const int om_half_width = om_map_width / 2; const int om_half_height = om_map_height / 2; - const bool viewing_weather = ( ( data.debug_weather || data.visible_weather ) && z == 10 ); + const bool viewing_weather = ( ( data.debug_weather || data.visible_weather ) && center.z == 10 ); // Target of current mission const tripoint target = g->u.get_active_mission_target(); @@ -463,7 +461,7 @@ void draw( const catacurses::window &w, const catacurses::window &wbar, const tr const mongroup *mgroup = nullptr; std::vector mgroups; if( data.debug_mongroup ) { - mgroups = overmap_buffer.monsters_at( center.x, center.y, center.z ); + mgroups = overmap_buffer.monsters_at( center ); for( const auto &mgp : mgroups ) { mgroup = mgp; if( mgp->horde ) { @@ -478,9 +476,8 @@ void draw( const catacurses::window &w, const catacurses::window &wbar, const tr std::array, cache_size> cache {{}}; size_t cache_next = 0; - const auto set_color_and_symbol = [&]( const oter_id & cur_ter, const int omx, const int omy, - const int z, std::string & ter_sym, - nc_color & ter_color ) { + const auto set_color_and_symbol = [&]( const oter_id & cur_ter, const tripoint & omp, + std::string & ter_sym, nc_color & ter_color ) { // First see if we have the oter_t cached oter_t const *info = nullptr; for( const auto &c : cache ) { @@ -497,14 +494,13 @@ void draw( const catacurses::window &w, const catacurses::window &wbar, const tr } // Ok, we found something if( info ) { - const bool explored = show_explored && overmap_buffer.is_explored( omx, omy, z ); + const bool explored = show_explored && overmap_buffer.is_explored( omp ); ter_color = explored ? c_dark_gray : info->get_color( uistate.overmap_show_land_use_codes ); ter_sym = info->get_symbol( uistate.overmap_show_land_use_codes ); } }; - const int offset_x = cursx - om_half_width; - const int offset_y = cursy - om_half_height; + const tripoint corner = center - point( om_half_width, om_half_height ); // For use with place_special: cache the color and symbol of each submap // and record the bounds to optimize lookups below @@ -540,12 +536,12 @@ void draw( const catacurses::window &w, const catacurses::window &wbar, const tr if( blink ) { const auto &npcs = overmap_buffer.get_npcs_near_player( sight_points ); for( const auto &np : npcs ) { - if( np->posz() != z ) { + if( np->posz() != center.z ) { continue; } const tripoint pos = np->global_omt_location(); - if( has_debug_vision || overmap_buffer.seen( pos.x, pos.y, pos.z ) ) { + if( has_debug_vision || overmap_buffer.seen( pos ) ) { auto iter = npc_color.find( pos ); nc_color np_color = np->basic_symbol_color(); if( iter == npc_color.end() ) { @@ -582,7 +578,7 @@ void draw( const catacurses::window &w, const catacurses::window &wbar, const tr player_path_route.push_back( tri_to_add ); } for( const auto &np : followers ) { - if( np->posz() != z ) { + if( np->posz() != center.z ) { continue; } if( !np->omt_path.empty() ) { @@ -608,60 +604,58 @@ void draw( const catacurses::window &w, const catacurses::window &wbar, const tr for( int i = 0; i < om_map_width; ++i ) { for( int j = 0; j < om_map_height; ++j ) { - const int omx = i + offset_x; - const int omy = j + offset_y; + const tripoint omp = corner + point( i, j ); oter_id cur_ter = oter_str_id::NULL_ID(); nc_color ter_color = c_black; std::string ter_sym = " "; - const bool see = has_debug_vision || overmap_buffer.seen( omx, omy, z ); + const bool see = has_debug_vision || overmap_buffer.seen( omp ); if( see ) { // Only load terrain if we can actually see it - cur_ter = overmap_buffer.ter( omx, omy, z ); + cur_ter = overmap_buffer.ter( omp ); } - const tripoint cur_pos {omx, omy, z}; // Check if location is within player line-of-sight - const bool los = see && g->u.overmap_los( cur_pos, sight_points ); - const bool los_sky = g->u.overmap_los( cur_pos, sight_points * 2 ); - int mycount = std::count( path_route.begin(), path_route.end(), cur_pos ); + const bool los = see && g->u.overmap_los( omp, sight_points ); + const bool los_sky = g->u.overmap_los( omp, sight_points * 2 ); + int mycount = std::count( path_route.begin(), path_route.end(), omp ); bool player_path_count = false; std::vector::iterator it; - it = std::find( player_path_route.begin(), player_path_route.end(), cur_pos ); + it = std::find( player_path_route.begin(), player_path_route.end(), omp ); if( it != player_path_route.end() ) { player_path_count = true; } - if( blink && cur_pos == orig ) { + if( blink && omp == orig ) { // Display player pos, should always be visible ter_color = g->u.symbol_color(); ter_sym = "@"; } else if( viewing_weather && ( data.debug_weather || los_sky ) ) { - const weather_type type = get_weather_at_point( tripoint( omx, omy, z ) ); + const weather_type type = get_weather_at_point( omp ); ter_color = weather::map_color( type ); ter_sym = weather::glyph( type ); - } else if( data.debug_scent && get_scent_glyph( cur_pos, ter_color, ter_sym ) ) { - } else if( blink && has_target && omx == target.x && omy == target.y ) { + } else if( data.debug_scent && get_scent_glyph( omp, ter_color, ter_sym ) ) { + } else if( blink && has_target && omp.xy() == target.xy() ) { // Mission target, display always, player should know where it is anyway. ter_color = c_red; ter_sym = "*"; - if( target.z > z ) { + if( target.z > center.z ) { ter_sym = "^"; - } else if( target.z < z ) { + } else if( target.z < center.z ) { ter_sym = "v"; } - } else if( blink && uistate.overmap_show_map_notes && overmap_buffer.has_note( cur_pos ) ) { + } else if( blink && uistate.overmap_show_map_notes && overmap_buffer.has_note( omp ) ) { // Display notes in all situations, even when not seen std::tie( ter_sym, ter_color, std::ignore ) = - get_note_display_info( overmap_buffer.note( cur_pos ) ); + get_note_display_info( overmap_buffer.note( omp ) ); } else if( !see ) { // All cases above ignore the seen-status, ter_color = c_dark_gray; ter_sym = "#"; // All cases below assume that see is true. - } else if( blink && npc_color.count( cur_pos ) != 0 ) { + } else if( blink && npc_color.count( omp ) != 0 ) { // Visible NPCs are cached already - ter_color = npc_color[ cur_pos ].color; + ter_color = npc_color[ omp ].color; ter_sym = "@"; } else if( blink && mycount != 0 && g->debug_pathfinding ) { ter_color = c_red; @@ -670,25 +664,25 @@ void draw( const catacurses::window &w, const catacurses::window &wbar, const tr ter_color = c_blue; ter_sym = "!"; } else if( blink && showhordes && los && - overmap_buffer.get_horde_size( omx, omy, z ) >= HORDE_VISIBILITY_SIZE ) { + overmap_buffer.get_horde_size( omp ) >= HORDE_VISIBILITY_SIZE ) { // Display Hordes only when within player line-of-sight ter_color = c_green; - ter_sym = overmap_buffer.get_horde_size( omx, omy, z ) > HORDE_VISIBILITY_SIZE * 2 ? "Z" : "z"; - } else if( blink && overmap_buffer.has_vehicle( omx, omy, z ) ) { + ter_sym = overmap_buffer.get_horde_size( omp ) > HORDE_VISIBILITY_SIZE * 2 ? "Z" : "z"; + } else if( blink && overmap_buffer.has_vehicle( omp ) ) { // Display Vehicles only when player can see the location ter_color = c_cyan; ter_sym = "c"; - } else if( !sZoneName.empty() && tripointZone.x == omx && tripointZone.y == omy ) { + } else if( !sZoneName.empty() && tripointZone.xy() == omp.xy() ) { ter_color = c_yellow; ter_sym = "Z"; } else if( !uistate.overmap_show_forest_trails && cur_ter && is_ot_match( "forest_trail", cur_ter, ot_match_type::type ) ) { // If forest trails shouldn't be displayed, and this is a forest trail, then // instead render it like a forest. - set_color_and_symbol( forest, omx, omy, z, ter_sym, ter_color ); + set_color_and_symbol( forest, omp, ter_sym, ter_color ); } else { // Nothing special, but is visible to the player. - set_color_and_symbol( cur_ter, omx, omy, z, ter_sym, ter_color ); + set_color_and_symbol( cur_ter, omp, ter_sym, ter_color ); } // Are we debugging monster groups? @@ -696,15 +690,15 @@ void draw( const catacurses::window &w, const catacurses::window &wbar, const tr // Check if this tile is the target of the currently selected group // Convert to position within overmap - int localx = omx * 2; - int localy = omy * 2; - sm_to_om_remain( localx, localy ); + point omp_in_om( omp.xy() ); + omt_to_om_remain( omp_in_om ); + point group_target = sm_to_omt_copy( mgroup->target.xy() ); - if( mgroup && mgroup->target.x / 2 == localx / 2 && mgroup->target.y / 2 == localy / 2 ) { + if( mgroup && group_target == omp_in_om ) { ter_color = c_red; ter_sym = "x"; } else { - const auto &groups = overmap_buffer.monsters_at( omx, omy, center.z ); + const auto &groups = overmap_buffer.monsters_at( omp ); for( auto &mgp : groups ) { if( mgp->type == mongroup_id( "GROUP_FOREST" ) ) { // Don't flood the map with forest creatures. @@ -732,14 +726,14 @@ void draw( const catacurses::window &w, const catacurses::window &wbar, const tr // Preview for place_terrain or place_special if( uistate.place_terrain || uistate.place_special ) { - if( blink && uistate.place_terrain && omx == cursx && omy == cursy ) { + if( blink && uistate.place_terrain && omp.xy() == center.xy() ) { ter_color = uistate.place_terrain->get_color(); ter_sym = uistate.place_terrain->get_symbol(); } else if( blink && uistate.place_special ) { - if( omx - cursx >= s_begin.x && omx - cursx <= s_end.x && - omy - cursy >= s_begin.y && omy - cursy <= s_end.y ) { - const point cache_point( omx - cursx, omy - cursy ); - const auto sm = special_cache.find( cache_point ); + const point from_center = omp.xy() - center.xy(); + if( from_center.x >= s_begin.x && from_center.x <= s_end.x && + from_center.y >= s_begin.y && from_center.y <= s_end.y ) { + const auto sm = special_cache.find( from_center ); if( sm != special_cache.end() ) { ter_color = sm->second.second; @@ -749,12 +743,12 @@ void draw( const catacurses::window &w, const catacurses::window &wbar, const tr } // Highlight areas that already have been generated if( MAPBUFFER.lookup_submap( - omt_to_sm_copy( tripoint( omx, omy, z ) ) ) ) { + omt_to_sm_copy( omp ) ) ) { ter_color = red_background( ter_color ); } } - if( omx == cursx && omy == cursy && !uistate.place_special ) { + if( omp.xy() == center.xy() && !uistate.place_special ) { csee = see; ccur_ter = cur_ter; mvwputch_hi( w, j, i, ter_color, ter_sym ); @@ -764,21 +758,18 @@ void draw( const catacurses::window &w, const catacurses::window &wbar, const tr } } - if( z == 0 && uistate.overmap_show_city_labels ) { - draw_city_labels( w, tripoint( cursx, cursy, z ) ); - draw_camp_labels( w, tripoint( cursx, cursy, z ) ); + if( center.z == 0 && uistate.overmap_show_city_labels ) { + draw_city_labels( w, center ); + draw_camp_labels( w, center ); } - if( has_target && blink && - ( target.x < offset_x || - target.x >= offset_x + om_map_width || - target.y < offset_y || - target.y >= offset_y + om_map_height ) ) { - int marker_x = clamp( target.x - offset_x, 0, om_map_width - 1 ); - int marker_y = clamp( target.y - offset_y, 0, om_map_height - 1 ); + rectangle screen_bounds( corner.xy(), corner.xy() + point( om_map_width, om_map_height ) ); + + if( has_target && blink && !screen_bounds.contains_half_open( target.xy() ) ) { + point marker = clamp_half_open( target.xy(), screen_bounds ) - corner.xy(); std::string marker_sym = " "; - switch( direction_from( cursx, cursy, target.x, target.y ) ) { + switch( direction_from( center, target ) ) { case NORTH: marker_sym = "^"; break; @@ -806,13 +797,13 @@ void draw( const catacurses::window &w, const catacurses::window &wbar, const tr default: break; //Do nothing } - mvwputch( w, marker_y, marker_x, c_red, marker_sym ); + mvwputch( w, marker.y, marker.x, c_red, marker_sym ); } std::vector> corner_text; if( uistate.overmap_show_map_notes ) { - const std::string ¬e_text = overmap_buffer.note( cursx, cursy, z ); + const std::string ¬e_text = overmap_buffer.note( center ); if( !note_text.empty() ) { const std::tuple note_info = get_note_display_info( note_text ); @@ -823,13 +814,13 @@ void draw( const catacurses::window &w, const catacurses::window &wbar, const tr } } - for( const auto &npc : overmap_buffer.get_npcs_near_omt( cursx, cursy, z, 0 ) ) { + for( const auto &npc : overmap_buffer.get_npcs_near_omt( center, 0 ) ) { if( !npc->marked_for_death ) { corner_text.emplace_back( npc->basic_symbol_color(), npc->name ); } } - for( auto &v : overmap_buffer.get_vehicle( cursx, cursy, z ) ) { + for( auto &v : overmap_buffer.get_vehicle( center ) ) { corner_text.emplace_back( c_white, v.name ); } @@ -854,7 +845,7 @@ void draw( const catacurses::window &w, const catacurses::window &wbar, const tr mvwputch( w, corner_text.size(), maxlen, c_white, LINE_XOOX ); } - if( !sZoneName.empty() && tripointZone.x == cursx && tripointZone.y == cursy ) { + if( !sZoneName.empty() && tripointZone.xy() == center.xy() ) { std::string sTemp = _( "Zone:" ); sTemp += " " + sZoneName; @@ -902,18 +893,17 @@ void draw( const catacurses::window &w, const catacurses::window &wbar, const tr } } else { const auto &ter = ccur_ter.obj(); - const auto sm_pos = omt_to_sm_copy( tripoint( cursx, cursy, z ) ); + const auto sm_pos = omt_to_sm_copy( center ); mvwputch( wbar, 1, 1, ter.get_color(), ter.get_symbol() ); lines = fold_and_print( wbar, 1, 3, 25, c_light_gray, overmap_buffer.get_description_at( sm_pos ) ); } } else if( viewing_weather ) { - const auto curs_pos = tripoint( cursx, cursy, z ); const bool weather_is_visible = ( data.debug_weather || - g->u.overmap_los( curs_pos, sight_points * 2 ) ); + g->u.overmap_los( center, sight_points * 2 ) ); if( weather_is_visible ) { - weather_datum weather = weather_data( get_weather_at_point( curs_pos ) ); + weather_datum weather = weather_data( get_weather_at_point( center ) ); mvwprintz( wbar, 1, 1, weather.color, weather.name ); } else { mvwprintz( wbar, 1, 1, c_dark_gray, _( "# Unexplored" ) ); @@ -956,7 +946,7 @@ void draw( const catacurses::window &w, const catacurses::window &wbar, const tr } const bool show_overlays = uistate.overmap_show_overlays || uistate.overmap_blinking; - const bool is_explored = overmap_buffer.is_explored( cursx, cursy, z ); + const bool is_explored = overmap_buffer.is_explored( center ); print_hint( "LEVEL_UP" ); print_hint( "LEVEL_DOWN" ); @@ -978,10 +968,10 @@ void draw( const catacurses::window &w, const catacurses::window &wbar, const tr print_hint( "QUIT" ); } - point omt( cursx, cursy ); + point omt = center.xy(); const point om = omt_to_om_remain( omt ); mvwprintz( wbar, getmaxy( wbar ) - 1, 1, c_red, - _( "LEVEL %i, %d'%d, %d'%d" ), z, om.x, omt.x, om.y, omt.y ); + _( "LEVEL %i, %d'%d, %d'%d" ), center.z, om.x, omt.x, om.y, omt.y ); // draw nice crosshair around the cursor if( blink && !uistate.place_terrain && !uistate.place_special ) { @@ -1268,12 +1258,12 @@ static void place_ter_or_special( tripoint &curs, const tripoint &orig, const bo } else if( action == "CONFIRM" ) { // Actually modify the overmap if( terrain ) { overmap_buffer.ter( curs ) = uistate.place_terrain->id.id(); - overmap_buffer.set_seen( curs.x, curs.y, curs.z, true ); + overmap_buffer.set_seen( curs, true ); } else { overmap_buffer.place_special( *uistate.place_special, curs, uistate.omedit_rotation, false, true ); for( const overmap_special_terrain &s_ter : uistate.place_special->terrains ) { const tripoint pos = curs + om_direction::rotate( s_ter.p, uistate.omedit_rotation ); - overmap_buffer.set_seen( pos.x, pos.y, pos.z, true ); + overmap_buffer.set_seen( pos, true ); } } break; @@ -1460,7 +1450,7 @@ static tripoint display( const tripoint &orig, const draw_data_t &data = draw_da } else if( action == "TOGGLE_CITY_LABELS" ) { uistate.overmap_show_city_labels = !uistate.overmap_show_city_labels; } else if( action == "TOGGLE_EXPLORED" ) { - overmap_buffer.toggle_explored( curs.x, curs.y, curs.z ); + overmap_buffer.toggle_explored( curs ); } else if( action == "TOGGLE_FAST_SCROLL" ) { fast_scroll = !fast_scroll; } else if( action == "TOGGLE_FOREST_TRAILS" ) { diff --git a/src/overmapbuffer.cpp b/src/overmapbuffer.cpp index 313905ef7b82e..d26dbc7716668 100644 --- a/src/overmapbuffer.cpp +++ b/src/overmapbuffer.cpp @@ -60,29 +60,27 @@ int camp_reference::get_distance_from_bounds() const return distance - omt_to_sm_copy( 4 ); } -std::string overmapbuffer::terrain_filename( const int x, const int y ) +std::string overmapbuffer::terrain_filename( const point &p ) { std::ostringstream filename; filename << g->get_world_base_save_path() << "/"; - filename << "o." << x << "." << y; + filename << "o." << p.x << "." << p.y; return filename.str(); } -std::string overmapbuffer::player_filename( const int x, const int y ) +std::string overmapbuffer::player_filename( const point &p ) { std::ostringstream filename; - filename << g->get_player_base_save_path() << ".seen." << x << "." << y; + filename << g->get_player_base_save_path() << ".seen." << p.x << "." << p.y; return filename.str(); } -overmap &overmapbuffer::get( const int x, const int y ) +overmap &overmapbuffer::get( const point &p ) { - const point p { x, y }; - if( last_requested_overmap != nullptr && last_requested_overmap->pos() == p ) { return *last_requested_overmap; } @@ -93,7 +91,7 @@ overmap &overmapbuffer::get( const int x, const int y ) } // That constructor loads an existing overmap or creates a new one. - overmap &new_om = *( overmaps[ p ] = std::make_unique( x, y ) ); + overmap &new_om = *( overmaps[ p ] = std::make_unique( p ) ); new_om.populate(); // Note: fix_mongroups might load other overmaps, so overmaps.back() is not // necessarily the overmap at (x,y) @@ -104,17 +102,15 @@ overmap &overmapbuffer::get( const int x, const int y ) return new_om; } -void overmapbuffer::create_custom_overmap( const int x, const int y, - overmap_special_batch &specials ) +void overmapbuffer::create_custom_overmap( const point &p, overmap_special_batch &specials ) { - point p( x, y ); if( last_requested_overmap != nullptr ) { auto om_iter = overmaps.find( p ); if( om_iter != overmaps.end() && om_iter->second.get() == last_requested_overmap ) { last_requested_overmap = nullptr; } } - overmap &new_om = *( overmaps[ p ] = std::make_unique( x, y ) ); + overmap &new_om = *( overmaps[ p ] = std::make_unique( p ) ); new_om.populate( specials ); } @@ -136,13 +132,13 @@ void overmapbuffer::fix_mongroups( overmap &new_overmap ) point smabs( mg.pos.x + new_overmap.pos().x * OMAPX * 2, mg.pos.y + new_overmap.pos().y * OMAPY * 2 ); point omp = sm_to_om_remain( smabs ); - if( !has( omp.x, omp.y ) ) { + if( !has( omp ) ) { // Don't generate new overmaps, as this can be called from the // overmap-generating code. ++it; continue; } - overmap &om = get( omp.x, omp.y ); + overmap &om = get( omp ); mg.pos.x = smabs.x; mg.pos.y = smabs.y; om.add_mon_group( mg ); @@ -180,7 +176,7 @@ void overmapbuffer::fix_npcs( overmap &new_overmap ) const tripoint npc_omt_pos = np.global_omt_location(); const point npc_om_pos = omt_to_om_copy( npc_omt_pos.x, npc_omt_pos.y ); const point &loc = new_overmap.pos(); - if( !has( npc_om_pos.x, npc_om_pos.y ) ) { + if( !has( npc_om_pos ) ) { // This can't really happen without save editing // We have no sane option here, just place the NPC on the edge debugmsg( "NPC %s is out of bounds, on non-generated overmap %d,%d", @@ -196,7 +192,7 @@ void overmapbuffer::fix_npcs( overmap &new_overmap ) } // Simplest case: just move the pointer - get( npc_om_pos.x, npc_om_pos.y ).insert_npc( ptr ); + get( npc_om_pos ).insert_npc( ptr ); } } @@ -215,45 +211,46 @@ void overmapbuffer::clear() last_requested_overmap = nullptr; } -const regional_settings &overmapbuffer::get_settings( int x, int y, int z ) +const regional_settings &overmapbuffer::get_settings( const tripoint &p ) { - ( void )z; - overmap &om = get_om_global( x, y ); + overmap &om = get_om_global( p ); return om.get_settings(); } -void overmapbuffer::add_note( int x, int y, int z, const std::string &message ) +void overmapbuffer::add_note( const tripoint &p, const std::string &message ) { - overmap &om = get_om_global( x, y ); - om.add_note( x, y, z, message ); + tripoint local( p ); + overmap &om = get_om_global( local.x, local.y ); + om.add_note( local, message ); } -void overmapbuffer::delete_note( int x, int y, int z ) +void overmapbuffer::delete_note( const tripoint &p ) { - if( has_note( x, y, z ) ) { - overmap &om = get_om_global( x, y ); - om.delete_note( x, y, z ); + if( has_note( p ) ) { + tripoint local( p ); + overmap &om = get_om_global( local.x, local.y ); + om.delete_note( local ); } } -void overmapbuffer::add_extra( int x, int y, int z, const string_id &id ) +void overmapbuffer::add_extra( const tripoint &p, const string_id &id ) { - overmap &om = get_om_global( x, y ); - om.add_extra( x, y, z, id ); + tripoint local( p ); + overmap &om = get_om_global( local.x, local.y ); + om.add_extra( local, id ); } -void overmapbuffer::delete_extra( int x, int y, int z ) +void overmapbuffer::delete_extra( const tripoint &p ) { - if( has_extra( x, y, z ) ) { - overmap &om = get_om_global( x, y ); - om.delete_extra( x, y, z ); + if( has_extra( p ) ) { + tripoint local( p ); + overmap &om = get_om_global( local.x, local.y ); + om.delete_extra( local ); } } -overmap *overmapbuffer::get_existing( int x, int y ) +overmap *overmapbuffer::get_existing( const point &p ) { - const point p {x, y}; - if( last_requested_overmap && last_requested_overmap->pos() == p ) { return last_requested_overmap; } @@ -266,10 +263,10 @@ overmap *overmapbuffer::get_existing( int x, int y ) // checked in a previous call of this function). return nullptr; } - if( file_exist( terrain_filename( x, y ) ) ) { + if( file_exist( terrain_filename( p ) ) ) { // File exists, load it normally (the get function // indirectly call overmap::open to do so). - return &get( x, y ); + return &get( p ); } // File does not exist (or not readable which is essentially // the same for our usage). A second call of this function with @@ -281,27 +278,26 @@ overmap *overmapbuffer::get_existing( int x, int y ) return nullptr; } -bool overmapbuffer::has( int x, int y ) +bool overmapbuffer::has( const point &p ) { - return get_existing( x, y ) != nullptr; + return get_existing( p ) != nullptr; } overmap &overmapbuffer::get_om_global( int &x, int &y ) { const point om_pos = omt_to_om_remain( x, y ); - return get( om_pos.x, om_pos.y ); + return get( om_pos ); } overmap &overmapbuffer::get_om_global( const point &p ) { const point om_pos = omt_to_om_copy( p ); - return get( om_pos.x, om_pos.y ); + return get( om_pos ); } overmap &overmapbuffer::get_om_global( const tripoint &p ) { - const point om_pos = omt_to_om_copy( { p.x, p.y } ); - return get( om_pos.x, om_pos.y ); + return get_om_global( p.xy() ); } overmap_with_local_coordinates overmapbuffer::get_om_global_with_coordinates( const tripoint &p ) @@ -309,26 +305,25 @@ overmap_with_local_coordinates overmapbuffer::get_om_global_with_coordinates( co int x = p.x; int y = p.y; const point om_pos = omt_to_om_remain( x, y ); - overmap *om = &get( om_pos.x, om_pos.y ); + overmap *om = &get( om_pos ); return { om, tripoint( x, y, p.z ) }; } overmap *overmapbuffer::get_existing_om_global( int &x, int &y ) { const point om_pos = omt_to_om_remain( x, y ); - return get_existing( om_pos.x, om_pos.y ); + return get_existing( om_pos ); } overmap *overmapbuffer::get_existing_om_global( const point &p ) { const point om_pos = omt_to_om_copy( p ); - return get_existing( om_pos.x, om_pos.y ); + return get_existing( om_pos ); } overmap *overmapbuffer::get_existing_om_global( const tripoint &p ) { - const tripoint om_pos = omt_to_om_copy( p ); - return get_existing( om_pos.x, om_pos.y ); + return get_existing_om_global( p.xy() ); } cata::optional @@ -338,7 +333,7 @@ overmapbuffer::get_existing_om_global_with_coordinates( int x = p.x; int y = p.y; const point om_pos = omt_to_om_remain( x, y ); - overmap *om = get_existing( om_pos.x, om_pos.y ); + overmap *om = get_existing( om_pos ); if( om == nullptr ) { return cata::nullopt; } @@ -359,53 +354,59 @@ bool overmapbuffer::is_omt_generated( const tripoint &loc ) return om->overmap_pointer->is_omt_generated( om->coordinates ); } -bool overmapbuffer::has_note( int x, int y, int z ) +bool overmapbuffer::has_note( const tripoint &p ) { - const overmap *om = get_existing_om_global( x, y ); - return ( om != nullptr ) && om->has_note( x, y, z ); + tripoint local( p ); + const overmap *om = get_existing_om_global( local.x, local.y ); + return ( om != nullptr ) && om->has_note( local ); } -const std::string &overmapbuffer::note( int x, int y, int z ) +const std::string &overmapbuffer::note( const tripoint &p ) { - const overmap *om = get_existing_om_global( x, y ); + tripoint local( p ); + const overmap *om = get_existing_om_global( local.x, local.y ); if( om == nullptr ) { static const std::string empty_string; return empty_string; } - return om->note( x, y, z ); + return om->note( local ); } -bool overmapbuffer::has_extra( int x, int y, int z ) +bool overmapbuffer::has_extra( const tripoint &p ) { - const overmap *om = get_existing_om_global( x, y ); - return ( om != nullptr ) && om->has_extra( x, y, z ); + tripoint local( p ); + const overmap *om = get_existing_om_global( local.x, local.y ); + return ( om != nullptr ) && om->has_extra( local ); } -const string_id &overmapbuffer::extra( int x, int y, int z ) +const string_id &overmapbuffer::extra( const tripoint &p ) { - const overmap *om = get_existing_om_global( x, y ); + tripoint local( p ); + const overmap *om = get_existing_om_global( local.x, local.y ); if( om == nullptr ) { static string_id id; return id; } - return om->extra( x, y, z ); + return om->extra( local ); } -bool overmapbuffer::is_explored( int x, int y, int z ) +bool overmapbuffer::is_explored( const tripoint &p ) { - const overmap *om = get_existing_om_global( x, y ); - return ( om != nullptr ) && om->is_explored( x, y, z ); + tripoint local( p ); + const overmap *om = get_existing_om_global( local.x, local.y ); + return ( om != nullptr ) && om->is_explored( local ); } -void overmapbuffer::toggle_explored( int x, int y, int z ) +void overmapbuffer::toggle_explored( const tripoint &p ) { - overmap &om = get_om_global( x, y ); - om.explored( x, y, z ) = !om.explored( x, y, z ); + tripoint local( p ); + overmap &om = get_om_global( local.x, local.y ); + om.explored( local ) = !om.explored( local ); } -bool overmapbuffer::has_horde( const int x, const int y, const int z ) +bool overmapbuffer::has_horde( const tripoint &p ) { - for( const auto &m : overmap_buffer.monsters_at( x, y, z ) ) { + for( const auto &m : overmap_buffer.monsters_at( p ) ) { if( m->horde ) { return true; } @@ -414,10 +415,10 @@ bool overmapbuffer::has_horde( const int x, const int y, const int z ) return false; } -int overmapbuffer::get_horde_size( const int x, const int y, const int z ) +int overmapbuffer::get_horde_size( const tripoint &p ) { int horde_size = 0; - for( const auto &m : overmap_buffer.monsters_at( x, y, z ) ) { + for( const auto &m : overmap_buffer.monsters_at( p ) ) { if( m->horde ) { if( !m->monsters.empty() ) { horde_size += m->monsters.size(); @@ -434,19 +435,20 @@ int overmapbuffer::get_horde_size( const int x, const int y, const int z ) return horde_size; } -bool overmapbuffer::has_camp( int x, int y, int z ) +bool overmapbuffer::has_camp( const tripoint &p ) { - if( z ) { + if( p.z ) { return false; } - const overmap *const om = get_existing_om_global( x, y ); + point local( p.xy() ); + const overmap *const om = get_existing_om_global( local.x, local.y ); if( !om ) { return false; } for( const auto &v : om->camps ) { - if( v.camp_omt_pos().x == x && v.camp_omt_pos().y == y ) { + if( v.camp_omt_pos().xy() == local ) { return true; } } @@ -454,19 +456,20 @@ bool overmapbuffer::has_camp( int x, int y, int z ) return false; } -bool overmapbuffer::has_vehicle( int x, int y, int z ) +bool overmapbuffer::has_vehicle( const tripoint &p ) { - if( z ) { + if( p.z ) { return false; } - const overmap *const om = get_existing_om_global( x, y ); + point local( p.xy() ); + const overmap *const om = get_existing_om_global( local.x, local.y ); if( !om ) { return false; } for( const auto &v : om->vehicles ) { - if( v.second.x == x && v.second.y == y ) { + if( v.second.p == local ) { return true; } } @@ -474,18 +477,19 @@ bool overmapbuffer::has_vehicle( int x, int y, int z ) return false; } -std::vector overmapbuffer::get_vehicle( int x, int y, int z ) +std::vector overmapbuffer::get_vehicle( const tripoint &p ) { std::vector result; - if( z != 0 ) { + if( p.z != 0 ) { return result; } - overmap *om = get_existing_om_global( x, y ); + point local( p.xy() ); + overmap *om = get_existing_om_global( local.x, local.y ); if( om == nullptr ) { return result; } for( const auto &ov : om->vehicles ) { - if( ov.second.x == x && ov.second.y == y ) { + if( ov.second.p == local ) { result.push_back( ov.second ); } } @@ -524,33 +528,31 @@ void overmapbuffer::move_hordes() } } -std::vector overmapbuffer::monsters_at( int x, int y, int z ) +std::vector overmapbuffer::monsters_at( const tripoint &p ) { // (x,y) are overmap terrain coordinates, they spawn 2x2 submaps, // but monster groups are defined with submap coordinates. + tripoint p_sm = omt_to_sm_copy( p ); std::vector result; - std::vector tmp = groups_at( x * 2, y * 2, z ); - result.insert( result.end(), tmp.begin(), tmp.end() ); - tmp = groups_at( x * 2, y * 2 + 1, z ); - result.insert( result.end(), tmp.begin(), tmp.end() ); - tmp = groups_at( x * 2 + 1, y * 2 + 1, z ); - result.insert( result.end(), tmp.begin(), tmp.end() ); - tmp = groups_at( x * 2 + 1, y * 2, z ); - result.insert( result.end(), tmp.begin(), tmp.end() ); + for( point offset : std::array { { { 0, 0 }, { 0, 1 }, { 1, 0 }, { 1, 1 } } } ) { + std::vector tmp = groups_at( p_sm + offset ); + result.insert( result.end(), tmp.begin(), tmp.end() ); + } return result; } -std::vector overmapbuffer::groups_at( int x, int y, int z ) +std::vector overmapbuffer::groups_at( const tripoint &p ) { std::vector result; - const point omp = sm_to_om_remain( x, y ); - if( !has( omp.x, omp.y ) ) { + point sm_within_om( p.xy() ); + const point omp = sm_to_om_remain( sm_within_om ); + if( !has( omp ) ) { return result; } - const tripoint dpos( x, y, z ); - overmap &om = get( omp.x, omp.y ); - for( auto it = om.zg.lower_bound( dpos ), end = om.zg.upper_bound( dpos ); it != end; ++it ) { - auto &mg = it->second; + overmap &om = get( omp ); + auto groups_range = om.zg.equal_range( tripoint( sm_within_om, p.z ) ); + for( auto it = groups_range.first; it != groups_range.second; ++it ) { + mongroup &mg = it->second; if( mg.empty() ) { continue; } @@ -600,8 +602,7 @@ void overmapbuffer::move_vehicle( vehicle *veh, const point &old_msp ) overmap &new_om = get_om_global( new_omt.x, new_omt.y ); // *_omt is now local to the overmap, and it's in overmap terrain system if( &old_om == &new_om ) { - new_om.vehicles[veh->om_id].x = new_omt.x; - new_om.vehicles[veh->om_id].y = new_omt.y; + new_om.vehicles[veh->om_id].p = new_omt; } else { old_om.vehicles.erase( veh->om_id ); add_vehicle( veh ); @@ -640,8 +641,7 @@ void overmapbuffer::add_vehicle( vehicle *veh ) id++; } om_vehicle &tracked_veh = om.vehicles[id]; - tracked_veh.x = omt.x; - tracked_veh.y = omt.y; + tracked_veh.p = omt; tracked_veh.name = veh->name; veh->om_id = id; } @@ -653,22 +653,25 @@ void overmapbuffer::add_camp( const basecamp &camp ) om.camps.push_back( camp ); } -bool overmapbuffer::seen( int x, int y, int z ) +bool overmapbuffer::seen( const tripoint &p ) { - const overmap *om = get_existing_om_global( x, y ); - return ( om != nullptr ) && const_cast( om )->seen( x, y, z ); + tripoint local( p ); + const overmap *om = get_existing_om_global( local.x, local.y ); + return ( om != nullptr ) && om->seen( local ); } -void overmapbuffer::set_seen( int x, int y, int z, bool seen ) +void overmapbuffer::set_seen( const tripoint &p, bool seen ) { - overmap &om = get_om_global( x, y ); - om.seen( x, y, z ) = seen; + tripoint local( p ); + overmap &om = get_om_global( local.x, local.y ); + om.seen( local ) = seen; } -oter_id &overmapbuffer::ter( int x, int y, int z ) +oter_id &overmapbuffer::ter( const tripoint &p ) { - overmap &om = get_om_global( x, y ); - return om.ter( x, y, z ); + tripoint local( p ); + overmap &om = get_om_global( local.x, local.y ); + return om.ter( local ); } bool overmapbuffer::reveal( const point ¢er, int radius, int z ) @@ -690,24 +693,23 @@ bool overmapbuffer::reveal( const tripoint ¢er, int radius, bool result = false; for( int i = -radius; i <= radius; i++ ) { for( int j = -radius; j <= radius; j++ ) { - const int x = center.x + i; - const int y = center.y + j; - if( seen( x, y, center.z ) ) { + const tripoint p = center + point( i, j ); + if( seen( p ) ) { continue; } if( trigdist && i * i + j * j > radius_squared ) { continue; } - // We need to make new ints to pass by reference to get_om_global, because it modifies them - // to be local to that overmap. - int local_x = center.x + i; - int local_y = center.y + j; - const oter_id &ter = get_om_global( local_x, local_y ).get_ter( local_x, local_y, 0 ); + // We need to make new coords to pass by reference to get_om_global, + // because it modifies them to be local to that overmap. + tripoint local = p; + const overmap &om = get_om_global( local.x, local.y ); + const oter_id &ter = om.get_ter( local.x, local.y, 0 ); if( !filter( ter ) ) { continue; } result = true; - set_seen( x, y, center.z, true ); + set_seen( p, true ); } } return result; @@ -849,16 +851,11 @@ bool overmapbuffer::check_overmap_special_type_existing( const overmap_special_i return om->overmap_pointer->check_overmap_special_type( id, om->coordinates ); } -bool overmapbuffer::check_ot( const std::string &type, ot_match_type match_type, int x, int y, - int z ) +bool overmapbuffer::check_ot( const std::string &type, ot_match_type match_type, const tripoint &p ) { - overmap &om = get_om_global( x, y ); - return om.check_ot( type, match_type, x, y, z ); -} -bool overmapbuffer::check_ot( const std::string &type, ot_match_type match_type, - const tripoint &loc ) -{ - return check_ot( type, match_type, loc.x, loc.y, loc.z ); + tripoint local = p; + overmap &om = get_om_global( local.x, local.y ); + return om.check_ot( type, match_type, local ); } bool overmapbuffer::check_overmap_special_type( const overmap_special_id &id, const tripoint &loc ) @@ -893,10 +890,10 @@ bool overmapbuffer::is_findable_location( const tripoint &location, const omt_fi return false; } - if( params.must_see && !seen( location.x, location.y, location.z ) ) { + if( params.must_see && !seen( location ) ) { return false; } - if( params.cant_see && seen( location.x, location.y, location.z ) ) { + if( params.cant_see && seen( location ) ) { return false; } @@ -1038,14 +1035,11 @@ std::shared_ptr overmapbuffer::find_npc( int id ) return nullptr; } -cata::optional overmapbuffer::find_camp( const int x, const int y ) +cata::optional overmapbuffer::find_camp( const point &p ) { for( auto &it : overmaps ) { - if( auto p = it.second->find_camp( x, y ) ) { - if( p ) { - basecamp *temp_camp = *p; - return temp_camp; - } + if( cata::optional camp = it.second->find_camp( p ) ) { + return camp; } } return cata::nullopt; @@ -1056,7 +1050,7 @@ void overmapbuffer::insert_npc( const std::shared_ptr &who ) assert( who ); const tripoint npc_omt_pos = who->global_omt_location(); const point npc_om_pos = omt_to_om_copy( npc_omt_pos.x, npc_omt_pos.y ); - get( npc_om_pos.x, npc_om_pos.y ).insert_npc( who ); + get( npc_om_pos ).insert_npc( who ); } std::shared_ptr overmapbuffer::remove_npc( const int id ) @@ -1077,7 +1071,7 @@ std::vector> overmapbuffer::get_npcs_near_player( int radiu omt_to_sm( plpos.x, plpos.y ); // INT_MIN is a (a bit ugly) way to inform get_npcs_near not to filter by z-level const int zpos = g->m.has_zlevels() ? INT_MIN : plpos.z; - return get_npcs_near( plpos.x, plpos.y, zpos, radius ); + return get_npcs_near( tripoint( plpos.xy(), zpos ), radius ); } std::vector overmapbuffer::get_overmaps_near( const tripoint &location, @@ -1094,7 +1088,7 @@ std::vector overmapbuffer::get_overmaps_near( const tripoint &locatio for( int x = start.x; x <= end.x; ++x ) { for( int y = start.y; y <= end.y; ++y ) { - if( const auto existing_om = get_existing( x, y ) ) { + if( overmap *existing_om = get_existing( point( x, y ) ) ) { result.emplace_back( existing_om ); } } @@ -1130,18 +1124,17 @@ std::vector> overmapbuffer::get_companion_mission_npcs() } // If z == INT_MIN, allow all z-levels -std::vector> overmapbuffer::get_npcs_near( int x, int y, int z, int radius ) +std::vector> overmapbuffer::get_npcs_near( const tripoint &p, int radius ) { std::vector> result; - tripoint p{ x, y, z }; for( auto &it : get_overmaps_near( p, radius ) ) { auto temp = it->get_npcs( [&]( const npc & guy ) { // Global position of NPC, in submap coordinates const tripoint pos = guy.global_sm_location(); - if( z != INT_MIN && pos.z != z ) { + if( p.z != INT_MIN && pos.z != p.z ) { return false; } - return square_dist( x, y, pos.x, pos.y ) <= radius; + return square_dist( p.xy(), pos.xy() ) <= radius; } ); result.insert( result.end(), temp.begin(), temp.end() ); } @@ -1149,18 +1142,18 @@ std::vector> overmapbuffer::get_npcs_near( int x, int y, in } // If z == INT_MIN, allow all z-levels -std::vector> overmapbuffer::get_npcs_near_omt( int x, int y, int z, +std::vector> overmapbuffer::get_npcs_near_omt( const tripoint &p, int radius ) { std::vector> result; - for( auto &it : get_overmaps_near( omt_to_sm_copy( x, y ), radius ) ) { + for( auto &it : get_overmaps_near( omt_to_sm_copy( p.xy() ), radius ) ) { auto temp = it->get_npcs( [&]( const npc & guy ) { // Global position of NPC, in submap coordinates tripoint pos = guy.global_omt_location(); - if( z != INT_MIN && pos.z != z ) { + if( p.z != INT_MIN && pos.z != p.z ) { return false; } - return square_dist( x, y, pos.x, pos.y ) <= radius; + return square_dist( p.xy(), pos.xy() ) <= radius; } ); result.insert( result.end(), temp.begin(), temp.end() ); } @@ -1285,7 +1278,7 @@ city_reference overmapbuffer::closest_known_city( const tripoint ¢er ) const auto it = std::find_if( cities.begin(), cities.end(), [this]( const city_reference & elem ) { const tripoint p = sm_to_omt_copy( elem.abs_sm_pos ); - return seen( p.x, p.y, p.z ); + return seen( p ); } ); if( it != cities.end() ) { @@ -1358,13 +1351,13 @@ static int modulo( int v, int m ) return r >= 0 ? r : r + m; } -void overmapbuffer::spawn_monster( const int x, const int y, const int z ) +void overmapbuffer::spawn_monster( const tripoint &p ) { // Create a copy, so we can reuse x and y later - point sm( x, y ); + point sm = p.xy(); const point omp = sm_to_om_remain( sm ); - overmap &om = get( omp.x, omp.y ); - const tripoint current_submap_loc( sm.x, sm.y, z ); + overmap &om = get( omp ); + const tripoint current_submap_loc( tripoint( sm, p.z ) ); auto monster_bucket = om.monster_map.equal_range( current_submap_loc ); std::for_each( monster_bucket.first, monster_bucket.second, [&]( std::pair &monster_entry ) { @@ -1377,10 +1370,9 @@ void overmapbuffer::spawn_monster( const int x, const int y, const int z ) point ms( modulo( this_monster.posx(), SEEX ), modulo( this_monster.posy(), SEEY ) ); assert( ms.x >= 0 && ms.x < SEEX ); assert( ms.y >= 0 && ms.y < SEEX ); - ms.x += x * SEEX; - ms.y += y * SEEY; + ms += sm_to_ms_copy( p.xy() ); // The monster position must be local to the main map when added via game::add_zombie - const tripoint local = tripoint( g->m.getlocal( ms.x, ms.y ), z ); + const tripoint local = tripoint( g->m.getlocal( ms.x, ms.y ), p.z ); assert( g->m.inbounds( local ) ); this_monster.spawn( local ); g->add_zombie( this_monster ); @@ -1394,7 +1386,7 @@ void overmapbuffer::despawn_monster( const monster &critter ) tripoint sm = ms_to_sm_copy( g->m.getabs( critter.pos() ) ); // Get the overmap coordinates and get the overmap, sm is now local to that overmap const point omp = sm_to_om_remain( sm.x, sm.y ); - overmap &om = get( omp.x, omp.y ); + overmap &om = get( omp ); // Store the monster using coordinates local to the overmap. om.monster_map.insert( std::make_pair( sm, critter ) ); } @@ -1454,9 +1446,9 @@ overmapbuffer::t_extras_vector overmapbuffer::get_extras( int z, const std::stri return result; } -bool overmapbuffer::is_safe( int x, int y, int z ) +bool overmapbuffer::is_safe( const tripoint &p ) { - for( auto &mongrp : monsters_at( x, y, z ) ) { + for( auto &mongrp : monsters_at( p ) ) { if( !mongrp->is_safe() ) { return false; } diff --git a/src/overmapbuffer.h b/src/overmapbuffer.h index b646af259c5e4..36de261d5b91a 100644 --- a/src/overmapbuffer.h +++ b/src/overmapbuffer.h @@ -110,71 +110,44 @@ class overmapbuffer public: overmapbuffer(); - static std::string terrain_filename( const int x, const int y ); - static std::string player_filename( const int x, const int y ); + static std::string terrain_filename( const point & ); + static std::string player_filename( const point & ); /** * Uses overmap coordinates, that means x and y are directly * compared with the position of the overmap. */ - overmap &get( const int x, const int y ); + overmap &get( const point & ); void save(); void clear(); - void create_custom_overmap( const int x, const int y, overmap_special_batch &specials ); + void create_custom_overmap( const point &, overmap_special_batch &specials ); /** * Uses global overmap terrain coordinates, creates the * overmap if needed. */ - oter_id &ter( int x, int y, int z ); - oter_id &ter( const tripoint &p ) { - return ter( p.x, p.y, p.z ); - } + oter_id &ter( const tripoint &p ); /** * Uses global overmap terrain coordinates. */ - bool has_note( int x, int y, int z ); - bool has_note( const tripoint &p ) { - return has_note( p.x, p.y, p.z ); - } - const std::string ¬e( int x, int y, int z ); - const std::string ¬e( const tripoint &p ) { - return note( p.x, p.y, p.z ); - } - void add_note( int x, int y, int z, const std::string &message ); - void add_note( const tripoint &p, const std::string &message ) { - add_note( p.x, p.y, p.z, message ); - } - void delete_note( int x, int y, int z ); - void delete_note( const tripoint &p ) { - delete_note( p.x, p.y, p.z ); - } - bool has_extra( int x, int y, int z ); - bool has_extra( const tripoint &p ) { - return has_extra( p.x, p.y, p.z ); - } - const string_id &extra( int x, int y, int z ); - const string_id &extra( const tripoint &p ) { - return extra( p.x, p.y, p.z ); - } - void add_extra( int x, int y, int z, const string_id &id ); - void add_extra( const tripoint &p, const string_id &id ) { - add_extra( p.x, p.y, p.z, id ); - } - void delete_extra( int x, int y, int z ); - void delete_extra( const tripoint &p ) { - delete_extra( p.x, p.y, p.z ); - } - bool is_explored( int x, int y, int z ); - void toggle_explored( int x, int y, int z ); - bool seen( int x, int y, int z ); - void set_seen( int x, int y, int z, bool seen = true ); - bool has_camp( int x, int y, int z ); - bool has_vehicle( int x, int y, int z ); - bool has_horde( int x, int y, int z ); - int get_horde_size( int x, int y, int z ); - std::vector get_vehicle( int x, int y, int z ); - const regional_settings &get_settings( int x, int y, int z ); + bool has_note( const tripoint &p ); + const std::string ¬e( const tripoint &p ); + void add_note( const tripoint &, const std::string &message ); + void delete_note( const tripoint &p ); + bool has_extra( const tripoint &p ); + const string_id &extra( const tripoint &p ); + void add_extra( const tripoint &p, const string_id &id ); + void delete_extra( const tripoint &p ); + bool is_explored( const tripoint &p ); + void toggle_explored( const tripoint &p ); + bool seen( const tripoint &p ); + void set_seen( const tripoint &p, bool seen = true ); + bool has_camp( const tripoint &p ); + bool has_vehicle( const tripoint &p ); + bool has_horde( const tripoint &p ); + int get_horde_size( const tripoint &p ); + std::vector get_vehicle( const tripoint &p ); + const regional_settings &get_settings( const tripoint &p ); /** * Accessors for horde introspection into overmaps. * Probably also useful for NPC overmap-scale navigation. @@ -199,10 +172,7 @@ class overmapbuffer * Check for any dangerous monster groups at the global overmap terrain coordinates. * If there are any, it's not safe. */ - bool is_safe( int x, int y, int z ); - bool is_safe( const tripoint &p ) { - return is_safe( p.x, p.y, p.z ); - } + bool is_safe( const tripoint &p ); /** * Move the tracking mark of the given vehicle. @@ -231,7 +201,7 @@ class overmapbuffer */ void add_camp( const basecamp &camp ); - cata::optional find_camp( const int x, const int y ); + cata::optional find_camp( const point &p ); /** * Get all npcs in a area with given radius around (x, y). * Only npcs on the given z-level are considered. @@ -243,7 +213,7 @@ class overmapbuffer * specific submap. */ - std::vector> get_npcs_near( int x, int y, int z, int radius ); + std::vector> get_npcs_near( const tripoint &p, int radius ); /** * Get all (currently loaded!) npcs that have a companion * mission set. @@ -255,7 +225,7 @@ class overmapbuffer * A radius of 0 returns all npcs that are on that specific * overmap terrain tile. */ - std::vector> get_npcs_near_omt( int x, int y, int z, int radius ); + std::vector> get_npcs_near_omt( const tripoint &p, int radius ); /** * Same as @ref get_npcs_near(int,int,int,int) but uses * player position as center. @@ -365,18 +335,18 @@ class overmapbuffer cata::optional get_existing_om_global_with_coordinates( const tripoint &p ); /** - * (x,y) are global overmap coordinates (same as @ref get). + * Pass global overmap coordinates (same as @ref get). * @returns true if the buffer has a overmap with * the given coordinates. */ - bool has( int x, int y ); + bool has( const point &p ); /** * Get an existing overmap, does not create a new one * and may return NULL if the requested overmap does not * exist. * (x,y) are global overmap coordinates (same as @ref get). */ - overmap *get_existing( int x, int y ); + overmap *get_existing( const point &p ); /** * Returns whether or not the location has been generated (e.g. mapgen has run). * @param loc is in world-global omt coordinates. @@ -413,22 +383,22 @@ class overmapbuffer void process_mongroups(); /** * Let hordes move a step. Note that this may move monster groups inside the reality bubble, - * therefor you should probably call @ref map::spawn_monsters to spawn them. + * therefore you should probably call @ref map::spawn_monsters to spawn them. */ void move_hordes(); // hordes -- this uses overmap terrain coordinates! - std::vector monsters_at( int x, int y, int z ); + std::vector monsters_at( const tripoint &p ); /** - * Monster groups at (x,y,z) - absolute submap coordinates. + * Monster groups at p - absolute submap coordinates. * Groups with no population are not included. */ - std::vector groups_at( int x, int y, int z ); + std::vector groups_at( const tripoint &p ); /** * Spawn monsters from the overmap onto the main map (game::m). - * (x,y,z) is an absolute *submap* coordinate. + * p is an absolute *submap* coordinate. */ - void spawn_monster( const int x, const int y, const int z ); + void spawn_monster( const tripoint &p ); /** * Despawn the monster back onto the overmap. The monsters position * (monster::pos()) is interpreted as relative to the main map. @@ -462,9 +432,6 @@ class overmapbuffer city_reference closest_known_city( const tripoint ¢er ); std::string get_description_at( const tripoint &where ); - inline std::string get_description_at( const point &where, const int z ) { - return get_description_at( tripoint( where, z ) ); - } /** * Place the specified overmap special directly on the map using the provided location and rotation. @@ -531,7 +498,6 @@ class overmapbuffer * overmap terrain coordinates. * This function may create a new overmap if needed. */ - bool check_ot( const std::string &otype, ot_match_type match_type, int x, int y, int z ); bool check_ot( const std::string &otype, ot_match_type match_type, const tripoint &loc ); bool check_overmap_special_type( const overmap_special_id &id, const tripoint &loc ); diff --git a/src/panels.cpp b/src/panels.cpp index d096db80476d8..ed818656f4544 100644 --- a/src/panels.cpp +++ b/src/panels.cpp @@ -217,15 +217,14 @@ void overmap_ui::draw_overmap_chunk( const catacurses::window &w_minimap, const for( int i = -( width / 2 ); i <= width - ( width / 2 ) - 1; i++ ) { for( int j = -( height / 2 ); j <= height - ( height / 2 ) - 1; j++ ) { - const int omx = cursx + i; - const int omy = cursy + j; + const tripoint omp( cursx + i, cursy + j, g->get_levz() ); nc_color ter_color; std::string ter_sym; - const bool seen = overmap_buffer.seen( omx, omy, g->get_levz() ); - const bool vehicle_here = overmap_buffer.has_vehicle( omx, omy, g->get_levz() ); - if( overmap_buffer.has_note( omx, omy, g->get_levz() ) ) { + const bool seen = overmap_buffer.seen( omp ); + const bool vehicle_here = overmap_buffer.has_vehicle( omp ); + if( overmap_buffer.has_note( omp ) ) { - const std::string ¬e_text = overmap_buffer.note( omx, omy, g->get_levz() ); + const std::string ¬e_text = overmap_buffer.note( omp ); ter_color = c_yellow; ter_sym = "N"; @@ -318,15 +317,15 @@ void overmap_ui::draw_overmap_chunk( const catacurses::window &w_minimap, const ter_color = c_cyan; ter_sym = "c"; } else { - const oter_id &cur_ter = overmap_buffer.ter( omx, omy, g->get_levz() ); + const oter_id &cur_ter = overmap_buffer.ter( omp ); ter_sym = cur_ter->get_symbol(); - if( overmap_buffer.is_explored( omx, omy, g->get_levz() ) ) { + if( overmap_buffer.is_explored( omp ) ) { ter_color = c_dark_gray; } else { ter_color = cur_ter->get_color(); } } - if( !drew_mission && targ.x == omx && targ.y == omy ) { + if( !drew_mission && targ.xy() == omp.xy() ) { // If there is a mission target, and it's not on the same // overmap terrain as the player character, mark it. // TODO: Inform player if the mission is above or below @@ -393,16 +392,13 @@ void overmap_ui::draw_overmap_chunk( const catacurses::window &w_minimap, const if( i > -3 && i < 3 && j > -3 && j < 3 ) { continue; // only do hordes on the border, skip inner map } - const int omx = cursx + i; - const int omy = cursy + j; - if( overmap_buffer.get_horde_size( omx, omy, g->get_levz() ) >= HORDE_VISIBILITY_SIZE ) { - const tripoint cur_pos{ - omx, omy, g->get_levz() - }; - if( overmap_buffer.seen( omx, omy, g->get_levz() ) - && g->u.overmap_los( cur_pos, sight_points ) ) { + const tripoint omp( cursx + i, cursy + j, g->get_levz() ); + int horde_size = overmap_buffer.get_horde_size( omp ); + if( horde_size >= HORDE_VISIBILITY_SIZE ) { + if( overmap_buffer.seen( omp ) + && g->u.overmap_los( omp, sight_points ) ) { mvwputch( w_minimap, j + 3, i + 3, c_green, - overmap_buffer.get_horde_size( omx, omy, g->get_levz() ) > HORDE_VISIBILITY_SIZE * 2 ? 'Z' : 'z' ); + horde_size > HORDE_VISIBILITY_SIZE * 2 ? 'Z' : 'z' ); } } } diff --git a/src/player.cpp b/src/player.cpp index 3fa655c2e733a..d92ed5d07bb60 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -83,6 +83,7 @@ #include "iuse.h" #include "lightmap.h" #include "line.h" +#include "math_defines.h" #include "monster.h" #include "omdata.h" #include "overmap_types.h" @@ -96,8 +97,6 @@ #include "flat_set.h" #include "stomach.h" -constexpr double SQRT_2 = 1.41421356237309504880; - const double MAX_RECOIL = 3000; const mtype_id mon_player_blob( "mon_player_blob" ); @@ -258,8 +257,6 @@ static const trait_id trait_ALBINO( "ALBINO" ); static const trait_id trait_AMPHIBIAN( "AMPHIBIAN" ); static const trait_id trait_ANTENNAE( "ANTENNAE" ); static const trait_id trait_ANTLERS( "ANTLERS" ); -static const trait_id trait_ARACHNID_ARMS( "ARACHNID_ARMS" ); -static const trait_id trait_ARACHNID_ARMS_OK( "ARACHNID_ARMS_OK" ); static const trait_id trait_ASTHMA( "ASTHMA" ); static const trait_id trait_BADBACK( "BADBACK" ); static const trait_id trait_BARK( "BARK" ); @@ -270,15 +267,12 @@ static const trait_id trait_CF_HAIR( "CF_HAIR" ); static const trait_id trait_CHAOTIC( "CHAOTIC" ); static const trait_id trait_CHAOTIC_BAD( "CHAOTIC_BAD" ); static const trait_id trait_CHEMIMBALANCE( "CHEMIMBALANCE" ); -static const trait_id trait_CHITIN2( "CHITIN2" ); -static const trait_id trait_CHITIN3( "CHITIN3" ); static const trait_id trait_CHITIN_FUR( "CHITIN_FUR" ); static const trait_id trait_CHITIN_FUR2( "CHITIN_FUR2" ); static const trait_id trait_CHITIN_FUR3( "CHITIN_FUR3" ); static const trait_id trait_CHLOROMORPH( "CHLOROMORPH" ); static const trait_id trait_CLUMSY( "CLUMSY" ); static const trait_id trait_COLDBLOOD4( "COLDBLOOD4" ); -static const trait_id trait_COMPOUND_EYES( "COMPOUND_EYES" ); static const trait_id trait_DEAF( "DEAF" ); static const trait_id trait_DEFT( "DEFT" ); static const trait_id trait_DEBUG_BIONIC_POWER( "DEBUG_BIONIC_POWER" ); @@ -301,12 +295,10 @@ static const trait_id trait_FASTREADER( "FASTREADER" ); static const trait_id trait_FAT( "FAT" ); static const trait_id trait_FELINE_FUR( "FELINE_FUR" ); static const trait_id trait_FLOWERS( "FLOWERS" ); -static const trait_id trait_FORGETFUL( "FORGETFUL" ); static const trait_id trait_FRESHWATEROSMOSIS( "FRESHWATEROSMOSIS" ); static const trait_id trait_FUR( "FUR" ); static const trait_id trait_GILLS( "GILLS" ); static const trait_id trait_GILLS_CEPH( "GILLS_CEPH" ); -static const trait_id trait_GOODMEMORY( "GOODMEMORY" ); static const trait_id trait_HATES_BOOKS( "HATES_BOOKS" ); static const trait_id trait_HEAVYSLEEPER( "HEAVYSLEEPER" ); static const trait_id trait_HEAVYSLEEPER2( "HEAVYSLEEPER2" ); @@ -317,8 +309,6 @@ static const trait_id trait_HORNS_POINTED( "HORNS_POINTED" ); static const trait_id trait_HUGE( "HUGE" ); static const trait_id trait_HUGE_OK( "HUGE_OK" ); static const trait_id trait_INFIMMUNE( "INFIMMUNE" ); -static const trait_id trait_INSECT_ARMS( "INSECT_ARMS" ); -static const trait_id trait_INSECT_ARMS_OK( "INSECT_ARMS_OK" ); static const trait_id trait_INSOMNIA( "INSOMNIA" ); static const trait_id trait_INT_SLIME( "INT_SLIME" ); static const trait_id trait_JITTERY( "JITTERY" ); @@ -407,12 +397,10 @@ static const trait_id trait_SORES( "SORES" ); static const trait_id trait_SPINES( "SPINES" ); static const trait_id trait_SPIRITUAL( "SPIRITUAL" ); static const trait_id trait_SQUEAMISH( "SQUEAMISH" ); -static const trait_id trait_STIMBOOST( "STIMBOOST" ); static const trait_id trait_STRONGSTOMACH( "STRONGSTOMACH" ); static const trait_id trait_SUNBURN( "SUNBURN" ); static const trait_id trait_SUNLIGHT_DEPENDENT( "SUNLIGHT_DEPENDENT" ); static const trait_id trait_TAIL_FIN( "TAIL_FIN" ); -static const trait_id trait_THICK_SCALES( "THICK_SCALES" ); static const trait_id trait_THORNS( "THORNS" ); static const trait_id trait_THRESH_SPIDER( "THRESH_SPIDER" ); static const trait_id trait_TOUGH_FEET( "TOUGH_FEET" ); @@ -432,8 +420,6 @@ static const trait_id trait_WEBBED( "WEBBED" ); static const trait_id trait_WEB_SPINNER( "WEB_SPINNER" ); static const trait_id trait_WEB_WALKER( "WEB_WALKER" ); static const trait_id trait_WEB_WEAVER( "WEB_WEAVER" ); -static const trait_id trait_WHISKERS( "WHISKERS" ); -static const trait_id trait_WHISKERS_RAT( "WHISKERS_RAT" ); static const trait_id trait_WOOLALLERGY( "WOOLALLERGY" ); stat_mod player::get_pain_penalty() const @@ -654,10 +640,10 @@ void player::process_turn() // SkillLevel::readBook (has no connection to the skill or the player), // player::read, player::practice, ... // Check for spontaneous discovery of martial art styles - for( auto &style : all_martialart_types() ) { + for( auto &style : autolearn_martialart_types() ) { const matype_id ma( style ); - if( can_autolearn( ma ) && !has_martialart( ma ) ) { + if( !has_martialart( ma ) && can_autolearn( ma ) ) { add_martialart( ma ); add_msg_if_player( m_info, _( "You have learned a new style: %s!" ), ma.obj().name ); } @@ -1694,7 +1680,7 @@ int player::run_cost( int base_cost, bool diag ) const movecost /= stamina_modifier; if( diag ) { - movecost *= SQRT_2; + movecost *= M_SQRT2; } return static_cast( movecost ); @@ -2738,13 +2724,7 @@ int player::rust_rate( bool return_stat_effect ) const int ret = ( ( get_option( "SKILL_RUST" ) == "vanilla" || get_option( "SKILL_RUST" ) == "capped" ) ? 500 : 500 - 35 * ( intel - 8 ) ); - if( has_trait( trait_FORGETFUL ) ) { - ret *= 1.33; - } - - if( has_trait( trait_GOODMEMORY ) ) { - ret *= .66; - } + ret *= mutation_value( "skill_rust_multiplier" ); if( ret < 0 ) { ret = 0; @@ -4926,8 +4906,7 @@ void player::suffer() if( mdata.hunger ) { // does not directly modify hunger, but burns kcal mod_stored_nutr( mdata.cost ); - // pretty well on your way to starving at 75% your healthy kcal storage - if( get_healthy_kcal() >= 3 * get_stored_kcal() / 4 ) { + if( get_bmi() < character_weight_category::underweight ) { add_msg_if_player( m_warning, _( "You're too malnourished to keep your %s going." ), mdata.name() ); tdata.powered = false; } @@ -7723,7 +7702,7 @@ bool player::wield( item &target ) if( !unwield() ) { return false; } - + cached_info.erase( "weapon_value" ); if( target.is_null() ) { return true; } @@ -11804,10 +11783,10 @@ std::pair player::get_hunger_description() const } else if( recently_ate ) { hunger_string = _( "Very Hungry" ); hunger_color = c_yellow; - } else if( get_kcal_percent() < 0.2f ) { + } else if( get_bmi() < character_weight_category::emaciated ) { hunger_string = _( "Starving!" ); hunger_color = c_red; - } else if( get_kcal_percent() < 0.5f ) { + } else if( get_bmi() < character_weight_category::underweight ) { hunger_string = _( "Near starving" ); hunger_color = c_red; } else { diff --git a/src/player.h b/src/player.h index 105cec440e4ac..e480b5e0f8468 100644 --- a/src/player.h +++ b/src/player.h @@ -397,6 +397,8 @@ class player : public Character void mutate(); /** Picks a random valid mutation in a category and mutate_towards() it */ void mutate_category( const std::string &mut_cat ); + /** Mutates toward one of the given mutations, upgrading or removing conflicts if necessary */ + bool mutate_towards( std::vector muts, int num_tries = INT_MAX ); /** Mutates toward the entered mutation, upgrading or removing conflicts if necessary */ bool mutate_towards( const trait_id &mut ); /** Removes a mutation, downgrading to the previous level if possible */ diff --git a/src/player_display.cpp b/src/player_display.cpp index c15d127a64029..98055212b5fd1 100644 --- a/src/player_display.cpp +++ b/src/player_display.cpp @@ -1034,7 +1034,9 @@ static void draw_initial_windows( const catacurses::window &w_stats, } if( you.kcal_speed_penalty() < 0 ) { pen = abs( you.kcal_speed_penalty() ); - mvwprintz( w_speed, line, 1, c_red, _( "Starving -%s%d%%" ), + const std::string inanition = you.get_bmi() < character_weight_category::underweight ? + _( "Starving" ) : _( "Underfed" ); + mvwprintz( w_speed, line, 1, c_red, _( "%-20s-%s%d%%" ), inanition, ( pen < 10 ? " " : "" ), pen ); line++; } @@ -1127,12 +1129,12 @@ void player::disp_info() effect_text.push_back( pain_text.str() ); } - int starvation_base_penalty = get_starvation() + 300; + const float bmi = get_bmi(); - if( starvation_base_penalty > 300 ) { + if( bmi < character_weight_category::underweight ) { std::stringstream starvation_text; - if( starvation_base_penalty > 1400 ) { + if( bmi < character_weight_category::emaciated ) { effect_name.push_back( _( "Severely Malnourished" ) ); starvation_text << _( "Your body is severely weakened by starvation. You might die if you don't start eating regular meals!\n \n" ); @@ -1142,15 +1144,11 @@ void player::disp_info() _( "Your body is weakened by starvation. Only time and regular meals will help you recover.\n \n" ); } - if( starvation_base_penalty > 500 ) { - starvation_text << _( "Strength" ) << " -" << static_cast( starvation_base_penalty / 500 ) << - " "; - if( starvation_base_penalty > 1000 ) { - starvation_text << _( "Dexterity" ) << " -" << static_cast( starvation_base_penalty / 1000 ) << - " "; - starvation_text << _( "Intelligence" ) << " -" << static_cast( starvation_base_penalty / 1000 ) - << " "; - } + if( bmi < character_weight_category::underweight ) { + const float str_penalty = 1.0f - ( ( bmi - 13.0f ) / 3.0f ); + starvation_text << _( "Strength" ) << " -" << string_format( "%2.0f%%\n", str_penalty * 100.0f ); + starvation_text << _( "Dexterity" ) << " -" << string_format( "%2.0f%%\n", str_penalty * 50.0f ); + starvation_text << _( "Intelligence" ) << " -" << string_format( "%2.0f%%", str_penalty * 50.0f ); } effect_text.push_back( starvation_text.str() ); diff --git a/src/point.cpp b/src/point.cpp new file mode 100644 index 0000000000000..7456e7abc219c --- /dev/null +++ b/src/point.cpp @@ -0,0 +1,34 @@ +#include "point.h" + +#include + +#include "cata_utility.h" + +std::string point::to_string() const +{ + std::ostringstream os; + os << *this; + return os.str(); +} + +std::string tripoint::to_string() const +{ + std::ostringstream os; + os << *this; + return os.str(); +} + +std::ostream &operator<<( std::ostream &os, const point &pos ) +{ + return os << "(" << pos.x << "," << pos.y << ")"; +} + +std::ostream &operator<<( std::ostream &os, const tripoint &pos ) +{ + return os << "(" << pos.x << "," << pos.y << "," << pos.z << ")"; +} + +point clamp_half_open( const point &p, const rectangle &r ) +{ + return point( clamp( p.x, r.p_min.x, r.p_max.x - 1 ), clamp( p.y, r.p_min.y, r.p_max.y - 1 ) ); +} diff --git a/src/point.h b/src/point.h index 96873212a69af..0bd35e0222f9e 100644 --- a/src/point.h +++ b/src/point.h @@ -54,8 +54,12 @@ struct point { return *this; } + + std::string to_string() const; }; +std::ostream &operator<<( std::ostream &, const point & ); + void serialize( const point &p, JsonOut &jsout ); void deserialize( point &p, JsonIn &jsin ); @@ -144,14 +148,17 @@ struct tripoint { return *this; } + point xy() const { + return point( x, y ); + } + + std::string to_string() const; + void serialize( JsonOut &jsout ) const; void deserialize( JsonIn &jsin ); }; -inline std::ostream &operator<<( std::ostream &os, const tripoint &pos ) -{ - return os << pos.x << "," << pos.y << "," << pos.z; -} +std::ostream &operator<<( std::ostream &, const tripoint & ); // Make tripoint hashable so it can be used as an unordered_set or unordered_map key, // or a component of one. @@ -210,8 +217,25 @@ struct rectangle { point p_max; constexpr rectangle() = default; constexpr rectangle( const point &P_MIN, const point &P_MAX ) : p_min( P_MIN ), p_max( P_MAX ) {} + + constexpr bool contains_half_open( const point &p ) const { + return p.x >= p_min.x && p.x < p_max.x && + p.y >= p_min.y && p.y < p_max.y; + } + + constexpr bool contains_inclusive( const point &p ) const { + return p.x >= p_min.x && p.x <= p_max.x && + p.y >= p_min.y && p.y <= p_max.y; + } }; +// Clamp p to the half-open rectangle r. +// This independently clamps each coordinate of p to the bounds of the +// rectangle. +// Useful for example to round an arbitrary point to the nearest point on the +// screen, or the nearest point in a particular submap. +point clamp_half_open( const point &p, const rectangle &r ); + struct box { tripoint p_min; tripoint p_max; @@ -219,6 +243,23 @@ struct box { constexpr box( const tripoint &P_MIN, const tripoint &P_MAX ) : p_min( P_MIN ), p_max( P_MAX ) {} explicit constexpr box( const rectangle &R, int Z1, int Z2 ) : p_min( tripoint( R.p_min, Z1 ) ), p_max( tripoint( R.p_max, Z2 ) ) {} + + constexpr bool contains_half_open( const tripoint &p ) const { + return p.x >= p_min.x && p.x < p_max.x && + p.y >= p_min.y && p.y < p_max.y && + p.z >= p_min.z && p.z < p_max.z; + } + + constexpr bool contains_inclusive( const tripoint &p ) const { + return p.x >= p_min.x && p.x <= p_max.x && + p.y >= p_min.y && p.y <= p_max.y && + p.z >= p_min.z && p.z <= p_max.z; + } + + void shrink( const tripoint &amount ) { + p_min += amount; + p_max -= amount; + } }; static constexpr tripoint tripoint_min { INT_MIN, INT_MIN, INT_MIN }; @@ -241,29 +282,6 @@ static constexpr point point_north_west{ -1, -1 }; static constexpr box box_zero( tripoint_zero, tripoint_zero ); static constexpr rectangle rectangle_zero( point_zero, point_zero ); -/** Checks if given tripoint is inbounds of given min and max tripoints using given clearance **/ -inline bool generic_inbounds( const tripoint &p, - const box &boundaries, - const box &clearance = box_zero ) -{ - return p.x >= boundaries.p_min.x + clearance.p_min.x && - p.x <= boundaries.p_max.x - clearance.p_max.x && - p.y >= boundaries.p_min.y + clearance.p_min.y && - p.y <= boundaries.p_max.y - clearance.p_max.y && - p.z >= boundaries.p_min.z + clearance.p_min.z && - p.z <= boundaries.p_max.z - clearance.p_max.z; -} - -/** Checks if given point is inbounds of given min and max point using given clearance **/ -inline bool generic_inbounds( const point &p, - const rectangle &boundaries, - const rectangle &clearance = rectangle_zero ) -{ - return generic_inbounds( tripoint( p, 0 ), - box( boundaries, 0, 0 ), - box( clearance, 0, 0 ) ); -} - struct sphere { int radius = 0; tripoint center = tripoint_zero; diff --git a/src/requirements.h b/src/requirements.h index 85449672161d2..b4862855b59b0 100644 --- a/src/requirements.h +++ b/src/requirements.h @@ -173,7 +173,7 @@ struct requirement_data { requirement_data() = default; requirement_data( const alter_tool_comp_vector &tools, const alter_quali_req_vector &qualities, const alter_item_comp_vector &components ) : tools( tools ), qualities( qualities ), - components( components ) {}; + components( components ) {} const requirement_id &id() const { return id_; diff --git a/src/rng.h b/src/rng.h index ab11ea9add8e5..555df4886a11a 100644 --- a/src/rng.h +++ b/src/rng.h @@ -23,7 +23,7 @@ using cata_default_random_engine = std::minstd_rand0; cata_default_random_engine &rng_get_engine(); unsigned int rng_bits(); -int rng( int val1, int val2 ); +int rng( int lo, int hi ); double rng_float( double val1, double val2 ); bool one_in( int chance ); bool one_turn_in( const time_duration &duration ); diff --git a/src/savegame.cpp b/src/savegame.cpp index 59042e8dd38a8..eaaec55fa5025 100644 --- a/src/savegame.cpp +++ b/src/savegame.cpp @@ -282,7 +282,7 @@ void game::load_weather( std::istream &fin ) if( fin.peek() == 'l' ) { std::string line; getline( fin, line ); - weather.lightning_active = ( line.compare( "lightning: 1" ) == 0 ); + weather.lightning_active = line == "lightning: 1"; } else { weather.lightning_active = false; } @@ -957,9 +957,9 @@ void overmap::unserialize( std::istream &fin ) if( tracker_member_name == "id" ) { jsin.read( id ); } else if( tracker_member_name == "x" ) { - jsin.read( new_tracker.x ); + jsin.read( new_tracker.p.x ); } else if( tracker_member_name == "y" ) { - jsin.read( new_tracker.y ); + jsin.read( new_tracker.p.y ); } else if( tracker_member_name == "name" ) { jsin.read( new_tracker.name ); } @@ -1388,8 +1388,8 @@ void overmap::serialize( std::ostream &fout ) const json.start_object(); json.member( "id", i.first ); json.member( "name", i.second.name ); - json.member( "x", i.second.x ); - json.member( "y", i.second.y ); + json.member( "x", i.second.p.x ); + json.member( "y", i.second.p.y ); json.end_object(); } json.end_array(); diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index b37221b3a963f..350e798ac33a1 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -1227,7 +1227,7 @@ void npc_chatbin::deserialize( JsonIn &jsin ) if( data.has_int( "first_topic" ) ) { int tmptopic = 0; data.read( "first_topic", tmptopic ); - first_topic = convert_talk_topic( talk_topic_enum( tmptopic ) ); + first_topic = convert_talk_topic( static_cast( tmptopic ) ); } else { data.read( "first_topic", first_topic ); } @@ -1311,7 +1311,7 @@ void npc_opinion::serialize( JsonOut &json ) const void npc_favor::deserialize( JsonIn &jsin ) { JsonObject jo = jsin.get_object(); - type = npc_favor_type( jo.get_int( "type" ) ); + type = static_cast( jo.get_int( "type" ) ); jo.read( "value", value ); jo.read( "itype_id", item_id ); if( jo.has_int( "skill_id" ) ) { @@ -1427,7 +1427,7 @@ void npc::load( JsonObject &data ) } if( data.read( "mission", misstmp ) ) { - mission = npc_mission( misstmp ); + mission = static_cast( misstmp ); static const std::set legacy_missions = {{ NPC_MISSION_LEGACY_1, NPC_MISSION_LEGACY_2, NPC_MISSION_LEGACY_3 @@ -1438,7 +1438,7 @@ void npc::load( JsonObject &data ) } } if( data.read( "previous_mission", misstmp ) ) { - previous_mission = npc_mission( misstmp ); + previous_mission = static_cast( misstmp ); static const std::set legacy_missions = {{ NPC_MISSION_LEGACY_1, NPC_MISSION_LEGACY_2, NPC_MISSION_LEGACY_3 @@ -1460,7 +1460,7 @@ void npc::load( JsonObject &data ) } if( data.read( "attitude", atttmp ) ) { - attitude = npc_attitude( atttmp ); + attitude = static_cast( atttmp ); static const std::set legacy_attitudes = {{ NPCATT_LEGACY_1, NPCATT_LEGACY_2, NPCATT_LEGACY_3, NPCATT_LEGACY_4, NPCATT_LEGACY_5, NPCATT_LEGACY_6 @@ -1471,7 +1471,7 @@ void npc::load( JsonObject &data ) } } if( data.read( "previous_attitude", atttmp ) ) { - previous_attitude = npc_attitude( atttmp ); + previous_attitude = static_cast( atttmp ); static const std::set legacy_attitudes = {{ NPCATT_LEGACY_1, NPCATT_LEGACY_2, NPCATT_LEGACY_3, NPCATT_LEGACY_4, NPCATT_LEGACY_5, NPCATT_LEGACY_6 @@ -2405,7 +2405,6 @@ void vehicle::deserialize( JsonIn &jsin ) face.init( fdir ); move.init( mdir ); data.read( "name", name ); - data.read( "base_name", base_name ); std::string temp_id; std::string temp_old_id; data.read( "owner", temp_id ); @@ -2529,7 +2528,6 @@ void vehicle::serialize( JsonOut &json ) const json.member( "skidding", skidding ); json.member( "of_turn_carry", of_turn_carry ); json.member( "name", name ); - json.member( "base_name", base_name ); json.member( "owner", owner ? owner->id.str() : "" ); json.member( "old_owner", old_owner ? old_owner->id.str() : "" ); json.member( "theft_time", theft_time ); diff --git a/src/savegame_legacy.cpp b/src/savegame_legacy.cpp index 9a9351a986839..b6f3893739d59 100644 --- a/src/savegame_legacy.cpp +++ b/src/savegame_legacy.cpp @@ -391,7 +391,7 @@ void overmap::unserialize_legacy( std::istream &fin ) // Bugfix for old saves: population of 2147483647 is far too much and will // crash the game. This specific number was caused by a bug in // overmap::add_mon_group. - if( mg.population == 2147483647ul ) { + if( mg.population == 2147483647U ) { mg.population = rng( 1, 10 ); } mg.diffuse = cd; @@ -433,7 +433,7 @@ void overmap::unserialize_legacy( std::istream &fin ) } else if( datatype == 'v' ) { om_vehicle v; int id; - fin >> id >> v.name >> v.x >> v.y; + fin >> id >> v.name >> v.p.x >> v.p.y; vehicles[id] = v; } else if( datatype == 'n' ) { // NPC // When we start loading a new NPC, check to see if we've accumulated items for diff --git a/src/scent_block.h b/src/scent_block.h index 3b13508e9b819..486e8a2bd8636 100644 --- a/src/scent_block.h +++ b/src/scent_block.h @@ -22,8 +22,6 @@ struct scent_block { data_mode mode; int intensity; }; - - data_block assignable; data_block assignment; tripoint origin; @@ -34,7 +32,6 @@ struct scent_block { : origin( subx * SEEX - 1, suby * SEEY - 1, subz ), scents( scents ), modification_count( 0 ) { for( int x = 0; x < SEEX + 2; ++x ) { for( int y = 0; y < SEEY + 2; ++y ) { - assignable[x][y] = scents.inbounds( origin + tripoint( x, y, 0 ) ); assignment[x][y] = { NONE, 0 }; } } @@ -46,19 +43,22 @@ struct scent_block { } for( int x = 0; x < SEEX + 2; ++x ) { for( int y = 0; y < SEEY + 2; ++y ) { - if( assignable[x][y] ) { - switch( assignment[x][y].mode ) { - case NONE: - break; - case SET: { - scents.set_unsafe( origin + tripoint( x, y, 0 ), assignment[x][y].intensity ); - break; + switch( assignment[x][y].mode ) { + case NONE: + break; + case SET: { + tripoint p = origin + tripoint( x, y, 0 ); + if( scents.inbounds( p ) ) { + scents.set_unsafe( p, assignment[x][y].intensity ); } - case MAX: { - tripoint p = origin + tripoint( x, y, 0 ); + break; + } + case MAX: { + tripoint p = origin + tripoint( x, y, 0 ); + if( scents.inbounds( p ) ) { scents.set_unsafe( p, std::max( assignment[x][y].intensity, scents.get_unsafe( p ) ) ); - break; } + break; } } } diff --git a/src/scent_map.cpp b/src/scent_map.cpp index 8abb7f8f504b8..102ba0058df84 100644 --- a/src/scent_map.cpp +++ b/src/scent_map.cpp @@ -122,15 +122,13 @@ bool scent_map::inbounds( const tripoint &p ) const if( !scent_map_z_level_inbounds ) { return false; } - const point scent_map_boundary_min( point_zero ); - const point scent_map_boundary_max( MAPSIZE_X, MAPSIZE_Y ); - const point scent_map_clearance_min( point_zero ); - const point scent_map_clearance_max( 1, 1 ); + static constexpr point scent_map_boundary_min( point_zero ); + static constexpr point scent_map_boundary_max( MAPSIZE_X, MAPSIZE_Y ); - const rectangle scent_map_boundaries( scent_map_boundary_min, scent_map_boundary_max ); - const rectangle scent_map_clearance( scent_map_clearance_min, scent_map_clearance_max ); + static constexpr rectangle scent_map_boundaries( + scent_map_boundary_min, scent_map_boundary_max ); - return generic_inbounds( { p.x, p.y }, scent_map_boundaries, scent_map_clearance ); + return scent_map_boundaries.contains_half_open( p.xy() ); } void scent_map::update( const tripoint ¢er, map &m ) diff --git a/src/start_location.cpp b/src/start_location.cpp index 96b47addc57a6..ac68885bf648f 100644 --- a/src/start_location.cpp +++ b/src/start_location.cpp @@ -214,7 +214,7 @@ tripoint start_location::find_player_initial_location() const // creating overmaps as necessary. const int radius = 3; for( const point &omp : closest_points_first( radius, point_zero ) ) { - overmap &omap = overmap_buffer.get( omp.x, omp.y ); + overmap &omap = overmap_buffer.get( omp ); const tripoint omtstart = omap.find_random_omt( target() ); if( omtstart != overmap::invalid_tripoint ) { return omtstart + point( omp.x * OMAPX, omp.y * OMAPY ); @@ -409,7 +409,7 @@ void start_location::add_map_extra( const tripoint &omtstart, void start_location::handle_heli_crash( player &u ) const { for( int i = 2; i < num_hp_parts; i++ ) { // Skip head + torso for balance reasons. - const auto part = hp_part( i ); + const auto part = static_cast( i ); const auto bp_part = u.hp_to_bp( part ); const int roll = static_cast( rng( 1, 8 ) ); switch( roll ) { diff --git a/src/tutorial.cpp b/src/tutorial.cpp index a0e863783a926..527442c41ac8c 100644 --- a/src/tutorial.cpp +++ b/src/tutorial.cpp @@ -66,7 +66,7 @@ bool tutorial_game::init() // overmap terrain coordinates const int lx = 50; const int ly = 50; - auto &starting_om = overmap_buffer.get( 0, 0 ); + auto &starting_om = overmap_buffer.get( point_zero ); for( int i = 0; i < OMAPX; i++ ) { for( int j = 0; j < OMAPY; j++ ) { starting_om.ter( i, j, -1 ) = rock; diff --git a/src/uistate.h b/src/uistate.h index 4ad0e9d9e51e0..5df93f9de12c7 100644 --- a/src/uistate.h +++ b/src/uistate.h @@ -56,7 +56,7 @@ class uistatedata bool editmap_nsa_viewmode = false; // true: ignore LOS and lighting bool overmap_blinking = true; // toggles active blinking of overlays. bool overmap_show_overlays = false; // whether overlays are shown or not. - bool overmap_show_map_notes = false; + bool overmap_show_map_notes = true; bool overmap_show_land_use_codes = false; // toggle land use code sym/color for terrain bool overmap_show_city_labels = true; bool overmap_show_hordes = true; diff --git a/src/veh_interact.cpp b/src/veh_interact.cpp index 059a1ba43fe3f..e98d6a1f0997f 100644 --- a/src/veh_interact.cpp +++ b/src/veh_interact.cpp @@ -24,6 +24,7 @@ #include "game.h" #include "handle_liquid.h" #include "itype.h" +#include "math_defines.h" #include "map.h" #include "map_selector.h" #include "messages.h" @@ -2397,7 +2398,13 @@ void veh_interact::display_name() { werase( w_name ); mvwprintz( w_name, 0, 1, c_light_gray, _( "Name: " ) ); - mvwprintz( w_name, 0, 1 + utf8_width( _( "Name: " ) ), c_light_green, veh->name ); + std::string fac_name = veh->get_owner() && + veh->get_owner() != g->faction_manager_ptr->get( faction_id( "your_followers" ) ) ? + veh->get_owner()->name : _( "Yours" ); + mvwprintz( w_name, 0, 1 + utf8_width( _( "Name: " ) ), + veh->get_owner() != g->faction_manager_ptr->get( faction_id( "your_followers" ) ) ? c_light_red : + c_light_green, string_format( _( "%s (%s)" ), veh->name, + veh->get_owner() == nullptr ? _( "not owned" ) : fac_name ) ); wrefresh( w_name ); } @@ -2943,9 +2950,8 @@ void veh_interact::complete_vehicle() int delta_x = headlight_target->x - ( veh->global_pos3().x + q.x ); int delta_y = headlight_target->y - ( veh->global_pos3().y + q.y ); - const double PI = 3.14159265358979f; dir = static_cast( atan2( static_cast( delta_y ), - static_cast( delta_x ) ) * 180.0 / PI ); + static_cast( delta_x ) ) * 180.0 / M_PI ); dir -= veh->face.dir(); while( dir < 0 ) { dir += 360; diff --git a/src/veh_interact.h b/src/veh_interact.h index f7a7bb81f6656..c272c7a1f54eb 100644 --- a/src/veh_interact.h +++ b/src/veh_interact.h @@ -87,6 +87,7 @@ class veh_interact catacurses::window w_list; catacurses::window w_details; catacurses::window w_name; + catacurses::window w_owner; vehicle *veh; bool has_wrench; diff --git a/src/vehicle.h b/src/vehicle.h index a7499b9a12cbe..42e780f4f3ca1 100644 --- a/src/vehicle.h +++ b/src/vehicle.h @@ -742,7 +742,6 @@ class vehicle } void set_owner( faction *new_owner ) { owner = new_owner; - name = string_format( _( "%s (%s)" ), base_name.empty() ? name : base_name, new_owner->name ); } void remove_owner() { owner = nullptr; @@ -1576,7 +1575,6 @@ class vehicle std::vector floating; // List of parts that provide buoyancy to boats // config values - std::string base_name; // vehicle name without ownership std::string name; // vehicle name /** * Type of the vehicle as it was spawned. This will never change, but it can be an invalid diff --git a/src/vehicle_display.cpp b/src/vehicle_display.cpp index 71e89b47d56c2..f135e2151267d 100644 --- a/src/vehicle_display.cpp +++ b/src/vehicle_display.cpp @@ -303,7 +303,7 @@ std::vector vehicle::get_printable_fuel_types() const { std::set opts; for( const auto &pt : parts ) { - if( ( pt.is_fuel_store() ) && pt.ammo_current() != "null" ) { + if( pt.is_fuel_store() && pt.ammo_current() != "null" ) { opts.emplace( pt.ammo_current() ); } } @@ -351,7 +351,7 @@ void vehicle::print_fuel_indicators( const catacurses::window &win, int y, int x } int yofs = 0; - int max_gauge = ( ( isHorizontal ) ? 12 : 5 ) + start_index; + int max_gauge = ( isHorizontal ? 12 : 5 ) + start_index; int max_size = std::min( static_cast( fuels.size() ), max_gauge ); std::map fuel_usages = fuel_usage(); @@ -362,7 +362,7 @@ void vehicle::print_fuel_indicators( const catacurses::window &win, int y, int x } // check if the current index is less than the max size minus 12 or 5, to indicate that there's more - if( ( start_index < static_cast( fuels.size() ) - ( ( isHorizontal ) ? 12 : 5 ) ) ) { + if( start_index < static_cast( fuels.size() ) - ( isHorizontal ? 12 : 5 ) ) { mvwprintz( win, y + yofs, x, c_light_green, ">" ); wprintz( win, c_light_gray, " for more" ); } @@ -403,7 +403,7 @@ void vehicle::print_fuel_indicator( const catacurses::window &win, int y, int x, if( debug_mode ) { mvwprintz( win, y, x + 6, f_color, "%d/%d", f_left, cap ); } else { - mvwprintz( win, y, x + 6, f_color, "%d", ( f_left * 100 ) / cap ); + mvwprintz( win, y, x + 6, f_color, "%d", f_left * 100 / cap ); wprintz( win, c_light_gray, "%c", 045 ); } } diff --git a/src/vehicle_part.cpp b/src/vehicle_part.cpp index fb84ec8470c7c..88ac5d6930ea8 100644 --- a/src/vehicle_part.cpp +++ b/src/vehicle_part.cpp @@ -95,7 +95,7 @@ std::string vehicle_part::name( bool with_prefix ) const } if( base.is_faulty() ) { - res += ( _( " (faulty)" ) ); + res += _( " (faulty)" ); } if( base.has_var( "contained_name" ) ) { @@ -113,7 +113,7 @@ int vehicle_part::hp() const { const int dur = info().durability; if( base.max_damage() > 0 ) { - return dur - ( dur * base.damage() / base.max_damage() ); + return dur - dur * base.damage() / base.max_damage(); } else { return dur; } @@ -131,7 +131,7 @@ int vehicle_part::damage_level( int max ) const double vehicle_part::health_percent() const { - return ( 1.0 - static_cast( base.damage() ) / base.max_damage() ); + return 1.0 - static_cast( base.damage() ) / base.max_damage(); } double vehicle_part::damage_percent() const diff --git a/src/vehicle_use.cpp b/src/vehicle_use.cpp index 94145169ccf1b..589d2044ef832 100644 --- a/src/vehicle_use.cpp +++ b/src/vehicle_use.cpp @@ -759,11 +759,10 @@ bool vehicle::fold_up() if( can_be_folded ) { bicycle.set_var( "weight", to_gram( total_mass() ) ); bicycle.set_var( "volume", total_folded_volume() / units::legacy_volume_factor ); - bicycle.set_var( "name", string_format( _( "folded %s" ), base_name.empty() ? name : base_name ) ); - bicycle.set_var( "vehicle_name", base_name.empty() ? name : base_name ); + bicycle.set_var( "name", string_format( _( "folded %s" ), name ) ); + bicycle.set_var( "vehicle_name", name ); // TODO: a better description? - bicycle.set_var( "description", string_format( _( "A folded %s." ), - base_name.empty() ? name : base_name ) ); + bicycle.set_var( "description", string_format( _( "A folded %s." ), name ) ); } g->m.add_item_or_charges( g->u.pos(), bicycle ); @@ -1432,7 +1431,7 @@ void vehicle::use_harness( int part, const tripoint &pos ) add_msg( m_info, _( "You untie your %s." ), f.get_name() ); f.remove_effect( effect_tied ); if( f.tied_item ) { - g->u.i_add( *f.tied_item, 0 ); + g->u.i_add( *f.tied_item ); f.tied_item = cata::nullopt; } } diff --git a/src/weather.cpp b/src/weather.cpp index 49c2ad4fbd72b..dcc05e67ccbe7 100644 --- a/src/weather.cpp +++ b/src/weather.cpp @@ -240,7 +240,7 @@ void item::add_rain_to_container( bool acid, int charges ) // The container has water, and the acid rain didn't turn it // into weak acid. Poison the water instead, assuming 1 // charge of acid would act like a charge of water with poison 5. - int total_poison = liq.poison * ( orig ) + ( 5 * added ); + int total_poison = liq.poison * orig + 5 * added; liq.poison = total_poison / liq.charges; int leftover_poison = total_poison - liq.poison * liq.charges; if( leftover_poison > rng( 0, liq.charges ) ) { @@ -711,8 +711,8 @@ int get_local_windchill( double temperature, double humidity, double windpower ) // Temperature is removed at the end, because get_local_windchill is meant to calculate the difference. // Source : http://en.wikipedia.org/wiki/Wind_chill#North_American_and_United_Kingdom_wind_chill_index - windchill = 35.74 + 0.6215 * tmptemp - 35.75 * ( pow( tmpwind, - 0.16 ) ) + 0.4275 * tmptemp * ( pow( tmpwind, 0.16 ) ) - tmptemp; + windchill = 35.74 + 0.6215 * tmptemp - 35.75 * pow( tmpwind, + 0.16 ) + 0.4275 * tmptemp * pow( tmpwind, 0.16 ) - tmptemp; if( tmpwind < 4 ) { windchill = 0; // This model fails when there is 0 wind. } @@ -723,8 +723,8 @@ int get_local_windchill( double temperature, double humidity, double windpower ) tmpwind = tmpwind * 0.44704; // Convert to meters per second. tmptemp = temp_to_celsius( tmptemp ); - windchill = ( 0.33 * ( ( humidity / 100.00 ) * 6.105 * exp( ( 17.27 * tmptemp ) / - ( 237.70 + tmptemp ) ) ) - 0.70 * tmpwind - 4.00 ); + windchill = 0.33 * ( humidity / 100.00 * 6.105 * exp( 17.27 * tmptemp / + ( 237.70 + tmptemp ) ) ) - 0.70 * tmpwind - 4.00; // Convert to Fahrenheit, but omit the '+ 32' because we are only dealing with a piece of the felt air temperature equation. windchill = windchill * 9 / 5; } @@ -770,21 +770,21 @@ std::string get_shortdirstring( int angle ) std::string dirstring; int dirangle = angle; if( dirangle <= 23 || dirangle > 338 ) { - dirstring = ( "N" ); + dirstring = "N"; } else if( dirangle <= 68 && dirangle > 23 ) { - dirstring = ( "NE" ); + dirstring = "NE"; } else if( dirangle <= 113 && dirangle > 68 ) { - dirstring = ( "E" ); + dirstring = "E"; } else if( dirangle <= 158 && dirangle > 113 ) { - dirstring = ( "SE" ); + dirstring = "SE"; } else if( dirangle <= 203 && dirangle > 158 ) { - dirstring = ( "S" ); + dirstring = "S"; } else if( dirangle <= 248 && dirangle > 203 ) { - dirstring = ( "SW" ); + dirstring = "SW"; } else if( dirangle <= 293 && dirangle > 248 ) { - dirstring = ( "W" ); + dirstring = "W"; } else if( dirangle <= 338 && dirangle > 293 ) { - dirstring = ( "NW" ); + dirstring = "NW"; } return dirstring; } @@ -795,21 +795,21 @@ std::string get_dirstring( int angle ) std::string dirstring; int dirangle = angle; if( dirangle <= 23 || dirangle > 338 ) { - dirstring = ( "North" ); + dirstring = "North"; } else if( dirangle <= 68 && dirangle > 23 ) { - dirstring = ( "North-East" ); + dirstring = "North-East"; } else if( dirangle <= 113 && dirangle > 68 ) { - dirstring = ( "East" ); + dirstring = "East"; } else if( dirangle <= 158 && dirangle > 113 ) { - dirstring = ( "South-East" ); + dirstring = "South-East"; } else if( dirangle <= 203 && dirangle > 158 ) { - dirstring = ( "South" ); + dirstring = "South"; } else if( dirangle <= 248 && dirangle > 203 ) { - dirstring = ( "South-West" ); + dirstring = "South-West"; } else if( dirangle <= 293 && dirangle > 248 ) { - dirstring = ( "West" ); + dirstring = "West"; } else if( dirangle <= 338 && dirangle > 293 ) { - dirstring = ( "North-West" ); + dirstring = "North-West"; } return dirstring; } @@ -1028,7 +1028,7 @@ int weather_manager::get_temperature( const tripoint &location ) } //underground temperature = average New England temperature = 43F/6C rounded to int const int temp = ( location.z < 0 ? AVERAGE_ANNUAL_TEMPERATURE : temperature ) + - ( g->new_game ? 0 : ( g->m.get_temperature( location ) + temp_mod ) ); + ( g->new_game ? 0 : g->m.get_temperature( location ) + temp_mod ); temperature_cache.emplace( std::make_pair( location, temp ) ); return temp; diff --git a/src/weather_gen.cpp b/src/weather_gen.cpp index e07c7d168c41f..edddb85787984 100644 --- a/src/weather_gen.cpp +++ b/src/weather_gen.cpp @@ -8,6 +8,7 @@ #include "game_constants.h" #include "json.h" +#include "math_defines.h" #include "rng.h" #include "simplexnoise.h" #include "weather.h" @@ -15,73 +16,109 @@ namespace { -// GCC doesn't like M_PI here for some reason -constexpr double PI = 3.141592653589793238463; -constexpr double tau = 2 * PI; +constexpr double tau = 2 * M_PI; } //namespace weather_generator::weather_generator() = default; int weather_generator::current_winddir = 1000; -w_point weather_generator::get_weather( const tripoint &location, const time_point &t, - unsigned seed ) const +struct weather_gen_common { + double x; + double y; + double z; + double ctn; + double seasonal_variation; + unsigned modSEED; + season_type season; +}; + +static weather_gen_common get_common_data( const tripoint &location, const time_point &t, + unsigned seed ) { + weather_gen_common result; // Integer x position / widening factor of the Perlin function. - const double x( location.x / 2000.0 ); + result.x = location.x / 2000.0; // Integer y position / widening factor of the Perlin function. - const double y( location.y / 2000.0 ); + result.y = location.y / 2000.0; // Integer turn / widening factor of the Perlin function. - const double z( to_turn( t + calendar::season_length() ) / 2000.0 ); - - const double dayFraction = time_past_midnight( t ) / 1_days; - + result.z = to_turn( t + calendar::season_length() ) / 2000.0; // Limit the random seed during noise calculation, a large value flattens the noise generator to zero // Windows has a rand limit of 32768, other operating systems can have higher limits - const unsigned modSEED = seed % SIMPLEX_NOISE_RANDOM_SEED_LIMIT; - // Noise factors - double T( raw_noise_4d( x, y, z, modSEED ) * 4.0 ); - double H( raw_noise_4d( x, y, z / 5, modSEED + 101 ) ); - double H2( raw_noise_4d( x, y, z, modSEED + 151 ) / 4 ); - double P( raw_noise_4d( x / 2.5, y / 2.5, z / 30, modSEED + 211 ) * 70 ); - double A( raw_noise_4d( x, y, z, modSEED ) * 8.0 ); - double W( raw_noise_4d( x / 2.5, y / 2.5, z / 200, modSEED ) * 10.0 ); - + result.modSEED = seed % SIMPLEX_NOISE_RANDOM_SEED_LIMIT; const double now( ( time_past_new_year( t ) + calendar::season_length() / 2 ) / calendar::year_length() ); // [0,1) - const double ctn( cos( tau * now ) ); - const season_type season = season_of_year( t ); - double mod_t( 0 ); // TODO: make this depend on latitude and altitude? + result.ctn = cos( tau * now ); // [-1, 1] + result.season = season_of_year( t ); + // Start and end at -1 going up to 1 in summer. + result.seasonal_variation = result.ctn * -1; // [-1, 1] + + return result; +} + +static double weather_temperature_from_common_data( const weather_generator &wg, + const weather_gen_common &common, const time_point &t ) +{ + const double x( common.x ); + const double y( common.y ); + const double z( common.z ); + + const unsigned modSEED = common.modSEED; + const double ctn( common.ctn ); // [-1, 1] + const season_type season = common.season; + + const double dayFraction = time_past_midnight( t ) / 1_days; + // manually specified seasonal temp variation from region_settings.json - if( season == WINTER ) { - mod_t += winter_temp_manual_mod; - } else if( season == SPRING ) { - mod_t += spring_temp_manual_mod; - } else if( season == SUMMER ) { - mod_t += summer_temp_manual_mod; - } else if( season == AUTUMN ) { - mod_t += autumn_temp_manual_mod; - } - const double current_t( base_temperature + - mod_t ); + const int seasonal_temp_mod[4] = { wg.spring_temp_manual_mod, wg.summer_temp_manual_mod, wg.autumn_temp_manual_mod, wg.winter_temp_manual_mod }; + const double current_t( wg.base_temperature + seasonal_temp_mod[ season ] ); // Start and end at -1 going up to 1 in summer. - const double seasonal_variation( ctn * -1 ); + const double seasonal_variation( common.seasonal_variation ); // [-1, 1] // Harsh winter nights, hot summers. const double season_atenuation( ctn / 2 + 1 ); // Make summers peak faster and winters not perma-frozen. const double season_dispersion( pow( 2, ctn + 1 ) - 2.3 ); + // Day-night temperature variation. double daily_variation( cos( tau * dayFraction - tau / 8 ) * -1 * season_atenuation / 2 + season_dispersion * -1 ); - // Add baseline to the noise. + + double T( raw_noise_4d( x, y, z, modSEED ) * 4.0 ); T += current_t; - // Add season curve offset to account for the winter-summer difference in day-night difference. - T += seasonal_variation * 8 * exp( -pow( current_t * 2.7 / 10 - 0.5, - 2 ) ); - // Add daily variation scaled to the inverse of the current baseline. A very specific and finicky adjustment curve. - T += daily_variation * 8 * exp( -pow( current_t / 30, - 2 ) ); - T = T * 9 / 5 + 32; // Convert to imperial. =| + T += seasonal_variation * 8 * exp( -pow( current_t * 2.7 / 10 - 0.5, 2 ) ); + T += daily_variation * 8 * exp( -pow( current_t / 30, 2 ) ); + + return T * 9 / 5 + 32; +} + +double weather_generator::get_weather_temperature( const tripoint &location, const time_point &t, + unsigned seed ) const +{ + return weather_temperature_from_common_data( *this, get_common_data( location, t, seed ), t ); +} +w_point weather_generator::get_weather( const tripoint &location, const time_point &t, + unsigned seed ) const +{ + const weather_gen_common common = get_common_data( location, t, seed ); + + const double x( common.x ); + const double y( common.y ); + const double z( common.z ); + + const unsigned modSEED = common.modSEED; + const double ctn( common.ctn ); // [-1, 1] + const season_type season = common.season; + + // Noise factors + const double T( weather_temperature_from_common_data( *this, common, t ) ); + double H( raw_noise_4d( x, y, z / 5, modSEED + 101 ) ); + double H2( raw_noise_4d( x, y, z, modSEED + 151 ) / 4 ); + double P( raw_noise_4d( x / 2.5, y / 2.5, z / 30, modSEED + 211 ) * 70 ); + double A( raw_noise_4d( x, y, z, modSEED ) * 8.0 ); + double W( raw_noise_4d( x / 2.5, y / 2.5, z / 200, modSEED ) * 10.0 ); + + // Start and end at -1 going up to 1 in summer. + const double seasonal_variation( common.seasonal_variation ); // [-1, 1] // Humidity variation double mod_h( 0 ); diff --git a/src/weather_gen.h b/src/weather_gen.h index 0b143a3792b1b..adc30359d4426 100644 --- a/src/weather_gen.h +++ b/src/weather_gen.h @@ -63,6 +63,8 @@ class weather_generator int get_water_temperature() const; void test_weather() const; + double get_weather_temperature( const tripoint &, const time_point &, unsigned ) const; + static weather_generator load( JsonObject &jo ); }; diff --git a/src/wish.cpp b/src/wish.cpp index 81c9bf8e28ceb..241642ab624fa 100644 --- a/src/wish.cpp +++ b/src/wish.cpp @@ -263,15 +263,16 @@ void debug_menu::wishmutate( player *p ) uistate.wishmutate_selected = wmenu.selected; if( rc != 0 ) { for( size_t i = 0; i < cb.vTraits.size(); i++ ) { - wmenu.entries[ i ].extratxt.txt.clear(); + uilist_entry &entry = wmenu.entries[ i ]; + entry.extratxt.txt.clear(); if( p->has_trait( cb.vTraits[ i ] ) ) { - wmenu.entries[ i ].text_color = c_green; + entry.text_color = c_green; cb.pTraits[ cb.vTraits[ i ] ] = true; if( p->has_base_trait( cb.vTraits[ i ] ) ) { - wmenu.entries[ i ].extratxt.txt = "T"; + entry.extratxt.txt = "T"; } } else { - wmenu.entries[ i ].text_color = wmenu.text_color; + entry.text_color = wmenu.text_color; cb.pTraits[ cb.vTraits[ i ] ] = false; } } @@ -508,9 +509,10 @@ void debug_menu::wishitem( player *p, int x, int y, int z ) item ity( opts[i], 0 ); wmenu.addentry( i, true, 0, string_format( _( "%.*s" ), wmenu.pad_right - 5, ity.tname( 1, false ) ) ); - wmenu.entries[i].extratxt.txt = ity.symbol(); - wmenu.entries[i].extratxt.color = ity.color(); - wmenu.entries[i].extratxt.left = 1; + mvwzstr &entry_extra_text = wmenu.entries[i].extratxt; + entry_extra_text.txt = ity.symbol(); + entry_extra_text.color = ity.color(); + entry_extra_text.left = 1; } do { diff --git a/tests/colony_list_test_helpers.h b/tests/colony_list_test_helpers.h new file mode 100644 index 0000000000000..47644b9a63049 --- /dev/null +++ b/tests/colony_list_test_helpers.h @@ -0,0 +1,47 @@ +#pragma once +#ifndef COLONY_LIST_TEST_HELPERS_H +#define COLONY_LIST_TEST_HELPERS_H + +// Fast xorshift+128 random number generator function +// original: https://codingforspeed.com/using-faster-psudo-random-generator-xorshift/ +static unsigned int xor_rand() +{ + static unsigned int x = 123456789; + static unsigned int y = 362436069; + static unsigned int z = 521288629; + static unsigned int w = 88675123; + + const unsigned int t = x ^ ( x << 11 ); + + // Rotate the static values (w rotation in return statement): + x = y; + y = z; + z = w; + + return w = w ^ ( w >> 19 ) ^ ( t ^ ( t >> 8 ) ); +} + +struct small_struct { + double *empty_field_1; + double unused_number; + unsigned int empty_field2; + double *empty_field_3; + int number; + unsigned int empty_field4; + + small_struct( const int num ) noexcept: number( num ) {}; +}; + +struct perfect_forwarding_test { + const bool success; + + perfect_forwarding_test( int && /*perfect1*/, int &perfect2 ) : success( true ) { + perfect2 = 1; + } + + template + perfect_forwarding_test( T && /*imperfect1*/, U && /*imperfect2*/ ) : success( false ) + {} +}; + +#endif // COLONY_LIST_TEST_HELPERS_H diff --git a/tests/colony_test.cpp b/tests/colony_test.cpp index 54692288cd9ef..30a96008bfba5 100644 --- a/tests/colony_test.cpp +++ b/tests/colony_test.cpp @@ -6,25 +6,7 @@ #include "catch/catch.hpp" #include "colony.h" - -// Fast xorshift+128 random number generator function -// original: https://codingforspeed.com/using-faster-psudo-random-generator-xorshift/ -static unsigned int xor_rand() -{ - static unsigned int x = 123456789; - static unsigned int y = 362436069; - static unsigned int z = 521288629; - static unsigned int w = 88675123; - - const unsigned int t = x ^ ( x << 11 ); - - // Rotate the static values (w rotation in return statement): - x = y; - y = z; - z = w; - - return w = w ^ ( w >> 19 ) ^ ( t ^ ( t >> 8 ) ); -} +#include "colony_list_test_helpers.h" TEST_CASE( "colony basics", "[colony]" ) { @@ -267,7 +249,7 @@ TEST_CASE( "colony basics", "[colony]" ) CHECK( test_colony_2.max_size() > test_colony_2.size() ); } -TEST_CASE( "insert and erase", "[colony]" ) +TEST_CASE( "colony insert and erase", "[colony]" ) { cata::colony test_colony; @@ -504,7 +486,7 @@ TEST_CASE( "insert and erase", "[colony]" ) CHECK( test_colony.size() == static_cast( count ) ); } -TEST_CASE( "range erase", "[colony]" ) +TEST_CASE( "colony range erase", "[colony]" ) { cata::colony test_colony; @@ -763,7 +745,7 @@ TEST_CASE( "range erase", "[colony]" ) CHECK( test_colony.size() == 10 ); } -TEST_CASE( "sort", "[colony]" ) +TEST_CASE( "colony sort", "[colony]" ) { cata::colony test_colony; @@ -805,7 +787,7 @@ TEST_CASE( "sort", "[colony]" ) CHECK( sorted ); } -TEST_CASE( "insertion methods", "[colony]" ) +TEST_CASE( "colony insertion methods", "[colony]" ) { cata::colony test_colony = {1, 2, 3}; @@ -888,19 +870,7 @@ TEST_CASE( "insertion methods", "[colony]" ) CHECK( sum == 12060 ); } -struct perfect_forwarding_test { - const bool success; - - perfect_forwarding_test( int && /*perfect1*/, int &perfect2 ) : success( true ) { - perfect2 = 1; - } - - template - perfect_forwarding_test( T && /*imperfect1*/, U && /*imperfect2*/ ) : success( false ) - {} -}; - -TEST_CASE( "perfect forwarding", "[colony]" ) +TEST_CASE( "colony perfect forwarding", "[colony]" ) { cata::colony test_colony; @@ -913,19 +883,7 @@ TEST_CASE( "perfect forwarding", "[colony]" ) CHECK( lvalueref == 1 ); } -struct small_struct { - double *empty_field_1; - double unused_number; - unsigned int empty_field2; - double *empty_field_3; - int number; - unsigned int empty_field4; - - small_struct( const int num ) noexcept: - number( num ) {}; -}; - -TEST_CASE( "emplace", "[colony]" ) +TEST_CASE( "colony emplace", "[colony]" ) { cata::colony test_colony; int sum1 = 0, sum2 = 0; @@ -945,7 +903,7 @@ TEST_CASE( "emplace", "[colony]" ) CHECK( test_colony.size() == 100 ); } -TEST_CASE( "group size and capacity", "[colony]" ) +TEST_CASE( "colony group size and capacity", "[colony]" ) { cata::colony test_colony; test_colony.change_group_sizes( 50, 100 ); @@ -988,7 +946,7 @@ TEST_CASE( "group size and capacity", "[colony]" ) CHECK( test_colony.capacity() == 3400 ); } -TEST_CASE( "splice", "[colony]" ) +TEST_CASE( "colony splice", "[colony]" ) { cata::colony test_colony_1, test_colony_2; diff --git a/tests/list_test.cpp b/tests/list_test.cpp new file mode 100644 index 0000000000000..4890ddda09579 --- /dev/null +++ b/tests/list_test.cpp @@ -0,0 +1,1176 @@ +#include // std::find +#include // log redirection +#include // abort +#include // std::greater +#include // std::move +#include // range-insert testing + +#include "catch/catch.hpp" +#include "colony_list_test_helpers.h" +#include "list.h" + + +TEST_CASE( "list basics", "[list]" ) +{ + { + cata::list test_list; + int ten = 10; + int twenty = 20; + + SECTION( "empty()" ) { + CHECK( test_list.empty() ); + + test_list.push_front( &ten ); + + CHECK( !test_list.empty() ); + } + + SECTION( "begin() and end()" ) { + test_list.push_front( &ten ); + + CHECK( **test_list.begin() == 10 ); + CHECK_FALSE( test_list.begin() == test_list.end() ); + + test_list.clear(); + + CHECK( test_list.begin() == test_list.end() ); + } + + for( int i = 0; i < 200; i++ ) { + test_list.push_back( &ten ); + test_list.push_back( &twenty ); + } + + SECTION( "iterator count/access" ) { + int count = 0; + int sum = 0; + for( cata::list::iterator it = test_list.begin(); it != test_list.end(); + ++it ) { + ++count; + sum += **it; + } + + CHECK( count == 400 ); + CHECK( sum == 6000 ); + } + + SECTION( "iterator distance" ) { + cata::list::iterator plus_twenty = test_list.begin(); + std::advance( plus_twenty, 20 ); + + cata::list::iterator plus_two_hundred = test_list.begin(); + std::advance( plus_two_hundred, 200 ); + + CHECK( std::distance( test_list.begin(), plus_twenty ) == 20 ); + CHECK( std::distance( test_list.begin(), plus_two_hundred ) == 200 ); + } + + SECTION( "iterator next/prev" ) { + cata::list::iterator next_iterator = std::next( test_list.begin(), 5 ); + cata::list::const_iterator prev_iterator = std::prev( test_list.cend(), 300 ); + + CHECK( std::distance( test_list.begin(), next_iterator ) == 5 ); + CHECK( std::distance( prev_iterator, test_list.cend() ) == 300 ); + } + + SECTION( "iterator/const_iterator equality" ) { + cata::list::const_iterator prev_iterator = std::prev( test_list.cend(), 300 ); + cata::list::iterator prev_iterator2 = std::prev( test_list.end(), 300 ); + + CHECK( prev_iterator == prev_iterator2 ); + } + + SECTION( "copy, equality, and inequality" ) { + cata::list test_list_2; + test_list_2 = test_list; + cata::list test_list_3( test_list ); + cata::list test_list_4( test_list_2, test_list_2.get_allocator() ); + + cata::list::iterator it1 = test_list.begin(); + cata::list::const_iterator cit( it1 ); + + CHECK( test_list_2.size() == 400 ); + CHECK( test_list_3.size() == 400 ); + CHECK( test_list_4.size() == 400 ); + + CHECK( test_list == test_list_2 ); + CHECK( test_list_2 == test_list_3 ); + + test_list_2.push_back( &ten ); + + CHECK( test_list_2 != test_list_3 ); + } + + SECTION( "reverse iterator count/access" ) { + int count = 0; + int sum = 0; + for( cata::list::reverse_iterator it = test_list.rbegin(); + it != test_list.rend(); ++it ) { + ++count; + sum += **it; + } + + CHECK( count == 400 ); + CHECK( sum == 6000 ); + } + + SECTION( "reverse iterator advance, next, and distance" ) { + cata::list::reverse_iterator r_iterator = test_list.rbegin(); + std::advance( r_iterator, 50 ); + + CHECK( std::distance( test_list.rbegin(), r_iterator ) == 50 ); + + cata::list::reverse_iterator r_iterator2 = std::next( r_iterator, 2 ); + + CHECK( std::distance( test_list.rbegin(), r_iterator2 ) == 52 ); + } + + + SECTION( "multiple iteration" ) { + int count = 0; + int sum = 0; + for( cata::list::iterator it = test_list.begin(); it != test_list.end(); + std::advance( it, 2 ) ) { + ++count; + sum += **it; + } + + CHECK( count == 200 ); + CHECK( sum == 2000 ); + } + + SECTION( "reverse iterator count/access" ) { + int count = 0; + int sum = 0; + for( cata::list::const_iterator it = test_list.cbegin(); + it != test_list.cend(); ++it ) { + ++count; + sum += **it; + } + + CHECK( count == 400 ); + CHECK( sum == 6000 ); + } + + SECTION( "reverse iterator count/access" ) { + int count = 0; + int sum = 0; + for( cata::list::const_reverse_iterator it = test_list.crbegin(); + it != test_list.crend(); ++it ) { + ++count; + sum += **it; + } + + CHECK( count == 400 ); + CHECK( sum == 6000 ); + } + + SECTION( "post erase iteration and shrink to fit" ) { + int count = 0; + for( cata::list::iterator it = std::next( test_list.begin() ); + it != test_list.end(); ++it ) { + ++count; + it = test_list.erase( it ); + + if( it == test_list.end() ) { + break; + } + } + + CHECK( count == 200 ); + CHECK( test_list.size() == 200 ); + + const size_t prev_capacity = test_list.capacity(); + test_list.shrink_to_fit(); + + CHECK( test_list.capacity() < prev_capacity ); + CHECK( test_list.capacity() == 200 ); + + count = 0; + for( cata::list::reverse_iterator it = test_list.rbegin(); + it != test_list.rend(); ) { + ++it; + cata::list::iterator it2 = it.base(); // grabs it--, essentially + test_list.erase( it2 ); + ++count; + } + + CHECK( count == 200 ); + CHECK( test_list.size() == 0 ); + } + + SECTION( "negative iteration" ) { + int count = 0; + for( cata::list::iterator it = std::prev( test_list.end() ); + it != test_list.begin(); --it ) { + ++count; + } + + CHECK( count == 399 ); + } + + SECTION( "negative multiple iteration" ) { + int count = 0; + for( cata::list::iterator it = std::prev( test_list.end() ); + it != test_list.begin() && + it != std::next( test_list.begin() ); std::advance( it, -2 ) ) { + ++count; + } + + CHECK( count == 199 ); + } + + SECTION( "move" ) { + cata::list test_list_2; + test_list_2 = std::move( test_list ); + + CHECK( test_list_2.size() == 400 ); + + cata::list test_list_3( test_list_2 ); + cata::list test_list_4( std::move( test_list_3 ), test_list_2.get_allocator() ); + + CHECK( test_list_4.size() == 400 ); + } + + SECTION( "swap() and max_size()" ) { + cata::list test_list_2; + test_list_2 = test_list; + + CHECK( test_list_2.size() == 400 ); + + test_list.push_back( &ten ); + + test_list.swap( test_list_2 ); + + CHECK( test_list.size() == test_list_2.size() - 1 ); + + swap( test_list, test_list_2 ); + + CHECK( test_list_2.size() == test_list.size() - 1 ); + + CHECK( test_list_2.max_size() > test_list_2.size() ); + } + } +} + +TEST_CASE( "list insert and erase", "[list]" ) +{ + cata::list test_list; + + for( int i = 0; i < 500000; i++ ) { + test_list.push_back( i ); + } + + SECTION( "size after insert" ) { + CHECK( test_list.size() == 500000 ); + } + + SECTION( "find iterator" ) { + cata::list::iterator found_item = std::find( test_list.begin(), test_list.end(), 5000 ); + + CHECK( *found_item == 5000 ); + } + + SECTION( "find reverse iterator" ) { + cata::list::reverse_iterator found_item2 = std::find( test_list.rbegin(), test_list.rend(), + 5000 ); + + CHECK( *found_item2 == 5000 ); + } + + SECTION( "erase alternating/randomly" ) { + for( cata::list::iterator it = test_list.begin(); it != test_list.end(); + ++it ) { + it = test_list.erase( it ); + } + + CHECK( test_list.size() == 250000 ); + + do { + for( cata::list::iterator it = test_list.begin(); it != test_list.end(); ) { + if( ( xor_rand() & 7 ) == 0 ) { + it = test_list.erase( it ); + } else { + ++it; + } + } + + } while( !test_list.empty() ); + + CHECK( test_list.size() == 0 ); + } + + SECTION( "erase randomly till half empty" ) { + int count = 0; + do { + for( cata::list::iterator it = test_list.begin(); it != test_list.end(); ) { + if( ( xor_rand() & 7 ) == 0 ) { + it = test_list.erase( it ); + ++count; + } else { + ++it; + } + } + + } while( count < 250000 ); + + CHECK( test_list.size() == 500000 - count ); + + for( int i = 0; i < count; i++ ) { + test_list.push_front( 1 ); + } + + CHECK( test_list.size() == 500000 ); + } + + SECTION( "alternating insert/erase" ) { + int count = 0; + for( cata::list::iterator it = test_list.begin(); it != test_list.end(); ) { + if( ++count == 5 ) { + count = 0; + it = test_list.erase( it ); + } else { + test_list.insert( it, 1 ); + ++it; + } + } + + CHECK( test_list.size() == 800000 ); + } + + test_list.clear(); + for( int i = 0; i < 500000; i++ ) { + test_list.push_back( 1 ); + } + + SECTION( "large multi increment erasure" ) { + cata::list::iterator it = test_list.begin(); + std::advance( it, 250000 ); + + for( ; it != test_list.end(); ) { + it = test_list.erase( it ); + } + + CHECK( test_list.size() == 250000 ); + + SECTION( "re-insert post heavy erasure" ) { + for( int i = 0; i < 250000; i++ ) { + test_list.push_front( 1 ); + } + + int sum = 0; + for( cata::list::iterator it = test_list.begin(); it != test_list.end(); + ++it ) { + sum += *it; + } + + CHECK( sum == 500000 ); + } + } + + SECTION( "large multi decrement erasure" ) { + cata::list::iterator end_iterator = test_list.end(); + std::advance( end_iterator, -250000 ); + + for( cata::list::iterator it = test_list.begin(); it != end_iterator; ) { + it = test_list.erase( it ); + } + + CHECK( test_list.size() == 250000 ); + + SECTION( "re-insert post heavy erasure" ) { + for( int i = 0; i < 250000; i++ ) { + test_list.push_front( 1 ); + } + + int sum = 0; + for( cata::list::iterator it = test_list.begin(); it != test_list.end(); + ++it ) { + sum += *it; + } + + CHECK( sum == 500000 ); + } + } + + SECTION( "erase from middle" ) { + cata::list::iterator end_iterator = test_list.end(); + std::advance( end_iterator, -50001 ); + cata::list::iterator begin_iterator = test_list.begin(); + std::advance( begin_iterator, 300000 ); + + for( cata::list::iterator it = begin_iterator; it != end_iterator; ) { + it = test_list.erase( it ); + } + + CHECK( test_list.size() == 350001 ); + } + + SECTION( "total erase edge case" ) { + cata::list::iterator temp_iterator = test_list.begin(); + std::advance( temp_iterator, 2 ); // Advance test 1 + + test_list.erase( temp_iterator ); + // Check edge-case with advance when erasures present in initial group + temp_iterator = test_list.begin(); + std::advance( temp_iterator, 500 ); + + for( cata::list::iterator it = test_list.begin(); it != test_list.end(); ) { + it = test_list.erase( it ); + } + + CHECK( test_list.empty() ); + } + + SECTION( "multiple sequential small insert/erase" ) { + test_list.clear(); + test_list.shrink_to_fit(); + + const size_t prev_capacity = test_list.capacity(); + test_list.reserve( 1000 ); + + CHECK( prev_capacity != test_list.capacity() ); + CHECK( test_list.capacity() == 1000 ); + + int count = 0; + for( int loop1 = 0; loop1 < 50000; loop1++ ) { + for( int loop = 0; loop < 10; loop++ ) { + if( ( xor_rand() & 7 ) == 0 ) { + test_list.push_back( 1 ); + ++count; + } + } + + for( cata::list::iterator it = test_list.begin(); it != test_list.end(); ) { + if( ( xor_rand() & 7 ) == 0 ) { + it = test_list.erase( it ); + --count; + } else { + ++it; + } + } + } + + CHECK( count == test_list.size() ); + } +} + +TEST_CASE( "list merge", "[list]" ) +{ + cata::list test_list; + test_list.insert( test_list.end(), {1, 3, 5, 7, 9} ); + cata::list test_list_2 = {2, 4, 6, 8, 10}; + + test_list.merge( test_list_2 ); + + bool passed = true; + int count = 0; + for( cata::list::iterator it = test_list.begin(); it != test_list.end(); ++it ) { + if( ++count != *it ) { + passed = false; + break; + } + } + + CHECK( passed ); +} + +TEST_CASE( "list splice", "[list]" ) +{ + cata::list test_list = {1, 2, 3, 4, 5}; + cata::list test_list_2 = {6, 7, 8, 9, 10}; + + SECTION( "splice at end" ) { + test_list.splice( test_list.end(), test_list_2 ); + + bool passed = true; + int count = 0; + for( cata::list::iterator it = test_list.begin(); it != test_list.end(); ++it ) { + if( ++count != *it ) { + passed = false; + break; + } + } + + CHECK( passed ); + } + + SECTION( "splice at begin" ) { + test_list.splice( test_list.begin(), test_list_2 ); + + int count = 0; + for( cata::list::iterator it = test_list.begin(); it != test_list.end(); ++it ) { + count += *it; + } + + CHECK( count == 55 ); + } + + SECTION( "splice past middle" ) { + cata::list::iterator it2 = test_list.begin(); + std::advance( it2, 3 ); + + test_list.splice( it2, test_list_2 ); + + int count = 0; + for( cata::list::iterator it = test_list.begin(); it != test_list.end(); ++it ) { + count += *it; + } + + test_list.clear(); + test_list_2.clear(); + + for( int i = 1; i < 25; i++ ) { + test_list.push_back( i ); + test_list_2.push_back( i + 25 ); + } + + cata::list::iterator it3 = test_list.begin(); + std::advance( it3, 18 ); + + test_list.splice( it3, test_list_2 ); + + count = 0; + for( cata::list::iterator it = test_list.begin(); it != test_list.end(); ++it ) { + count += *it; + } + + CHECK( count == 1200 ); + } + + SECTION( "large splice" ) { + test_list.clear(); + test_list_2.clear(); + + for( int i = 5; i < 36; i++ ) { + test_list.push_back( i ); + test_list_2.push_front( i ); + } + + + test_list.splice( test_list.begin(), test_list_2 ); + + int count = 0; + for( cata::list::iterator it = test_list.begin(); it != test_list.end(); ++it ) { + count += *it; + } + + CHECK( count == 1240 ); + } +} + +TEST_CASE( "list sort and reverse", "[list]" ) +{ + cata::list test_list; + + for( int i = 0; i < 500; ++i ) { + test_list.push_back( xor_rand() & 65535 ); + } + + SECTION( "less than (default)" ) { + test_list.sort(); + + bool passed = true; + int previous = 0; + for( cata::list::iterator it = test_list.begin(); it != test_list.end(); ++it ) { + if( *it < previous ) { + passed = false; + break; + } + previous = *it; + } + + CHECK( passed ); + } + + SECTION( "greater than (predicate)" ) { + test_list.sort( std::greater() ); + + bool passed = true; + int previous = 65535; + for( cata::list::iterator it = test_list.begin(); it != test_list.end(); ++it ) { + if( *it > previous ) { + passed = false; + break; + } + previous = *it; + } + + CHECK( passed ); + + SECTION( "reverse" ) { + test_list.reverse(); + + passed = true; + previous = 0; + for( cata::list::iterator it = test_list.begin(); it != test_list.end(); ++it ) { + + if( *it < previous ) { + passed = false; + break; + } + + previous = *it; + } + + CHECK( passed ); + } + } +} + +TEST_CASE( "list unique", "[list]" ) +{ + cata::list test_list = {1, 1, 2, 3, 3, 4, 5, 5}; + + SECTION( "control case" ) { + bool passed = true; + int previous = 0; + for( cata::list::iterator it = test_list.begin(); it != test_list.end(); ++it ) { + if( *it == previous ) { + passed = false; + } + + previous = *it; + } + + CHECK_FALSE( passed ); + } + + SECTION( "invoke unique" ) { + test_list.unique(); + + bool passed = true; + int previous = 0; + for( cata::list::iterator it = test_list.begin(); it != test_list.end(); ++it ) { + if( *it == previous ) { + passed = false; + break; + } + + previous = *it; + } + + CHECK( passed ); + } +} + +TEST_CASE( "list remove", "[list]" ) +{ + cata::list test_list = {1, 3, 1, 50, 16, 15, 2, 22}; + + SECTION( "remove_if()" ) { + test_list.remove_if( []( int value ) { + return value > 15; + } ); + + bool passed = true; + for( cata::list::iterator it = test_list.begin(); it != test_list.end(); ++it ) { + if( *it > 15 ) { + passed = false; + break; + } + } + + CHECK( passed ); + } + + SECTION( "remove()" ) { + test_list.remove( 1 ); + + bool passed = true; + for( cata::list::iterator it = test_list.begin(); it != test_list.end(); ++it ) { + if( *it == 1 ) { + passed = false; + break; + } + } + + CHECK( passed ); + } + + SECTION( "remove() till empty" ) { + test_list.remove( 1 ); + test_list.remove( 22 ); + test_list.remove( 15 ); + test_list.remove( 16 ); + test_list.remove( 3 ); + test_list.remove( 50 ); + test_list.remove( 2 ); + + CHECK( test_list.empty() ); + } +} + +TEST_CASE( "list reserve", "[list]" ) +{ + cata::list test_list; + + test_list.reserve( 4097 ); + + CHECK( test_list.capacity() >= 4097 ); + + test_list.push_back( 15 ); + + int count = 0; + for( cata::list::iterator it = test_list.begin(); it != test_list.end(); ++it ) { + ++count; + } + + CHECK( test_list.size() == static_cast( count ) ); + + test_list.insert( test_list.end(), 10000, 15 ); + + count = 0; + for( cata::list::iterator it = test_list.begin(); it != test_list.end(); ++it ) { + ++count; + } + + CHECK( test_list.size() == 10001 ); + CHECK( count == 10001 ); + CHECK( test_list.capacity() >= 10001 ); + + test_list.reserve( 15000 ); + + CHECK( test_list.capacity() >= 15000 ); +} + +TEST_CASE( "list resize", "[list]" ) +{ + cata::list test_list = { 1, 2, 3, 4, 5, 6, 7 }; + + test_list.resize( 2 ); + + int count = 0; + for( cata::list::iterator it = test_list.begin(); it != test_list.end(); ++it ) { + ++count; + } + + CHECK( test_list.size() == 2 ); + CHECK( count == 2 ); +} + +TEST_CASE( "list assign", "[list]" ) +{ + cata::list test_list; + + SECTION( "range assign" ) { + std::vector test_vector = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + test_list.assign( test_vector.begin(), test_vector.end() ); + + bool passed = true; + int count = 0; + for( cata::list::iterator it = test_list.begin(); it != test_list.end(); ++it ) { + if( ++count != *it ) { + passed = false; + break; + } + } + + CHECK( test_list.size() == 10 ); + CHECK( count == 10 ); + CHECK( passed ); + } + + SECTION( "fill assign" ) { + test_list.assign( 20, 1 ); + + bool passed = true; + int count = 0; + for( cata::list::iterator it = test_list.begin(); it != test_list.end(); ++it ) { + if( *it != 1 ) { + passed = false; + break; + } + ++count; + } + + CHECK( test_list.size() == 20 ); + CHECK( count == 20 ); + CHECK( passed ); + } + + SECTION( "initializer list assign" ) { + std::initializer_list inlist = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; + + test_list.assign( inlist ); + + bool passed = true; + int count = 11; + for( cata::list::iterator it = test_list.begin(); it != test_list.end(); ++it ) { + if( --count != *it ) { + passed = false; + break; + } + } + + CHECK( test_list.size() == 10 ); + CHECK( count == 1 ); + CHECK( passed ); + } +} + +TEST_CASE( "list insert", "[list]" ) +{ + cata::list test_list; + + std::vector test_vector = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + test_list.insert( test_list.end(), test_vector.begin(), test_vector.end() ); + + SECTION( "range insert" ) { + + bool passed = true; + int count = 0; + for( cata::list::iterator it = test_list.begin(); it != test_list.end(); ++it ) { + if( ++count != *it ) { + passed = false; + } + } + + CHECK( passed ); + } + + test_list.insert( test_list.begin(), 50, 50000 ); + + SECTION( "fill insert" ) { + int count = 0; + for( cata::list::iterator it = test_list.begin(); it != test_list.end(); ++it ) { + ++count; + } + + CHECK( count == 60 ); + CHECK( test_list.size() == 60 ); + } + + SECTION( "erase/insert randomly til empty" ) { + while( !test_list.empty() ) { + for( cata::list::iterator it = test_list.begin(); it != test_list.end(); ) { + if( ( xor_rand() & 15 ) == 0 ) { + test_list.insert( it, 13 ); + } + + if( ( xor_rand() & 7 ) == 0 ) { + it = test_list.erase( it ); + } else { + ++it; + } + } + } + + CHECK( test_list.empty() ); + } +} + +TEST_CASE( "list emplace, move, copy, and reverse iterate", "[list]" ) +{ + cata::list test_list; + + for( int counter = 0; counter < 254; counter++ ) { + test_list.emplace_back( counter ); + } + + SECTION( "emplace_back() success" ) { + bool passed = true; + int count = 0; + for( cata::list::iterator it = test_list.begin(); it != test_list.end(); ++it ) { + if( count++ != it->number ) { + passed = false; + break; + } + } + + CHECK( passed ); + } + + SECTION( "emplace_back() return value" ) { + small_struct &temp = test_list.emplace_back( 254 ); + CHECK( temp.number == 254 ); + } + + SECTION( "reverse iteration" ) { + bool passed = true; + int count = 254; + for( cata::list::reverse_iterator rit = test_list.rbegin(); + rit != test_list.rend(); ++rit ) { + if( --count != rit->number ) { + passed = false; + break; + } + } + + CHECK( passed ); + } + + for( int counter = -1; counter != -255; --counter ) { + test_list.emplace_front( counter ); + } + + SECTION( "emplace_front()" ) { + bool passed = true; + int count = -255; + for( cata::list::iterator it = test_list.begin(); it != test_list.end(); ++it ) { + if( ++count != it->number ) { + passed = false; + break; + } + } + + CHECK( passed ); + } + + SECTION( "emplace_front() return value" ) { + small_struct &temp = test_list.emplace_front( -255 ); + CHECK( temp.number == -255 ); + } + + SECTION( "reverse iteration 2" ) { + bool passed = true; + int count = 254; + for( cata::list::reverse_iterator rit = test_list.rbegin(); + rit != test_list.rend(); ++rit ) { + if( --count != rit->number ) { + passed = false; + break; + } + } + + CHECK( passed ); + } + + cata::list test_list_2( std::move( test_list ) ); + + SECTION( "move constructor" ) { + bool passed = true; + int count = 254; + for( cata::list::reverse_iterator rit = test_list_2.rbegin(); + rit != test_list_2.rend(); ++rit ) { + if( --count != rit->number ) { + passed = false; + break; + } + } + + CHECK( passed ); + CHECK( test_list.empty() ); + } + + SECTION( "emplace post-moved list" ) { + // Reuse the moved list will cause segmentation fault + test_list.emplace_back( 3 ); + CHECK( test_list.size() == 1 ); + } + + test_list = std::move( test_list_2 ); + + SECTION( "move assignment" ) { + bool passed = true; + int count = 254; + for( cata::list::reverse_iterator rit = test_list.rbegin(); + rit != test_list.rend(); ++rit ) { + if( --count != rit->number ) { + passed = false; + break; + } + } + + CHECK( passed ); + CHECK( test_list_2.empty() ); + } + + SECTION( "copy assignment" ) { + test_list_2 = test_list; + + bool passed = true; + int count = 254; + for( cata::list::reverse_iterator rit = test_list_2.rbegin(); + rit != test_list_2.rend(); + ++rit ) { + if( --count != rit->number ) { + passed = false; + break; + } + } + + CHECK( passed ); + } + + SECTION( "copy constructor" ) { + cata::list list3( test_list ); + + bool passed = true; + int count = 254; + for( cata::list::reverse_iterator rit = list3.rbegin(); + rit != list3.rend(); ++rit ) { + if( --count != rit->number ) { + passed = false; + break; + } + } + + CHECK( passed ); + } +} + +TEST_CASE( "list reorder", "[list]" ) +{ + cata::list test_list; + + for( int i = 0; i < 255; ++i ) { + test_list.push_back( i ); + } + + // Used for the post reorder data consistency test + int original_sum = 0; + for( cata::list::iterator it = test_list.begin(); it != test_list.end(); ++it ) { + original_sum += *it; + } + + cata::list::iterator it1 = test_list.begin(); + cata::list::iterator it2 = test_list.begin(); + cata::list::iterator it3 = test_list.begin(); + + std::advance( it1, 25 ); + std::advance( it2, 5 ); + + test_list.reorder( it2, it1 ); + + it1 = test_list.begin(); + std::advance( it1, 5 ); + + SECTION( "single reorder" ) { + CHECK( *it1 == 25 ); + } + + + it1 = test_list.begin(); + std::advance( it1, 152 ); + + test_list.reorder( test_list.begin(), it1 ); + + SECTION( "single reorder to begin" ) { + CHECK( test_list.front() == 152 ); + } + + test_list.reorder( test_list.end(), it2 ); + + SECTION( "single reorder to end" ) { + it1 = std::prev( test_list.end() ); + CHECK( *it1 == 5 ); + } + + it1 = test_list.begin(); + it2 = test_list.begin(); + + std::advance( it1, 50 ); + std::advance( it2, 60 ); + std::advance( it3, 70 ); + + test_list.reorder( it3, it1, it2 ); + + SECTION( "range reorder" ) { + it3 = test_list.begin(); + std::advance( it3, 60 ); + + bool passed = true; + for( int test = 50; test < 60; test++ ) { + if( *it3 != test ) { + passed = false; + } + ++it3; + } + + CHECK( passed == true ); + } + + it1 = test_list.begin(); + it2 = test_list.begin(); + + std::advance( it1, 80 ); + std::advance( it2, 120 ); + + test_list.reorder( test_list.end(), it1, it2 ); + + SECTION( "range reorer to end" ) { + it3 = test_list.end(); + std::advance( it3, -41 ); + + bool passed = true; + for( int test = 80; test < 120; test++ ) { + if( *it3 != test ) { + passed = false; + } + ++it3; + } + + CHECK( passed == true ); + } + + it1 = test_list.begin(); + it2 = test_list.begin(); + + std::advance( it1, 40 ); + std::advance( it2, 45 ); + + test_list.reorder( test_list.begin(), it1, it2 ); + + SECTION( "range reorder to begin" ) { + it3 = test_list.begin(); + + bool passed = true; + for( int test = 40; test < 45; test++ ) { + if( *it3 != test ) { + passed = false; + } + ++it3; + } + + CHECK( passed == true ); + } + + SECTION( "post reorder data consistency" ) { + int sum = 0; + for( it1 = test_list.begin(); it1 != test_list.end(); ++it1 ) { + sum += *it1; + } + + CHECK( sum == original_sum ); + } +} + +TEST_CASE( "list insertion styles", "[list]" ) +{ + cata::list test_list = {1, 2, 3}; + + CHECK( test_list.size() == 3 ); + + cata::list test_list_2( test_list.begin(), test_list.end() ); + + CHECK( test_list_2.size() == 3 ); + + cata::list test_list_3( 5000, 2 ); + + CHECK( test_list_3.size() == 5000 ); + + test_list_2.insert( test_list_2.end(), 500000, 5 ); + + CHECK( test_list_2.size() == 500003 ); + + std::vector some_ints( 500, 2 ); + + test_list_2.insert( test_list_2.begin(), some_ints.begin(), some_ints.end() ); + + CHECK( test_list_2.size() == 500503 ); +} + +TEST_CASE( "list perfect forwarding", "[list]" ) +{ + cata::list test_list; + + int lvalue = 0; + int &lvalueref = lvalue; + + test_list.emplace( test_list.end(), 7, lvalueref ); + + CHECK( ( *test_list.begin() ).success ); + CHECK( lvalueref == 1 ); +} diff --git a/tests/npc_talk_test.cpp b/tests/npc_talk_test.cpp index 2ea8e2f58e8f2..b49c4935f2450 100644 --- a/tests/npc_talk_test.cpp +++ b/tests/npc_talk_test.cpp @@ -75,8 +75,8 @@ static void gen_response_lines( dialogue &d, size_t expected_count ) static void change_om_type( const std::string &new_type ) { - const point omt_pos = ms_to_omt_copy( g->m.getabs( g->u.posx(), g->u.posy() ) ); - oter_id &omt_ref = overmap_buffer.ter( omt_pos.x, omt_pos.y, g->u.posz() ); + const tripoint omt_pos = ms_to_omt_copy( g->m.getabs( g->u.pos() ) ); + oter_id &omt_ref = overmap_buffer.ter( omt_pos ); omt_ref = oter_id( new_type ); } diff --git a/tests/overmap_test.cpp b/tests/overmap_test.cpp index d54d31901ea93..a0f9b0524a7a0 100644 --- a/tests/overmap_test.cpp +++ b/tests/overmap_test.cpp @@ -38,11 +38,11 @@ TEST_CASE( "default_overmap_generation_always_succeeds" ) int overmaps_to_construct = 10; for( point candidate_addr : closest_points_first( 10, { 0, 0 } ) ) { // Skip populated overmaps. - if( overmap_buffer.has( candidate_addr.x, candidate_addr.y ) ) { + if( overmap_buffer.has( candidate_addr ) ) { continue; } overmap_special_batch test_specials = overmap_specials::get_default_batch( candidate_addr ); - overmap_buffer.create_custom_overmap( candidate_addr.x, candidate_addr.y, test_specials ); + overmap_buffer.create_custom_overmap( candidate_addr, test_specials ); for( const auto &special_placement : test_specials ) { auto special = special_placement.special_details; INFO( "In attempt #" << overmaps_to_construct @@ -84,10 +84,10 @@ TEST_CASE( "default_overmap_generation_has_non_mandatory_specials_at_origin" ) overmap_special_batch test_specials = overmap_special_batch( origin, specials ); // Run the overmap creation, which will try to place our specials. - overmap_buffer.create_custom_overmap( origin.x, origin.y, test_specials ); + overmap_buffer.create_custom_overmap( origin, test_specials ); // Get the origin overmap... - overmap *test_overmap = overmap_buffer.get_existing( origin.x, origin.y ); + overmap *test_overmap = overmap_buffer.get_existing( origin ); // ...and assert that the optional special exists on this map. bool found_optional = false; diff --git a/tests/point_test.cpp b/tests/point_test.cpp new file mode 100644 index 0000000000000..d626c7c8e000b --- /dev/null +++ b/tests/point_test.cpp @@ -0,0 +1,49 @@ +#include "catch/catch.hpp" + +#include "point.h" + +#include "stringmaker.h" + +TEST_CASE( "rectangle_containment", "[point]" ) +{ + rectangle r( point( 0, 0 ), point( 2, 2 ) ); + CHECK( !r.contains_half_open( point( 0, -1 ) ) ); + CHECK( r.contains_half_open( point( 0, 0 ) ) ); + CHECK( r.contains_half_open( point( 0, 1 ) ) ); + CHECK( !r.contains_half_open( point( 0, 2 ) ) ); + CHECK( !r.contains_half_open( point( 0, 3 ) ) ); + + CHECK( !r.contains_inclusive( point( 0, -1 ) ) ); + CHECK( r.contains_inclusive( point( 0, 0 ) ) ); + CHECK( r.contains_inclusive( point( 0, 1 ) ) ); + CHECK( r.contains_inclusive( point( 0, 2 ) ) ); + CHECK( !r.contains_inclusive( point( 0, 3 ) ) ); +} + +TEST_CASE( "box_shrinks", "[point]" ) +{ + box b( tripoint( 0, 0, 0 ), tripoint( 3, 3, 3 ) ); + CAPTURE( b ); + CHECK( b.contains_half_open( tripoint( 1, 0, 0 ) ) ); + CHECK( b.contains_half_open( tripoint( 2, 1, 2 ) ) ); + b.shrink( tripoint( 1, 1, 0 ) ); + CAPTURE( b ); + // Shrank in the x and y directions + CHECK( !b.contains_half_open( tripoint( 1, 0, 0 ) ) ); + CHECK( !b.contains_half_open( tripoint( 2, 1, 2 ) ) ); + // Didn't shrink in the z direction + CHECK( b.contains_half_open( tripoint( 1, 1, 0 ) ) ); + CHECK( b.contains_half_open( tripoint( 1, 1, 2 ) ) ); +} + +TEST_CASE( "point_to_string", "[point]" ) +{ + CHECK( point( 0, 1 ).to_string() == "(0,1)" ); + CHECK( tripoint( -1, 0, 1 ).to_string() == "(-1,0,1)" ); +} + +TEST_CASE( "tripoint_xy", "[point]" ) +{ + tripoint p( 1, 2, 3 ); + CHECK( p.xy() == point( 1, 2 ) ); +} diff --git a/tests/stringmaker.h b/tests/stringmaker.h index 54a4671ca61df..fff2a850c48a4 100644 --- a/tests/stringmaker.h +++ b/tests/stringmaker.h @@ -17,6 +17,20 @@ struct StringMaker { } }; +template<> +struct StringMaker { + static std::string convert( const rectangle &r ) { + return string_format( "[%s-%s]", r.p_min.to_string(), r.p_max.to_string() ); + } +}; + +template<> +struct StringMaker { + static std::string convert( const box &b ) { + return string_format( "[%s-%s]", b.p_min.to_string(), b.p_max.to_string() ); + } +}; + } // namespace Catch #endif // CATA_TESTS_STRINGMAKER_H diff --git a/tests/test_main.cpp b/tests/test_main.cpp index fac9ea7e31ac3..38f3be29485a1 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -149,7 +149,7 @@ static void init_global_game_state( const std::vector &mods, g->m = map( get_option( "ZLEVELS" ) ); overmap_special_batch empty_specials( { 0, 0 } ); - overmap_buffer.create_custom_overmap( 0, 0, empty_specials ); + overmap_buffer.create_custom_overmap( point_zero, empty_specials ); g->m.load( g->get_levx(), g->get_levy(), g->get_levz(), false ); } diff --git a/tools/iwyu/vs.stdlib.imp b/tools/iwyu/vs.stdlib.imp index 07db16d9b8d80..b7335ad4c3962 100644 --- a/tools/iwyu/vs.stdlib.imp +++ b/tools/iwyu/vs.stdlib.imp @@ -1,5 +1,7 @@ [ { symbol: ["M_PI", "private", "\"math_defines.h\"", "public"] }, + { symbol: ["M_PI_2", "private", "\"math_defines.h\"", "public"] }, + { symbol: ["M_SQRT2", "private", "\"math_defines.h\"", "public"] }, { symbol: ["nanosleep", "private", "\"posix_time.h\"", "public"] }, { symbol: ["timespec", "private", "\"posix_time.h\"", "public"] }, ]