diff --git a/Makefile b/Makefile index eec8f528a41ce..f7431a5b6d854 100644 --- a/Makefile +++ b/Makefile @@ -322,6 +322,7 @@ WARNINGS += -Wimplicit-fallthrough=0 endif CXXFLAGS += $(WARNINGS) $(DEBUG) $(DEBUGSYMS) $(PROFILE) $(OTHERS) -MMD -MP +TOOL_CXXFLAGS = -DCATA_IN_TOOL BINDIST_EXTRAS += README.md data doc BINDIST = $(BUILD_PREFIX)cataclysmdda-$(VERSION).tar.gz @@ -801,7 +802,7 @@ localization: lang/compile_mo.sh $(LANGUAGES) $(CHKJSON_BIN): $(CHKJSON_SOURCES) - $(CXX) $(CXXFLAGS) -Isrc/chkjson -Isrc $(CHKJSON_SOURCES) -o $(CHKJSON_BIN) + $(CXX) $(CXXFLAGS) $(TOOL_CXXFLAGS) -Isrc/chkjson -Isrc $(CHKJSON_SOURCES) -o $(CHKJSON_BIN) json-check: $(CHKJSON_BIN) ./$(CHKJSON_BIN) @@ -1040,7 +1041,8 @@ style-all-json: json_formatter find data -name "*.json" -print0 | xargs -0 -L 1 $(JSON_FORMATTER_BIN) json_formatter: $(JSON_FORMATTER_SOURCES) - $(CXX) $(CXXFLAGS) -Itools/format -Isrc $(JSON_FORMATTER_SOURCES) -o $(JSON_FORMATTER_BIN) + $(CXX) $(CXXFLAGS) $(TOOL_CXXFLAGS) -Itools/format -Isrc \ + $(JSON_FORMATTER_SOURCES) -o $(JSON_FORMATTER_BIN) tests: version $(BUILD_PREFIX)cataclysm.a $(MAKE) -C tests diff --git a/data/json/furniture_and_terrain/furniture-medical.json b/data/json/furniture_and_terrain/furniture-medical.json index 7dd43708361d4..ed7fe56116d92 100644 --- a/data/json/furniture_and_terrain/furniture-medical.json +++ b/data/json/furniture_and_terrain/furniture-medical.json @@ -92,7 +92,6 @@ "coverage": 60, "required_str": 16, "max_volume": 4000, - "insulation": 2, "looks_like": "f_washer", "flags": [ "CONTAINER", "PLACE_ITEM", "BLOCKSDOOR", "FLAT_SURF" ], "deconstruct": { @@ -138,7 +137,6 @@ "coverage": 60, "required_str": 16, "max_volume": 4000, - "insulation": 2, "looks_like": "f_washer", "flags": [ "CONTAINER", "PLACE_ITEM", "BLOCKSDOOR", "FLAT_SURF", "SEALED" ], "deconstruct": { @@ -183,7 +181,6 @@ "coverage": 80, "required_str": 18, "max_volume": 7000, - "insulation": 6, "looks_like": "f_fridge", "flags": [ "CONTAINER", "PLACE_ITEM", "BLOCKSDOOR" ], "deconstruct": { diff --git a/data/json/items/gun/50.json b/data/json/items/gun/50.json index 1af682e239d8a..089e934490b7d 100644 --- a/data/json/items/gun/50.json +++ b/data/json/items/gun/50.json @@ -95,7 +95,7 @@ "weight": "14000 g", "volume": "3500 ml", "price": 1500000, - "to-hit": -1, + "to_hit": -1, "bashing": 13, "material": "steel", "ammo": "50", @@ -119,7 +119,7 @@ "weight": "11800 g", "volume": "3127 ml", "price": 870000, - "to-hit": -1, + "to_hit": -1, "bashing": 12, "material": "steel", "ammo": "50", @@ -143,7 +143,7 @@ "weight": "8504 g", "volume": "1387 ml", "price": 239500, - "to-hit": -1, + "to_hit": -1, "bashing": 12, "material": "steel", "ammo": "50", diff --git a/data/json/items/items_holiday.json b/data/json/items/items_holiday.json index 1e394d6ebfbc2..b290b75aa8a3e 100644 --- a/data/json/items/items_holiday.json +++ b/data/json/items/items_holiday.json @@ -46,8 +46,7 @@ "type": "transform", "target": "plastic_jack_o_lantern", "msg": "The candle winks out inside the lantern.", - "menu_text": "Extinguish", - "revert_to": "plastic_jack_o_lantern" + "menu_text": "Extinguish" }, "flags": [ "LIGHT_6", "TRADER_AVOID" ] } diff --git a/data/json/items/tools.json b/data/json/items/tools.json index 8a29ff728221a..cecf51d1effc3 100644 --- a/data/json/items/tools.json +++ b/data/json/items/tools.json @@ -1192,7 +1192,7 @@ "initial_charges": 4000, "max_charges": 4000, "charges_per_use": 1, - "use_action": { "type": "WATER_PURIFIER", "moves": 70 } + "use_action": "WATER_PURIFIER" }, { "id": "char_smoker", @@ -5269,7 +5269,6 @@ "max_charges": 50, "use_action": { "type": "fireweapon_off", - "active": true, "target_id": "shishkebab_on", "moves": 10, "noise": 10, diff --git a/data/json/mapgen/basecamps/modular_firestation1.json b/data/json/mapgen/basecamps/modular_firestation1.json index 56a5ea0a1157a..070983896923f 100644 --- a/data/json/mapgen/basecamps/modular_firestation1.json +++ b/data/json/mapgen/basecamps/modular_firestation1.json @@ -50,14 +50,14 @@ ], "palettes": [ "fbmc_firestation1_palette" ], "place_furniture": [ - { "furn": "f_null", "x": 0, "y": 2, "chance": 100 }, - { "furn": "f_null", "x": 0, "y": 3, "chance": 100 }, - { "furn": "f_null", "x": 1, "y": 2, "chance": 100 }, - { "furn": "f_null", "x": 1, "y": 3, "chance": 100 }, - { "furn": "f_null", "x": 1, "y": 4, "chance": 100 }, - { "furn": "f_null", "x": 2, "y": 2, "chance": 100 }, - { "furn": "f_null", "x": 2, "y": 3, "chance": 100 }, - { "furn": "f_null", "x": 2, "y": 4, "chance": 100 } + { "furn": "f_null", "x": 0, "y": 2 }, + { "furn": "f_null", "x": 0, "y": 3 }, + { "furn": "f_null", "x": 1, "y": 2 }, + { "furn": "f_null", "x": 1, "y": 3 }, + { "furn": "f_null", "x": 1, "y": 4 }, + { "furn": "f_null", "x": 2, "y": 2 }, + { "furn": "f_null", "x": 2, "y": 3 }, + { "furn": "f_null", "x": 2, "y": 4 } ] } }, @@ -69,7 +69,7 @@ "place_nested": [ { "chunks": [ "fbmc_firestation1_kitchen1" ], "x": 1, "y": 9 } ], "place_loot": [ { "item": "2x4", "repeat": 24, "x": 2, "y": 10, "chance": 100 }, - { "item": "nail", "charges": 36, "x": 2, "y": 10, "chance": 100 } + { "item": "nail", "repeat": 36, "x": 2, "y": 10, "chance": 100 } ] } }, @@ -135,14 +135,14 @@ "object": { "mapgensize": [ 6, 6 ], "place_furniture": [ - { "furn": "f_null", "x": 0, "y": 3, "chance": 100 }, - { "furn": "f_null", "x": 0, "y": 4, "chance": 100 }, - { "furn": "f_null", "x": 2, "y": 1, "chance": 100 }, - { "furn": "f_null", "x": 2, "y": 2, "chance": 100 }, - { "furn": "f_null", "x": 1, "y": 0, "chance": 100 }, - { "furn": "f_null", "x": 0, "y": 0, "chance": 100 }, - { "furn": "f_null", "x": 1, "y": 1, "chance": 100 }, - { "furn": "f_null", "x": 1, "y": 2, "chance": 100 } + { "furn": "f_null", "x": 0, "y": 3 }, + { "furn": "f_null", "x": 0, "y": 4 }, + { "furn": "f_null", "x": 2, "y": 1 }, + { "furn": "f_null", "x": 2, "y": 2 }, + { "furn": "f_null", "x": 1, "y": 0 }, + { "furn": "f_null", "x": 0, "y": 0 }, + { "furn": "f_null", "x": 1, "y": 1 }, + { "furn": "f_null", "x": 1, "y": 2 } ] } }, diff --git a/data/json/mapgen/cemetery_4square.json b/data/json/mapgen/cemetery_4square.json index 763a95ec7619e..a621df08c00d8 100644 --- a/data/json/mapgen/cemetery_4square.json +++ b/data/json/mapgen/cemetery_4square.json @@ -414,7 +414,7 @@ { "item": "church", "x": [ 11, 12 ], "y": [ 5, 6 ], "chance": 65 }, { "item": "jackets", "x": [ 12 ], "y": [ 14 ], "chance": 50 } ], - "place_npcs": [ { "chance": 7, "class": "SEER_Brigitte_LaCroix", "x": 18, "y": 6 } ] + "place_npcs": [ { "class": "SEER_Brigitte_LaCroix", "x": 18, "y": 6 } ] } } ] diff --git a/data/json/mapgen/megastore.json b/data/json/mapgen/megastore.json index f39cfef19c188..766414abc9595 100644 --- a/data/json/mapgen/megastore.json +++ b/data/json/mapgen/megastore.json @@ -79,11 +79,11 @@ "type": "palette", "id": "mega_goods_a", "items": { - "b": { "item": "novels", "chance": 15, "count-min": 1, "count-max": 2 }, + "b": { "item": "novels", "chance": 15, "repeat": [ 1, 2 ] }, "f": { "item": "produce", "chance": 15 }, "F": { "item": "fridgesnacks", "chance": 15 }, "k": { "item": "shirts", "chance": 15 }, - "K": { "item": "shoes", "chance": 15, "count-min": 1, "count-max": 2 }, + "K": { "item": "shoes", "chance": 15, "repeat": [ 1, 2 ] }, "r": { "item": "pants", "chance": 15 }, "R": { "item": "jackets", "chance": 15 }, "x": { "item": "child_items", "chance": 15 }, @@ -95,8 +95,8 @@ "id": "mega_goods_b", "items": { "b": { "item": "homebooks", "chance": 20 }, - "f": { "item": "fridgesnacks", "chance": 15, "count-min": 1, "count-max": 2 }, - "F": { "item": "fridge", "chance": 15, "count-min": 1, "count-max": 2 }, + "f": { "item": "fridgesnacks", "chance": 15, "repeat": [ 1, 2 ] }, + "F": { "item": "fridge", "chance": 15, "repeat": [ 1, 2 ] }, "k": { "item": "shoes", "chance": 15 }, "K": { "item": "cannedfood", "chance": 15 }, "r": { "item": "hardware", "chance": 15 }, @@ -111,11 +111,11 @@ "items": { "b": { "item": "waitingroom", "chance": 10 }, "f": { "item": "fridge", "chance": 15 }, - "F": { "item": "produce", "chance": 15, "count-min": 1, "count-max": 3 }, + "F": { "item": "produce", "chance": 15, "repeat": [ 1, 3 ] }, "k": { "item": "cannedfood", "chance": 15 }, "K": { "item": "pasta", "chance": 15 }, "r": { "item": "hardware", "chance": 15 }, - "R": { "item": "cleaning", "chance": 15, "count-min": 1, "count-max": 2 }, + "R": { "item": "cleaning", "chance": 15, "repeat": [ 1, 2 ] }, "x": { "item": "shoes", "chance": 15 }, "X": { "item": "dresser", "chance": 15 } } @@ -124,14 +124,14 @@ "type": "palette", "id": "mega_goods_d", "items": { - "b": { "item": "waitingroom", "chance": 15, "count-min": 1, "count-max": 4 }, - "f": { "item": "vending_food_items", "chance": 15, "count-min": 1, "count-max": 2 }, + "b": { "item": "waitingroom", "chance": 15, "repeat": [ 1, 4 ] }, + "f": { "item": "vending_food_items", "chance": 15, "repeat": [ 1, 2 ] }, "F": { "item": "kitchen", "chance": 15 }, "k": { "item": "pasta", "chance": 15 }, "K": { "item": "winter", "chance": 15 }, "r": { "item": "sports", "chance": 15 }, "R": { "item": "camping", "chance": 15 }, - "x": { "item": "cleaning", "chance": 15, "count-min": 1, "count-max": 2 }, + "x": { "item": "cleaning", "chance": 15, "repeat": [ 1, 2 ] }, "X": { "item": "pasta", "chance": 15 } } }, @@ -141,7 +141,7 @@ "items": { "b": { "item": "waitingroom", "chance": 15 }, "f": { "item": "produce", "chance": 15 }, - "F": { "item": "vending_drink_items", "chance": 15, "count-min": 1, "count-max": 3 }, + "F": { "item": "vending_drink_items", "chance": 15, "repeat": [ 1, 3 ] }, "k": { "item": "cleaning", "chance": 15 }, "K": { "item": "camping", "chance": 15 }, "r": { "item": "mechanics", "chance": 15 }, @@ -156,10 +156,10 @@ "items": { "b": { "item": "novels", "chance": 15 }, "f": { "item": "fridgesnacks", "chance": 15 }, - "F": { "item": "vending_drink_items", "chance": 15, "count-min": 1, "count-max": 3 }, + "F": { "item": "vending_drink_items", "chance": 15, "repeat": [ 1, 3 ] }, "k": { "item": "shirts", "chance": 15 }, "K": { "item": "shoes", "chance": 15 }, - "r": { "item": "snacks", "chance": 25, "count-min": 1, "count-max": 2 }, + "r": { "item": "snacks", "chance": 25, "repeat": [ 1, 2 ] }, "R": { "item": "shelter", "chance": 15 }, "x": { "item": "textbooks", "chance": 15 }, "X": { "item": "dresser", "chance": 15 } @@ -212,7 +212,7 @@ { "type": "mapgen", "om_terrain": "megastore_0_0_0", - "comment": "ground floor. top left corner", + "//": "ground floor. top left corner", "weight": 100, "method": "json", "object": { @@ -255,7 +255,7 @@ { "type": "mapgen", "om_terrain": "megastore_0_0_0", - "comment": "ground floor. top left corner", + "//": "ground floor. top left corner", "weight": 100, "method": "json", "object": { @@ -298,7 +298,7 @@ { "type": "mapgen", "om_terrain": "megastore_0_0_0", - "comment": "ground floor. top left corner", + "//": "ground floor. top left corner", "weight": 100, "method": "json", "object": { @@ -341,7 +341,7 @@ { "type": "mapgen", "om_terrain": "megastore_0_0_0", - "comment": "ground floor. top left corner", + "//": "ground floor. top left corner", "weight": 100, "method": "json", "object": { @@ -384,7 +384,7 @@ { "type": "mapgen", "om_terrain": "megastore_0_0_0", - "comment": "ground floor. top left corner", + "//": "ground floor. top left corner", "weight": 100, "method": "json", "object": { @@ -427,7 +427,7 @@ { "type": "mapgen", "om_terrain": "megastore_1_0_0", - "comment": "ground floor. top edge", + "//": "ground floor. top edge", "weight": 100, "method": "json", "object": { @@ -465,7 +465,7 @@ { "type": "mapgen", "om_terrain": "megastore_1_0_0", - "comment": "ground floor. top edge", + "//": "ground floor. top edge", "weight": 100, "method": "json", "object": { @@ -503,7 +503,7 @@ { "type": "mapgen", "om_terrain": "megastore_1_0_0", - "comment": "ground floor. top edge", + "//": "ground floor. top edge", "weight": 100, "method": "json", "object": { @@ -541,7 +541,7 @@ { "type": "mapgen", "om_terrain": "megastore_1_0_0", - "comment": "ground floor. top edge", + "//": "ground floor. top edge", "weight": 100, "method": "json", "object": { @@ -579,7 +579,7 @@ { "type": "mapgen", "om_terrain": "megastore_1_0_0", - "comment": "ground floor. top edge", + "//": "ground floor. top edge", "weight": 100, "method": "json", "object": { @@ -617,7 +617,7 @@ { "type": "mapgen", "om_terrain": "megastore_2_0_0", - "comment": "ground floor. top right corner", + "//": "ground floor. top right corner", "weight": 100, "method": "json", "object": { @@ -657,7 +657,7 @@ { "type": "mapgen", "om_terrain": "megastore_2_0_0", - "comment": "ground floor. top right corner", + "//": "ground floor. top right corner", "weight": 100, "method": "json", "object": { @@ -697,7 +697,7 @@ { "type": "mapgen", "om_terrain": "megastore_2_0_0", - "comment": "ground floor. top right corner", + "//": "ground floor. top right corner", "weight": 100, "method": "json", "object": { @@ -737,7 +737,7 @@ { "type": "mapgen", "om_terrain": "megastore_2_0_0", - "comment": "ground floor. top right corner", + "//": "ground floor. top right corner", "weight": 100, "method": "json", "object": { @@ -777,7 +777,7 @@ { "type": "mapgen", "om_terrain": "megastore_2_0_0", - "comment": "ground floor. top right corner", + "//": "ground floor. top right corner", "weight": 100, "method": "json", "object": { @@ -817,7 +817,7 @@ { "type": "mapgen", "om_terrain": "megastore_0_1_0", - "comment": "ground floor. left edge", + "//": "ground floor. left edge", "weight": 100, "method": "json", "object": { @@ -856,7 +856,7 @@ { "type": "mapgen", "om_terrain": "megastore_0_1_0", - "comment": "ground floor. left edge", + "//": "ground floor. left edge", "weight": 100, "method": "json", "object": { @@ -897,7 +897,7 @@ { "type": "mapgen", "om_terrain": "megastore_0_1_0", - "comment": "ground floor. left edge", + "//": "ground floor. left edge", "weight": 10, "method": "json", "object": { @@ -936,7 +936,7 @@ { "type": "mapgen", "om_terrain": "megastore_0_1_0", - "comment": "ground floor. left edge", + "//": "ground floor. left edge", "weight": 100, "method": "json", "object": { @@ -975,7 +975,7 @@ { "type": "mapgen", "om_terrain": "megastore_0_1_0", - "comment": "ground floor. left edge", + "//": "ground floor. left edge", "weight": 100, "method": "json", "object": { @@ -1014,7 +1014,7 @@ { "type": "mapgen", "om_terrain": "megastore_1_1_0", - "comment": "ground floor. middle area", + "//": "ground floor. middle area", "weight": 100, "method": "json", "object": { @@ -1053,7 +1053,7 @@ { "type": "mapgen", "om_terrain": "megastore_1_1_0", - "comment": "ground floor. middle area", + "//": "ground floor. middle area", "weight": 100, "method": "json", "object": { @@ -1092,7 +1092,7 @@ { "type": "mapgen", "om_terrain": "megastore_1_1_0", - "comment": "ground floor. middle area", + "//": "ground floor. middle area", "weight": 100, "method": "json", "object": { @@ -1131,7 +1131,7 @@ { "type": "mapgen", "om_terrain": "megastore_1_1_0", - "comment": "ground floor. middle area", + "//": "ground floor. middle area", "weight": 100, "method": "json", "object": { @@ -1170,7 +1170,7 @@ { "type": "mapgen", "om_terrain": "megastore_1_1_0", - "comment": "ground floor. middle area", + "//": "ground floor. middle area", "weight": 100, "method": "json", "object": { @@ -1209,7 +1209,7 @@ { "type": "mapgen", "om_terrain": "megastore_2_1_0", - "comment": "ground floor. right edge", + "//": "ground floor. right edge", "weight": 100, "method": "json", "object": { @@ -1248,7 +1248,7 @@ { "type": "mapgen", "om_terrain": "megastore_2_1_0", - "comment": "ground floor. right edge", + "//": "ground floor. right edge", "weight": 100, "method": "json", "object": { @@ -1287,7 +1287,7 @@ { "type": "mapgen", "om_terrain": "megastore_2_1_0", - "comment": "ground floor. right edge", + "//": "ground floor. right edge", "weight": 100, "method": "json", "object": { @@ -1326,7 +1326,7 @@ { "type": "mapgen", "om_terrain": "megastore_2_1_0", - "comment": "ground floor. right edge", + "//": "ground floor. right edge", "weight": 100, "method": "json", "object": { @@ -1366,7 +1366,7 @@ { "type": "mapgen", "om_terrain": "megastore_2_1_0", - "comment": "ground floor. right edge", + "//": "ground floor. right edge", "weight": 100, "method": "json", "object": { @@ -1405,7 +1405,7 @@ { "type": "mapgen", "om_terrain": "megastore_0_2_0", - "comment": "ground floor. bottom left corner", + "//": "ground floor. bottom left corner", "weight": 100, "method": "json", "object": { @@ -1444,7 +1444,7 @@ { "type": "mapgen", "om_terrain": "megastore_0_2_0", - "comment": "ground floor. bottom left corner", + "//": "ground floor. bottom left corner", "weight": 100, "method": "json", "object": { @@ -1483,7 +1483,7 @@ { "type": "mapgen", "om_terrain": "megastore_0_2_0", - "comment": "ground floor. bottom left corner", + "//": "ground floor. bottom left corner", "weight": 100, "method": "json", "object": { @@ -1523,7 +1523,7 @@ { "type": "mapgen", "om_terrain": "megastore_0_2_0", - "comment": "ground floor. bottom left corner", + "//": "ground floor. bottom left corner", "weight": 100, "method": "json", "object": { @@ -1562,7 +1562,7 @@ { "type": "mapgen", "om_terrain": "megastore_0_2_0", - "comment": "ground floor. bottom left corner", + "//": "ground floor. bottom left corner", "weight": 100, "method": "json", "object": { @@ -1602,7 +1602,7 @@ { "type": "mapgen", "om_terrain": "megastore_1_2_0", - "comment": "ground floor. entrance", + "//": "ground floor. entrance", "weight": 10, "method": "json", "object": { @@ -1643,7 +1643,7 @@ { "type": "mapgen", "om_terrain": "megastore_1_2_0", - "comment": "ground floor. entrance", + "//": "ground floor. entrance", "weight": 100, "method": "json", "object": { @@ -1683,7 +1683,7 @@ { "type": "mapgen", "om_terrain": "megastore_2_2_0", - "comment": "ground floor. bottom right corner", + "//": "ground floor. bottom right corner", "weight": 100, "method": "json", "object": { @@ -1723,7 +1723,7 @@ { "type": "mapgen", "om_terrain": "megastore_2_2_0", - "comment": "ground floor. bottom right corner", + "//": "ground floor. bottom right corner", "weight": 100, "method": "json", "object": { @@ -1762,7 +1762,7 @@ { "type": "mapgen", "om_terrain": "megastore_2_2_0", - "comment": "ground floor. bottom right corner", + "//": "ground floor. bottom right corner", "weight": 100, "method": "json", "object": { @@ -1796,13 +1796,13 @@ "rotation": 2, "palettes": [ "megastore", "mega_goods_c" ], "set": [ { "square": "terrain", "x": 9, "y": 9, "x2": 14, "y2": 14, "id": "t_concrete" } ], - "items": { "C": { "item": "produce", "chance": 30, "count": 3 } } + "items": { "C": { "item": "produce", "chance": 30, "repeat": 3 } } } }, { "type": "mapgen", "om_terrain": "megastore_2_2_0", - "comment": "ground floor. bottom right corner", + "//": "ground floor. bottom right corner", "weight": 100, "method": "json", "object": { @@ -1841,7 +1841,7 @@ { "type": "mapgen", "om_terrain": "megastore_2_2_0", - "comment": "ground floor. bottom right corner", + "//": "ground floor. bottom right corner", "weight": 100, "method": "json", "object": { @@ -1880,7 +1880,7 @@ { "type": "mapgen", "om_terrain": "megastore_0_0_1", - "comment": "second floor. top left corner", + "//": "second floor. top left corner", "weight": 10, "method": "json", "object": { @@ -1918,7 +1918,7 @@ { "type": "mapgen", "om_terrain": "megastore_1_0_1", - "comment": "second floor. top edge", + "//": "second floor. top edge", "weight": 10, "method": "json", "object": { @@ -1956,7 +1956,7 @@ { "type": "mapgen", "om_terrain": "megastore_2_0_1", - "comment": "second floor top right corner", + "//": "second floor top right corner", "weight": 10, "method": "json", "object": { @@ -1995,7 +1995,7 @@ { "type": "mapgen", "om_terrain": "megastore_0_1_1", - "comment": "second floor left edge", + "//": "second floor left edge", "weight": 10, "method": "json", "object": { @@ -2033,7 +2033,7 @@ { "type": "mapgen", "om_terrain": "megastore_1_1_1", - "comment": "second floor. middle area", + "//": "second floor. middle area", "weight": 10, "method": "json", "object": { @@ -2071,7 +2071,7 @@ { "type": "mapgen", "om_terrain": "megastore_2_1_1", - "comment": "second floor. right edge", + "//": "second floor. right edge", "weight": 10, "method": "json", "object": { @@ -2109,7 +2109,7 @@ { "type": "mapgen", "om_terrain": "megastore_0_2_1", - "comment": "second floor. bottom left corner", + "//": "second floor. bottom left corner", "weight": 10, "method": "json", "object": { @@ -2147,7 +2147,7 @@ { "type": "mapgen", "om_terrain": "megastore_1_2_1", - "comment": "second floor. bottom edge", + "//": "second floor. bottom edge", "weight": 10, "method": "json", "object": { @@ -2185,7 +2185,7 @@ { "type": "mapgen", "om_terrain": "megastore_2_2_1", - "comment": "second floor bottom right corner", + "//": "second floor bottom right corner", "weight": 10, "method": "json", "object": { @@ -2223,7 +2223,7 @@ { "type": "mapgen", "om_terrain": "megastore_0_0_roof", - "comment": "roof. top left corner.", + "//": "roof. top left corner.", "weight": 10, "method": "json", "object": { @@ -2275,7 +2275,7 @@ { "type": "mapgen", "om_terrain": "megastore_1_0_roof", - "comment": "roof. top edge", + "//": "roof. top edge", "weight": 10, "method": "json", "object": { @@ -2327,7 +2327,7 @@ { "type": "mapgen", "om_terrain": "megastore_2_0_roof", - "comment": "third floor. top right corner", + "//": "third floor. top right corner", "weight": 10, "method": "json", "object": { @@ -2379,7 +2379,7 @@ { "type": "mapgen", "om_terrain": "megastore_0_1_roof", - "comment": "roof. left edge", + "//": "roof. left edge", "weight": 10, "method": "json", "object": { @@ -2434,7 +2434,7 @@ { "type": "mapgen", "om_terrain": "megastore_1_1_roof", - "comment": "roof. middle area", + "//": "roof. middle area", "weight": 10, "method": "json", "object": { @@ -2479,7 +2479,7 @@ { "type": "mapgen", "om_terrain": "megastore_2_1_roof", - "comment": "roof. right edge", + "//": "roof. right edge", "weight": 10, "method": "json", "object": { @@ -2530,7 +2530,7 @@ { "type": "mapgen", "om_terrain": "megastore_0_2_roof", - "comment": "third floor. bottom left corner", + "//": "third floor. bottom left corner", "weight": 10, "method": "json", "object": { @@ -2575,7 +2575,7 @@ { "type": "mapgen", "om_terrain": "megastore_1_2_roof", - "comment": "roof. bottom edge", + "//": "roof. bottom edge", "weight": 10, "method": "json", "object": { @@ -2626,7 +2626,7 @@ { "type": "mapgen", "om_terrain": "megastore_2_2_roof", - "comment": "third floor bottom right corner", + "//": "third floor bottom right corner", "weight": 10, "method": "json", "object": { diff --git a/data/json/martialarts.json b/data/json/martialarts.json index 06b7322751afe..4589f7b195057 100644 --- a/data/json/martialarts.json +++ b/data/json/martialarts.json @@ -1286,7 +1286,6 @@ "description": "Your attacks are stronger if you are not holding anything in your hands.\n\n+33% bash damage when not using a weapon.", "unarmed_allowed": true, "min_unarmed": 0, - "strictly_unarmed": true, "mult_bonuses": [ [ "damage", "bash", 1.33 ] ] } ], diff --git a/data/json/npcs/NPC_Brigitte_LaCroix.json b/data/json/npcs/NPC_Brigitte_LaCroix.json index c6e039765f692..6a6484ceb88a5 100644 --- a/data/json/npcs/NPC_Brigitte_LaCroix.json +++ b/data/json/npcs/NPC_Brigitte_LaCroix.json @@ -220,13 +220,8 @@ "origins": [ "ORIGIN_SECONDARY" ], "end": { "effect": [ - { - "u_add_var": "NC_SEER_MISSION_1", - "type": "general", - "context": "mission", - "value": "yes", - "u_add_trait": "seer_mark" - } + { "u_add_var": "NC_SEER_MISSION_1", "type": "general", "context": "mission", "value": "yes" }, + { "u_add_trait": "seer_mark" } ] }, "dialogue": { @@ -244,7 +239,6 @@ { "id": "TALK_MISSION_LIST_SEER", "type": "talk_topic", - "category": "CATEGORY_MISSION_SEER", "dynamic_line": { "has_no_available_mission": { "has_no_assigned_mission": "The song is... quiet for now. Perhaps with time, more notes will be etched in the bones of this world.", @@ -284,7 +278,6 @@ { "id": "TALK_MISSION_OFFER_SEER", "type": "talk_topic", - "category": "CATEGORY_MISSION_SEER", "dynamic_line": "If you wish to be set on the path to enlightenment, first you must learn to listen and hear the song. Go out, butcher a creature and feel the power between your fingertips. Then bring me the bones and I shall carve them for you. ", "responses": [ { @@ -298,7 +291,6 @@ { "id": "TALK_MISSION_ACCEPTED_SEER", "type": "talk_topic", - "category": "CATEGORY_MISSION_SEER", "dynamic_line": "Excellent. Now be on your way.", "responses": [ { "text": "Consider it done. But I also wanted to ask...", "topic": "TALK_BONE_SEER" }, @@ -310,14 +302,12 @@ { "id": "TALK_MISSION_ADVICE_SEER", "type": "talk_topic", - "category": "CATEGORY_MISSION_SEER", "dynamic_line": "The shambling corpses we see all around move in discord. Their song can be used, but for an Acolyte, this would be needlessly hard. Be sure to carve an unspoiled living creature.", "responses": [ { "text": "So, a creature that isn't a zombie, or a monster. Got it.", "topic": "TALK_NONE" } ] }, { "id": "TALK_SHARE_EQUIPMENT_SEER", "type": "talk_topic", - "category": "CATEGORY_MISSION_SEER", "dynamic_line": "The path to enlightenment is for you to walk. For me to aid you would ultimately impede your progress and muddle your song.", "responses": [ { "text": "I see. Very well then.", "topic": "TALK_NONE" } ] }, @@ -326,9 +316,6 @@ "type": "talk_topic", "dynamic_line": { "u_has_trait": "seer_mark", - "type": "dialogue", - "context": "mark_of_the_seer", - "value": "yes", "no": "Only those who bear my mark will prove themselves worthy of my skills.", "yes": "You bear my mark, meaning I believe you have potential to learn to truly listen to the Song. Yes, I will lend my skills to you, for now." }, diff --git a/data/json/recipes/basecamps/recipe_primitive_field.json b/data/json/recipes/basecamps/recipe_primitive_field.json index ffebaf7ae5406..a0d667d9bd1da 100644 --- a/data/json/recipes/basecamps/recipe_primitive_field.json +++ b/data/json/recipes/basecamps/recipe_primitive_field.json @@ -893,7 +893,7 @@ "autolearn": false, "never_learn": true, "time": "510 m", - "blueprint_requires": [ { "id": "not_an_upgrade" } ], + "//blueprint_requires": [ { "id": "not_an_upgrade" } ], "qualities": [ [ { "id": "DIG", "level": 1 } ] ], "components": [ [ [ "stick", 3 ] ] ] }, @@ -908,7 +908,7 @@ "autolearn": false, "never_learn": true, "time": "1530 m", - "blueprint_requires": [ { "id": "not_an_upgrade" } ], + "//blueprint_requires": [ { "id": "not_an_upgrade" } ], "qualities": [ [ { "id": "DIG", "level": 2 } ] ], "components": [ [ [ "pointy_stick", 68 ] ] ] }, diff --git a/data/json/techniques.json b/data/json/techniques.json index 60ed80ab25a2b..ba7b824cdba9a 100644 --- a/data/json/techniques.json +++ b/data/json/techniques.json @@ -1358,7 +1358,6 @@ "messages": [ "You snatch %s's weapon", " snatches %s's weapon" ], "min_unarmed": 4, "unarmed_allowed": true, - "strictly_unarmed": true, "weighting": 2, "take_weapon": true, "mult_bonuses": [ [ "damage", "bash", 0.5 ] ] diff --git a/data/mods/Magiclysm/enchanted/enchanted_belts.json b/data/mods/Magiclysm/enchanted/enchanted_belts.json index 1d787da7ae3bc..07bc968f28179 100644 --- a/data/mods/Magiclysm/enchanted/enchanted_belts.json +++ b/data/mods/Magiclysm/enchanted/enchanted_belts.json @@ -42,7 +42,7 @@ "description": "The mythical belt of Thor, god of thunder. Or at least so it appears. It doubles the wearer's base strength.", "material": [ "superalloy" ], "material_thickness": 10, - "environmental": 20, + "environmental_protection": 20, "weight_capacity_bonus": "50 kg", "relic_data": { "passive_effects": [ { "has": "WORN", "condition": "ALWAYS", "values": [ { "value": "STRENGTH", "multiply": 1.0 } ] } ] diff --git a/data/mods/Magiclysm/enchanted/enchanted_boots.json b/data/mods/Magiclysm/enchanted/enchanted_boots.json index 94eac597c14ed..569ef955cfe12 100644 --- a/data/mods/Magiclysm/enchanted/enchanted_boots.json +++ b/data/mods/Magiclysm/enchanted/enchanted_boots.json @@ -5,7 +5,7 @@ "copy-from": "boots_hiking", "name": "seven league boots", "name_plural": "pairs of seven league boots", - "looks-like": "boots_hiking", + "looks_like": "boots_hiking", "description": "Rugged yet extremely comfortable and well fitting boots of worn leather and steel, they look like they've seen a lot of use and will likely see a lot more. They make your movement a lot less work.", "relic_data": { "passive_effects": [ { "has": "WORN", "condition": "ALWAYS", "values": [ { "value": "MOVE_COST", "add": -30 } ] } ] }, "encumbrance": 8, @@ -20,7 +20,7 @@ "copy-from": "boots_hiking", "name": "boots of haste", "name_plural": "pairs of boots of haste", - "looks-like": "boots_hiking", + "looks_like": "boots_hiking", "description": "Rugged yet extremely comfortable and well fitting boots of worn leather and steel, they look like they've seen a lot of use and will likely see a lot more. They make your movement a lot less work.", "relic_data": { "passive_effects": [ @@ -36,14 +36,13 @@ }, { "id": "mboots_escape", - "type": "ARMOR", + "type": "TOOL_ARMOR", "copy-from": "boots_hiking", "name": "escape boots", "name_plural": "escape boots", - "looks-like": "boots_hiking", + "looks_like": "boots_hiking", "description": "Rugged yet extremely comfortable and well fitting boots of worn leather and steel, these boots can be activated once a day to escape from nasty situations, teleporting you a good distance in a random direction.", "initial_charges": 24, - "charges": 24, "max_charges": 24, "charges_per_use": 24, "artifact_data": { "charge_type": "ARTC_TIME" }, @@ -60,7 +59,7 @@ "copy-from": "boots_hiking", "name": "boots of grounding", "name_plural": "pairs of boots of grounding", - "looks-like": "boots_hiking", + "looks_like": "boots_hiking", "description": "Rugged yet extremely comfortable and well fitting boots of leather with small engraved runes seemingly filled with rubber. When worn, you are immune to damage from electricity.", "encumbrance": 8, "warmth": 30, diff --git a/data/mods/Magiclysm/enchanted/enchanted_bracers.json b/data/mods/Magiclysm/enchanted/enchanted_bracers.json index c85971be21428..9a3aa81295346 100644 --- a/data/mods/Magiclysm/enchanted/enchanted_bracers.json +++ b/data/mods/Magiclysm/enchanted/enchanted_bracers.json @@ -76,7 +76,6 @@ "name_plural": "lesser bracers of lightning", "description": "A light but extremely sturdy steel bracer with an ornate bundle of lightning bolts engraved on the top, silver accentuates the intricate design. It protects your body with a light aura to reduce electrical damage you take, as well as being able to release a Jolt spell 3 times a day.", "initial_charges": 72, - "charges": 72, "max_charges": 72, "charges_per_use": 24, "artifact_data": { "charge_type": "ARTC_TIME" }, @@ -92,7 +91,6 @@ "name_plural": "greater bracers of lightning", "description": "A light but extremely sturdy steel bracer with an ornate bundle of lightning bolts engraved on the top, gold accentuates the intricate design. It protects your body with a strong aura to reduce electrical damage you take, as well as being able to release a Lightning Bolt spell 3 times a day.", "initial_charges": 72, - "charges": 72, "max_charges": 72, "charges_per_use": 24, "artifact_data": { "charge_type": "ARTC_TIME" }, diff --git a/data/mods/Magiclysm/enchanted/enchanted_masks.json b/data/mods/Magiclysm/enchanted/enchanted_masks.json index 85b649ae26848..dc94dac1711a0 100644 --- a/data/mods/Magiclysm/enchanted/enchanted_masks.json +++ b/data/mods/Magiclysm/enchanted/enchanted_masks.json @@ -24,7 +24,7 @@ "type": "TOOL_ARMOR", "id": "mmask_disappearance", "name": "mask of disappearance", - "plural": "masks of disappearance", + "name_plural": "masks of disappearance", "description": "A mask with no facial features at all, just eye and mouth holes, upon activation it makes everything ignore your presence for a while.", "copy-from": "mmask", "initial_charges": 12, @@ -37,7 +37,7 @@ "type": "TOOL_ARMOR", "id": "mmask_vision", "name": "mask of perfect vision", - "plural": "masks of perfect vision", + "name_plural": "masks of perfect vision", "description": "A decidedly steampunk-looking half mask that covers the eye area of the face, it has large lenses that correct and greatly enhance the vision of the wearer.", "copy-from": "mmask", "covers": [ "EYES" ], diff --git a/data/mods/Magiclysm/enchanted/enchanted_misc.json b/data/mods/Magiclysm/enchanted/enchanted_misc.json index 2e84c898c26f5..02504d53b1c9a 100644 --- a/data/mods/Magiclysm/enchanted/enchanted_misc.json +++ b/data/mods/Magiclysm/enchanted/enchanted_misc.json @@ -16,7 +16,6 @@ "qualities": [ [ "HAMMER", 1 ] ], "flags": [ "ALLOWS_REMOTE_USE", "FIRE" ], "initial_charges": 60, - "charges": 60, "max_charges": 60, "charges_per_use": 1, "sub": "hotplate", @@ -32,7 +31,7 @@ "menu_text": "Activate torch mode", "moves": 150 }, - { "type": "firestarter", "moves_cost": 5 }, + { "type": "firestarter" }, { "flame": false, "type": "cauterize" } ] }, diff --git a/data/mods/Magiclysm/enchanted/enchanted_rings.json b/data/mods/Magiclysm/enchanted/enchanted_rings.json index a5ee7ee8b0c5c..016d95249481c 100644 --- a/data/mods/Magiclysm/enchanted/enchanted_rings.json +++ b/data/mods/Magiclysm/enchanted/enchanted_rings.json @@ -72,7 +72,6 @@ "description": "An ornate silver ring engraved with daggers that conjures a near perfect throwing knife into your hand on activation.", "use_action": { "type": "cast_spell", "spell_id": "conj_throwing_blade1", "no_fail": true, "level": 1, "need_worn": true }, "initial_charges": 5, - "charges": 5, "max_charges": 5, "charges_per_use": 1, "artifact_data": { "charge_type": "ARTC_TIME" } diff --git a/data/mods/Magiclysm/enchanted/enchanted_wands.json b/data/mods/Magiclysm/enchanted/enchanted_wands.json index 35d9a2d17fbbf..6725a495a6f86 100644 --- a/data/mods/Magiclysm/enchanted/enchanted_wands.json +++ b/data/mods/Magiclysm/enchanted/enchanted_wands.json @@ -31,7 +31,6 @@ "color": "brown", "flags": [ "BELT_CLIP", "NONCONDUCTIVE" ], "initial_charges": 20, - "charges": 20, "max_charges": 20, "charges_per_use": 1 }, diff --git a/data/mods/Magiclysm/items/spell_scrolls.json b/data/mods/Magiclysm/items/spell_scrolls.json index 671854bd9c9d6..25bfcd7b68a58 100644 --- a/data/mods/Magiclysm/items/spell_scrolls.json +++ b/data/mods/Magiclysm/items/spell_scrolls.json @@ -624,6 +624,6 @@ "id": "spell_scroll_lava_bomb", "name": "Scroll of Lava Bomb", "description": "You tear up the ground beneath you to fire a lava bomb: a globe of lava surrounded by hot, solid rock. It shatters upon impact, spraying shards of rock and lava everywhere.", - "use_action": { "type": "learn_spell", "spell": [ "lava_bomb" ] } + "use_action": { "type": "learn_spell", "spells": [ "lava_bomb" ] } } ] diff --git a/data/mods/Magiclysm/monsters/dragon.json b/data/mods/Magiclysm/monsters/dragon.json index 09ee6ab5d7fd5..bafa29ae94150 100644 --- a/data/mods/Magiclysm/monsters/dragon.json +++ b/data/mods/Magiclysm/monsters/dragon.json @@ -120,7 +120,6 @@ "melee_cut": 28, "armor_bash": 14, "armor_cut": 30, - "armor_pierce": 12, "dodge": 3, "//": "Large means powerful, but it does not mean fast.", "speed": 100, diff --git a/data/mods/mapspecials_demo/megastore/mega_1brc_03.json b/data/mods/mapspecials_demo/megastore/mega_1brc_03.json index dbdb219c51e74..2b2920e6c43ab 100644 --- a/data/mods/mapspecials_demo/megastore/mega_1brc_03.json +++ b/data/mods/mapspecials_demo/megastore/mega_1brc_03.json @@ -36,7 +36,7 @@ "rotation": 2, "palettes": [ "megastore", "mega_goods_c" ], "set": [ { "square": "terrain", "x": 9, "y": 9, "x2": 14, "y2": 14, "id": "t_concrete" } ], - "items": { "C": { "item": "produce", "chance": 30, "count": 3 } } + "items": { "C": { "item": "produce", "chance": 30, "repeat": 3 } } } } ] diff --git a/src/assign.h b/src/assign.h index d978ee04b2774..d8ee1253cf84c 100644 --- a/src/assign.h +++ b/src/assign.h @@ -56,16 +56,21 @@ bool assign( JsonObject &jo, const std::string &name, T &val, bool strict = fals // Object via which to report errors which differs for proportional/relative values JsonObject err = jo; + err.allow_omitted_members(); + JsonObject relative = jo.get_object( "relative" ); + relative.allow_omitted_members(); + JsonObject proportional = jo.get_object( "proportional" ); + proportional.allow_omitted_members(); // Do not require strict parsing for relative and proportional values as rules // such as +10% are well-formed independent of whether they affect base value - if( jo.get_object( "relative" ).read( name, out ) ) { - err = jo.get_object( "relative" ); + if( relative.read( name, out ) ) { + err = relative; strict = false; out += val; - } else if( jo.get_object( "proportional" ).read( name, scalar ) ) { - err = jo.get_object( "proportional" ); + } else if( proportional.read( name, scalar ) ) { + err = proportional; if( scalar <= 0 || scalar == 1 ) { err.throw_error( "invalid proportional scalar", name ); } @@ -73,7 +78,6 @@ bool assign( JsonObject &jo, const std::string &name, T &val, bool strict = fals out = val * scalar; } else if( !jo.read( name, out ) ) { - return false; } @@ -96,15 +100,12 @@ inline bool assign( JsonObject &jo, const std::string &name, bool &val, bool str { bool out; - // Object via which to report errors which differs for proportional/relative values - JsonObject err = jo; - if( !jo.read( name, out ) ) { return false; } if( strict && out == val ) { - report_strict_violation( err, "assignment does not update value", name ); + report_strict_violation( jo, "assignment does not update value", name ); } val = out; @@ -171,8 +172,10 @@ template typename std::enable_if::value, bool>::type assign( JsonObject &jo, const std::string &name, std::set &val, bool = false ) { - auto add = jo.get_object( "extend" ); - auto del = jo.get_object( "delete" ); + JsonObject add = jo.get_object( "extend" ); + add.allow_omitted_members(); + JsonObject del = jo.get_object( "delete" ); + del.allow_omitted_members(); if( jo.has_string( name ) || jo.has_array( name ) ) { val = jo.get_tags( name ); @@ -239,21 +242,26 @@ inline bool assign( JsonObject &jo, const std::string &name, units::volume &val, // Object via which to report errors which differs for proportional/relative values JsonObject err = jo; + err.allow_omitted_members(); + JsonObject relative = jo.get_object( "relative" ); + relative.allow_omitted_members(); + JsonObject proportional = jo.get_object( "proportional" ); + proportional.allow_omitted_members(); // Do not require strict parsing for relative and proportional values as rules // such as +10% are well-formed independent of whether they affect base value - if( jo.get_object( "relative" ).has_member( name ) ) { + if( relative.has_member( name ) ) { units::volume tmp; - err = jo.get_object( "relative" ); + err = relative; if( !parse( err, tmp ) ) { err.throw_error( "invalid relative value specified", name ); } strict = false; out = val + tmp; - } else if( jo.get_object( "proportional" ).has_member( name ) ) { + } else if( proportional.has_member( name ) ) { double scalar; - err = jo.get_object( "proportional" ); + err = proportional; if( !err.read( name, scalar ) || scalar <= 0 || scalar == 1 ) { err.throw_error( "invalid proportional scalar", name ); } @@ -299,21 +307,26 @@ inline bool assign( JsonObject &jo, const std::string &name, units::mass &val, // Object via which to report errors which differs for proportional/relative values JsonObject err = jo; + err.allow_omitted_members(); + JsonObject relative = jo.get_object( "relative" ); + relative.allow_omitted_members(); + JsonObject proportional = jo.get_object( "proportional" ); + proportional.allow_omitted_members(); // Do not require strict parsing for relative and proportional values as rules // such as +10% are well-formed independent of whether they affect base value - if( jo.get_object( "relative" ).has_member( name ) ) { + if( relative.has_member( name ) ) { units::mass tmp; - err = jo.get_object( "relative" ); + err = relative; if( !parse( err, tmp ) ) { err.throw_error( "invalid relative value specified", name ); } strict = false; out = val + tmp; - } else if( jo.get_object( "proportional" ).has_member( name ) ) { + } else if( proportional.has_member( name ) ) { double scalar; - err = jo.get_object( "proportional" ); + err = proportional; if( !err.read( name, scalar ) || scalar <= 0 || scalar == 1 ) { err.throw_error( "invalid proportional scalar", name ); } @@ -359,21 +372,26 @@ inline bool assign( JsonObject &jo, const std::string &name, units::money &val, // Object via which to report errors which differs for proportional/relative values JsonObject err = jo; + err.allow_omitted_members(); + JsonObject relative = jo.get_object( "relative" ); + relative.allow_omitted_members(); + JsonObject proportional = jo.get_object( "proportional" ); + proportional.allow_omitted_members(); // Do not require strict parsing for relative and proportional values as rules // such as +10% are well-formed independent of whether they affect base value - if( jo.get_object( "relative" ).has_member( name ) ) { + if( relative.has_member( name ) ) { units::money tmp; - err = jo.get_object( "relative" ); + err = relative; if( !parse( err, tmp ) ) { err.throw_error( "invalid relative value specified", name ); } strict = false; out = val + tmp; - } else if( jo.get_object( "proportional" ).has_member( name ) ) { + } else if( proportional.has_member( name ) ) { double scalar; - err = jo.get_object( "proportional" ); + err = proportional; if( !err.read( name, scalar ) || scalar <= 0 || scalar == 1 ) { err.throw_error( "invalid proportional scalar", name ); } @@ -424,21 +442,26 @@ inline bool assign( JsonObject &jo, const std::string &name, units::energy &val, // Object via which to report errors which differs for proportional/relative values JsonObject err = jo; + err.allow_omitted_members(); + JsonObject relative = jo.get_object( "relative" ); + relative.allow_omitted_members(); + JsonObject proportional = jo.get_object( "proportional" ); + proportional.allow_omitted_members(); // Do not require strict parsing for relative and proportional values as rules // such as +10% are well-formed independent of whether they affect base value - if( jo.get_object( "relative" ).has_member( name ) ) { + if( relative.has_member( name ) ) { units::energy tmp; - err = jo.get_object( "relative" ); + err = relative; if( !parse( err, tmp ) ) { err.throw_error( "invalid relative value specified", name ); } strict = false; out = val + tmp; - } else if( jo.get_object( "proportional" ).has_member( name ) ) { + } else if( proportional.has_member( name ) ) { double scalar; - err = jo.get_object( "proportional" ); + err = proportional; if( !err.read( name, scalar ) || scalar <= 0 || scalar == 1 ) { err.throw_error( "invalid proportional scalar", name ); } @@ -484,7 +507,7 @@ class time_duration; template inline typename std::enable_if::type, time_duration>::value, bool>::type -read_with_factor( JsonObject jo, const std::string &name, T &val, const T &factor ) +read_with_factor( JsonObject &jo, const std::string &name, T &val, const T &factor ) { int tmp; if( jo.read( name, tmp, false ) ) { @@ -514,16 +537,21 @@ std::enable_if::type, time_duration>::value, // Object via which to report errors which differs for proportional/relative values JsonObject err = jo; + err.allow_omitted_members(); + JsonObject relative = jo.get_object( "relative" ); + relative.allow_omitted_members(); + JsonObject proportional = jo.get_object( "proportional" ); + proportional.allow_omitted_members(); // Do not require strict parsing for relative and proportional values as rules // such as +10% are well-formed independent of whether they affect base value - if( read_with_factor( jo.get_object( "relative" ), name, out, factor ) ) { - err = jo.get_object( "relative" ); + if( read_with_factor( relative, name, out, factor ) ) { + err = relative; strict = false; out = out + val; - } else if( jo.get_object( "proportional" ).read( name, scalar ) ) { - err = jo.get_object( "proportional" ); + } else if( proportional.read( name, scalar ) ) { + err = proportional; if( scalar <= 0 || scalar == 1 ) { err.throw_error( "invalid proportional scalar", name ); } diff --git a/src/condition.cpp b/src/condition.cpp index ab7059a992b89..ef21944f562be 100644 --- a/src/condition.cpp +++ b/src/condition.cpp @@ -32,7 +32,7 @@ const efftype_id effect_currently_busy( "currently_busy" ); // throws an error on failure, so no need to return -std::string get_talk_varname( JsonObject jo, const std::string &member, bool check_value ) +std::string get_talk_varname( JsonObject &jo, const std::string &member, bool check_value ) { if( !jo.has_string( "type" ) || !jo.has_string( "context" ) || ( check_value && !jo.has_string( "value" ) ) ) { @@ -61,7 +61,7 @@ void read_condition( JsonObject &jo, const std::string &member_name, return sub_condition( d ); }; } else if( jo.has_object( member_name ) ) { - const JsonObject con_obj = jo.get_object( member_name ); + JsonObject con_obj = jo.get_object( member_name ); conditional_t sub_condition( con_obj ); condition = [sub_condition]( const T & d ) { return sub_condition( d ); @@ -877,7 +877,7 @@ void conditional_t::set_mission_has_generic_rewards() } template -conditional_t::conditional_t( JsonObject jo ) +conditional_t::conditional_t( JsonObject &jo ) { // improve the clarity of NPC setter functions const bool is_npc = true; @@ -890,7 +890,8 @@ conditional_t::conditional_t( JsonObject jo ) conditional_t type_condition( ja.next_string() ); conditionals.emplace_back( type_condition ); } else if( ja.test_object() ) { - conditional_t type_condition( ja.next_object() ); + JsonObject cond = ja.next_object(); + conditional_t type_condition( cond ); conditionals.emplace_back( type_condition ); } else { ja.skip_value(); @@ -921,7 +922,8 @@ conditional_t::conditional_t( JsonObject jo ) return false; }; } else if( jo.has_object( "not" ) ) { - const conditional_t sub_condition = conditional_t( jo.get_object( "not" ) ); + JsonObject cond = jo.get_object( "not" ); + const conditional_t sub_condition = conditional_t( cond ); found_sub_member = true; condition = [sub_condition]( const T & d ) { return !sub_condition( d ); diff --git a/src/condition.h b/src/condition.h index ceafb2b6cb96e..cb3ce71b6379d 100644 --- a/src/condition.h +++ b/src/condition.h @@ -48,7 +48,7 @@ const std::unordered_set complex_conds = { { }; } // namespace dialogue_data -std::string get_talk_varname( JsonObject jo, const std::string &member, bool check_value = true ); +std::string get_talk_varname( JsonObject &jo, const std::string &member, bool check_value = true ); // the truly awful declaration for the conditional_t loading helper_function template @@ -70,7 +70,7 @@ struct conditional_t { public: conditional_t() = default; conditional_t( const std::string &type ); - conditional_t( JsonObject jo ); + conditional_t( JsonObject &jo ); void set_has_any_trait( JsonObject &jo, const std::string &member, bool is_npc = false ); void set_has_trait( JsonObject &jo, const std::string &member, bool is_npc = false ); diff --git a/src/dialogue.h b/src/dialogue.h index 4bb4497fcab7d..499348cc9381b 100644 --- a/src/dialogue.h +++ b/src/dialogue.h @@ -70,7 +70,7 @@ struct talk_trial { bool roll( dialogue &d ) const; talk_trial() = default; - talk_trial( JsonObject ); + talk_trial( JsonObject & ); }; struct talk_topic { @@ -94,20 +94,20 @@ struct talk_effect_fun_t { talk_effect_fun_t( std::function ); talk_effect_fun_t( std::function ); void set_companion_mission( const std::string &role_id ); - void set_add_effect( JsonObject jo, const std::string &member, bool is_npc = false ); - void set_remove_effect( JsonObject jo, const std::string &member, bool is_npc = false ); - void set_add_trait( JsonObject jo, const std::string &member, bool is_npc = false ); - void set_remove_trait( JsonObject jo, const std::string &member, bool is_npc = false ); - void set_add_var( JsonObject jo, const std::string &member, bool is_npc = false ); - void set_remove_var( JsonObject jo, const std::string &member, bool is_npc = false ); - void set_adjust_var( JsonObject jo, const std::string &member, bool is_npc = false ); + void set_add_effect( JsonObject &jo, const std::string &member, bool is_npc = false ); + void set_remove_effect( JsonObject &jo, const std::string &member, bool is_npc = false ); + void set_add_trait( JsonObject &jo, const std::string &member, bool is_npc = false ); + void set_remove_trait( JsonObject &jo, const std::string &member, bool is_npc = false ); + void set_add_var( JsonObject &jo, const std::string &member, bool is_npc = false ); + void set_remove_var( JsonObject &jo, const std::string &member, bool is_npc = false ); + void set_adjust_var( JsonObject &jo, const std::string &member, bool is_npc = false ); void set_u_buy_item( const std::string &item_name, int cost, int count, const std::string &container_name ); void set_u_spend_cash( int amount ); void set_u_sell_item( const std::string &item_name, int cost, int count ); - void set_consume_item( JsonObject jo, const std::string &member, int count, + void set_consume_item( JsonObject &jo, const std::string &member, int count, bool is_npc = false ); - void set_remove_item_with( JsonObject jo, const std::string &member, bool is_npc = false ); + void set_remove_item_with( JsonObject &jo, const std::string &member, bool is_npc = false ); void set_npc_change_faction( const std::string &faction_name ); void set_npc_change_class( const std::string &class_name ); void set_change_faction_rep( int rep_change ); @@ -119,7 +119,7 @@ struct talk_effect_fun_t { void set_npc_aim_rule( const std::string &setting ); void set_npc_cbm_reserve_rule( const std::string &setting ); void set_npc_cbm_recharge_rule( const std::string &setting ); - void set_mapgen_update( JsonObject jo, const std::string &member ); + void set_mapgen_update( JsonObject &jo, const std::string &member ); void set_bulk_trade_accept( bool is_trade, bool is_npc = false ); void set_npc_gets_item( bool to_use ); void set_add_mission( std::string mission_id ); @@ -171,11 +171,11 @@ struct talk_effect_t { void set_effect_consequence( std::function ptr, dialogue_consequence con ); void load_effect( JsonObject &jo ); - void parse_sub_effect( JsonObject jo ); + void parse_sub_effect( JsonObject &jo ); void parse_string_effect( const std::string &effect_id, JsonObject &jo ); talk_effect_t() = default; - talk_effect_t( JsonObject ); + talk_effect_t( JsonObject & ); /** * Functions that are called when the response is chosen. @@ -217,7 +217,7 @@ struct talk_response { std::set get_consequences( const dialogue &d ) const; talk_response(); - talk_response( JsonObject ); + talk_response( JsonObject & ); }; struct dialogue { @@ -365,7 +365,7 @@ class json_talk_response public: json_talk_response() = default; - json_talk_response( JsonObject jo ); + json_talk_response( JsonObject &jo ); /** * Callback from @ref json_talk_topic::gen_responses, see there. @@ -395,7 +395,7 @@ class json_dynamic_line_effect std::function condition; talk_effect_t effect; public: - json_dynamic_line_effect( JsonObject jo, const std::string &id ); + json_dynamic_line_effect( JsonObject &jo, const std::string &id ); bool test_condition( const dialogue &d ) const; void apply( dialogue &d ) const; }; diff --git a/src/flag.cpp b/src/flag.cpp index 7e9e3363188d1..ccf7f3d0da620 100644 --- a/src/flag.cpp +++ b/src/flag.cpp @@ -24,6 +24,10 @@ void json_flag::load( JsonObject &jo ) jo.read( "conflicts", f.conflicts_ ); jo.read( "inherit", f.inherit_ ); jo.read( "craft_inherit", f.craft_inherit_ ); + + // FIXME: most flags have a "context" field that isn't used for anything + // Test for it here to avoid errors about unvisited members + jo.has_member( "context" ); } void json_flag::check_consistency() diff --git a/src/generic_factory.h b/src/generic_factory.h index 327a48efc3321..e82ec476f396c 100644 --- a/src/generic_factory.h +++ b/src/generic_factory.h @@ -778,11 +778,13 @@ class generic_typed_reader return false; } else { if( jo.has_object( "extend" ) ) { - auto tmp = jo.get_object( "extend" ); + JsonObject tmp = jo.get_object( "extend" ); + tmp.allow_omitted_members(); derived.insert_values_from( tmp, member_name, container ); } if( jo.has_object( "delete" ) ) { - auto tmp = jo.get_object( "delete" ); + JsonObject tmp = jo.get_object( "delete" ); + tmp.allow_omitted_members(); derived.erase_values_from( tmp, member_name, container ); } return true; diff --git a/src/init.cpp b/src/init.cpp index 69a29f999bc56..c869a92692b46 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -142,7 +142,7 @@ static void load_ignored_type( JsonObject &jo ) // This does nothing! // This function is used for types that are to be ignored // (for example for testing or for unimplemented types) - ( void ) jo; + jo.allow_omitted_members(); } void DynamicDataLoader::add( const std::string &type, diff --git a/src/input.cpp b/src/input.cpp index 5ba6a5ad43ef7..9de0ba04fc625 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -199,6 +199,13 @@ void input_manager::load( const std::string &file_name, bool is_user_preferences // JSON object representing the action JsonObject action = jsin.get_object(); + const std::string type = action.get_string( "type", "keybinding" ); + if( type != "keybinding" ) { + debugmsg( "Only objects of type 'keybinding' (not %s) should appear in the " + "keybindings file '%s'", type, file_name ); + continue; + } + const std::string action_id = action.get_string( "id" ); const std::string context = action.get_string( "category", default_context_id ); t_actions &actions = action_contexts[context]; diff --git a/src/item_factory.cpp b/src/item_factory.cpp index 0f9bc6fce5de3..f3893f5ae9c3a 100644 --- a/src/item_factory.cpp +++ b/src/item_factory.cpp @@ -1670,6 +1670,11 @@ void Item_factory::load( islot_comestible &slot, JsonObject &jo, const std::stri { bool strict = src == "dda"; + JsonObject relative = jo.get_object( "relative" ); + JsonObject proportional = jo.get_object( "proportional" ); + relative.allow_omitted_members(); + proportional.allow_omitted_members(); + assign( jo, "comestible_type", slot.comesttype, strict ); assign( jo, "tool", slot.tool, strict ); assign( jo, "charges", slot.def_charges, strict, 1 ); @@ -1717,12 +1722,12 @@ void Item_factory::load( islot_comestible &slot, JsonObject &jo, const std::stri slot.kcal = jo.get_int( "calories" ); got_calories = true; - } else if( jo.get_object( "relative" ).has_int( "calories" ) ) { - slot.kcal += jo.get_object( "relative" ).get_int( "calories" ); + } else if( relative.has_int( "calories" ) ) { + slot.kcal += relative.get_int( "calories" ); got_calories = true; - } else if( jo.get_object( "proportional" ).has_float( "calories" ) ) { - slot.kcal *= jo.get_object( "proportional" ).get_float( "calories" ); + } else if( proportional.has_float( "calories" ) ) { + slot.kcal *= proportional.get_float( "calories" ); got_calories = true; } else if( jo.has_int( "nutrition" ) ) { @@ -1747,15 +1752,14 @@ void Item_factory::load( islot_comestible &slot, JsonObject &jo, const std::stri } } - } else if( jo.has_object( "relative" ) ) { - auto rel = jo.get_object( "relative" ); - if( rel.has_int( "vitamins" ) ) { + } else { + if( relative.has_int( "vitamins" ) ) { // allows easy specification of 'fortified' comestibles for( auto &v : vitamin::all() ) { - slot.vitamins[ v.first ] += rel.get_int( "vitamins" ); + slot.vitamins[ v.first ] += relative.get_int( "vitamins" ); } - } else if( rel.has_array( "vitamins" ) ) { - auto vits = rel.get_array( "vitamins" ); + } else if( relative.has_array( "vitamins" ) ) { + auto vits = relative.get_array( "vitamins" ); while( vits.has_more() ) { auto pair = vits.next_array(); slot.vitamins[ vitamin_id( pair.get_string( 0 ) ) ] += pair.get_int( 1 ); @@ -2198,10 +2202,8 @@ void Item_factory::load_basic_info( JsonObject &jo, itype &def, const std::strin load_slot_optional( def.brewable, jo, "brewable", src ); load_slot_optional( def.fuel, jo, "fuel", src ); load_slot_optional( def.relic_data, jo, "relic_data", src ); - - // optional gunmod slot may also specify mod data load_slot_optional( def.gunmod, jo, "gunmod_data", src ); - load_slot_optional( def.mod, jo, "gunmod_data", src ); + load_slot_optional( def.mod, jo, "mod_data", src ); if( jo.has_string( "abstract" ) ) { def.id = jo.get_string( "abstract" ); diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index 5a4c1cede4063..446c6cbb392a3 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -3835,7 +3835,7 @@ place_trap_actor::place_trap_actor( const std::string &type ) : place_trap_actor::data::data() : trap( trap_str_id::NULL_ID() ) {} -void place_trap_actor::data::load( JsonObject obj ) +void place_trap_actor::data::load( JsonObject &obj ) { assign( obj, "trap", trap ); assign( obj, "done_message", done_message ); @@ -3851,7 +3851,8 @@ void place_trap_actor::load( JsonObject &obj ) assign( obj, "needs_neighbor_terrain", needs_neighbor_terrain ); assign( obj, "bury_question", bury_question ); if( !bury_question.empty() ) { - buried_data.load( obj.get_object( "bury" ) ); + JsonObject buried_json = obj.get_object( "bury" ); + buried_data.load( buried_json ); } unburied_data.load( obj ); assign( obj, "outer_layer_trap", outer_layer_trap ); diff --git a/src/iuse_actor.h b/src/iuse_actor.h index 4201bfcab45b0..fd6404cfa3ab1 100644 --- a/src/iuse_actor.h +++ b/src/iuse_actor.h @@ -957,7 +957,7 @@ class place_trap_actor : public iuse_actor int practice = 0; /** Move points that are used when placing the trap. */ int moves = 100; - void load( JsonObject obj ); + void load( JsonObject &obj ); }; /** Whether one can place the trap when underwater. */ bool allow_underwater = false; diff --git a/src/json.cpp b/src/json.cpp index 0792740a21063..893761b3b6eb6 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -16,9 +16,13 @@ #include #include +#include "cata_utility.h" + // JSON parsing and serialization tools for Cataclysm-DDA. // For documentation, see the included header, json.h. +#define dbg(x) DebugLog((x), D_MAIN) << __FILE__ << ":" << __LINE__ << ": " + static bool is_whitespace( char ch ) { // These are all the valid whitespace characters allowed by RFC 4627. @@ -92,19 +96,21 @@ JsonObject::JsonObject( JsonIn &j ) final_separator = jsin->get_ate_separator(); } -JsonObject::JsonObject( const JsonObject &jo ) -{ - jsin = jo.jsin; - start = jo.start; - positions = jo.positions; - end = jo.end; - final_separator = jo.final_separator; -} - -JsonObject &JsonObject::operator=( const JsonObject &jo ) = default; - void JsonObject::finish() { +#ifndef CATA_IN_TOOL + if( report_unvisited_members && !reported_unvisited_members && !std::uncaught_exception() ) { + reported_unvisited_members = true; + for( const std::pair &p : positions ) { + const std::string &name = p.first; + if( !visited_members.count( name ) && !string_starts_with( name, "//" ) && + name != "blueprint" ) { + dbg( D_ERROR ) << "Failed to visit member '" << name << "' in JsonObject at " + << jsin->line_number( start ) << ":\n" << str() << std::endl; + } + } + } +#endif if( jsin && jsin->good() ) { jsin->seek( end ); jsin->set_ate_separator( final_separator ); @@ -120,9 +126,15 @@ bool JsonObject::empty() return positions.empty(); } +void JsonObject::allow_omitted_members() +{ + report_unvisited_members = false; +} + int JsonObject::verify_position( const std::string &name, const bool throw_exception ) { + visited_members.insert( name ); int pos = positions[name]; // initialized to 0 if it doesn't exist if( pos > start ) { return pos; @@ -159,7 +171,11 @@ std::string JsonObject::line_number() std::string JsonObject::str() { - if( jsin ) { + // If we're getting the string form, we might be re-parsing later, so don't + // complain about unvisited members. + allow_omitted_members(); + + if( jsin && end >= start ) { return jsin->substr( start, end - start ); } else { return "{}"; @@ -208,6 +224,7 @@ bool JsonObject::get_bool( const std::string &name ) bool JsonObject::get_bool( const std::string &name, const bool fallback ) { + visited_members.insert( name ); int pos = positions[name]; if( pos <= start ) { return fallback; @@ -225,6 +242,7 @@ int JsonObject::get_int( const std::string &name ) int JsonObject::get_int( const std::string &name, const int fallback ) { + visited_members.insert( name ); int pos = positions[name]; if( pos <= start ) { return fallback; @@ -242,6 +260,7 @@ double JsonObject::get_float( const std::string &name ) double JsonObject::get_float( const std::string &name, const double fallback ) { + visited_members.insert( name ); int pos = positions[name]; if( pos <= start ) { return fallback; @@ -259,6 +278,7 @@ std::string JsonObject::get_string( const std::string &name ) std::string JsonObject::get_string( const std::string &name, const std::string &fallback ) { + visited_members.insert( name ); int pos = positions[name]; if( pos <= start ) { return fallback; @@ -271,6 +291,7 @@ std::string JsonObject::get_string( const std::string &name, const std::string & JsonArray JsonObject::get_array( const std::string &name ) { + visited_members.insert( name ); int pos = positions[name]; if( pos <= start ) { return JsonArray(); // empty array @@ -301,6 +322,7 @@ std::vector JsonObject::get_string_array( const std::string &name ) JsonObject JsonObject::get_object( const std::string &name ) { + visited_members.insert( name ); int pos = positions[name]; if( pos <= start ) { return JsonObject(); // empty object @@ -1365,10 +1387,11 @@ bool JsonIn::read( JsonDeserializer &j, bool throw_on_error ) // WARNING: for occasional use only. std::string JsonIn::line_number( int offset_modifier ) { + if( !stream || stream->fail() ) { + return "???"; + } if( stream->eof() ) { return "EOF"; - } else if( stream->fail() ) { - return "???"; } // else stream is fine int pos = tell(); int line = 1; diff --git a/src/json.h b/src/json.h index 57c371a98bb48..70dd8e12b4722 100644 --- a/src/json.h +++ b/src/json.h @@ -758,31 +758,52 @@ class JsonOut * if (!jo.read("messages", messages)) { * DebugLog() << "No messages."; * } + * + * + * Automatic error checking + * ------------------------ + * + * By default, when a JsonObject is destroyed (or when you call finish) it will + * check to see whether every member of the object was referenced in some way + * (even simply checking for the existence of the member is suffucient). + * + * If not all the members were referenced, then an error will be written to the + * log (which in particular will cause the tests to fail). + * + * If you don't want this behaviour, then call allow_omitted_members() before + * the JsonObject is destroyed. Calling str() also suppresses it (on the basis + * that you may be intending to re-parse that string later). */ class JsonObject { private: std::map positions; + std::set visited_members; int start; int end; bool final_separator; + bool report_unvisited_members = true; + bool reported_unvisited_members = false; JsonIn *jsin; int verify_position( const std::string &name, bool throw_exception = true ); public: JsonObject( JsonIn &jsin ); - JsonObject( const JsonObject &jo ); JsonObject() : start( 0 ), end( 0 ), jsin( nullptr ) {} + JsonObject( const JsonObject & ) = default; + JsonObject( JsonObject && ) = default; + JsonObject &operator=( const JsonObject & ) = default; + JsonObject &operator=( JsonObject && ) = default; ~JsonObject() { finish(); } - JsonObject &operator=( const JsonObject & ); void finish(); // moves the stream to the end of the object size_t size(); bool empty(); + void allow_omitted_members(); bool has_member( const std::string &name ); // true iff named member exists std::set get_member_names(); std::string str(); // copy object json as string @@ -852,6 +873,7 @@ class JsonObject // but the read fails. template bool read( const std::string &name, T &t, bool throw_on_error = true ) { + visited_members.insert( name ); int pos = positions[name]; if( pos <= start ) { return false; @@ -1053,6 +1075,7 @@ template std::set JsonObject::get_tags( const std::string &name ) { std::set res; + visited_members.insert( name ); int pos = positions[ name ]; if( pos <= start ) { return res; diff --git a/src/mapgen.cpp b/src/mapgen.cpp index 46befa276ecdd..81e0676ab2a41 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -256,7 +256,7 @@ void calculate_mapgen_weights() // TODO: rename as it runs jsonfunction setup } // Not really calculate weights, but let's keep it here for now for( auto &pr : nested_mapgen ) { - for( auto &ptr : pr.second ) { + for( std::unique_ptr &ptr : pr.second ) { ptr->setup(); } } @@ -330,6 +330,7 @@ load_mapgen_function( JsonObject &jio, const std::string &id_base, } } } + jio.allow_omitted_members(); return nullptr; // nothing } else if( jio.has_string( "method" ) ) { const std::string mgtype = jio.get_string( "method" ); @@ -624,6 +625,7 @@ void mapgen_function_json_base::setup_setmap( JsonArray &parray ) const jmapgen_int tmp_x( pjo, "x" ); const jmapgen_int tmp_y( pjo, "y" ); if( !check_inbounds( tmp_x, tmp_y, pjo ) ) { + pjo.allow_omitted_members(); continue; } if( setmap_optype != JMAPGEN_SETMAP_OPTYPE_POINT ) { @@ -1772,6 +1774,8 @@ void jmapgen_objects::load_objects( JsonArray parray ) if( check_bounds( where, jsi ) ) { add( where, std::make_shared( jsi ) ); + } else { + jsi.allow_omitted_members(); } } } @@ -1785,6 +1789,7 @@ void jmapgen_objects::load_objects( JsonArray parray ) where.offset( m_offset ); if( !check_bounds( where, jsi ) ) { + jsi.allow_omitted_members(); continue; } @@ -1969,6 +1974,7 @@ void mapgen_palette::load_place_mapings( JsonObject &jo, const std::string &memb pjo.throw_error( "format map key must be 1 character", key ); } JsonObject sub = pjo.get_object( key ); + sub.allow_omitted_members(); if( !sub.has_member( member_name ) ) { continue; } @@ -2215,7 +2221,7 @@ void mapgen_function_json_base::setup_common() } } -bool mapgen_function_json_base::setup_common( JsonObject jo ) +bool mapgen_function_json_base::setup_common( JsonObject &jo ) { bool qualifies = setup_internal( jo ); JsonArray parray; diff --git a/src/mapgen.h b/src/mapgen.h index 2ac767a272edf..8bf9cf02d24f6 100644 --- a/src/mapgen.h +++ b/src/mapgen.h @@ -287,7 +287,7 @@ class mapgen_function_json_base virtual ~mapgen_function_json_base(); void setup_common(); - bool setup_common( JsonObject jo ); + bool setup_common( JsonObject &jo ); void setup_setmap( JsonArray &parray ); // Returns true if the mapgen qualifies at this point already virtual bool setup_internal( JsonObject &jo ) = 0; diff --git a/src/mission.h b/src/mission.h index de6d3ebf9aacc..492c93dbd915a 100644 --- a/src/mission.h +++ b/src/mission.h @@ -179,7 +179,7 @@ tripoint get_om_terrain_pos( const mission_target_params ¶ms ); void set_assign_om_target( JsonObject &jo, std::vector> &funcs ); bool set_update_mapgen( JsonObject &jo, std::vector> &funcs ); -bool load_funcs( JsonObject jo, std::vector> &funcs ); +bool load_funcs( JsonObject &jo, std::vector> &funcs ); } // namespace mission_util struct mission_goal_condition_context { diff --git a/src/mission_util.cpp b/src/mission_util.cpp index 16f7b16058d21..d772071b88f56 100644 --- a/src/mission_util.cpp +++ b/src/mission_util.cpp @@ -441,6 +441,7 @@ bool mission_util::set_update_mapgen( JsonObject &jo, bool defer = false; mapgen_update_func update_map = add_mapgen_update_func( jo, defer ); if( defer ) { + jo.allow_omitted_members(); return false; } @@ -461,7 +462,7 @@ bool mission_util::set_update_mapgen( JsonObject &jo, return true; } -bool mission_util::load_funcs( JsonObject jo, +bool mission_util::load_funcs( JsonObject &jo, std::vector> &funcs ) { if( jo.has_string( "reveal_om_ter" ) ) { diff --git a/src/missiondef.cpp b/src/missiondef.cpp index 66b5676504c8e..cc26941b18aa5 100644 --- a/src/missiondef.cpp +++ b/src/missiondef.cpp @@ -285,6 +285,7 @@ void mission_type::load( JsonObject &jo, const std::string &src ) JsonObject j_start = jo.get_object( phase ); if( !parse_funcs( j_start, phase_func ) ) { deferred.emplace_back( jo.str(), src ); + j_start.allow_omitted_members(); return false; } } diff --git a/src/mod_manager.cpp b/src/mod_manager.cpp index ea21bf2513e57..29f17913d7712 100644 --- a/src/mod_manager.cpp +++ b/src/mod_manager.cpp @@ -202,6 +202,7 @@ void mod_manager::load_modfile( JsonObject &jo, const std::string &path ) { if( !jo.has_string( "type" ) || jo.get_string( "type" ) != "MOD_INFO" ) { // Ignore anything that is not a mod-info + jo.allow_omitted_members(); return; } diff --git a/src/monstergenerator.cpp b/src/monstergenerator.cpp index f8780c515420c..8eb838421e14b 100644 --- a/src/monstergenerator.cpp +++ b/src/monstergenerator.cpp @@ -726,11 +726,13 @@ void mtype::load( JsonObject &jo, const std::string &src ) // Note: special_attacks left as is, new attacks are added to it! // Note: member name prefixes are compatible with those used by generic_typed_reader if( jo.has_object( "extend" ) ) { - auto tmp = jo.get_object( "extend" ); + JsonObject tmp = jo.get_object( "extend" ); + tmp.allow_omitted_members(); add_special_attacks( tmp, "special_attacks", src ); } if( jo.has_object( "delete" ) ) { - auto tmp = jo.get_object( "delete" ); + JsonObject tmp = jo.get_object( "delete" ); + tmp.allow_omitted_members(); remove_special_attacks( tmp, "special_attacks", src ); } } @@ -896,7 +898,7 @@ void MonsterGenerator::add_attack( const mtype_special_attack &wrapper ) attack_map.emplace( wrapper->id, wrapper ); } -mtype_special_attack MonsterGenerator::create_actor( JsonObject obj, const std::string &src ) const +mtype_special_attack MonsterGenerator::create_actor( JsonObject &obj, const std::string &src ) const { // Legacy support: tolerate attack types being specified as the type const std::string type = obj.get_string( "type", "monster_attack" ); diff --git a/src/monstergenerator.h b/src/monstergenerator.h index eb1da36a77fde..001c1066e7be4 100644 --- a/src/monstergenerator.h +++ b/src/monstergenerator.h @@ -86,7 +86,7 @@ class MonsterGenerator void add_attack( const mtype_special_attack &wrapper ); /** Gets an actor object without saving it anywhere */ - mtype_special_attack create_actor( JsonObject obj, const std::string &src ) const; + mtype_special_attack create_actor( JsonObject &obj, const std::string &src ) const; // finalization void apply_species_attributes( mtype &mon ); diff --git a/src/npctalk.cpp b/src/npctalk.cpp index 78e080f3ce7ea..a165c3f3ab539 100644 --- a/src/npctalk.cpp +++ b/src/npctalk.cpp @@ -1766,7 +1766,7 @@ talk_topic dialogue::opt( dialogue_window &d_win, const talk_topic &topic ) return effects.apply( *this ); } -talk_trial::talk_trial( JsonObject jo ) +talk_trial::talk_trial( JsonObject &jo ) { static const std::unordered_map types_map = { { #define WRAP(value) { #value, TALK_TRIAL_##value } @@ -1840,7 +1840,7 @@ void talk_effect_fun_t::set_companion_mission( const std::string &role_id ) }; } -void talk_effect_fun_t::set_add_effect( JsonObject jo, const std::string &member, bool is_npc ) +void talk_effect_fun_t::set_add_effect( JsonObject &jo, const std::string &member, bool is_npc ) { std::string new_effect = jo.get_string( member ); bool permanent = false; @@ -1864,7 +1864,7 @@ void talk_effect_fun_t::set_add_effect( JsonObject jo, const std::string &member }; } -void talk_effect_fun_t::set_remove_effect( JsonObject jo, const std::string &member, bool is_npc ) +void talk_effect_fun_t::set_remove_effect( JsonObject &jo, const std::string &member, bool is_npc ) { std::string old_effect = jo.get_string( member ); function = [is_npc, old_effect]( const dialogue & d ) { @@ -1876,7 +1876,7 @@ void talk_effect_fun_t::set_remove_effect( JsonObject jo, const std::string &mem }; } -void talk_effect_fun_t::set_add_trait( JsonObject jo, const std::string &member, bool is_npc ) +void talk_effect_fun_t::set_add_trait( JsonObject &jo, const std::string &member, bool is_npc ) { std::string new_trait = jo.get_string( member ); function = [is_npc, new_trait]( const dialogue & d ) { @@ -1888,7 +1888,7 @@ void talk_effect_fun_t::set_add_trait( JsonObject jo, const std::string &member, }; } -void talk_effect_fun_t::set_remove_trait( JsonObject jo, const std::string &member, bool is_npc ) +void talk_effect_fun_t::set_remove_trait( JsonObject &jo, const std::string &member, bool is_npc ) { std::string old_trait = jo.get_string( member ); function = [is_npc, old_trait]( const dialogue & d ) { @@ -1900,7 +1900,7 @@ void talk_effect_fun_t::set_remove_trait( JsonObject jo, const std::string &memb }; } -void talk_effect_fun_t::set_add_var( JsonObject jo, const std::string &member, bool is_npc ) +void talk_effect_fun_t::set_add_var( JsonObject &jo, const std::string &member, bool is_npc ) { const std::string var_name = get_talk_varname( jo, member ); const std::string &value = jo.get_string( "value" ); @@ -1913,7 +1913,7 @@ void talk_effect_fun_t::set_add_var( JsonObject jo, const std::string &member, b }; } -void talk_effect_fun_t::set_remove_var( JsonObject jo, const std::string &member, bool is_npc ) +void talk_effect_fun_t::set_remove_var( JsonObject &jo, const std::string &member, bool is_npc ) { const std::string var_name = get_talk_varname( jo, member, false ); function = [is_npc, var_name]( const dialogue & d ) { @@ -1925,7 +1925,7 @@ void talk_effect_fun_t::set_remove_var( JsonObject jo, const std::string &member }; } -void talk_effect_fun_t::set_adjust_var( JsonObject jo, const std::string &member, bool is_npc ) +void talk_effect_fun_t::set_adjust_var( JsonObject &jo, const std::string &member, bool is_npc ) { const std::string var_name = get_talk_varname( jo, member, false ); const int value = jo.get_int( "adjustment" ); @@ -2023,7 +2023,7 @@ void talk_effect_fun_t::set_u_sell_item( const std::string &item_name, int cost, }; } -void talk_effect_fun_t::set_consume_item( JsonObject jo, const std::string &member, int count, +void talk_effect_fun_t::set_consume_item( JsonObject &jo, const std::string &member, int count, bool is_npc ) { const std::string &item_name = jo.get_string( member ); @@ -2048,7 +2048,7 @@ void talk_effect_fun_t::set_consume_item( JsonObject jo, const std::string &memb }; } -void talk_effect_fun_t::set_remove_item_with( JsonObject jo, const std::string &member, +void talk_effect_fun_t::set_remove_item_with( JsonObject &jo, const std::string &member, bool is_npc ) { const std::string &item_name = jo.get_string( member ); @@ -2190,7 +2190,7 @@ void talk_effect_fun_t::set_npc_cbm_recharge_rule( const std::string &setting ) }; } -void talk_effect_fun_t::set_mapgen_update( JsonObject jo, const std::string &member ) +void talk_effect_fun_t::set_mapgen_update( JsonObject &jo, const std::string &member ) { mission_target_params target_params = mission_util::parse_mission_om_target( jo ); std::vector update_ids; @@ -2412,7 +2412,7 @@ talk_topic talk_effect_t::apply( dialogue &d ) const return next_topic; } -talk_effect_t::talk_effect_t( JsonObject jo ) +talk_effect_t::talk_effect_t( JsonObject &jo ) { load_effect( jo ); if( jo.has_object( "topic" ) ) { @@ -2422,7 +2422,7 @@ talk_effect_t::talk_effect_t( JsonObject jo ) } } -void talk_effect_t::parse_sub_effect( JsonObject jo ) +void talk_effect_t::parse_sub_effect( JsonObject &jo ) { talk_effect_fun_t subeffect_fun; const bool is_npc = true; @@ -2711,7 +2711,7 @@ talk_response::talk_response() dialogue_spell = spell_id(); } -talk_response::talk_response( JsonObject jo ) +talk_response::talk_response( JsonObject &jo ) { if( jo.has_member( "truefalsetext" ) ) { JsonObject truefalse_jo = jo.get_object( "truefalsetext" ); @@ -2725,10 +2725,12 @@ talk_response::talk_response( JsonObject jo ) }; } if( jo.has_member( "trial" ) ) { - trial = talk_trial( jo.get_object( "trial" ) ); + JsonObject trial_obj = jo.get_object( "trial" ); + trial = talk_trial( trial_obj ); } if( jo.has_member( "success" ) ) { - success = talk_effect_t( jo.get_object( "success" ) ); + JsonObject success_obj = jo.get_object( "success" ); + success = talk_effect_t( success_obj ); } else if( jo.has_string( "topic" ) ) { // This is for simple topic switching without a possible failure success.next_topic = talk_topic( jo.get_string( "topic" ) ); @@ -2740,7 +2742,8 @@ talk_response::talk_response( JsonObject jo ) jo.throw_error( "the failure effect is mandatory if a talk_trial has been defined" ); } if( jo.has_member( "failure" ) ) { - failure = talk_effect_t( jo.get_object( "failure" ) ); + JsonObject failure_obj = jo.get_object( "failure" ); + failure = talk_effect_t( failure_obj ); } // TODO: mission_selected @@ -2777,13 +2780,14 @@ json_talk_repeat_response::json_talk_repeat_response( JsonObject jo ) jo.throw_error( "Repeat response with empty repeat information!" ); } if( jo.has_object( "response" ) ) { - response = json_talk_response( jo.get_object( "response" ) ); + JsonObject response_obj = jo.get_object( "response" ); + response = json_talk_response( response_obj ); } else { jo.throw_error( "Repeat response with no response!" ); } } -json_talk_response::json_talk_response( JsonObject jo ) +json_talk_response::json_talk_response( JsonObject &jo ) : actual_response( jo ) { load_condition( jo ); @@ -2975,7 +2979,7 @@ dynamic_line_t::dynamic_line_t( JsonArray ja ) }; } -json_dynamic_line_effect::json_dynamic_line_effect( JsonObject jo, const std::string &id ) +json_dynamic_line_effect::json_dynamic_line_effect( JsonObject &jo, const std::string &id ) { std::function tmp_condition; read_condition( jo, "condition", tmp_condition, true ); @@ -3022,18 +3026,21 @@ void json_talk_topic::load( JsonObject &jo ) id = jo.get_array( "id" ).next_string(); } if( jo.has_object( "speaker_effect" ) ) { - speaker_effects.emplace_back( jo.get_object( "speaker_effect" ), id ); + JsonObject speaker_effect = jo.get_object( "speaker_effect" ); + speaker_effects.emplace_back( speaker_effect, id ); } else if( jo.has_array( "speaker_effect" ) ) { JsonArray ja = jo.get_array( "speaker_effect" ); while( ja.has_more() ) { - speaker_effects.emplace_back( ja.next_object(), id ); + JsonObject speaker_effect = ja.next_object(); + speaker_effects.emplace_back( speaker_effect, id ); } } } JsonArray ja = jo.get_array( "responses" ); responses.reserve( responses.size() + ja.size() ); while( ja.has_more() ) { - responses.emplace_back( ja.next_object() ); + JsonObject response = ja.next_object(); + responses.emplace_back( response ); } if( jo.has_object( "repeat_responses" ) ) { repeat_responses.emplace_back( jo.get_object( "repeat_responses" ) ); diff --git a/src/options.cpp b/src/options.cpp index 944c44fa45ea8..4ef015a0e8e27 100644 --- a/src/options.cpp +++ b/src/options.cpp @@ -41,6 +41,8 @@ #include #include +#define dbg(x) DebugLog((x), D_MAIN) << __FILE__ << ":" << __LINE__ << ": " + bool trigdist; bool use_tiles; bool log_from_top; @@ -2783,6 +2785,11 @@ void options_manager::deserialize( JsonIn &jsin ) const std::string value = migrateOptionValue( joOptions.get_string( "name" ), joOptions.get_string( "value" ) ); + // Verify format of options file + if( !joOptions.has_string( "info" ) || !joOptions.has_string( "default" ) ) { + dbg( D_ERROR ) << "options object " << name << " was missing info or default"; + } + add_retry( name, value ); options[ name ].setValue( value ); } diff --git a/src/panels.cpp b/src/panels.cpp index 7d634530dddcb..07eb039530ca5 100644 --- a/src/panels.cpp +++ b/src/panels.cpp @@ -2216,6 +2216,8 @@ void panel_manager::deserialize( JsonIn &jsin ) JsonObject joPanel = jaPanels.next_object(); std::string name = joPanel.get_string( "name" ); + bool toggle = joPanel.get_bool( "toggle" ); + for( auto it2 = layout.begin() + std::distance( layout.begin(), it ); it2 != layout.end(); ++it2 ) { if( it2->get_name() == name ) { if( it->get_name() != name ) { @@ -2223,7 +2225,7 @@ void panel_manager::deserialize( JsonIn &jsin ) layout.erase( it2 ); it = layout.insert( it, panel ); } - it->toggle = joPanel.get_bool( "toggle" ); + it->toggle = toggle; ++it; break; } diff --git a/src/veh_type.cpp b/src/veh_type.cpp index b65f3c85c9d0e..caf0833de4253 100644 --- a/src/veh_type.cpp +++ b/src/veh_type.cpp @@ -165,7 +165,7 @@ static void parse_vp_reqs( JsonObject &obj, const std::string &id, const std::st if( !obj.has_object( key ) ) { return; } - auto src = obj.get_object( key ); + JsonObject src = obj.get_object( key ); auto sk = src.get_array( "skills" ); if( !sk.empty() ) { @@ -974,7 +974,7 @@ void vehicle_prototype::load( JsonObject &jo ) vgroups[vgroup_id( jo.get_string( "id" ) )].add_vehicle( vproto_id( jo.get_string( "id" ) ), 100 ); - const auto add_part_obj = [&]( JsonObject part, point pos ) { + const auto add_part_obj = [&]( JsonObject & part, point pos ) { part_def pt; pt.pos = pos; pt.part = vpart_id( part.get_string( "part" ) ); diff --git a/src/worldfactory.cpp b/src/worldfactory.cpp index 5ac9cc8c519c3..6679694ee52c0 100644 --- a/src/worldfactory.cpp +++ b/src/worldfactory.cpp @@ -34,6 +34,8 @@ using namespace std::placeholders; +#define dbg(x) DebugLog((x), D_MAIN) << __FILE__ << ":" << __LINE__ << ": " + static const std::string SAVE_MASTER( "master.gsav" ); static const std::string SAVE_EXTENSION( ".sav" ); @@ -1331,6 +1333,11 @@ void WORLD::load_options( JsonIn &jsin ) const std::string value = opts.migrateOptionValue( jo.get_string( "name" ), jo.get_string( "value" ) ); + // Verify format of options file + if( !jo.has_string( "info" ) || !jo.has_string( "default" ) ) { + dbg( D_ERROR ) << "options object " << name << " was missing info or default"; + } + if( name == "CORE_VERSION" ) { version = std::max( std::atoi( value.c_str() ), 0 ); continue;