@@ -3936,7 +3936,7 @@
"color": "dark_gray",
"gunmod_data": {
"location": "underbarrel",
"mod_targets": [ "shotgun", "rifle" ],
"mod_targets": [ "shotgun", "rifle", "smg", "crossbow" ],
"dispersion_modifier": 10,
"mode_modifier": [ [ "REACH", "bayonet", 2, [ "MELEE" ] ] ],
"min_skills": [ [ "weapon", 2 ], [ "melee", 1 ] ]
@@ -5148,7 +5148,7 @@
"menu_text": "Turn off",
"type": "transform"
},
"flags": [ "LIGHT_10", "TRADER_AVOID" ]
"flags": [ "LIGHT_10", "TRADER_AVOID", "FIRE" ]
},
{
"id": "oxygen_tank",
@@ -5622,10 +5622,7 @@
"bashing": 6,
"material": [ "steel", "plastic" ],
"symbol": ";",
"color": "dark_gray",
"ammo": "battery",
"initial_charges": 100,
"max_charges": 100
"color": "dark_gray"
},
{
"id": "primitive_adze",
@@ -439,6 +439,7 @@
"primitive_knife",
"q_staff",
"rebar",
"scimitar",
"scythe",
"scythe_war",
"shock_staff",
@@ -998,6 +998,7 @@
"armor_bash": 18,
"armor_cut": 14,
"vision_day": 50,
"vision_night": 35,
"starting_ammo": { "40mm_frag": 100, "556": 1000 },
"path_settings": { "max_dist": 10 },
"special_attacks": [ [ "CHICKENBOT", 4 ] ],
@@ -1152,7 +1152,7 @@
"count": 30,
"start": "ranch_construct_16",
"origins": [ "ORIGIN_SECONDARY" ],
"followup": "MISSION_RANCH_FOREMAN_17",
"followup": "MISSION_NULL",
"dialogue": {
"describe": "We need help...",
"offer": "Although we are an agricultural outpost, we are restricted to growing plants that are compatible with the New England climate during the warmer months. The easiest way to work around that is to build green houses to supplement our external fields. There isn't going to be an easy way to build these, we are going to need a massive number of glass sheets to enclose the frames. The first house will need 30 sheets of glass if you are still interested.",
@@ -3,8 +3,9 @@
"type" : "npc",
"id" : "old_guard_rep",
"comment" : "Appears in the refugee center as a source of missions and info. Faction critical.",
"comment" : "name+ is appended to the end of the npc's name. If you remove the comma it sets the name as is.",
"name+" : ", Representative",
"comment" : "name_suffix is appended to the end of the npc's name.",
"comment" : "using 'name_unique' instead will set the guy's name as is.",
"name_suffix" : "Representative",
"comment" : "Class is based on the enum in npc.h. The important ones are 0=NC_NONE, 2=NC_SHOPKEEP,",
"comment" : "3=NC_HACKER, 4=NC_DOCTOR, 5=NC_TRADER, 6=NC_NINJA, 7=NC_COWBOY, 8=NC_SCIENTIST,",
"comment" : "9=NC_BOUNTY_HUNTER, 10=NC_THUG, 11=NC_SCAVENGER, 13=NC_HUNTER, 14=NC_SOLDIER.",
@@ -22,7 +23,7 @@
"type" : "npc",
"id" : "old_guard_soldier",
"comment" : "Generic guard for the old guard.",
"name+" : ", Soldier",
"name_suffix" : "Soldier",
"class" : "NC_SOLDIER",
"attitude" : 0,
"mission" : 7,
@@ -32,7 +33,7 @@
"type" : "npc",
"id" : "old_guard_necropolis_cpt",
"comment" : "Commander in Necropolis",
"name+" : ", CPT",
"name_suffix" : "CPT",
"class" : "NC_SOLDIER",
"attitude" : 0,
"mission" : 7,
@@ -43,7 +44,7 @@
"type" : "npc",
"id" : "old_guard_necropolis_commo",
"comment" : "In charge of outside communications in Necropolis",
"name+" : ", SFC",
"name_suffix" : "SFC",
"class" : "NC_SOLDIER",
"attitude" : 0,
"mission" : 7,
@@ -54,7 +55,7 @@
"type" : "npc",
"id" : "evac_merchant",
"comment" : "Appears in the refugee center as shopkeeper with missions. Faction critical.",
"name+" : ", Merchant",
"name_suffix" : "Merchant",
"class" : "NC_EVAC_SHOPKEEP",
"attitude" : 0,
"mission" : 3,
@@ -65,7 +66,7 @@
"type" : "npc",
"id" : "evac_broker",
"comment" : "Appears in the refugee center as a bulk trader. Promotes production of nonperishable food.",
"name+" : ", Broker",
"name_suffix" : "Broker",
"class" : "NC_BOUNTY_HUNTER",
"attitude" : 0,
"mission" : 7,
@@ -75,7 +76,7 @@
"type" : "npc",
"id" : "evac_guard1",
"comment" : "Appears in the refugee center as a guard with custom dialogue.",
"name+" : ", Guard",
"name_suffix" : "Guard",
"class" : "NC_BOUNTY_HUNTER",
"attitude" : 0,
"mission" : 7,
@@ -85,7 +86,7 @@
"type" : "npc",
"id" : "evac_guard2",
"comment" : "Appears in the refugee center as a guard with custom dialogue.",
"name+" : ", Guard",
"name_suffix" : "Guard",
"class" : "NC_BOUNTY_HUNTER",
"attitude" : 0,
"mission" : 7,
@@ -95,7 +96,7 @@
"type" : "npc",
"id" : "evac_guard3",
"comment" : "Appears in the refugee center as a guard with custom dialogue.",
"name+" : ", Guard",
"name_suffix" : "Guard",
"class" : "NC_BOUNTY_HUNTER",
"attitude" : 0,
"mission" : 7,
@@ -105,7 +106,7 @@
"type" : "npc",
"id" : "guard",
"comment" : "A generic guard for the Free Merchants faction.",
"name+" : ", Guard",
"name_suffix" : "Guard",
"class" : "NC_BOUNTY_HUNTER",
"attitude" : 0,
"mission" : 7,
@@ -115,7 +116,7 @@
"type" : "npc",
"id" : "hostile_guard",
"comment" : "A generic hostile guard for the Free Merchants faction. For where the player shouldn't venture.'",
"name+" : ", Guard",
"name_suffix" : "Guard",
"class" : "NC_BOUNTY_HUNTER",
"attitude" : 10,
"mission" : 7,
@@ -125,7 +126,7 @@
"type" : "npc",
"id" : "ranch_foreman",
"comment" : "Appears at the ranch after you progress in the refugee center quests. Faction critical.",
"name+" : ", Foreman",
"name_suffix" : "Foreman",
"class" : "NC_BOUNTY_HUNTER",
"attitude" : 0,
"mission" : 7,
@@ -136,7 +137,7 @@
"type" : "npc",
"id" : "ranch_construction_1",
"comment" : "Flavor",
"name+" : ", Carpenter",
"name_suffix" : "Carpenter",
"class" : "NC_HUNTER",
"attitude" : 0,
"mission" : 7,
@@ -146,7 +147,7 @@
"type" : "npc",
"id" : "ranch_construction_2",
"comment" : "Construction skill trainer",
"name+" : ", Carpenter",
"name_suffix" : "Carpenter",
"class" : "NC_THUG",
"attitude" : 0,
"mission" : 7,
@@ -156,7 +157,7 @@
"type" : "npc",
"id" : "ranch_woodcutter_1",
"comment" : "Can purchase wood",
"name+" : ", Lumberjack",
"name_suffix" : "Lumberjack",
"class" : "NC_COWBOY",
"attitude" : 0,
"mission" : 7,
@@ -166,7 +167,7 @@
"type" : "npc",
"id" : "ranch__woodcutter_2",
"comment" : "Flavor",
"name+" : ", Woodworker",
"name_suffix" : "Woodworker",
"class" : "NC_COWBOY",
"attitude" : 0,
"mission" : 7,
@@ -176,7 +177,7 @@
"type" : "npc",
"id" : "ranch_crop_overseer",
"comment" : "Flavor",
"name+" : ", Crop Overseer",
"name_suffix" : "Crop Overseer",
"class" : "NC_BOUNTY_HUNTER",
"attitude" : 0,
"mission" : 7,
@@ -186,7 +187,7 @@
"type" : "npc",
"id" : "ranch_farmer_1",
"comment" : "Flavor",
"name+" : ", Farmer",
"name_suffix" : "Farmer",
"class" : "NC_THUG",
"attitude" : 0,
"mission" : 7,
@@ -196,7 +197,7 @@
"type" : "npc",
"id" : "ranch_farmer_2",
"comment" : "Flavor",
"name+" : ", Farmer",
"name_suffix" : "Farmer",
"class" : "NC_HUNTER",
"attitude" : 0,
"mission" : 7,
@@ -206,7 +207,7 @@
"type" : "npc",
"id" : "ranch_ill_1",
"comment" : "Flavor",
"name+" : ", Laborer",
"name_suffix" : "Laborer",
"class" : "NC_HUNTER",
"attitude" : 0,
"mission" : 7,
@@ -216,7 +217,7 @@
"type" : "npc",
"id" : "ranch_nurse_1",
"comment" : "Mission source for clinic. Provides medical attention.",
"name+" : ", Nurse",
"name_suffix" : "Nurse",
"gender" : "female",
"class" : "NC_HUNTER",
"attitude" : 0,
@@ -228,7 +229,7 @@
"type" : "npc",
"id" : "ranch_doctor",
"comment" : "Provides advanced medical attention.",
"name+" : ", Doctor",
"name_suffix" : "Doctor",
"class" : "NC_DOCTOR",
"attitude" : 0,
"mission" : 7,
@@ -238,7 +239,7 @@
"type" : "npc",
"id" : "ranch_scrapper_1",
"comment" : "Flavor",
"name+" : ", Scrapper",
"name_suffix" : "Scrapper",
"class" : "NC_HUNTER",
"attitude" : 0,
"mission" : 7,
@@ -248,7 +249,7 @@
"type" : "npc",
"id" : "ranch_scavenger_1",
"comment" : "Mission source, shopkeep",
"name+" : ", Scavenger Boss",
"name_suffix" : "Scavenger Boss",
"class" : "NC_JUNK_SHOPKEEP",
"attitude" : 0,
"mission" : 3,
@@ -259,7 +260,7 @@
"type" : "npc",
"id" : "ranch_bartender",
"comment" : "Mission source, shopkeep",
"name+" : ", Bartender",
"name_suffix" : "Bartender",
"class" : "NC_BARTENDER",
"attitude" : 0,
"mission" : 3,
@@ -270,7 +271,7 @@
"type" : "npc",
"id" : "ranch_barber",
"comment" : "Provides hair cuts",
"name+" : ", Barber",
"name_suffix" : "Barber",
"class" : "NC_HUNTER",
"attitude" : 0,
"mission" : 7,
@@ -280,7 +281,7 @@
"type" : "npc",
"id" : "commune_guard",
"comment" : "A generic guard for the Tacoma Commune faction.",
"name+" : ", Guard",
"name_suffix" : "Guard",
"class" : "NC_BOUNTY_HUNTER",
"attitude" : 0,
"mission" : 7,
@@ -290,7 +291,7 @@
"type" : "npc",
"id" : "scavenger_hunter",
"comment" : "Appears in the refugee center as a trader.",
"name+" : ", Hunter",
"name_suffix" : "Hunter",
"class" : "NC_HUNTER",
"attitude" : 0,
"mission" : 7,
@@ -300,7 +301,7 @@
"type" : "npc",
"id" : "scavenger_merc",
"comment" : "Appears in the refugee center as a partner for hire.",
"name+" : ", Merc",
"name_suffix" : "Merc",
"class" : "NC_COWBOY",
"attitude" : 0,
"mission" : 7,
@@ -310,7 +311,8 @@
"type" : "npc",
"id" : "arsonist",
"comment" : "Appears in the refugee center as a unique trader and potential mission provider.",
"name+" : "Makayla Sanchez, Arsonist",
"name_unique" : "Makayla Sanchez",
"name_suffix" : "Arsonist",
"comment" : "Gender is only referenced when the npc has a complete unique name",
"gender" : "female",
"class" : "NC_ARSONIST",
@@ -322,7 +324,7 @@
"type" : "npc",
"id" : "thug",
"comment" : "Generic melee focused guard for the Hell's Raiders faction.",
"name+" : ", Thug",
"name_suffix" : "Thug",
"class" : "NC_THUG",
"attitude" : 0,
"mission" : 7,
@@ -332,7 +334,7 @@
"type" : "npc",
"id" : "bandit",
"comment" : "Generic pistol/rifle focused guard for the Hell's Raiders faction.",
"name+" : ", Bandit",
"name_suffix" : "Bandit",
"class" : "NC_SCAVENGER",
"attitude" : 0,
"mission" : 7,
@@ -342,7 +344,7 @@
"type" : "npc",
"id" : "apis",
"comment" : "Bee mutant or mutant bee (supposed to be ambiguous). Should be 'it', but we'll go with 'she' because it's a bee.",
"name+" : "Apis",
"name_unique" : "Apis",
"class" : "NC_APIS",
"gender" : "female",
"attitude" : 0,
@@ -4401,27 +4401,28 @@
}
],
"items": {
"both": [
"pants_fur",
"trenchcoat_fur",
"backpack_leather",
"boots_fur",
"gloves_fur",
"hat_fur",
"scarf_fur",
"quiver",
"sheath",
"shortbow",
"arrow_wood",
"arrow_wood",
"primitive_knife",
"shelter_kit",
"leather_funnel",
"rock_pot",
"waterskin",
"fur_rollmat",
"fire_drill"
]
"both": {
"items": [
"pants_fur",
"trenchcoat_fur",
"backpack_leather",
"boots_fur",
"gloves_fur",
"hat_fur",
"scarf_fur",
"shortbow",
"shelter_kit",
"leather_funnel",
"rock_pot",
"waterskin",
"fur_rollmat",
"fire_drill"
],
"entries": [
{ "item": "primitive_knife", "container-item": "sheath" },
{ "item": "arrow_wood", "charges": 20, "container-item": "quiver" }
]
}
},
"flags" : ["SCEN_ONLY"]
},
@@ -4448,18 +4449,17 @@
"tank_top",
"socks",
"fishing_waders",
"knife_hunting",
"knit_scarf",
"fishing_rod_professional",
"fish_bait",
"fish_trap",
"vest",
"hat_boonie",
"jacket_flannel",
"sheath"
"jacket_flannel"
],
"entries": [
{ "item": "lighter", "charges": 100 }
{ "item": "lighter", "charges": 100 },
{ "item": "knife_hunting", "container-item": "sheath" }
]
},
"male": [
@@ -13,7 +13,7 @@
"components": [
[ [ "medical_tape", 20 ], ["duct_tape", 40] ],
[ [ "aspirin", 10 ], [ "codeine", 2 ] ],
[ [ "chem_ethanol", 250 ], [ "denat_alcohol", 250 ], [ "disinfectant", 1 ], [ "thyme_oil", 1 ], [ "bfipowder", 1 ] ],
[ [ "disinfectant", 1 ], [ "thyme_oil", 1 ], [ "bfipowder", 1 ], [ "chem_ethanol", 250 ], [ "denat_alcohol", 250 ] ],
[ [ "saline", 10 ], [ "eyedrops", 20 ] ],
[ [ "bandages", 3 ], [ "quikclot", 1 ] ]
]
@@ -65,7 +65,7 @@
"components": [
[ [ "rag", 3 ], [ "medical_gauze", 1 ] ],
[ [ "duct_tape", 20 ], [ "medical_tape", 5 ] ],
[ [ "chem_ethanol", 250 ], [ "denat_alcohol", 250 ], [ "disinfectant", 1 ], [ "thyme_oil", 1 ] ]
[ [ "disinfectant", 1 ], [ "thyme_oil", 1 ], [ "chem_ethanol", 250 ], [ "denat_alcohol", 250 ] ]
]
},
{
@@ -859,8 +859,7 @@
"tools": [
[
[ "fire", -1 ],
[ "toolset", 2 ],
[ "press", 2 ]
[ "toolset", 2 ]
]
],
"components": [
@@ -897,8 +896,7 @@
"tools": [
[
[ "fire", -1 ],
[ "toolset", 2 ],
[ "press", 2 ]
[ "toolset", 2 ]
]
],"components": [
[
@@ -930,8 +928,7 @@
"tools": [
[
[ "fire", -1 ],
[ "toolset", 2 ],
[ "press", 2 ]
[ "toolset", 2 ]
]
], "components": [
[
@@ -1223,8 +1220,7 @@
],
[
[ "fire", -1 ],
[ "toolset", 2 ],
[ "press", 2 ]
[ "toolset", 2 ]
]
],
"components": [
@@ -1355,6 +1355,7 @@
"difficulty": 4,
"time": 60000,
"autolearn": true,
"using": [ [ "rope_natural", 4 ] ],
"qualities":[
{"id":"HAMMER","level":2},
{"id":"SAW_M","level":1},
@@ -1372,10 +1373,6 @@
[
[ "frame", 4 ]
],
[
["rope_30", 4],
["vine_30", 4]
],
[
[ "steel_chunk", 4 ],
[ "scrap", 12 ]
@@ -1835,6 +1832,37 @@
[ "string_6", 180 ]
]
]
},{
"type" : "recipe",
"result": "rope_makeshift_6",
"category": "CC_OTHER",
"subcategory": "CSC_OTHER_MATERIALS",
"skill_used": "survival",
"time": 7500,
"reversible": true,
"autolearn": true,
"components": [
[
[ "withered", 36 ],
[ "straw_pile", 36 ]
]
]
},{
"type" : "recipe",
"result": "rope_makeshift_30",
"category": "CC_OTHER",
"subcategory": "CSC_OTHER_MATERIALS",
"skill_used": "survival",
"time": 7500,
"reversible": true,
"autolearn": true,
"components": [
[
[ "rope_makeshift_6", 5 ],
[ "withered", 180 ],
[ "straw_pile", 180 ]
]
]
},{
"type" : "recipe",
"result": "wire",
@@ -2276,15 +2304,8 @@
"time": 4000,
"reversible": true,
"autolearn": true,
"components": [
[
[ "2x4", 3 ],
[ "stick", 6 ]
],
[
[ "rope_6", 3 ]
]
]
"using": [ [ "rope_natural_short", 3 ] ],
"components": [ [ [ "2x4", 3 ], [ "stick", 6 ] ] ]
},{
"type" : "recipe",
"result": "wheel_wood",
@@ -2340,14 +2361,8 @@
"time": 6000,
"reversible": true,
"autolearn": true,
"components": [
[
[ "2x4", 6 ]
],
[
[ "rope_6", 2]
]
]
"using": [ [ "rope_natural_short", 2 ] ],
"components": [ [ [ "2x4", 6 ] ] ]
},{
"type" : "recipe",
"result": "steel_plate",
@@ -5746,6 +5761,7 @@
"components": [
[ [ "chitin_piece", 20 ] ],
[
[ "rope_makeshift_6", 2 ],
[ "rope_6", 2 ],
[ "superglue", 2 ],
[ "bone_glue", 4 ]
@@ -5768,6 +5784,7 @@
[ "bone_tainted", 50 ]
],
[
[ "rope_makeshift_6", 4 ],
[ "rope_6", 4 ],
[ "superglue", 2 ],
[ "bone_glue", 4 ]
@@ -44,6 +44,18 @@
"//": "Materials for use when sewing items",
"components": [ [ [ "thread", 1 ], [ "sinew", 1 ], [ "plant_fibre", 1 ], [ "yarn", 1 ] ] ]
},
{
"id": "rope_natural",
"type": "requirement",
"//": "Materials used for lashing, when choice of makeshift rope or vine is sensible",
"components": [ [ [ "rope_6", 5 ], [ "rope_30", 1 ], [ "rope_makeshift_6", 5 ], [ "rope_makeshift_30", 1 ], ], [ "vine_30", 1 ] ] ]
},
{
"id": "rope_natural_short",
"type": "requirement",
"//": "Materials used for lashing, when choice of makeshift rope is sensible",
"components": [ [ [ "rope_6", 1 ], [ "rope_makeshift_6", 1 ] ]
},
{
"id": "steel_standard",
"type": "requirement",
@@ -1586,7 +1586,7 @@
"items": [
{ "item": "stick", "count": 1 },
{ "item": "sheet", "count": 2 },
{ "item": "string_36", "prob": 50 }
{ "item": "withered", "count": 12 }
]
},
"bash": {
@@ -1600,7 +1600,7 @@
{ "item": "sheet", "count": [0, 1] },
{ "item": "rag", "count": [2, 5] },
{ "item": "stick", "count": 1 },
{ "item": "string_36", "count": 1 }
{ "item": "withered", "count": [2, 12] }
]
}
},{
@@ -1619,7 +1619,7 @@
"ter_set": "t_door_frame",
"items": [
{ "item": "2x4", "count": 6 },
{ "item": "rope_6", "count": 2 }
{ "item": "rope_makeshift_6", "count": 2 }
]
},
"bash": {
@@ -1629,8 +1629,8 @@
"ter_set": "t_door_frame",
"items": [
{ "item": "2x4", "count": [2, 5] },
{ "item": "rope_6", "count": [0, 1] },
{ "item": "string_36", "count": [1, 6] },
{ "item": "rope_makeshift_6", "count": [0, 1] },
{ "item": "withered", "count": [2, 12] },
{ "item": "splinter", "count": [5, 10] }
]
}
@@ -1652,7 +1652,7 @@
"items": [
{ "item": "stick", "count": 1 },
{ "item": "sheet", "count": 2 },
{ "item": "string_36", "prob": 50 }
{ "item": "withered", "count": 12 }
]
},
"close": "t_door_curtain_c",
@@ -1667,7 +1667,7 @@
{ "item": "sheet", "count": [0, 1] },
{ "item": "rag", "count": [2, 5] },
{ "item": "stick", "count": 1 },
{ "item": "string_36", "count": 1 }
{ "item": "withered", "count": [2, 12] }
]
}
},{
@@ -1687,7 +1687,7 @@
"ter_set": "t_door_frame",
"items": [
{ "item": "2x4", "count": 6 },
{ "item": "rope_6", "count": 2 }
{ "item": "rope_makeshift_6", "count": 2 }
]
},
"close": "t_door_makeshift_c",
@@ -1698,8 +1698,8 @@
"ter_set": "t_door_frame",
"items": [
{ "item": "2x4", "count": [2, 5] },
{ "item": "rope_6", "count": [0, 1] },
{ "item": "string_36", "count": [1, 6] },
{ "item": "rope_makeshift_6", "count": [0, 1] },
{ "item": "withered", "count": [2, 12] },
{ "item": "splinter", "count": [5, 10] }
]
}
@@ -4676,15 +4676,15 @@
"sound_fail": "whump.",
"ter_set": "t_null",
"items": [
{ "item": "rope_6", "count": [3, 4] },
{ "item": "rope_makeshift_6", "count": [3, 4] },
{ "item": "2x4", "count": [1, 4] },
{ "item": "splinter", "count": [2, 4] }
]
},
"deconstruct": {
"ter_set": "t_dirtfloor",
"items": [
{ "item": "rope_30", "count": 1 },
{ "item": "rope_makeshift_30", "count": 1 },
{ "item": "2x4", "count": 8 }
]
}
@@ -6024,7 +6024,7 @@
"deconstruct": {
"ter_set": "t_water_dp",
"items": [
{ "item": "rope_6", "count": [3, 4] },
{ "item": "rope_makeshift_6", "count": [3, 4] },
{ "item": "2x4", "count": 8 }
]
},
@@ -52,7 +52,7 @@
"components": [
[ [ "rag", 3 ], [ "medical_gauze", 1 ] ],
[ [ "duct_tape", 20 ], [ "medical_tape", 5 ] ],
[ [ "chem_ethanol", 250 ], [ "denat_alcohol", 250 ], [ "disinfectant", 1 ], [ "thyme_oil", 1 ], [ "hard_liquor", 28, "LIST" ] ]
[ [ "disinfectant", 1 ], [ "thyme_oil", 1 ], [ "chem_ethanol", 250 ], [ "denat_alcohol", 250 ], [ "hard_liquor", 28, "LIST" ] ]
],
"flags": [ "BLIND_HARD" ]
},
@@ -35,7 +35,6 @@
"qualities":[ {"id":"CHEM","level":2} ],
"tools": [
[
[ "press", 50 ],
[ "hotplate", 50 ],
[ "toolset", 50 ],
[ "fire", -1 ]
@@ -70,7 +69,6 @@
"qualities":[ {"id":"CHEM","level":2} ],
"tools": [
[
[ "press", 50 ],
[ "hotplate", 50 ],
[ "toolset", 50 ],
[ "fire", -1 ]
@@ -104,7 +102,6 @@
"qualities":[ {"id":"CHEM","level":2} ],
"tools": [
[
[ "press", 50 ],
[ "hotplate", 50 ],
[ "toolset", 50 ],
[ "fire", -1 ]
@@ -132,7 +129,6 @@
"qualities":[ {"id":"CHEM","level":2} ],
"tools": [
[
[ "press", 50 ],
[ "hotplate", 50 ],
[ "toolset", 50 ],
[ "fire", -1 ]
@@ -16,7 +16,6 @@
[
[ "fire", -1 ],
[ "toolset", 2 ],
[ "press", 2 ],
[ "hotplate", 2 ]
]
],
@@ -1062,10 +1062,8 @@
"time": 8000,
"reversible": true,
"autolearn": true,
"components": [
[ [ "snare_trigger", 1 ] ],
[ [ "rope_6", 1 ] ]
]
"using": [ [ "rope_natural_short", 1 ] ],
"components": [ [ [ "snare_trigger", 1 ] ] ]
},{
"type" : "recipe",
"result": "crossbow_makeshift",
@@ -50,8 +50,7 @@
"tools": [
[
[ "fire", -1 ],
[ "toolset", 10 ],
[ "press", 10 ]
[ "toolset", 10 ]
]
],
"components": [
@@ -81,8 +80,7 @@
"tools": [
[
[ "fire", -1 ],
[ "toolset", 10 ],
[ "press", 10 ]
[ "toolset", 10 ]
]
],
"components": [
@@ -112,8 +110,7 @@
"tools": [
[
[ "fire", -1 ],
[ "toolset", 10 ],
[ "press", 10 ]
[ "toolset", 10 ]
],
[ ["tongs", -1] ],
[
@@ -148,8 +145,7 @@
"tools": [
[
[ "fire", -1 ],
[ "toolset", 20 ],
[ "press", 20 ]
[ "toolset", 20 ]
]
],
"components": [
@@ -179,8 +175,7 @@
"tools": [
[
[ "fire", -1 ],
[ "toolset", 20 ],
[ "press", 20 ]
[ "toolset", 20 ]
],
[ ["tongs", -1] ],
[
@@ -895,7 +895,7 @@
"damage_modifier": 80,
"folded_volume": 24,
"breaks_into": [ { "item": "flamethrower_simple", "count": [ 0, 1 ] } ],
"flags": [ "TURRET", "FOLDABLE", "NEEDS_BATTERY_MOUNT", "TOOL_NONE" ]
"flags": [ "TURRET", "FOLDABLE", "NEEDS_BATTERY_MOUNT", "TOOL_NONE", "USE_TANKS" ]
},
{
"id": "flamethrower2",
@@ -913,7 +913,7 @@
"damage_modifier": 80,
"folded_volume": 19,
"breaks_into": [ { "item": "flamethrower", "count": [ 0, 1 ] } ],
"flags": [ "TURRET", "FOLDABLE", "NEEDS_BATTERY_MOUNT", "TOOL_NONE" ]
"flags": [ "TURRET", "FOLDABLE", "NEEDS_BATTERY_MOUNT", "TOOL_NONE", "USE_TANKS" ]
},
{
"id": "flamethrower3",
@@ -931,7 +931,7 @@
"damage_modifier": 80,
"folded_volume": 19,
"breaks_into": [ { "item": "rm451_flamethrower", "count": [ 0, 1 ] } ],
"flags": [ "TURRET", "FOLDABLE", "NEEDS_BATTERY_MOUNT", "TOOL_NONE" ]
"flags": [ "TURRET", "FOLDABLE", "NEEDS_BATTERY_MOUNT", "TOOL_NONE", "USE_TANKS" ]
},
{
"id": "fn_fal",
@@ -1,7 +1,37 @@
[
{
"id": "bfeedfuel",
"type": "AMMO",
"category": "fuel",
"name": "liquified blob feed",
"name_plural": "bfeedfuel",
"description": "Liquified blob feed, useful for fueling certain blob based vehicle parts",
"weight": 50,
"volume": 1,
"price": 40,
"price_postapoc": 800,
"phase": "liquid",
"container": "jerrycan",
"material": "hydrocarbons",
"fuel": { "energy": 35 },
"symbol": "=",
"color": "red",
"count": 250,
"stack_size": 250,
"range": 4,
"damage": 5,
"pierce": 5
},
{
"id": "blobrep",
"type": "requirement",
"//": "Material for fixing parts made from blobs.",
"components": [ [ [ "bfeed", 1 ] ] ]
},
{
"id": "bfeed",
"type": "AMMO",
"category": "fuel",
"name": "blob feed",
"name_plural": "blob feed",
"description": "An amalgam of various types of organic material, contains everything the blob needs to be healthy. You think...",
@@ -152,6 +182,7 @@
"copy-from": "flamethrower_simple",
"type": "GUN",
"name": "simple flamethrower",
"range": 2,
"extend": {
"ammo_effects": [ "STREAM" ],
"flags": [ "FIRE_20" ]

Large diffs are not rendered by default.

@@ -1,4 +1,18 @@
[
{
"result": "bfeedfuel",
"type": "recipe",
"category": "CC_CHEM",
"subcategory": "CSC_CHEM_FUEL",
"skill_used": "cooking",
"difficulty": 6,
"time": 1000,
"autolearn": true,
"components": [
[ [ "water", 1 ], [ "water_clean", 1 ] ],
[ [ "bfeed", 1 ] ]
]
},
{
"result": "bfeed",
"type": "recipe",
@@ -159,7 +159,7 @@
"range": 5,
"damage_modifier": 80,
"breaks_into": [ { "item": "flamethrower", "prob": 50 } ],
"flags": [ "TURRET", "MANUAL" ]
"flags": [ "TURRET", "MANUAL", "USE_TANKS" ]
},
{
"id": "auto_m1918",
@@ -136,7 +136,7 @@
"damage_modifier": 80,
"folded_volume": 19,
"breaks_into": [ { "item": "rm451_flamethrower", "count": [ 0, 1 ] } ],
"flags": [ "TURRET", "FOLDABLE", "TOOL_NONE" ]
"flags": [ "TURRET", "MANUAL", "FOLDABLE", "TOOL_NONE", "USE_TANKS" ]
},
{
"id": "nailer",
@@ -2,10 +2,10 @@
{
"type": "MOD_INFO",
"ident": "makeshift",
"name": "More Craftables",
"name": "Makeshift Items Mod",
"authors": [ "Noctifer-de-Mortem" ],
"maintainers": [ "Noctifer-de-Mortem" ],
"description": "Allows crafting of more improvised item variants",
"description": "Adds more improvised item variants and rebalances existing ones.",
"category": "items",
"dependencies": [ "dda" ]
}
@@ -38,7 +38,7 @@
"reversible": true,
"autolearn": true,
"using": [ [ "cordage", 1 ] ],
"components": [ [ [ "makeshift_machete", 1 ] ] ]
"components": [ [ [ "makeshift_machete", 1 ], [ "blade", 1 ], [ "machete", 1 ], [ "survivor_machete", 1 ] ] ]
},
{
"result": "pistol_bayonet",
@@ -181,6 +181,20 @@
"name": "Aim turrets manually",
"bindings":[ { "input_method": "keyboard", "key": "X" } ]
},
{
"id": "TURRET_MANUAL_OVERRIDE",
"type": "keybinding",
"category": "VEHICLE",
"name": "Aim automatic turrets",
"bindings":[ { "input_method": "keyboard", "key": "V" } ]
},
{
"id": "TURRET_SINGLE_FIRE",
"type": "keybinding",
"category": "VEHICLE",
"name": "Aim individual turret",
"bindings":[ { "input_method": "keyboard", "key": "T" } ]
},
{
"id": "TURRET_TARGET_MODE",
"type": "keybinding",

This file was deleted.

This file was deleted.

Binary file not shown.
File renamed without changes.
@@ -4,7 +4,15 @@
"id" : "menu_move",
"volume" : 100,
"files" : [
"nenadsimic_menu_selection_click.wav"
"menu_move.wav"
]
},
{
"type": "sound_effect",
"id" : "menu_error",
"volume" : 100,
"files" : [
"menu_error.wav"
]
}
]
@@ -1,19 +1,24 @@
Current policy is to update code to the standard style when changing a
substantial portion of it.
# Code Style Guide

Current policy is to update code to the standard style when changing a substantial portion of it.

Note that versions of astyle before 2.05 will handle many c++11 constructs incorrectly.

Blocks of code can be passed through astyle to ensure that their formatting is correct:

astyle --style=1tbs --attach-inlines --indent=spaces=4 --align-pointer=name --max-code-length=100 --break-after-logical --indent-classes --indent-preprocessor --indent-switches --indent-col1-comments --min-conditional-indent=0 --pad-oper --unpad-paren --pad-paren-in --add-brackets --convert-tabs
astyle --style=1tbs --attach-inlines --indent=spaces=4 --align-pointer=name --max-code-length=100 --break-after-logical --indent-classes --indent-preprocessor --indent-switches --indent-col1-comments --min-conditional-indent=0 --pad-oper --unpad-paren --pad-paren-in --add-brackets --convert-tabs

These options are mirrored in `.astylerc`, `Cataclysm-DDA.sublime-project` and `doc/CODE_STYLE.txt`

These options are mirrored in .astylerc, Cataclysm-DDA.sublime-project and doc/CODE_STYLE.txt
For example, from `vi`, set marks a and b around the block, then:

For example, from vi, set marks a and b around the block, then:
:'a,'b ! astyle --style=1tbs --attach-inlines --indent=spaces=4 --align-pointer=name --max-code-length=100 --break-after-logical --indent-classes --indent-preprocessor --indent-switches --indent-col1-comments --min-conditional-indent=0 --pad-oper --unpad-paren --pad-paren-in --add-brackets --convert-tabs
:'a,'b ! astyle --style=1tbs --attach-inlines --indent=spaces=4 --align-pointer=name --max-code-length=100 --break-after-logical --indent-classes --indent-preprocessor --indent-switches --indent-col1-comments --min-conditional-indent=0 --pad-oper --unpad-paren --pad-paren-in --add-brackets --convert-tabs

Here's an example that illustrates the most common points of style.
## Code Example

Here's an example that illustrates the most common points of style:

````c++
int foo( int arg1, int *arg2 )
{
if( arg1 < 5 ) {
@@ -37,8 +42,11 @@ int foo( int arg1, int *arg2 )
}
return 0;
}
````
Here's a helpful workflow to astyle some low-hanging fruit:
````shell
# Astyle all the things.
make astyle-all
# List the changed files.
@@ -55,3 +63,4 @@ git add -p
# commit and push!
git commit -m "Updated astyling coverage."
git push
````
@@ -1,29 +1,28 @@
Item spawn system:
=====
# Item spawn system

1. Collection or Distribution:
====
## Collection or Distribution

In a Collection each entry is chosen independently from the other entries. Therefor the probability associated with each entry is absolute, in the range of 0...1. In the json files it is implemented as percentage with values from 0 to 100.
In a Collection each entry is chosen independently from the other entries. Therefore the probability associated with each entry is absolute, in the range of 0...1. In the json files it is implemented as percentage with values from 0 to 100.

A probability of 0 (or negative) means the entry is never chosen, a probability of 100% means its always chosen. The default is 100, because it's the most useful value. Default 0 would mean the entry can be removed anyway.

A Distribution is a weighted list like the current system. Exactly one entry is chosen from it. The probability of each entry is relative to the probability of the other entries. A probability of 0 (or negative) means it is never chosen.

An example: Suppose item A has a probability of 30 and item B has a probability of 20. Then the probabilities of the 4 combinations of A and B are:

Combination | Collection | Distribution
---------------------------------------
Neither A nor B | 56% | 0%
Only A | 24% | 60%
Only B | 14% | 40%
Both A and B | 6% | 0%

2. Format:
====
| Combination | Collection | Distribution |
| ----------------|------------|------------- |
| Neither A nor B | 56% | 0% |
| Only A | 24% | 60% |
| Only B | 14% | 40% |
| Both A and B | 6% | 0% |

## Format

The format is this:
```

```json
{
"type": "item_group",
"subtype": "<subtype>",
@@ -34,39 +33,43 @@ The format is this:
}
```

`subtype` is optional. It can be "collection" or "distribution". If unspecified, it defaults to `old`,
which denotes that this item group uses the old format (which is technically a distribution).
`subtype` is optional. It can be `collection` or `distribution`. If unspecified, it defaults to `old`, which denotes that this item group uses the old format (which is technically a distribution).

There are some caveats to watch out for when using `ammo` or `magazine`; see Section 4.
There are [some caveats](#ammo-and-magazines) to watch out for when using `ammo` or `magazine`.

3. The Entries Array
====
### Entries array

The `entries` list contains entries, each entry can be one of the following:
```
{ "item": "<item-id>", ... }
```
or
```
{ "group": "<group-id>", ... }
```
or
```
{ "distribution": [
"An array of entries, each of which can match any of these 4 formats"
] }
```
or
```
{ "collection": [
"An array of entries, each of which can match any of these 4 formats"
] }
```
The `entries` list contains entries, each of which can be one of the following:

1. Item
```json
{ "item": "<item-id>", ... }
```

2. Group
``` json
{ "group": "<group-id>", ... }
```

3. Distribution
```json
{ "distribution": [
"An array of entries, each of which can match any of these 4 formats"
] }
```

4. Collection
```json
{ "collection": [
"An array of entries, each of which can match any of these 4 formats"
] }
```

The game decides based on the existence of either the `item` or the `group` value if the entry denotes a item or a reference to another item group.

Each entry can have more values (shown above as `...`). They allow further properties of the item(s):
```

```json
"damage": <number>|<array>,
"damage-min": <number>,
"damage-max": <number>,
@@ -83,72 +86,74 @@ Each entry can have more values (shown above as `...`). They allow further prope
"container-item": "<container-item-id>",
"container-group": "<group-id>",
```

`contents` is added as contents of the created item. It is not checked if they can be put into the item. This allows water, that contains a book, that contains a steel frame, that contains a corpse.

`count` makes the item spawn repeat to create more items, each time creating a new item.
```

```json
"damage-min": 0,
"damage-max": 3,
"count": 4
"charges": [10, 100]
```

This will create 4 items, they can have different damage levels as the damage value is rolled separately for each of these items. Each item has charges (AKA ammo) in the range of 10 to 100 (inclusive); if the item needs a magazine before it can have charges, that will be taken care of for you. Using an array (which must have 2 entries) for charges/count/damage is equivalent to writing explicit min and max values. In other words `"count": [a,b]` is the same as `"count-min": a, "count-max": b`.

The container is checked and the item is put inside the container, and the charges of the item are capped/increased to match the size of the container.

4. Ammo and Magazines
====
### Ammo and Magazines

Here are some ways to make items spawn with/without ammo/magazines (note that `ammo-item` can
be specified for guns and magazines in the entries array to use a non-default ammo type):

A)
Specify an ammo/magazine chance (introduced in Section 2) for the entire item group.
`ammo` specifies the percent chance that the entries will spawn fully loaded (if it needs a magazine, it will be added for you).
`magazine` specifies the percent chance that the entries will spawn with a magazine.
Both of these default to 0 if unspecified.
* Secify an ammo/magazine chance (introduced in Section 2) for the entire item group.
`ammo` specifies the percent chance that the entries will spawn fully loaded (if it needs a magazine, it will be added for you).
`magazine` specifies the percent chance that the entries will spawn with a magazine.
Both of these default to 0 if unspecified.

Note that `ammo` and `magazine` only apply to tools, guns, and magazines. Furthermore, they don't apply to tools whose entry explicitly specifies
how much ammo (charges) to spawn with, or to tools whose JSON item definition specifies a random amount or a fixed, nonzero amount of
initial charges.
Note that `ammo` and `magazine` only apply to tools, guns, and magazines. Furthermore, they don't apply to tools whose entry explicitly specifies how much ammo (charges) to spawn with, or to tools whose JSON item definition specifies a random amount or a fixed, nonzero amount of initial charges.

If any item groups are referenced from your item group, then their ammo/magazine chances are
ignored, and yours are used instead.
If any item groups are referenced from your item group, then their ammo/magazine chances are ignored, and yours are used instead.

B)
Use `charges`, `charges-min`, or `charges-max` in the entries array. A default magazine will be
added for you if needed.
* Use `charges`, `charges-min`, or `charges-max` in the entries array. A default magazine will be added for you if needed.

5. Shortcuts:
====
## Shortcuts

This:
```

```json
"items": [ "<id-1>", [ "<id-2>", 10 ] ]
```
means the same as
```

means the same as:

```json
"entries": [ { "item": "<id-1>" }, { "item": "<id-2>", "prob": 10 } ]
```

In other words: a single string denotes an item id, an array (which must contain a string and a number) denotes an item id and a probability.

Similar for groups:
```
This is true for groups as well:

```json
"groups": [ "<id-1>", [ "<id-2>", 10 ] ]
```

This format does not support further properties of the created items, the probability is only optional for entries of collections!
This format does not support further properties of the created items - the probability is only optional for entries of collections!

The content of "entries", "items" and "groups" are all added if those members exist. This will have the item "<id-1>" appear twice in the item group:
```
The content of "entries", "items" and "groups" are all added if those members exist. This will have the item `<id-1>` appear twice in the item group:

```json
{
"items": [ "<id-1>" ],
"entries": [ { "item": "<id-1>" } ]
}
```

Another example. The group "milk" spawns a container (taken from milk_containers) that contains milk (maximal amount that fits into the container, because there is no specific charges value defined).
```

```json
{
"type" : "item_group",
"id": "milk_containers",
@@ -168,15 +173,15 @@ Another example. The group "milk" spawns a container (taken from milk_containers
},
```

6. Inlined item groups
====
## Inlined item groups

In some places one can define an item group directly instead of giving the id of a group. One can not refer to that group as it has no visible id (it has an unspecific/random id internally). This is most useful when the group is very specific to the place it is used and wont ever appear anywhere else.

At some places one can define an item group directly instead of giving the id of a group. One can not refer to that group as it has no visible id (it has an unspecific/random id internally). This is most useful when the group is very specific to the place it is used and wont ever appear anywhere else.
As an example: monster death drops (`death_drops` entry in the `MONSTER` object, see [JSON_INFO.md](https://github.com/CleverRaven/Cataclysm-DDA/blob/master/doc/JSON_INFO.md)) can do this. If the monster is very specific (e.g. a special robot, a unique endgame monster), the item spawned upon its death won't (in that form) appear in any other group.

As an example: monster death drops ("death_drops" entry in the "MONSTER" object, see JSON_INFO.md) can do this. If the monster is very specific (e.g. a special robot, a unique endgame monster), the item spawned upon its death wont (in that form) appear in any other group.
Therefore, this snippet:

Therefor this snippet:
```JSON
```json
{
"type": "item_group",
"id": "specific_group_id",
@@ -187,9 +192,10 @@ Therefor this snippet:
"death_drops": "specific_group_id"
}
```

is equivalent to:

```JSON
```json
{
"death_drops": {
"subtype": "distribution",
@@ -198,10 +204,11 @@ is equivalent to:
}
```

The inline group is read like any other group and one can use all the properties mentioned above. Its "type" and its "id" members are always ignored.
The inline group is read like any other group and one can use all the properties mentioned above. Its `type` and its `id` members are always ignored.

Instead of a full JSON object, one can also write a JSON array. The default subtype is used and the array is read like the "entries" array (see above). Each entry of that array must be a JSON object. Example:
```JSON

```json
{
"death_drops": [
{ "item": "rag", "damage": 2 }, { "item": "bowling_ball" }
@@ -211,12 +218,19 @@ Instead of a full JSON object, one can also write a JSON array. The default subt

----

### Notes

You can test your item groups in the game:
- load a game and call the debug menu, (If a key isn't bound to the debug menu or you forgot it, use <ESC> -> '1')
- choose "Test Item Group".
- select the item group you want to debug. It will spawn items based on that 100 times and count the spawned items. They are displayed, sorted by their frequency.
- You can filter anything in the debug menu using '/'

----
1. Load a game and call the debug menu
> *TIP* (If a key isn't bound to the debug menu or you forgot it, use <kbd>ESC</kbd> > <kbd>1</kbd>)
2. Choose "Test Item Group".

3. Select the item group you want to debug.

The game will then spawn items in that group 100 times and count the spawned items. They'll then be displayed, sorted by their frequency.

> *TIP*: You can filter anything in the debug menu using <kbd>/</kbd>
You should not add items to the item group "EMPTY_GROUP". This group can be used when the game requires a group id, but you don't want to spawn any items there. The group will never spawn items.
You should not add items to the item group `EMPTY_GROUP`. This group can be used when the game requires a group id, but you don't want to spawn any items there. The group will never spawn items.

Large diffs are not rendered by default.

@@ -158,10 +158,12 @@ The above example only illustrate the mapgen entries, not the actual format for
> "lua" - requires
```"script": [
```
"script": [
" -- array of lines ",
" -- etc ",
]```
]
```
### 1.2.1 "om_terrain":
**required for standalone**
@@ -1,8 +1,9 @@
# Martial arts and technique JSON file contents

###Martial arts
### Martial arts

```C++
"type" : "martial_art",
"type" : "martial_art",
"id" : "style_debug", // Unique ID. Must be one continuous word,
// use underscores if necessary.
"name" : "Debug Mastery", // In-game name displayed
@@ -21,7 +22,8 @@
]
```

###Techniques
### Techniques

```C++
"id" : "tec_debug_arpen", // Unique ID. Must be one continuous word
"name" : "phasing strike", // In-game name displayed
@@ -37,7 +39,8 @@
"movecost_mult" : 0.3 // Any bonuses, as described below
```

###Buffs
### Buffs

```C++
"id" : "debug_elem_resist", // Unique ID. Must be one continuous word
"name" : "Elemental resistance", // In-game name displayed
@@ -58,23 +61,27 @@
]
```

###Bonuses
### Bonuses

Bonuses contain 2 to 4 tokens, in order:

* Affected statistic. Any of: "hit", "dodge", "block", "speed", "movecost", "damage", "armor", "arpen"
* Damage type ("bash", "cut", "heat", etc.) when the statistic is damage, armor or arpen
* Scaling stat. Any of: "str", "dex", "int", "per"
* The value of the bonus itself

Bonuses must be written in the correct order.

If affected statistic requires a damage type, a damage type must be provided. Otherwise damage type must not be specified.

If scaling stat is specified, the value of the bonus is multiplied by given stat of the user stat.

Tokens of "useless" type will not cause an error, but will not have any effect.
For example, "speed" in a technique will have no effect ("movecost" should be used for techniques).
Tokens of `useless` type will not cause an error, but will not have any effect.
For example, `speed` in a technique will have no effect (`movecost` should be used for techniques).

Currently extra elemental damage is not applied, but extra elemental armor is (after regular armor).

Examples:
* "flat_bonuses" : [["armor", "bash", "str", 0.3]], // Incoming bashing damage is decreased by 30% of strength value. Only useful on buffs
* "mult_bonuses" : [["damage", "cut", "dex", 0.1]], // All cutting damage dealt is multiplied by `(10% of dexterity)*(damage)`
* "flat_bonuses" : [["movecost", "str", -1.0]], // Move cost is decreased by 100% of strength value
* `flat_bonuses : [["armor", "bash", "str", 0.3]], // Incoming bashing damage is decreased by 30% of strength value. Only useful on buffs`
* ``mult_bonuses : [["damage", "cut", "dex", 0.1]], // All cutting damage dealt is multiplied by `(10% of dexterity)*(damage)` ``
* `flat_bonuses : [["movecost", "str", -1.0]], // Move cost is decreased by 100% of strength value`
@@ -0,0 +1,55 @@
# Modding guide

Certain features of the game can be modified without rebuilding the game from source code; you should be able to just modify the pertinent files and run the game to see your changes. This attempts to document these features.

An in-depth review of all json files and their appropriate fields is available in JSON_INFO.txt.

## Adding a profession.

Let's say we want to add a "survivalist" profession.

We'll say this profession starts with archery, survival, traps, beef jerky and a few survival items. We'll set the starting cost at 3 points since it modifies skills and items. We could do that with the following entries:

````json
{
"ident" : "survivalist",
"name": "Wilderness Survivalist",
"description": "You live off the wild and wander the world; you've never
had a place to live or items other than what you can
scavenge off the land. People called you crazy; now,
after the disaster, you're the sane one.",
"points" : 3,
"items": ["pants_cargo", "boots", "beltrig", "selfbow", "jerky", "arrow_field_point"],
"skills" : [
{
"name": "archery",
"level" : 1
},
{
"name" : "survival",
"level" : 1
},
{
"name" : "traps",
"level" : 1
}
]
},
````

After adding this to the professions.json file, we should see the new profession show up in the character creation screen.

## Adding an item
1. Open the appropriate json in `data/json/items` and add your item near similar types.

> **NOTE:** See `JSON_INFO` for a more in-depth review of each individual item .json file/flags
2. Add the item to `data/json/itemgroups` to have it spawn in in-game locations

## Important note on json files

The following characters: `[ { , } ] : "` are *very* important when adding or modifying the JSON files for technical reasons. This means a single missing `,` or `[` or `}` can be the difference between a working file and a hanging game at startup.

Many editors have features that let you track `{ [` and `] }` to see if they're balanced (ie, have a matching opposite); [Notepad++](https://notepad-plus-plus.org/) is a popular, free editor on Windows that contains this feature. On Linux, there are a plethora of options, and you probably already have a preferred one 🙂

This file was deleted.

This file was deleted.

@@ -1,4 +1,4 @@
## Tests
# Tests

There are a number of test cases in `tests/`. The test framework is [Catch](https://github.com/philsquared/Catch). For tutorials, please visit [https://github.com/philsquared/Catch/blob/master/docs/tutorial.md](https://github.com/philsquared/Catch/blob/master/docs/tutorial.md).

@@ -1,13 +1,10 @@
Info on translating Cataclysm-DDA into another language.
# Translating Cataclysm-DDA

## Translators

TRANSLATORS
===========
The current official locations for translating Cataclysm-DDA are [Launchpad Translations](https://translations.launchpad.net/cdda) and [Transifex Translations](https://www.transifex.com/projects/p/cataclysm-dda).

The current official locations for translating Cataclysm-DDA is at
[Launchpad Translations](https://translations.launchpad.net/cdda) and [Transifex Translations](https://www.transifex.com/projects/p/cataclysm-dda).

You can join to your language translation project:
You can lend your support to a language translation project:

On [Transifex](https://www.transifex.com/projects/p/cataclysm-dda/):
* Argentinian
@@ -29,75 +26,44 @@ On [Launchpad](https://translations.launchpad.net/cdda):
* Serbian (inactive)
* Vietnamese (inactive)

If your language is not translated you can add it into any translation project on launchpad.net or transifex.com(prefered).
Using Launchpad Translations anyone can help translate.
All you need to do is set up a Launchpad account,
and tell it your preferred language.
After that, your language should show up for translation,
even if you are starting a new translation from scratch.

If you have any questions or comments about translation,
feel free to post in the "Translations Team Discussion" subforum of
[the Cataclysm-DDA forums](http://smf.cataclysmdda.com/).

There are some issues specific to Cataclysm-DDA,
(and some specific to translating computer programs in general,)
which translators should be aware of,
such as the use of terms like `%s` and `%3$d` (leave them as they are),
and the use of tags like `<name>` (don't translate the tags).

Information about these,
and any other issues specific to individual languages,
can be found in Cataclysm-DDA's `lang/notes/` folder.
General notes for all translators are in
`lang/notes/README_all_translators.txt`,
and notes specific to a language may be stored as `<lang_id>.txt`,
for example `lang/notes/de.txt` for German.

Cataclysm-DDA has more than 14,000 translatable strings,
but don't be discouraged.
The more translators there are,
the easier it becomes :).


MAINTAINERS
===========

Several steps need to be done in the correct order,
to correctly merge and maintain the translation files.

There are scripts available for these,
so usually the process will be as follows:

1. Download the translations in .po format.
2. Put them in `lang/incoming/`,
ensuring they are named consistently with the files in `lang/po/`.
Don't see your language in the list above? You can add it into any translation project on launchpad.net or transifex.com (prefered).

All you need to do is set up a Launchpad account, and tell it your preferred language. After that, your language should show up for translation - even if you are starting a new translation from scratch!

If you have any questions or comments about translation, feel free to post in the "Translations Team Discussion" subforum of [the Cataclysm-DDA forums](http://smf.cataclysmdda.com/).

### Tips

There are some issues specific to Cataclysm-DDA, (and some specific to translating computer programs in general) which translators should be aware of. These include the use of terms like `%s` and `%3$d` (leave them as they are), and the use of tags like `<name>` (don't translate the tags).

Information about these, and any other issues specific to individual languages, can be found in Cataclysm-DDA's `lang/notes/` folder.

General notes for all translators are in `lang/notes/README_all_translators.txt`, and notes specific to a language may be stored as `<lang_id>.txt`, for example `lang/notes/de.txt` for German.

Cataclysm-DDA has more than 14,000 translatable strings, but don't be discouraged. The more translators there are, the easier it becomes 😄


## Maintainers

Several steps need to be done in the correct order to correctly merge and maintain the translation files.

There are scripts available for these, so usually the process will be as follows:

1. Download the translations in `.po` format.
2. Put them in `lang/incoming/`, ensuring they are named consistently with the files in `lang/po/`.
3. Run `lang/update_pot.sh` to update `lang/po/cataclysm-dda.pot`.
4. Run `lang/merge_po.sh` to update `lang/po/*.po`.
This will also merge the translations from `lang/incoming/`.

This will also merge the translations from `lang/incoming/`.

These steps should be enough to keep the translation files up-to-date.

To compile the .po files into .mo files for use,
run `lang/compile_mo.sh`.
It will create a directory in `lang/mo/` for each language found.

Also note that both `lang/merge_po.sh` and `lang/compile_mo.sh`
accept arguments specifying which languages to merge or compile.
So to compile only the translation for, say, Traditional Chinese (zh_TW),
one would run `lang/compile_mo.sh zh_TW`.

After compiling the appropriate .mo file,
if your system is using that language,
the translations will be automatically used when you run cataclysm.

If your system locale is different from the one you want to test,
the easiest way to do so is to find out your locale identifier,
compile the translation you want to test,
then rename the directory in `lang/mo/` to your locale identifier.

So for example if your local language is New Zealand English (en_NZ),
and you want to test the Russian (ru) translation,
the steps would be `lang/compile_mo.sh ru`,
`mv lang/mo/ru lang/mo/en_NZ`,
`./cataclysm`.
To compile the .po files into `.mo` files for use, run `lang/compile_mo.sh`. It will create a directory in `lang/mo/` for each language found.

Also note that both `lang/merge_po.sh` and `lang/compile_mo.sh` accept arguments specifying which languages to merge or compile. So to compile only the translation for, say, Traditional Chinese (zh_TW), one would run `lang/compile_mo.sh zh_TW`.

After compiling the appropriate .mo file, if your system is using that language, the translations will be automatically used when you run cataclysm.

If your system locale is different from the one you want to test, the easiest way to do so is to find out your locale identifier, compile the translation you want to test, then rename the directory in `lang/mo/` to your locale identifier.

So for example if your local language is New Zealand English (en_NZ), and you want to test the Russian (ru) translation, the steps would be `lang/compile_mo.sh ru`, `mv lang/mo/ru lang/mo/en_NZ`, `./cataclysm`.
@@ -76,7 +76,6 @@ def warning_supressed(filename):
"MONSTER_FACTION",
"monstergroup",
"MONSTER_WHITELIST",
"npc", # FIXME right now this object is unextractable
"overlay_order",
"overmap_special",
"profession_item_substitutions",
@@ -127,6 +126,7 @@ def warning_supressed(filename):
"morale_type",
"mutation",
"morale_type",
"npc",
"npc_class",
"overmap_terrain",
"skill",
@@ -734,6 +734,12 @@ def extract(item, infilename):
else:
writestr(outfile, name, **kwargs)
wrote = True
if "name_suffix" in item:
writestr(outfile, item["name_suffix"], **kwargs)
wrote = True
if "name_unique" in item:
writestr(outfile, item["name_unique"], **kwargs)
wrote = True
if "use_action" in item:
extract_use_action_msgs(outfile, item["use_action"], item.get("name"), kwargs)
wrote = True
133,228 lang/po/pl.po

Large diffs are not rendered by default.

@@ -121,6 +121,34 @@ classes = {
{ name = "sunrise", rval = "calendar", args = { } },
}
},
mutation_branch = {
string_id = "trait_id",
attributes = {
activated = { type = "bool", writable = true },
bodytemp_max = { type = "int", writable = true },
bodytemp_min = { type = "int", writable = true },
bodytemp_sleep = { type = "int", writable = true },
cooldown = { type = "int", writable = true },
cost = { type = "int", writable = true },
description = { type = "string", writable = true },
fatigue = { type = "bool", writable = true },
hunger = { type = "bool", writable = true },
mixed_effect = { type = "bool", writable = true },
name = { type = "string", writable = true },
points = { type = "int", writable = true },
profession = { type = "bool", writable = true },
purifiable = { type = "bool", writable = true },
startingtrait = { type = "bool", writable = true },
thirst = { type = "bool", writable = true },
threshold = { type = "bool", writable = true },
ugliness = { type = "int", writable = true },
valid = { type = "bool", writable = true },
visibility = { type = "int", writable = true },
},
functions = {
{ name = "get_display_color", rval = "int", args = { } },
}
},
Character = {
parent = "Creature",
attributes = {
@@ -164,11 +192,11 @@ classes = {
{ name = "gibType", rval = "field_id", args = { } },
{ name = "has_active_bionic", rval = "bool", args = { "string" } },
{ name = "has_active_item", rval = "bool", args = { "string" } },
{ name = "has_active_mutation", rval = "bool", args = { "string" } },
{ name = "has_base_trait", rval = "bool", args = { "string" } },
{ name = "has_active_mutation", rval = "bool", args = { "trait_id" } },
{ name = "has_base_trait", rval = "bool", args = { "trait_id" } },
{ name = "has_bionic", rval = "bool", args = { "string" } },
{ name = "has_nv", rval = "bool", args = { } },
{ name = "has_trait", rval = "bool", args = { "string" } },
{ name = "has_trait", rval = "bool", args = { "trait_id" } },
{ name = "has_trait_flag", rval = "bool", args = { "string" } },
{ name = "i_add", rval = "item&", args = { "item" } },
{ name = "i_add_or_drop", rval = "bool", args = { "item" } },
@@ -186,13 +214,13 @@ classes = {
{ name = "mod_int_bonus", rval = nil, args = { "int" } },
{ name = "mod_stat", rval = nil, args = { "string", "int" } },
{ name = "move_effects", rval = "bool", args = { "bool" } },
{ name = "mutation_effect", rval = nil, args = { "string" } },
{ name = "mutation_loss_effect", rval = nil, args = { "string" } },
{ name = "mutation_effect", rval = nil, args = { "trait_id" } },
{ name = "mutation_loss_effect", rval = nil, args = { "trait_id" } },
{ name = "normalize", rval = nil, args = { } },
{ name = "pick_name", rval = nil, args = { } },
{ name = "pick_name", rval = nil, args = { "bool" } },
{ name = "random_bad_trait", rval = "string", args = { } },
{ name = "random_good_trait", rval = "string", args = { } },
{ name = "random_bad_trait", rval = "trait_id", args = { } },
{ name = "random_good_trait", rval = "trait_id", args = { } },
{ name = "recalc_hp", rval = nil, args = { } },
{ name = "recalc_sight_limits", rval = nil, args = { } },
{ name = "remove_mission_items", rval = nil, args = { "int" } },
@@ -202,11 +230,11 @@ classes = {
{ name = "reset_stats", rval = nil, args = { } },
{ name = "set_turn_died", rval = nil, args = { "int" } },
{ name = "symbol", rval = "string", args = { } },
{ name = "set_mutation", rval = nil, args = { "string" } },
{ name = "set_mutation", rval = nil, args = { "trait_id" } },
{ name = "symbol_color", rval = "int", args = { } },
{ name = "unset_mutation", rval = nil, args = { "string" } },
{ name = "toggle_trait", rval = nil, args = { "string" } },
{ name = "trait_by_invlet", rval = "string", args = { "int" } },
{ name = "unset_mutation", rval = nil, args = { "trait_id" } },
{ name = "toggle_trait", rval = nil, args = { "trait_id" } },
{ name = "trait_by_invlet", rval = "trait_id", args = { "int" } },
{ name = "volume_capacity", rval = "volume", args = { } },
{ name = "volume_carried", rval = "volume", args = { } },
{ name = "weight_capacity", rval = "int", args = { } },
@@ -571,23 +599,23 @@ classes = {
{ name = "has_alarm_clock", rval = "bool", args = { } },
{ name = "has_amount", rval = "bool", args = { "string", "int" } },
{ name = "has_charges", rval = "bool", args = { "string", "int" } },
{ name = "has_child_flag", rval = "bool", args = { "string" } },
{ name = "has_conflicting_trait", rval = "bool", args = { "string" } },
{ name = "has_child_flag", rval = "bool", args = { "trait_id" } },
{ name = "has_conflicting_trait", rval = "bool", args = { "trait_id" } },
{ name = "has_destination", rval = "bool", args = { } },
{ name = "has_grab_break_tec", rval = "bool", args = { } },
{ name = "has_gun_for_ammo", rval = "bool", args = { "ammotype" } },
{ name = "has_higher_trait", rval = "bool", args = { "string" } },
{ name = "has_higher_trait", rval = "bool", args = { "trait_id" } },
{ name = "has_identified", rval = "bool", args = { "string" } },
{ name = "has_item", rval = "bool", args = { "item" } },
{ name = "has_item_with_flag", rval = "bool", args = { "string" } },
{ name = "has_lower_trait", rval = "bool", args = { "string" } },
{ name = "has_lower_trait", rval = "bool", args = { "trait_id" } },
{ name = "has_mabuff", rval = "bool", args = { "mabuff_id" } },
{ name = "has_martialart", rval = "bool", args = { "matype_id" } },
{ name = "has_miss_recovery_tec", rval = "bool", args = { } },
{ name = "has_mission_item", rval = "bool", args = { "int" } },
{ name = "has_morale_to_craft", rval = "bool", args = { } },
{ name = "has_morale_to_read", rval = "bool", args = { } },
{ name = "has_opposite_trait", rval = "bool", args = { "string" } },
{ name = "has_opposite_trait", rval = "bool", args = { "trait_id" } },
{ name = "has_technique", rval = "bool", args = { "matec_id" } },
{ name = "has_two_arms", rval = "bool", args = { } },
{ name = "has_watch", rval = "bool", args = { } },
@@ -673,9 +701,9 @@ classes = {
{ name = "get_morale_level", rval = "int", args = { } },
{ name = "mutate", rval = nil, args = { } },
{ name = "mutate_category", rval = nil, args = { "string" } },
{ name = "mutate_towards", rval = "bool", args = { "string" } },
{ name = "mutate_towards", rval = nil, args = { "string" } },
{ name = "mutation_ok", rval = "bool", args = { "string", "bool", "bool" } },
{ name = "mutate_towards", rval = "bool", args = { "trait_id" } },
{ name = "mutate_towards", rval = nil, args = { "trait_id" } },
{ name = "mutation_ok", rval = "bool", args = { "trait_id", "bool", "bool" } },
{ name = "natural_attack_restricted_on", rval = "bool", args = { "body_part" } },
{ name = "normalize", rval = nil, args = { } },
{ name = "num_bionics", rval = "int", args = { } },
@@ -708,7 +736,7 @@ classes = {
{ name = "process_bionic", rval = nil, args = { "int" } },
{ name = "process_effects", rval = nil, args = { } },
{ name = "process_turn", rval = nil, args = { } },
{ name = "purifiable", rval = "bool", args = { "string" } },
{ name = "purifiable", rval = "bool", args = { "trait_id" } },
{ name = "reach_attack", rval = nil, args = { "tripoint" } },
{ name = "read", rval = "bool", args = { "int" } },
{ name = "read_speed", rval = "int", args = { "bool" } },
@@ -720,8 +748,8 @@ classes = {
{ name = "regen", rval = nil, args = { "int" } },
{ name = "rem_addiction", rval = nil, args = { "add_type" } },
{ name = "remove_bionic", rval = nil, args = { "string" } },
{ name = "remove_child_flag", rval = nil, args = { "string" } },
{ name = "remove_mutation", rval = nil, args = { "string" } },
{ name = "remove_child_flag", rval = nil, args = { "trait_id" } },
{ name = "remove_mutation", rval = nil, args = { "trait_id" } },
{ name = "remove_random_bionic", rval = "bool", args = { } },
{ name = "reset_stats", rval = nil, args = { } },
{ name = "rooted", rval = nil, args = { } },
@@ -739,7 +767,7 @@ classes = {
{ name = "sees", rval = "bool", args = { "tripoint", "bool" } },
{ name = "sees_with_infrared", rval = "bool", args = { "Creature" } },
{ name = "setID", rval = nil, args = { "int" } },
{ name = "set_cat_level_rec", rval = nil, args = { "string" } },
{ name = "set_cat_level_rec", rval = nil, args = { "trait_id" } },
{ name = "set_highest_cat_level", rval = nil, args = { } },
{ name = "get_skill_level", rval = "int", args = { "skill_id" } },
{ name = "set_skill_level", rval = nil, args = { "skill_id", "int" } },
@@ -770,8 +798,6 @@ classes = {
{ name = "takeoff", rval = bool, args = { "item" } },
{ name = "talk_skill", rval = "int", args = { } },
{ name = "temp_equalizer", rval = nil, args = { "body_part", "body_part" } },
{ name = "throw_dex_mod", rval = "int", args = { "bool" } },
{ name = "throw_dex_mod", rval = "int", args = { } },
{ name = "throw_range", rval = "int", args = { "item" } },
{ name = "toggle_move_mode", rval = nil, args = { } },
{ name = "try_to_sleep", rval = nil, args = { } },
@@ -1453,7 +1479,7 @@ classes = {
{ name = "has_effect", rval = "bool", args = { "efftype_id" } },
{ name = "has_effect", rval = "bool", args = { "efftype_id", "body_part" } },
{ name = "has_grab_break_tec", rval = "bool", args = { } },
{ name = "has_trait", rval = "bool", args = { "string" } },
{ name = "has_trait", rval = "bool", args = { "trait_id" } },
{ name = "has_weapon", rval = "bool", args = { } },
{ name = "hit_roll", rval = "float", args = { } },
{ name = "hp_percentage", rval = "int", args = { } },
365,525 q

Large diffs are not rendered by default.

@@ -557,9 +557,8 @@ action_id handle_action_menu()
#define REGISTER_ACTION(name) entries.push_back(uimenu_entry(name, true, hotkey_for_action(name), \
ctxt.get_action_name(action_ident(name))));
#define REGISTER_CATEGORY(name) categories_by_int[last_category] = name; \
catgname = _(name);\
catgname += "...";\
capitalize_letter(catgname,0);\
catgname = name; \
catgname += "..."; \
entries.push_back(uimenu_entry(last_category, true, -1, catgname)); \
last_category++;

@@ -654,13 +653,13 @@ action_id handle_action_menu()
}
}

REGISTER_CATEGORY( "look" );
REGISTER_CATEGORY( "interact" );
REGISTER_CATEGORY( "inventory" );
REGISTER_CATEGORY( "combat" );
REGISTER_CATEGORY( "craft" );
REGISTER_CATEGORY( "info" );
REGISTER_CATEGORY( "misc" );
REGISTER_CATEGORY( _( "Look" ) );
REGISTER_CATEGORY( _( "Interact" ) );
REGISTER_CATEGORY( _( "Inventory" ) );
REGISTER_CATEGORY( _( "Combat" ) );
REGISTER_CATEGORY( _( "Craft" ) );
REGISTER_CATEGORY( _( "Info" ) );
REGISTER_CATEGORY( _( "Misc" ) );
if( hotkey_for_action( ACTION_QUICKSAVE ) > -1 ) {
REGISTER_ACTION( ACTION_QUICKSAVE );
}
@@ -676,18 +675,18 @@ action_id handle_action_menu()
entry->txt += "..."; // help _is_a menu.
}
if( hotkey_for_action( ACTION_DEBUG ) > -1 ) {
REGISTER_CATEGORY( "debug" ); // register with globalkey
REGISTER_CATEGORY( _( "Debug" ) ); // register with globalkey
if( ( entry = &entries.back() ) ) {
entry->hotkey = hotkey_for_action( ACTION_DEBUG );
}
}
} else if( category == "look" ) {
} else if( category == _( "Look" ) ) {
REGISTER_ACTION( ACTION_LOOK );
REGISTER_ACTION( ACTION_PEEK );
REGISTER_ACTION( ACTION_LIST_ITEMS );
REGISTER_ACTION( ACTION_ZONES );
REGISTER_ACTION( ACTION_MAP );
} else if( category == "inventory" ) {
} else if( category == _( "Inventory" ) ) {
REGISTER_ACTION( ACTION_INVENTORY );
REGISTER_ACTION( ACTION_ADVANCEDINV );
REGISTER_ACTION( ACTION_SORT_ARMOR );
@@ -706,7 +705,7 @@ action_id handle_action_menu()
REGISTER_ACTION( ACTION_READ );
REGISTER_ACTION( ACTION_WIELD );
REGISTER_ACTION( ACTION_UNLOAD );
} else if( category == "debug" ) {
} else if( category == _( "Debug" ) ) {
REGISTER_ACTION( ACTION_DEBUG );
if( ( entry = &entries.back() ) ) {
entry->txt += "..."; // debug _is_a menu.
@@ -720,7 +719,7 @@ action_id handle_action_menu()
#endif // TILES
REGISTER_ACTION( ACTION_DISPLAY_SCENT );
REGISTER_ACTION( ACTION_TOGGLE_DEBUG_MODE );
} else if( category == "interact" ) {
} else if( category == _( "Interact" ) ) {
REGISTER_ACTION( ACTION_EXAMINE );
REGISTER_ACTION( ACTION_SMASH );
REGISTER_ACTION( ACTION_MOVE_DOWN );
@@ -731,7 +730,7 @@ action_id handle_action_menu()
REGISTER_ACTION( ACTION_PICKUP );
REGISTER_ACTION( ACTION_GRAB );
REGISTER_ACTION( ACTION_BUTCHER );
} else if( category == "combat" ) {
} else if( category == _( "Combat" ) ) {
REGISTER_ACTION( ACTION_TOGGLE_MOVE );
REGISTER_ACTION( ACTION_FIRE );
REGISTER_ACTION( ACTION_RELOAD );
@@ -742,20 +741,20 @@ action_id handle_action_menu()
REGISTER_ACTION( ACTION_TOGGLE_SAFEMODE );
REGISTER_ACTION( ACTION_TOGGLE_AUTOSAFE );
REGISTER_ACTION( ACTION_IGNORE_ENEMY );
} else if( category == "craft" ) {
} else if( category == _( "Craft" ) ) {
REGISTER_ACTION( ACTION_CRAFT );
REGISTER_ACTION( ACTION_RECRAFT );
REGISTER_ACTION( ACTION_LONGCRAFT );
REGISTER_ACTION( ACTION_CONSTRUCT );
REGISTER_ACTION( ACTION_DISASSEMBLE );
} else if( category == "info" ) {
} else if( category == _( "Info" ) ) {
REGISTER_ACTION( ACTION_PL_INFO );
REGISTER_ACTION( ACTION_MISSIONS );
REGISTER_ACTION( ACTION_KILLS );
REGISTER_ACTION( ACTION_FACTIONS );
REGISTER_ACTION( ACTION_MORALE );
REGISTER_ACTION( ACTION_MESSAGES );
} else if( category == "misc" ) {
} else if( category == _( "Misc" ) ) {
REGISTER_ACTION( ACTION_WAIT );
REGISTER_ACTION( ACTION_SLEEP );
REGISTER_ACTION( ACTION_BIONICS );
@@ -29,6 +29,9 @@ void active_item_cache::remove( std::list<item>::iterator it, point location )

void active_item_cache::add( std::list<item>::iterator it, point location )
{
if( has( it, location ) ) {
return;
}
active_items[it->processing_speed()].push_back( item_reference{ location, it, &*it } );
active_item_set[ &*it ] = false;
}
@@ -142,7 +142,7 @@ void activity_handlers::burrow_finish( player_activity *act, player *p )
p->mod_fatigue( 10 );
}
g->m.destroy( pos, true );

act->set_to_null();
}

@@ -210,6 +210,13 @@ void set_up_butchery( player_activity &act, player &u )

const mtype *corpse = items[act.index].get_mtype();
int time_to_cut = 0;

if (corpse->has_flag( MF_HUMAN )) {
add_msg( m_good, _( "You're not that beastly...yet" ) );
act.set_to_null();
return;
}

switch( corpse->size ) {
// Time (roughly) in turns to cut up the corpse
case MS_TINY:
@@ -253,7 +260,6 @@ void butchery_drops_hardcoded( const mtype *corpse, player *p, int age, const st
int sinews = 0;
int feathers = 0;
int wool = 0;
bool stomach = false;

int max_practice = 4;
switch( corpse->size ) {
@@ -309,7 +315,6 @@ void butchery_drops_hardcoded( const mtype *corpse, player *p, int age, const st
sinews += std::min( 0, roll_butchery() - 8 );
feathers += std::min( 0, roll_butchery() - 1 );
wool += std::min( 0, roll_butchery() );
stomach = roll_butchery() >= 0;

int practice = std::max( 0, 4 + pieces + roll_butchery() );

@@ -344,27 +349,6 @@ void butchery_drops_hardcoded( const mtype *corpse, player *p, int age, const st
}
}

if( stomach ) {
const itype_id meat = corpse->get_meat_itype();
if( meat == "meat" ) {
if( corpse->size == MS_SMALL || corpse->size == MS_MEDIUM ) {
g->m.spawn_item( p->pos(), "stomach", 1, 0, age );
p->add_msg_if_player( m_good, _( "You harvest the stomach!" ) );
} else if( corpse->size == MS_LARGE || corpse->size == MS_HUGE ) {
g->m.spawn_item( p->pos(), "stomach_large", 1, 0, age );
p->add_msg_if_player( m_good, _( "You harvest the stomach!" ) );
}
} else if( meat == "human_flesh" ) {
if( corpse->size == MS_SMALL || corpse->size == MS_MEDIUM ) {
g->m.spawn_item( p->pos(), "hstomach", 1, 0, age );
p->add_msg_if_player( m_good, _( "You harvest the stomach!" ) );
} else if( corpse->size == MS_LARGE || corpse->size == MS_HUGE ) {
g->m.spawn_item( p->pos(), "hstomach_large", 1, 0, age );
p->add_msg_if_player( m_good, _( "You harvest the stomach!" ) );
}
}
}

if( ( corpse->has_flag( MF_FUR ) || corpse->has_flag( MF_LEATHER ) ||
corpse->has_flag( MF_CHITIN ) ) && skins > 0 ) {
p->add_msg_if_player( m_good, _( "You manage to skin the %s!" ), corpse->nname().c_str() );
@@ -498,14 +482,10 @@ void butchery_drops_hardcoded( const mtype *corpse, player *p, int age, const st
chunk.set_mtype( corpse );

// for now don't drop tainted or cannibal. parts overhaul of taint system to not require excessive item duplication
bool make_offal = !chunk.is_tainted() && !chunk.has_flag( "CANNIBALISM" ) &&
!chunk.made_of ( material_id ( "veggy" ) );
item parts( make_offal ? "offal" : meat, age );
parts.set_mtype( corpse );

g->m.add_item_or_charges( p->pos(), chunk );
for( int i = 1; i <= pieces; ++i ) {
g->m.add_item_or_charges( p->pos(), one_in( 3 ) ? parts : chunk );
g->m.add_item_or_charges( p->pos(), chunk );
}
}
}
@@ -925,7 +905,7 @@ void activity_handlers::forage_finish( player_activity *act, player *p )
const int max_exp = 2 * ( max_forage_skill - p->get_skill_level( skill_survival ) );
// Award experience for foraging attempt regardless of success
p->practice( skill_survival, rng(1, max_exp), max_forage_skill );

act->set_to_null();
}

@@ -1117,7 +1097,7 @@ void activity_handlers::pickaxe_finish( player_activity *act, player *p )
// Betcha wish you'd opted for the J-Hammer ;P
p->mod_hunger( 15 );
p->mod_thirst( 15 );
if( p->has_trait( "STOCKY_TROGLO" ) ) {
if( p->has_trait( trait_id( "STOCKY_TROGLO" ) ) ) {
p->mod_fatigue( 20 ); // Yep, dwarves can dig longer before tiring
} else {
p->mod_fatigue( 30 );
@@ -1439,7 +1419,7 @@ void activity_handlers::oxytorch_do_turn( player_activity *act, player *p )
if( act->values[0] <= 0 ) {
return;
}

item &it = p->i_at( act->position );
// act->values[0] is the number of charges yet to be consumed
const long charges_used = std::min( long( act->values[0] ), it.ammo_required() );
@@ -1527,12 +1507,12 @@ repeat_type repeat_menu( const std::string &title, repeat_type last_selection )
uimenu rmenu;
rmenu.text = title;
rmenu.return_invalid = true;

rmenu.addentry( REPEAT_ONCE, true, '1', _("Repeat once") );
rmenu.addentry( REPEAT_FOREVER, true, '2', _("Repeat as long as you can") );
rmenu.addentry( REPEAT_FULL, true, '3', _("Repeat until fully repaired, but don't reinforce") );
rmenu.addentry( REPEAT_EVENT, true, '4', _("Repeat until success/failure/level up") );

rmenu.selected = last_selection;

rmenu.query();
@@ -1799,7 +1779,7 @@ void activity_handlers::clear_rubble_finish( player_activity *act, player *p )
const int bonus = act->index * act->index;
p->mod_hunger ( 10 / bonus );
p->mod_thirst ( 10 / bonus );

act->set_to_null();
}

@@ -165,7 +165,7 @@ void addict_effect( player &u, addiction &add )
}

case ADD_MUTAGEN:
if( u.has_trait( "MUT_JUNKIE" ) ) {
if( u.has_trait( trait_id( "MUT_JUNKIE" ) ) ) {
if( one_in( 600 - 50 * in ) ) {
u.add_msg_if_player( m_warning, rng( 0,
6 ) < in ? _( "You so miss the exquisite rainbow of post-humanity." ) :
@@ -1421,7 +1421,7 @@ void advanced_inventory::display()
werase( minimap );
werase( mm_border );
draw_border( head );
Messages::display_messages( head, 2, 1, w_width - 1, 4 );
Messages::display_messages( head, 2, 1, w_width - 1, head_height - 2 );
draw_minimap();
const std::string msg = _( "< [?] show help >" );
mvwprintz( head, 0,
@@ -554,7 +554,7 @@ void player::sort_armor()
} else if( invlet_to_position( invlet ) != INT_MIN ) {
++iiter;
} else {
w.invlet = invlet;
inv.reassign_item( w, invlet );
++witer;
++iiter;
}
@@ -63,6 +63,10 @@ const efftype_id effect_took_xanax( "took_xanax" );
const efftype_id effect_visuals( "visuals" );
const efftype_id effect_weed_high( "weed_high" );

static const trait_id trait_HYPEROPIC( "HYPEROPIC" );
static const trait_id trait_MYOPIC( "MYOPIC" );
static const trait_id trait_PROF_MED( "PROF_MED" );

namespace
{
std::map<std::string, bionic_data> bionics;
@@ -764,7 +768,7 @@ int bionic_manip_cos( int p_int, int s_electronics, int s_firstaid, int s_mechan
s_mechanics * 1;

// Medical residents have some idea what they're doing
if( g->u.has_trait( "PROF_MED" ) ) {
if( g->u.has_trait( trait_PROF_MED ) ) {
pl_skill += 3;
add_msg( m_neutral, _( "You prep yourself to begin surgery." ) );
}
@@ -1009,7 +1013,7 @@ void bionics_install_failure( player *u, int difficulty, int success )
u->get_skill_level( skilll_firstaid ) * 3 +
u->get_skill_level( skilll_mechanics ) * 1;
// Medical residents get a substantial assist here
if( u->has_trait( "PROF_MED" ) ) {
if( u->has_trait( trait_PROF_MED ) ) {
pl_skill += 6;
}

@@ -1047,7 +1051,7 @@ void bionics_install_failure( player *u, int difficulty, int success )
break;
}

if( u->has_trait( "PROF_MED" ) ) {
if( u->has_trait( trait_PROF_MED ) ) {
//~"Complications" is USian medical-speak for "unintended damage from a medical procedure".
add_msg( m_neutral, _( "Your training helps you minimize the complications." ) );
// In addition to the bonus, medical residents know enough OR protocol to avoid botching.
@@ -1064,7 +1068,7 @@ void bionics_install_failure( player *u, int difficulty, int success )
switch( fail_type ) {

case 1:
if( !( u->has_trait( "NOPAIN" ) ) ) {
if( !( u->has_trait( trait_id( "NOPAIN" ) ) ) ) {
add_msg( m_bad, _( "It really hurts!" ) );
u->mod_pain( rng( failure_level * 3, failure_level * 6 ) );
}
@@ -1158,7 +1162,7 @@ int player::get_used_bionics_slots( const body_part bp ) const
std::map<body_part, int> player::bionic_installation_issues( const std::string &bioid )
{
std::map<body_part, int> issues;
if( !has_trait( "DEBUG_CBM_SLOTS" ) ) {
if( !has_trait( trait_id( "DEBUG_CBM_SLOTS" ) ) ) {
return issues;
}
for( auto &elem : bionics[ bioid ].occupied_bodyparts ) {
@@ -1391,7 +1395,7 @@ void check_bionics()
bio.first.c_str(), bio.second.fake_item.c_str() );
}
for( const auto &mid : bio.second.canceled_mutations ) {
if( !mutation_branch::has( mid ) ) {
if( !mid.is_valid() ) {
debugmsg( "Bionic %s cancels undefined mutation %s",
bio.first.c_str(), mid.c_str() );
}
@@ -12,6 +12,8 @@ class player;

struct quality;
using quality_id = string_id<quality>;
struct mutation_branch;
using trait_id = string_id<mutation_branch>;

struct bionic_data {
bionic_data();
@@ -65,7 +67,7 @@ struct bionic_data {
* Mutations/trait that are removed upon installing this CBM.
* E.g. enhanced optic bionic may cancel HYPEROPIC trait.
*/
std::vector<std::string> canceled_mutations;
std::vector<trait_id> canceled_mutations;
/**
* Additional bionics that are installed automatically when this
* bionic is installed. This can be used to install several bionics
@@ -12,9 +12,12 @@
#include "json.h"
#include "filesystem.h"
#include "item_search.h"
#include "rng.h"

#include <algorithm>
#include <cmath>
#include <string>
#include <locale>

double round_up( double val, unsigned int dp )
{
@@ -487,46 +490,40 @@ bool read_from_file_optional( const std::string &path, JsonDeserializer &reader
} );
}

std::string native_to_utf8( const std::string &str )
{
if( get_options().has_option( "ENCODING_CONV" ) && !get_option<bool>( "ENCODING_CONV" ) ) {
return str;
}
#if defined(_WIN32) || defined(WINDOWS)
// native encoded string --> Unicode sequence --> UTF-8 string
int unicode_size = MultiByteToWideChar( CP_ACP, 0, str.c_str(), -1, NULL, 0 ) + 1;
std::wstring unicode( unicode_size, '\0' );
MultiByteToWideChar( CP_ACP, 0, str.c_str(), -1, &unicode[0], unicode_size );
int utf8_size = WideCharToMultiByte( CP_UTF8, 0, &unicode[0], -1, NULL, 0, NULL, 0 ) + 1;
std::string result( utf8_size, '\0' );
WideCharToMultiByte( CP_UTF8, 0, &unicode[0], -1, &result[0], utf8_size, NULL, 0 );
while( !result.empty() && result.back() == '\0' ) {
result.pop_back();
}
return result;
#else
return str;
#endif
}

std::string utf8_to_native( const std::string &str )
{
if( get_options().has_option( "ENCODING_CONV" ) && !get_option<bool>( "ENCODING_CONV" ) ) {
return str;
std::string obscure_message( const std::string &str, std::function<char( void )> f )
{
//~ translators: place some random 1-width characters here in your language if possible, or leave it as is
std::string gibberish_narrow = _( "abcdefghijklmnopqrstuvwxyz" );
//~ translators: place some random 2-width characters here in your language if possible, or leave it as is
std::string gibberish_wide =
_( "に坂索トし荷測のンおク妙免イロコヤ梅棋厚れ表幌" );
std::wstring w_gibberish_narrow = utf8_to_wstr( gibberish_narrow );
std::wstring w_gibberish_wide = utf8_to_wstr( gibberish_wide );
std::wstring w_str = utf8_to_wstr( str );
char transformation[2] = { 0 }; // a trailing NULL terminator is necessary for utf8_width function
for( size_t i = 0; i < w_str.size(); ++i ) {
transformation[0] = f();
std::string this_char = wstr_to_utf8( std::wstring( 1, w_str[i] ) );
if( transformation[0] == -1 ) {
continue;
} else if( transformation[0] == 0 ) {
if( utf8_width( this_char ) == 1 ) {
w_str[i] = random_entry( w_gibberish_narrow );
} else {
w_str[i] = random_entry( w_gibberish_wide );
}
} else {
// Only support the case eg. replace current character to symbols like # or ?
if( utf8_width( transformation ) != 1 ) {
debugmsg( "target character isn't narrow" );
}
// A 2-width wide character in the original string should be replace by two narrow characters
w_str.replace( i, 1, utf8_to_wstr( std::string( utf8_width( this_char ), transformation[0] ) ) );
}
}
#if defined(_WIN32) || defined(WINDOWS)
// UTF-8 string --> Unicode sequence --> native encoded string
int unicode_size = MultiByteToWideChar( CP_UTF8, 0, str.c_str(), -1, NULL, 0 ) + 1;
std::wstring unicode( unicode_size, '\0' );
MultiByteToWideChar( CP_UTF8, 0, str.c_str(), -1, &unicode[0], unicode_size );
int native_size = WideCharToMultiByte( CP_ACP, 0, &unicode[0], -1, NULL, 0, NULL, 0 ) + 1;
std::string result( native_size, '\0' );
WideCharToMultiByte( CP_ACP, 0, &unicode[0], -1, &result[0], native_size, NULL, 0 );
while( !result.empty() && result.back() == '\0' ) {
result.pop_back();
std::string result = wstr_to_utf8( w_str );
if( utf8_width( str ) != utf8_width( result ) ) {
debugmsg( "utf8_width differ between original string and obscured string" );
}
return result;
#else
return str;
#endif
}
@@ -392,7 +392,21 @@ bool write_to_file_exclusive( const std::string &path,

std::istream &safe_getline( std::istream &ins, std::string &str );

std::string native_to_utf8( const std::string &str );
std::string utf8_to_native( const std::string &str );
/** Apply fuzzy effect to a string like:
* Hello, world! --> H##lo, wurl#!
*
* @param str the original string to be processed
* @param f the function that guides how to mess the message
* f() will be called for each character (lingual, not byte):
* [-] f() == -1 : nothing will be done
* [-] f() == 0 : the said character will be replace by a random character
* [-] f() == ch : the said character will be replace by ch
*
* @return The processed string
*
*/

std::string obscure_message( const std::string &str, std::function<char( void )> f );


#endif // CAT_UTILITY_H
@@ -3,6 +3,7 @@
#include "debug.h"
#include "cursesdef.h"
#include "wcwidth.h"
#include "options.h"

//copied from SDL2_ttf code
//except type changed from unsigned to uint32_t
@@ -403,6 +404,94 @@ std::string base64_decode(std::string str)
return decoded_data;
}

inline void strip_trailing_nulls( std::wstring &str )
{
while( !str.empty() && str.back() == '\0' ) {
str.pop_back();
}
}

inline void strip_trailing_nulls( std::string &str )
{
while( !str.empty() && str.back() == '\0' ) {
str.pop_back();
}
}

std::wstring utf8_to_wstr( const std::string &str )
{
#if defined(_WIN32) || defined(WINDOWS)
int sz = MultiByteToWideChar( CP_UTF8, 0, str.c_str(), -1, NULL, 0 ) + 1;
std::wstring wstr( sz, '\0' );
MultiByteToWideChar( CP_UTF8, 0, str.c_str(), -1, &wstr[0], sz );
strip_trailing_nulls( wstr );
return wstr;
#else
std::size_t sz = std::mbstowcs( NULL, str.c_str(), str.size() );
std::wstring wstr( sz, '\0' );
std::mbstowcs( &wstr[0], str.c_str(), sz );
strip_trailing_nulls( wstr );
return wstr;
#endif
}

std::string wstr_to_utf8( const std::wstring &wstr )
{
#if defined(_WIN32) || defined(WINDOWS)
int sz = WideCharToMultiByte( CP_UTF8, 0, wstr.c_str(), -1, NULL, 0, NULL, NULL );
std::string str( sz, '\0' );
WideCharToMultiByte( CP_UTF8, 0, wstr.c_str(), -1, &str[0], sz, NULL, NULL );
strip_trailing_nulls( str );
return str;
#else
std::size_t sz = std::wcstombs( NULL, wstr.c_str(), wstr.size() );
std::string str( sz, '\0' );
std::wcstombs( &str[0], wstr.c_str(), sz );
strip_trailing_nulls( str );
return str;
#endif
}

std::string native_to_utf8( const std::string &str )
{
if( get_options().has_option( "ENCODING_CONV" ) && !get_option<bool>( "ENCODING_CONV" ) ) {
return str;
}
#if defined(_WIN32) || defined(WINDOWS)
// native encoded string --> Unicode sequence --> UTF-8 string
int unicode_size = MultiByteToWideChar( CP_ACP, 0, str.c_str(), -1, NULL, 0 ) + 1;
std::wstring unicode( unicode_size, '\0' );
MultiByteToWideChar( CP_ACP, 0, str.c_str(), -1, &unicode[0], unicode_size );
int utf8_size = WideCharToMultiByte( CP_UTF8, 0, &unicode[0], -1, NULL, 0, NULL, 0 ) + 1;
std::string result( utf8_size, '\0' );
WideCharToMultiByte( CP_UTF8, 0, &unicode[0], -1, &result[0], utf8_size, NULL, 0 );
strip_trailing_nulls( result );
return result;
#else
return str;
#endif
}

std::string utf8_to_native( const std::string &str )
{
if( get_options().has_option( "ENCODING_CONV" ) && !get_option<bool>( "ENCODING_CONV" ) ) {
return str;
}
#if defined(_WIN32) || defined(WINDOWS)
// UTF-8 string --> Unicode sequence --> native encoded string
int unicode_size = MultiByteToWideChar( CP_UTF8, 0, str.c_str(), -1, NULL, 0 ) + 1;
std::wstring unicode( unicode_size, '\0' );
MultiByteToWideChar( CP_UTF8, 0, str.c_str(), -1, &unicode[0], unicode_size );
int native_size = WideCharToMultiByte( CP_ACP, 0, &unicode[0], -1, NULL, 0, NULL, 0 ) + 1;
std::string result( native_size, '\0' );
WideCharToMultiByte( CP_ACP, 0, &unicode[0], -1, &result[0], native_size, NULL, 0 );
strip_trailing_nulls( result );
return result;
#else
return str;
#endif
}

int center_text_pos(const char *text, int start_pos, int end_pos)
{
int full_screen = end_pos - start_pos + 1;
@@ -39,6 +39,12 @@ std::string utf8_truncate( std::string s, size_t length );
std::string base64_encode( std::string str );
std::string base64_decode( std::string str );

std::wstring utf8_to_wstr( const std::string &str );
std::string wstr_to_utf8( const std::wstring &wstr );

std::string native_to_utf8( const std::string &str );
std::string utf8_to_native( const std::string &str );

/**
* UTF8-Wrapper over std::string.
* It looks and feels like a std::string, but uses code points counts
@@ -31,6 +31,7 @@
#include "field.h"
#include "filesystem.h"
#include "string_input_popup.h"
#include "mutation.h"
extern "C" {
#include "lua.h"
#include "lualib.h"