diff --git a/data/json/effects_on_condition/dream_eocs.json b/data/json/effects_on_condition/dream_eocs.json index cc84fb4ab1895..271a39fb61e85 100644 --- a/data/json/effects_on_condition/dream_eocs.json +++ b/data/json/effects_on_condition/dream_eocs.json @@ -38,7 +38,7 @@ }, "effect": [ { - "switch": { "u_val": "health" }, + "switch": { "math": [ "u_val('health')" ] }, "cases": [ { "case": -100, "effect": { "u_message": "health_horrible", "snippet": true, "type": "bad" } }, { "case": -50, "effect": { "u_message": "health_very_bad", "snippet": true, "type": "bad" } }, diff --git a/data/json/monster_special_attacks/spells.json b/data/json/monster_special_attacks/spells.json index bd59e76b44c09..1b6d3a4981cbb 100644 --- a/data/json/monster_special_attacks/spells.json +++ b/data/json/monster_special_attacks/spells.json @@ -813,7 +813,7 @@ "case": 8, "effect": [ { - "switch": { "rand": 10 }, + "switch": { "math": [ "rand(10)" ] }, "cases": [ { "case": 0, diff --git a/data/json/npcs/TALK_TEST.json b/data/json/npcs/TALK_TEST.json index 984ea36d9c5c8..01ace39e1b56f 100644 --- a/data/json/npcs/TALK_TEST.json +++ b/data/json/npcs/TALK_TEST.json @@ -963,19 +963,13 @@ { "text": "Owed amount equals 14", "topic": "TALK_DONE", - "condition": { "math": [ "u_val('owed')", "==", "14" ] } + "condition": { "math": [ "n_val('owed')", "==", "14" ] } }, { "text": "Driving skill more than or equal to 5", "topic": "TALK_DONE", "condition": { "math": [ "u_skill('driving')", ">=", "5" ] } }, - { - "text": "A random number between 0 and 10 equals 11.", - "topic": "TALK_DONE", - "//": "Should never be true, kept here to show how the syntax is used.", - "condition": { "math": [ "rand(10)", "==", "11" ] } - }, { "text": "Temperature is 21.", "topic": "TALK_DONE", @@ -1022,7 +1016,7 @@ { "text": "Mana is at 2%.", "topic": "TALK_DONE", - "condition": { "math": [ "u_val('mana_percentage')", "==", "2" ] } + "condition": { "math": [ "round( u_val('mana_percentage') )", "==", "3" ] } }, { "text": "Hunger is 26.", "topic": "TALK_DONE", "condition": { "math": [ "u_val('hunger')", "==", "26" ] } }, { "text": "Thirst is 27.", "topic": "TALK_DONE", "condition": { "math": [ "u_val('thirst')", "==", "27" ] } }, @@ -1034,7 +1028,7 @@ { "text": "Stored kcal is at 100% of healthy.", "topic": "TALK_DONE", - "condition": { "math": [ "u_val('stored_kcal_percentage')", "==", "100" ] } + "condition": { "math": [ "round( u_val('stored_kcal_percentage') )", "==", "100" ] } }, { "text": "Has 3 glass bottles.", @@ -1159,7 +1153,7 @@ "topic": "TALK_DONE", "effect": { "math": [ "u_test_var_time_test_test", "=", "10" ] } }, - { "text": "Sets owed to 12.", "topic": "TALK_DONE", "effect": { "math": [ "u_val('owed')", "=", "12" ] } }, + { "text": "Sets owed to 12.", "topic": "TALK_DONE", "effect": { "math": [ "n_val('owed')", "=", "12" ] } }, { "text": "Sets skill level in driving to 10.", "topic": "TALK_DONE", diff --git a/data/mods/MindOverMatter/effectoncondition/eoc_awakening.json b/data/mods/MindOverMatter/effectoncondition/eoc_awakening.json index 0941d84692539..b26fd70b03396 100644 --- a/data/mods/MindOverMatter/effectoncondition/eoc_awakening.json +++ b/data/mods/MindOverMatter/effectoncondition/eoc_awakening.json @@ -612,161 +612,17 @@ "effect": [ { "math": [ "u_awakening_reducer", "=", "(portal_storm_awakening_odds(u_awakening_countup))" ] }, { - "switch": { "global_val": "var", "var_name": "ps_str" }, - "cases": [ - { - "case": 1, - "effect": { - "weighted_list_eocs": [ - [ "EOC_PORTAL_NULL_AWAKENING", { "const": 110 } ], - [ "EOC_PORTAL_BIOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_CLAIR_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_ELECTRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PHOTOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PYRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TELEKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TEEP_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PORTER_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_VITA_AWAKENING", { "const": 1 } ] - ] - } - }, - { - "case": 2, - "effect": { - "weighted_list_eocs": [ - [ "EOC_PORTAL_NULL_AWAKENING", { "const": 90 } ], - [ "EOC_PORTAL_BIOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_CLAIR_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_ELECTRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PHOTOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PYRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TELEKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TEEP_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PORTER_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_VITA_AWAKENING", { "const": 1 } ] - ] - } - }, - { - "case": 3, - "effect": { - "weighted_list_eocs": [ - [ "EOC_PORTAL_NULL_AWAKENING", { "const": 70 } ], - [ "EOC_PORTAL_BIOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_CLAIR_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_ELECTRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PHOTOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PYRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TELEKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TEEP_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PORTER_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_VITA_AWAKENING", { "const": 1 } ] - ] - } - }, - { - "case": 4, - "effect": { - "weighted_list_eocs": [ - [ "EOC_PORTAL_NULL_AWAKENING", { "const": 60 } ], - [ "EOC_PORTAL_BIOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_CLAIR_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_ELECTRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PHOTOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PYRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TELEKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TEEP_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PORTER_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_VITA_AWAKENING", { "const": 1 } ] - ] - } - }, - { - "case": 5, - "effect": { - "weighted_list_eocs": [ - [ "EOC_PORTAL_NULL_AWAKENING", { "const": 45 } ], - [ "EOC_PORTAL_BIOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_CLAIR_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_ELECTRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PHOTOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PYRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TELEKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TEEP_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PORTER_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_VITA_AWAKENING", { "const": 1 } ] - ] - } - }, - { - "case": 6, - "effect": { - "weighted_list_eocs": [ - [ "EOC_PORTAL_NULL_AWAKENING", { "const": 30 } ], - [ "EOC_PORTAL_BIOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_CLAIR_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_ELECTRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PHOTOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PYRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TELEKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TEEP_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PORTER_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_VITA_AWAKENING", { "const": 1 } ] - ] - } - }, - { - "case": 8, - "effect": { - "weighted_list_eocs": [ - [ "EOC_PORTAL_NULL_AWAKENING", { "const": 25 } ], - [ "EOC_PORTAL_BIOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_CLAIR_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_ELECTRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PHOTOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PYRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TELEKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TEEP_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PORTER_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_VITA_AWAKENING", { "const": 1 } ] - ] - } - }, - { - "case": 10, - "effect": { - "weighted_list_eocs": [ - [ "EOC_PORTAL_NULL_AWAKENING", { "const": 20 } ], - [ "EOC_PORTAL_BIOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_CLAIR_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_ELECTRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PHOTOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PYRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TELEKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TEEP_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PORTER_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_VITA_AWAKENING", { "const": 1 } ] - ] - } - }, - { - "case": 15, - "effect": { - "weighted_list_eocs": [ - [ "EOC_PORTAL_NULL_AWAKENING", { "const": 10 } ], - [ "EOC_PORTAL_BIOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_CLAIR_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_ELECTRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PHOTOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PYRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TELEKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TEEP_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PORTER_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_VITA_AWAKENING", { "const": 1 } ] - ] - } - } + "weighted_list_eocs": [ + [ "EOC_PORTAL_NULL_AWAKENING", { "math": [ "147 * e^(-0.24 * ps_str) + 5" ] } ], + [ "EOC_PORTAL_BIOKIN_AWAKENING", 1 ], + [ "EOC_PORTAL_CLAIR_AWAKENING", 1 ], + [ "EOC_PORTAL_ELECTRO_AWAKENING", 1 ], + [ "EOC_PORTAL_PHOTOKIN_AWAKENING", 1 ], + [ "EOC_PORTAL_PYRO_AWAKENING", 1 ], + [ "EOC_PORTAL_TELEKIN_AWAKENING", 1 ], + [ "EOC_PORTAL_TEEP_AWAKENING", 1 ], + [ "EOC_PORTAL_PORTER_AWAKENING", 1 ], + [ "EOC_PORTAL_VITA_AWAKENING", 1 ] ] } ] @@ -788,160 +644,17 @@ "effect": [ { "math": [ "u_awakening_reducer", "=", "(portal_storm_awakening_odds(u_awakening_countup))" ] }, { - "switch": { "global_val": "var", "var_name": "ps_str" }, - "cases": [ - { - "case": 1, - "effect": { - "weighted_list_eocs": [ - [ "EOC_PORTAL_NULL_AWAKENING", { "const": 120 } ], - [ "EOC_PORTAL_BIOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_CLAIR_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_ELECTRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PHOTOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PYRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TELEKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TEEP_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PORTER_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_VITA_AWAKENING", { "const": 1 } ] - ] - } - }, - { - "case": 2, - "effect": { - "weighted_list_eocs": [ - [ "EOC_PORTAL_NULL_AWAKENING", { "const": 90 } ], - [ "EOC_PORTAL_BIOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_CLAIR_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_ELECTRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PHOTOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PYRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TELEKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TEEP_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PORTER_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_VITA_AWAKENING", { "const": 1 } ] - ] - } - }, - { - "case": 3, - "effect": { - "weighted_list_eocs": [ - [ "EOC_PORTAL_NULL_AWAKENING", { "const": 70 } ], - [ "EOC_PORTAL_BIOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_CLAIR_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_ELECTRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PHOTOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PYRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TELEKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TEEP_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PORTER_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_VITA_AWAKENING", { "const": 1 } ] - ] - } - }, - { - "case": 4, - "effect": { - "weighted_list_eocs": [ - [ "EOC_PORTAL_NULL_AWAKENING", { "const": 60 } ], - [ "EOC_PORTAL_BIOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_CLAIR_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_ELECTRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PHOTOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PYRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TELEKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TEEP_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PORTER_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_VITA_AWAKENING", { "const": 1 } ] - ] - } - }, - { - "case": 5, - "effect": { - "weighted_list_eocs": [ - [ "EOC_PORTAL_NULL_AWAKENING", { "const": 45 } ], - [ "EOC_PORTAL_BIOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_CLAIR_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_ELECTRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PHOTOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PYRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TELEKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TEEP_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PORTER_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_VITA_AWAKENING", { "const": 1 } ] - ] - } - }, - { - "case": 6, - "effect": { - "weighted_list_eocs": [ - [ "EOC_PORTAL_NULL_AWAKENING", { "const": 30 } ], - [ "EOC_PORTAL_BIOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_CLAIR_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_ELECTRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PHOTOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PYRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TELEKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TEEP_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PORTER_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_VITA_AWAKENING", { "const": 1 } ] - ] - } - }, - { - "case": 8, - "effect": { - "weighted_list_eocs": [ - [ "EOC_PORTAL_NULL_AWAKENING", { "const": 25 } ], - [ "EOC_PORTAL_BIOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_CLAIR_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_ELECTRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PHOTOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PYRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TELEKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TEEP_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PORTER_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_VITA_AWAKENING", { "const": 1 } ] - ] - } - }, - { - "case": 10, - "effect": { - "weighted_list_eocs": [ - [ "EOC_PORTAL_NULL_AWAKENING", { "const": 20 } ], - [ "EOC_PORTAL_BIOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_CLAIR_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_ELECTRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PHOTOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PYRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TELEKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TEEP_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PORTER_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_VITA_AWAKENING", { "const": 1 } ] - ] - } - }, - { - "case": 15, - "effect": { - "weighted_list_eocs": [ - [ "EOC_PORTAL_NULL_AWAKENING", { "const": 10 } ], - [ "EOC_PORTAL_BIOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_CLAIR_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PHOTOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PYRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TELEKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TEEP_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PORTER_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_VITA_AWAKENING", { "const": 1 } ] - ] - } - } + "weighted_list_eocs": [ + [ "EOC_PORTAL_NULL_AWAKENING", { "math": [ "147 * e^(-0.24 * ps_str) + 5" ] } ], + [ "EOC_PORTAL_BIOKIN_AWAKENING", 1 ], + [ "EOC_PORTAL_CLAIR_AWAKENING", 1 ], + [ "EOC_PORTAL_ELECTRO_AWAKENING", 1 ], + [ "EOC_PORTAL_PHOTOKIN_AWAKENING", 1 ], + [ "EOC_PORTAL_PYRO_AWAKENING", 1 ], + [ "EOC_PORTAL_TELEKIN_AWAKENING", 1 ], + [ "EOC_PORTAL_TEEP_AWAKENING", 1 ], + [ "EOC_PORTAL_PORTER_AWAKENING", 1 ], + [ "EOC_PORTAL_VITA_AWAKENING", 1 ] ] } ] @@ -963,161 +676,17 @@ "effect": [ { "math": [ "u_awakening_reducer", "=", "(portal_storm_awakening_odds(u_awakening_countup))" ] }, { - "switch": { "global_val": "var", "var_name": "ps_str" }, - "cases": [ - { - "case": 1, - "effect": { - "weighted_list_eocs": [ - [ "EOC_PORTAL_NULL_AWAKENING", { "const": 120 } ], - [ "EOC_PORTAL_BIOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_CLAIR_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_ELECTRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PHOTOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PYRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TELEKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TEEP_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PORTER_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_VITA_AWAKENING", { "const": 1 } ] - ] - } - }, - { - "case": 2, - "effect": { - "weighted_list_eocs": [ - [ "EOC_PORTAL_NULL_AWAKENING", { "const": 90 } ], - [ "EOC_PORTAL_BIOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_CLAIR_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_ELECTRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PHOTOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PYRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TELEKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TEEP_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PORTER_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_VITA_AWAKENING", { "const": 1 } ] - ] - } - }, - { - "case": 3, - "effect": { - "weighted_list_eocs": [ - [ "EOC_PORTAL_NULL_AWAKENING", { "const": 70 } ], - [ "EOC_PORTAL_BIOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_CLAIR_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_ELECTRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PHOTOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PYRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TELEKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TEEP_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PORTER_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_VITA_AWAKENING", { "const": 1 } ] - ] - } - }, - { - "case": 4, - "effect": { - "weighted_list_eocs": [ - [ "EOC_PORTAL_NULL_AWAKENING", { "const": 60 } ], - [ "EOC_PORTAL_BIOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_CLAIR_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_ELECTRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PHOTOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PYRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TELEKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TEEP_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PORTER_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_VITA_AWAKENING", { "const": 1 } ] - ] - } - }, - { - "case": 5, - "effect": { - "weighted_list_eocs": [ - [ "EOC_PORTAL_NULL_AWAKENING", { "const": 45 } ], - [ "EOC_PORTAL_BIOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_CLAIR_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_ELECTRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PHOTOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PYRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TELEKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TEEP_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PORTER_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_VITA_AWAKENING", { "const": 1 } ] - ] - } - }, - { - "case": 6, - "effect": { - "weighted_list_eocs": [ - [ "EOC_PORTAL_NULL_AWAKENING", { "const": 30 } ], - [ "EOC_PORTAL_BIOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_CLAIR_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_ELECTRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PHOTOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PYRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TELEKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TEEP_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PORTER_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_VITA_AWAKENING", { "const": 1 } ] - ] - } - }, - { - "case": 8, - "effect": { - "weighted_list_eocs": [ - [ "EOC_PORTAL_NULL_AWAKENING", { "const": 25 } ], - [ "EOC_PORTAL_BIOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_CLAIR_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_ELECTRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PHOTOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PYRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TELEKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TEEP_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PORTER_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_VITA_AWAKENING", { "const": 1 } ] - ] - } - }, - { - "case": 10, - "effect": { - "weighted_list_eocs": [ - [ "EOC_PORTAL_NULL_AWAKENING", { "const": 20 } ], - [ "EOC_PORTAL_BIOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_CLAIR_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_ELECTRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PHOTOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PYRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TELEKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TEEP_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PORTER_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_VITA_AWAKENING", { "const": 1 } ] - ] - } - }, - { - "case": 15, - "effect": { - "weighted_list_eocs": [ - [ "EOC_PORTAL_NULL_AWAKENING", { "const": 10 } ], - [ "EOC_PORTAL_BIOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_CLAIR_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_ELECTRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PHOTOKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PYRO_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TELEKIN_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_TEEP_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_PORTER_AWAKENING", { "const": 1 } ], - [ "EOC_PORTAL_VITA_AWAKENING", { "const": 1 } ] - ] - } - } + "weighted_list_eocs": [ + [ "EOC_PORTAL_NULL_AWAKENING", { "math": [ "147 * e^(-0.24 * ps_str) + 5" ] } ], + [ "EOC_PORTAL_BIOKIN_AWAKENING", 1 ], + [ "EOC_PORTAL_CLAIR_AWAKENING", 1 ], + [ "EOC_PORTAL_ELECTRO_AWAKENING", 1 ], + [ "EOC_PORTAL_PHOTOKIN_AWAKENING", 1 ], + [ "EOC_PORTAL_PYRO_AWAKENING", 1 ], + [ "EOC_PORTAL_TELEKIN_AWAKENING", 1 ], + [ "EOC_PORTAL_TEEP_AWAKENING", 1 ], + [ "EOC_PORTAL_PORTER_AWAKENING", 1 ], + [ "EOC_PORTAL_VITA_AWAKENING", 1 ] ] } ] diff --git a/data/mods/MindOverMatter/effectoncondition/eoc_items.json b/data/mods/MindOverMatter/effectoncondition/eoc_items.json index 438d05ac829be..6222856e02147 100644 --- a/data/mods/MindOverMatter/effectoncondition/eoc_items.json +++ b/data/mods/MindOverMatter/effectoncondition/eoc_items.json @@ -5,7 +5,7 @@ "condition": { "math": [ "u_school_level('CLAIRSENTIENT')", ">=", "10" ] }, "effect": [ { - "switch": { "rand": 4 }, + "switch": { "math": [ "rand(4)" ] }, "cases": [ { "case": 0, @@ -52,7 +52,7 @@ ], "false_effect": [ { - "switch": { "rand": 4 }, + "switch": { "math": [ "rand(4)" ] }, "cases": [ { "case": 0, diff --git a/data/mods/Xedra_Evolved/mutations/mutation_eocs.json b/data/mods/Xedra_Evolved/mutations/mutation_eocs.json index 4114b012e4aba..587f4ebb7b9df 100644 --- a/data/mods/Xedra_Evolved/mutations/mutation_eocs.json +++ b/data/mods/Xedra_Evolved/mutations/mutation_eocs.json @@ -15,8 +15,8 @@ "run_eocs": { "id": "eoc_eggsac_mantain_or_remove_switch", "condition": { "u_has_trait": "EGGSAC_SURVIVABLE" }, - "effect": { "weighted_list_eocs": [ [ "eggsac_mantain", 10 ], [ "eggsac_remove", { "u_val": "strength" } ] ] }, - "false_effect": { "weighted_list_eocs": [ [ "eggsac_mantain", 25 ], [ "eggsac_remove", { "u_val": "strength" } ] ] } + "effect": { "weighted_list_eocs": [ [ "eggsac_mantain", 10 ], [ "eggsac_remove", { "math": [ "u_val('strength')" ] } ] ] }, + "false_effect": { "weighted_list_eocs": [ [ "eggsac_mantain", 25 ], [ "eggsac_remove", { "math": [ "u_val('strength')" ] } ] ] } } } }, diff --git a/doc/NPCs.md b/doc/NPCs.md index d42ec6760550d..bde9122a92bd9 100644 --- a/doc/NPCs.md +++ b/doc/NPCs.md @@ -983,7 +983,7 @@ Condition | Type | Description `"u_rule" or "npc_rule"` | string or [variable object](#variable-object) | `true` if u or the NPC follower AI rule for that matches string is set. `"u_override" or "npc_override"` | string or [variable object](#variable-object)| `true` if u or the NPC has an override for the string. `"has_pickup_list" or "u_has_pickup_list" or "npc_has_pickup_list"` | simple string | `true` if u or the NPC has a pickup list. -`"roll_contested""`, `difficulty`: int or [variable object](#variable-object), (*optional* `die_size : `int or [variable object](#variable-object) ) | [math expression](#math) | Compares a roll against a difficulty. Returns true if a random number between 1 and `die_size` (defaults to 10) plus the integer expression is greater than `difficulty`. For example { "u_roll_contested": { "u_val": "strength" }, "difficulty": 6 } will return whether a random number between 1 and 10 plus strength is greater than 6. +`"roll_contested""`, `difficulty`: int or [variable object](#variable-object), (*optional* `die_size : `int or [variable object](#variable-object) ) | [math expression](#math) | Compares a roll against a difficulty. Returns true if a random number between 1 and `die_size` (defaults to 10) plus the integer expression is greater than `difficulty`. For example { "u_roll_contested": { "math": [ "u_val('strength')" ] }, "difficulty": 6 } will return whether a random number between 1 and 10 plus strength is greater than 6. #### NPC only conditions @@ -1201,61 +1201,6 @@ Mutator Name | Required Keys | Description `"loc_relative_u"` | `target`: String or [variable object](#variable-object). | target should be a string like "(x,y,z)" where x,y,z are coordinates relative to the player. Returns the abs_ms coordinates as a string (ready to store as a location variable), in the form "(x,y,z)" of the provided point relative to the player. So `"target":"(0,1,0)"` would return the point south of the player. `"topic_item"` | | Returns current topic_item as a string. See [Repeat Responses](#repeat-responses) -#### List of values that can be read and/or written to - -Example | Description ---- | --- -`"const": 5` | A constant value, in this case 5. Can be read but not written to. -`"rand": 20` | A random value between 0 and a given value, in this case 20. Can be read but not written to. -`"u_val": "strength"` | Player character's strength. Can be read but not written to. Replace `"strength"` with `"dexterity"`, `"intelligence"`, or `"perception"` to get such values. -`"u_val": "strength_base"` | Player character's strength. Replace `"strength_base"` with `"dexterity_base"`, `"intelligence_base"`, or `"perception_base"` to get such values. -`"u_val": "strength_bonus"` | Player character's current strength bonus. Replace `"strength_bonus"` with `"dexterity_bonus"`, `"intelligence_bonus"`, or `"perception_bonus"` to get such values. -`"u_val": "var"` | Custom variable. `"var_name"`, `"type"`, and `"context"` must also be specified. If `global_val` is used then a global variable will be used. If `default` is given as either an int or a variable_object then that value will be used if the variable is empty. If `default_time` is the same thing will happen, but it will be parsed as a time string aka "10 hours". Otherwise 0 will be used if the variable is empty. -`"u_val": "allies"` | Number of allies the character has. Only supported for the player character. Can be read but not written to. -`"u_val": "cash"` | Amount of money the character has. Only supported for the player character. Can be read but not written to. -`"u_val": "owed"` | Owed money to the NPC you're talking to. -`"u_val": "sold"` | Amount sold to the NPC you're talking to. -`"u_val": "dodge"` | Current effective dodge of the character. -`"u_val": "pos_x"` | Player character x coordinate. "pos_y" and "pos_z" also works as expected. -`"u_val": "power"` | Bionic power in millijoule. -`"u_val": "power_max"` | Max bionic power in millijoule. Can be read but not written to. -`"u_val": "power_percentage"` | Percentage of max bionic power. Should be a number between 0 to 100. -`"u_val": "morale"` | The current morale. Can be read but not written to for players and for monsters can be read and written to. -`"u_val": "mana"` | Current mana. -`"u_val": "mana_max"` | Max mana. Can be read but not written to. -`"u_val": "hunger"` | Current perceived hunger. Can be read but not written to. -`"u_val": "thirst"` | Current thirst. -`"u_val": "instant_thirst"` | Current thirst minus water in the stomach that hasn't been absorbed by the body yet. -`"u_val": "stored_kcal"` | Stored kcal in the character's body. 55'000 is considered healthy. -`"u_val": "stored_kcal_percentage"` | a value of 100 represents 55'000 kcal, which is considered healthy. -`"u_val": "exp"` | Total experience earned. -`"u_val": "stim"` | Current stim level. -`"u_val": "pkill"` | Current painkiller level. -`"u_val": "rad"` | Current radiation level. -`"u_val": "focus"` | Current focus level. -`"u_val": "activity_level"` | Current activity level index as a floored integer, from 0-5. Roughly: 0.45 = SLEEP_EXERCISE (floored and returns 0), 0.5 = NO_EXERCISE(floored and returns 0), 1 = LIGHT_EXERCISE, 2 = MODERATE_EXERCISE, 3 = BRISK_EXERCISE, 4 = ACTIVE_EXERCISE, 5 = EXTRA_EXERCISE. -`"u_val": "fatigue"` | Current fatigue level. -`"u_val": "stamina"` | Current stamina level. -`"u_val": "health"` | Current health level. -`"u_val": "sleep_deprivation"` | Current sleep deprivation level. -`"u_val": "anger"` | Current anger level, only works for monsters. -`"u_val": "friendly"` | Current friendly level, only works for monsters. -`"u_val": "npc_anger"` | Current anger level the npc has. -`"u_val": "npc_trust"` | Current trust the npc has for you. -`"u_val": "npc_fear"` | Current fear of the npc. -`"u_val": "npc_value"` | Current value npc places on you. -`"u_val": "fine_detail_vision_mod"` | Returned values range from 1.0 (unimpeded vision) to 11.0 (totally blind). -`"u_val": "age"` | Current age in years. -`"u_val": "body_temp"` | Current body temperature. -`"u_val": "body_temp_delta"` | Difference in temperature between the hottest/coldest part and what feels like the hottest/coldest part. -`"u_val": "bmi_permil"` | Current BMI per mille (Body Mass Index x 1000) -`"u_val": "height"` | Current height in cm. When setting there is a range for your character size category. Setting it too high or low will use the limit instead. For tiny its 58, and 87. For small its 88 and 144. For medium its 145 and 200. For large its 201 and 250. For huge its 251 and 320. -`"u_val": "size"` | Size category from 1 (tiny) to 5 (huge). Read-only. -`"u_val": "grab_strength"` | Grab strength as defined in the monster definition. Read-only, returns false on characters. -`"u_val": "volume"` | Current volume in ml. read only. Cullently, doesn't work for characters, but for monsters and items. -`"u_val": "weight"` | Current weight in mg. read only. -`"math"` | An array math object. - ### Math A `math` object lets you evaluate math expressions and assign them to dialogue variables or compare them for conditions. It takes the form ```JSON @@ -1366,7 +1311,6 @@ _some functions support array arguments or kwargs, denoted with square brackets | Function | Eval | Assign |Scopes | Description | |----------|------|--------|-------|-------------| -| u_val(`s`/`v`) | ✅ | vary | u, n | Return the value of specific quality character, NPC or item has. [Full list of values that can be read and/or written to can be found here.](#list-of-values-that-can-be-read-andor-written-to)

Example:
`"condition": { "math": [ "u_val('strength')", ">=", "8"] }`| | armor(`s`/`v`,`s`/`v`) | ✅ | ❌ | u, n | Return the numerical value for a characters armor on a body part, for a damage type.
Arguments are damagetype ID, bodypart ID.

Example:
`"condition": { "math": [ "u_armor('bash', 'torso')", ">=", "5"] }`| | attack_speed() | ✅ | ❌ | u, n | Return the characters current adjusted attack speed with their current weapon.

Example:
`"condition": { "math": [ "u_attack_speed()", ">=", "10"] }`| | addiction_intensity(`s`/`v`) | ✅ | ❌ | u, n | Return the characters current intensity of the given addiction.
Argument is addiction type ID.

Example:
`"condition": { "math": [ "u_addiction_intensity('caffeine')", ">=", "1"] }`| @@ -1410,21 +1354,66 @@ _some functions support array arguments or kwargs, denoted with square brackets | time(`s`/`v`) | ✅ | ✅ | N/A
(global) | Return a numeric value (in turns) for a time period string (see [Units](JSON_INFO.md#units)).

Special Values:
`now` - returns duration since turn zero
`cataclysm` - returns duration between cataclysm and turn zero

`time('now')` can serve as an assignment target to change current turn.

Optional kwargs:
`unit`: specify return unit. Assumes `turns` if unspecified or empty.

Example:
`{ "math": [ "time('now') - u_timer_caravan_RandEnc", ">", "time('1 h')" ] }`| | time_since(`v`)
time_since('cataclysm')
time_since('midnight') | ✅ | ❌ | N/A
(global) | Convenience function that returns a numeric value (in turns) for the time period since time point stored in variable.

Special values:
`cataclysm` - return time since start of cataclysm
`midnight` - return time since midnight

Optional kwargs:
`unit`: specify return unit. Assumes `turns` if unspecified or empty.

Returns -1 if the argument is an undefined variable

Example:
`{ "math": [ "time_since(u_timer_caravan_RandEnc)", ">", "time('1 h')" ] }`
`{ "math": [ "time_since('cataclysm', 'unit':'years') > 1" ] }`| | time_until_eoc(`s`/`v`) | ✅ | ❌ | N/A
(global) | Returns time until next scheduled run of an EOC. Argument is EOC id.

Optional kwargs:
`unit`: specify return unit

Returns -1 is the EOC is not scheduled. | +| val(`s`) | ✅ | varies | u, n | Return or set a Character or item value. Argument is a [Character/item aspect](#list-of-character-and-item-aspects).

These are all in one function for legacy reasons and are generally poorly tested. If you need one of them, consider porting it to a native math function.

Example:
`{ "math": [ "u_val('strength')", "=", "2" ] }`| | vitamin(`s`/`v`) | ✅ | ✅ | u, n | Return or set the characters vitamin level.
Argument is vitamin ID.

Example:
`{ "math": [ "u_vitamin('mutagen')", "=", "0" ] }`| | warmth(`s`/`v`) | ✅ | ❌ | u, n | Return the characters warmth on a body part.
Argument is bodypart ID.

Example:
The value displayed in-game is calculated as follows.
`"{ "math": [ "u_warmth_in_game", "=", "(u_warmth('torso') / 100) * 2 - 100"] }`| | weather(`s`) | ✅ | ✅ | N/A
(global) | Return or set a weather aspect

Aspect must be one of:
`temperature` (in Kelvin),
`humidity` (as percentage),
`pressure` (in millibar),
`windpower` (in mph).
`precipitation` (in mm / h) either 0.5 (very_light ), 1.5 (light), or 3 (heavy). Read only.

Temperature conversion functions are available: `celsius()`, `fahrenheit()`, `from_celsius()`, and `from_fahrenheit()`.

Examples:
`{ "math": [ "weather('temperature')", "<", "from_fahrenheit( 33 )" ] }`
`{ "math": [ "fahrenheit( weather('temperature') )", "==", "21" ] }`| | damage_level() | ✅ | ❌ | u, n | Return the damage level of the talker, which must be an item.

Example:
`"condition": { "math": [ "n_damage_level()", "<", "1" ] }`| -More examples: +#### List of Character and item aspects +These can be read or written to with `val()`. + +| Value | Supports assignment | Description | +--- | --- | --- | +| `activity_level` | ❌ | Current activity level index as a floored integer, from 0-5. Roughly:
0.45 = SLEEP_EXERCISE (floored and returns 0),
0.5 = NO_EXERCISE(floored and returns 0),
1 = LIGHT_EXERCISE,
2 = MODERATE_EXERCISE,
3 = BRISK_EXERCISE,
4 = ACTIVE_EXERCISE,
5 = EXTRA_EXERCISE. | +| `age` | ✅ | Current age in years. | +| `allies` | ❌ | The avatar's number of allies | +| `anger` | ✅ | Current anger level. Only works for monsters. | +| `bmi_permil` | ❌ | Current BMI per mille (Body Mass Index x 1000) | +| `body_temp` | ❌ | Current body temperature. | +| `body_temp_delta` | ❌ | Difference in temperature between the hottest/coldest part and what feels like the hottest/coldest part. | +| `cash` | ❌ | Amount of money | +| `dodge` | ❌ | Current effective dodge | +| `exp` | ✅ | Total experience earned. | +| `fatigue` | ✅ | Current fatigue level. | +| `fine_detail_vision_mod` | ❌ | Returned values range from 1.0 (unimpeded vision) to 11.0 (totally blind). | +| `focus` | ✅ | Current focus level. | +| `friendly` | ✅ | Current friendly level. Only works for monsters. | +| `grab_strength` | ❌ | Grab strength as defined in the monster definition. Only works for monsters | +| `health` | ❌ | Current health level. | +| `height` | ✅ | Current height in cm. When setting there is a range for your character size category. Setting it too high or low will use the limit instead. For tiny its 58, and 87. For small its 88 and 144. For medium its 145 and 200. For large its 201 and 250. For huge its 251 and 320. | +| `hunger` | ❌ | Current perceived hunger. | +| `instant_thirst` | ❌ | Current thirst minus water in the stomach that hasn't been absorbed by the body yet. | +| `mana` | ✅ | Current mana. | +| `mana_max` | ❌ | Max mana. | +| `mana_percentage` | ❌ | Current mana as percent. | +| `morale` | ✅* | The current morale. Assigment only works for monsters. | +| `npc_anger` | ✅ | Current anger level towards the avatar | +| `npc_fear` | ✅ | Current fear towards the avatar | +| `npc_trust` | ✅ | Current trust the npc places in the avatar | +| `npc_value` | ✅ | Current value npc places on the avatar | +| `owed` | ✅ | Amount of money the Character owes the avatar. | +| `pkill` | ✅ | Current painkiller level. | +| `pos_x`
`pos_y`
`pos_z` | ✅ | Coordinate in the reality bubble | +| `power` | ✅ | Bionic power in millijoule. | +| `power_percentage` | ✅ | Percentage of max bionic power | +| `power_max` | ❌ | Max bionic power in millijoule. | +| `rad` | ✅ | Current radiation level. | +| `size` | ❌ | Size category from 1 (tiny) to 5 (huge). | +| `sleep_deprivation` | ✅ | Current sleep deprivation level. | +| `sold` | ✅ | Amount of money the avatar has sold the Character | +| `stamina` | ✅ | Current stamina level. | +| `stim` | ✅ | Current stim level. | +| `stored_kcal` | ✅ | Stored kcal in the character's body. 55'000 is considered healthy. | +| `stored_kcal_percentage` | ✅ | a value of 100 represents 55'000 kcal, which is considered healthy. | +| `strength`
`dexterity`
`intelligence`
`pereception` | ✅ | Current attributes | +| `strength_base`
`dexterity_base`
`intelligence_base`
`pereception_base` | ✅ | Base attributes | +| `strength_bonus`
`dexterity_bonus`
`intelligence_bonus`
`pereception_bonus` | ✅ | Bonus attributes | +| `thirst` | ✅ | Current thirst. | +| `volume` | ❌ | Current volume in mL. Only works for monsters | +| `weight` | ❌ | Current weight in mg. | + -```JSON - { "math": [ "u_val('age')" ] }, - { "math": [ "time('1 h')" ] }, - { "math": [ "u_proficiency('prof_test', 'format': 'percent')" ] }, - { "math": [ "100 + rand(100)" ] }, - { "math": [ "rnd(0,1)" ] }, - { "math": [ "rnd(u_vitamin('mutagen'), u_vitamin('mutagen') * 2)" ] } -``` #### Math functions defined in JSON Math functions can be defined in JSON like this ```JSON diff --git a/src/condition.cpp b/src/condition.cpp index 419633fc5ad56..7453df288a0a4 100644 --- a/src/condition.cpp +++ b/src/condition.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include @@ -38,7 +37,6 @@ #include "mapdata.h" #include "martialarts.h" #include "math_parser.h" -#include "math_parser_shim.h" #include "mission.h" #include "mtype.h" #include "mutation.h" @@ -1609,12 +1607,12 @@ conditional_t::func f_has_reason() conditional_t::func f_roll_contested( const JsonObject &jo, const std::string_view member ) { - std::function get_check = conditional_t::get_get_dbl( jo.get_object( - member ) ); + dbl_or_var get_check = get_dbl_or_var( jo, member ); dbl_or_var difficulty = get_dbl_or_var( jo, "difficulty", true ); dbl_or_var die_size = get_dbl_or_var( jo, "die_size", false, 10 ); return [get_check, difficulty, die_size]( dialogue & d ) { - return rng( 1, die_size.evaluate( d ) ) + get_check( d ) > difficulty.evaluate( d ); + return rng( 1, die_size.evaluate( d ) ) + get_check.evaluate( d ) > + difficulty.evaluate( d ); }; } @@ -1868,571 +1866,225 @@ std::function conditional_t::get_get_string( co return ret_func; } -template -// NOLINTNEXTLINE(readability-function-cognitive-complexity): not my problem!! -std::function conditional_t::get_get_dbl( J const &jo ) +namespace +{ +std::unordered_map const f_get_vals = { + { "activity_level", &talker::get_activity_level }, + { "age", &talker::get_age }, + { "anger", &talker::get_anger }, + { "bmi_permil", &talker::get_bmi_permil }, + { "cash", &talker::cash }, + { "dexterity_base", &talker::get_dex_max }, + { "dexterity_bonus", &talker::get_dex_bonus }, + { "dexterity", &talker::dex_cur }, + { "exp", &talker::get_kill_xp }, + { "fatigue", &talker::get_fatigue }, + { "fine_detail_vision_mod", &talker::get_fine_detail_vision_mod }, + { "focus", &talker::focus_cur }, + { "friendly", &talker::get_friendly }, + { "grab_strength", &talker::get_grab_strength }, + { "health", &talker::get_health }, + { "height", &talker::get_height }, + { "hunger", &talker::get_hunger }, + { "instant_thirst", &talker::get_instant_thirst }, + { "intelligence_base", &talker::get_int_max }, + { "intelligence_bonus", &talker::get_int_bonus }, + { "intelligence", &talker::int_cur }, + { "mana_max", &talker::mana_max }, + { "mana", &talker::mana_cur }, + { "morale", &talker::morale_cur }, + { "npc_anger", &talker::get_npc_anger }, + { "npc_fear", &talker::get_npc_fear }, + { "npc_trust", &talker::get_npc_trust }, + { "npc_value", &talker::get_npc_value }, + { "owed", &talker::debt }, + { "perception_base", &talker::get_per_max }, + { "perception_bonus", &talker::get_per_bonus }, + { "perception", &talker::per_cur }, + { "pkill", &talker::get_pkill }, + { "pos_x", &talker::posx }, + { "pos_y", &talker::posy }, + { "pos_z", &talker::posz }, + { "rad", &talker::get_rad }, + { "size", &talker::get_size }, + { "sleep_deprivation", &talker::get_sleep_deprivation }, + { "sold", &talker::sold }, + { "stamina", &talker::get_stamina }, + { "stim", &talker::get_stim }, + { "stored_kcal", &talker::get_stored_kcal }, + { "strength_base", &talker::get_str_max }, + { "strength_bonus", &talker::get_str_bonus }, + { "strength", &talker::str_cur }, + { "thirst", &talker::get_thirst }, + { "volume", &talker::get_volume }, + { "weight", &talker::get_weight }, +}; +} // namespace + +// Consider adding new, single-purpose math functions instead of feeding this monster another else-if +std::function conditional_t::get_get_dbl( std::string_view checked_value, + char scope ) { - if( jo.has_member( "const" ) ) { - const double const_value = jo.get_float( "const" ); - return [const_value]( dialogue const & ) { - return const_value; + const bool is_npc = scope == 'n'; + + if( auto iter = f_get_vals.find( checked_value ); iter != f_get_vals.end() ) { + return [is_npc, func = iter->second ]( dialogue & d ) { + return ( d.actor( is_npc )->*func )(); }; - } else if( jo.has_member( "rand" ) ) { - int max_value = jo.get_int( "rand" ); - return [max_value]( dialogue const & ) { - return rng( 0, max_value ); + + } else if( checked_value == "allies" ) { + if( is_npc ) { + throw std::invalid_argument( "Can't get allies count for NPCs" ); + } + return []( dialogue const & ) { + return g->allies().size(); }; - } else if( jo.has_member( "u_val" ) || jo.has_member( "npc_val" ) || - jo.has_member( "global_val" ) || jo.has_member( "context_val" ) ) { - const bool is_npc = jo.has_member( "npc_val" ); - const bool is_global = jo.has_member( "global_val" ); - const bool is_context = jo.has_member( "context_val" ); - const std::string checked_value = is_npc ? jo.get_string( "npc_val" ) : - ( is_global ? jo.get_string( "global_val" ) : ( is_context ? jo.get_string( "context_val" ) : - jo.get_string( "u_val" ) ) ); - if( checked_value == "strength" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->str_cur(); - }; - } else if( checked_value == "dexterity" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->dex_cur(); - }; - } else if( checked_value == "intelligence" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->int_cur(); - }; - } else if( checked_value == "perception" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->per_cur(); - }; - } else if( checked_value == "strength_base" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->get_str_max(); - }; - } else if( checked_value == "dexterity_base" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->get_dex_max(); - }; - } else if( checked_value == "intelligence_base" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->get_int_max(); - }; - } else if( checked_value == "perception_base" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->get_per_max(); - }; - } else if( checked_value == "strength_bonus" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->get_str_bonus(); - }; - } else if( checked_value == "dexterity_bonus" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->get_dex_bonus(); - }; - } else if( checked_value == "intelligence_bonus" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->get_int_bonus(); - }; - } else if( checked_value == "perception_bonus" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->get_per_bonus(); - }; - } else if( checked_value == "dodge" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->get_character()->get_dodge(); - }; - } else if( checked_value == "var" ) { - var_info info( {}, {} ); - if constexpr( std::is_same_v ) { - info = read_var_info( jo ); - } - return [info]( dialogue const & d ) { - std::string var = read_var_value( info, d ); - if( !var.empty() ) { - // NOLINTNEXTLINE(cert-err34-c) - return std::atof( var.c_str() ); - } else if( !info.default_val.empty() ) { - // NOLINTNEXTLINE(cert-err34-c) - return std::atof( info.default_val.c_str() ); - } - return 0.0; - }; - } else if( checked_value == "allies" ) { - if( is_npc ) { - jo.throw_error( "allies count not supported for NPCs. In " + jo.str() ); - } else { - return []( dialogue const & ) { - return static_cast( g->allies().size() ); - }; - } - } else if( checked_value == "cash" ) { - if( is_npc ) { - jo.throw_error( "cash count not supported for NPCs. In " + jo.str() ); - } else { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->cash(); - }; + } else if( checked_value == "dodge" ) { + return [is_npc]( dialogue const & d ) { + return static_cast( d.actor( is_npc ) )->get_character()->get_dodge(); + }; + } else if( checked_value == "power_percentage" ) { + return [is_npc]( dialogue const & d ) { + // Energy in milijoule + units::energy::value_type power_max = d.actor( is_npc )->power_max().value(); + if( power_max == 0 ) { + return 0.0; //Default value if character does not have power, avoids division with 0. } - } else if( checked_value == "owed" ) { - if( is_npc ) { - jo.throw_error( "owed amount not supported for NPCs. In " + jo.str() ); - } else { - return []( dialogue const & d ) { - return d.actor( true )->debt(); - }; + return static_cast( d.actor( is_npc )->power_cur().value() * 100.0L / power_max ); + }; + } else if( checked_value == "mana_percentage" ) { + return [is_npc]( dialogue const & d ) { + int mana_max = d.actor( is_npc )->mana_max(); + if( mana_max == 0 ) { + return 0.0; //Default value if character does not have mana, avoids division with 0. } - } else if( checked_value == "sold" ) { - if( is_npc ) { - jo.throw_error( "owed amount not supported for NPCs. In " + jo.str() ); - } else { - return []( dialogue const & d ) { - return d.actor( true )->sold(); - }; + return d.actor( is_npc )->mana_cur() * 100.0 / mana_max; + }; + } else if( checked_value == "stored_kcal_percentage" ) { + // 100% is 5 BMI's worth of kcal, which is considered healthy (this varies with height). + return [is_npc]( dialogue const & d ) { + int divisor = d.actor( is_npc )->get_healthy_kcal() / 100; + //if no data default to default height of 175cm + if( divisor == 0 ) { + divisor = 118169 / 100; } - } else if( checked_value == "pos_x" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->posx(); - }; - } else if( checked_value == "pos_y" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->posy(); - }; - } else if( checked_value == "pos_z" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->posz(); - }; - } else if( checked_value == "power" ) { - return [is_npc]( dialogue const & d ) { - // Energy in milijoule - return static_cast( d.actor( is_npc )->power_cur().value() ); - }; - } else if( checked_value == "power_max" ) { - return [is_npc]( dialogue const & d ) { - // Energy in milijoule - return static_cast( d.actor( is_npc )->power_max().value() ); - }; - } else if( checked_value == "power_percentage" ) { - return [is_npc]( dialogue const & d ) { - // Energy in milijoule - int power_max = d.actor( is_npc )->power_max().value(); - if( power_max == 0 ) { - return 0; //Default value if character does not have power, avoids division with 0. - } else { - return static_cast( d.actor( is_npc )->power_cur().value() * 100 ) / power_max; - } - }; - } else if( checked_value == "morale" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->morale_cur(); - }; - } else if( checked_value == "focus" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->focus_cur(); - }; - } else if( checked_value == "mana" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->mana_cur(); - }; - } else if( checked_value == "mana_max" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->mana_max(); - }; - } else if( checked_value == "mana_percentage" ) { - return [is_npc]( dialogue const & d ) { - int mana_max = d.actor( is_npc )->mana_max(); - if( mana_max == 0 ) { - return 0; //Default value if character does not have mana, avoids division with 0. - } else { - return ( d.actor( is_npc )->mana_cur() * 100 ) / mana_max; - } - }; - } else if( checked_value == "hunger" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->get_hunger(); - }; - } else if( checked_value == "thirst" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->get_thirst(); - }; - } else if( checked_value == "instant_thirst" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->get_instant_thirst(); - }; - } else if( checked_value == "stored_kcal" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->get_stored_kcal(); - }; - } else if( checked_value == "stored_kcal_percentage" ) { - // 100% is 5 BMI's worth of kcal, which is considered healthy (this varies with height). - return [is_npc]( dialogue const & d ) { - int divisor = d.actor( is_npc )->get_healthy_kcal() / 100; - //if no data default to default height of 175cm - if( divisor == 0 ) { - divisor = 118169 / 100; - } - return d.actor( is_npc )->get_stored_kcal() / divisor; - }; - } else if( checked_value == "exp" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->get_kill_xp(); - }; - } else if( checked_value == "stim" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->get_stim(); - }; - } else if( checked_value == "pkill" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->get_pkill(); - }; - } else if( checked_value == "rad" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->get_rad(); - }; - } else if( checked_value == "focus" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->focus_cur(); - }; - } else if( checked_value == "activity_level" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->get_activity_level(); - }; - } else if( checked_value == "fatigue" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->get_fatigue(); - }; - } else if( checked_value == "stamina" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->get_stamina(); - }; - } else if( checked_value == "sleep_deprivation" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->get_sleep_deprivation(); - }; - } else if( checked_value == "anger" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->get_anger(); - }; - } else if( checked_value == "friendly" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->get_friendly(); - }; - } else if( checked_value == "age" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->get_age(); - }; - } else if( checked_value == "height" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->get_height(); - }; - } else if( checked_value == "bmi_permil" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->get_bmi_permil(); - }; - } else if( checked_value == "size" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->get_size(); - }; - } else if( checked_value == "volume" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->get_volume(); - }; - } else if( checked_value == "weight" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->get_weight(); - }; - } else if( checked_value == "grab_strength" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->get_grab_strength(); - }; - } else if( checked_value == "fine_detail_vision_mod" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->get_fine_detail_vision_mod(); - }; - } else if( checked_value == "health" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->get_health(); - }; - } else if( checked_value == "body_temp" ) { - return [is_npc]( dialogue const & d ) { - return units::to_legacy_bodypart_temp( d.actor( is_npc )->get_body_temp() ); - }; - } else if( checked_value == "body_temp_delta" ) { - return [is_npc]( dialogue const & d ) { - return units::to_legacy_bodypart_temp_delta( d.actor( is_npc )->get_body_temp_delta() ); - }; - } else if( checked_value == "npc_trust" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->get_npc_trust(); - }; - } else if( checked_value == "npc_fear" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->get_npc_fear(); - }; - } else if( checked_value == "npc_value" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->get_npc_value(); - }; - } else if( checked_value == "npc_anger" ) { - return [is_npc]( dialogue const & d ) { - return d.actor( is_npc )->get_npc_anger(); - }; - } - } else if( jo.has_array( "math" ) ) { - // no recursive math through shim - if constexpr( std::is_same_v ) { - eoc_math math; - math.from_json( jo, "math", eoc_math::type_t::ret ); - return [math = std::move( math )]( dialogue & d ) { - return math.act( d ); - }; - } + return static_cast( d.actor( is_npc )->get_stored_kcal() ) / divisor; + }; + } else if( checked_value == "body_temp" ) { + return [is_npc]( dialogue const & d ) { + return units::to_legacy_bodypart_temp( d.actor( is_npc )->get_body_temp() ); + }; + } else if( checked_value == "body_temp_delta" ) { + return [is_npc]( dialogue const & d ) { + return units::to_legacy_bodypart_temp_delta( d.actor( is_npc )->get_body_temp_delta() ); + }; + } else if( checked_value == "power" ) { + return [is_npc]( dialogue const & d ) { + // Energy in milijoule + return d.actor( is_npc )->power_cur().value(); + }; + } else if( checked_value == "power_max" ) { + return [is_npc]( dialogue const & d ) { + // Energy in milijoule + return d.actor( is_npc )->power_max().value(); + }; } - jo.throw_error( "unrecognized number source in " + jo.str() ); - return []( dialogue const & ) { - return 0.0; - }; + + throw std::invalid_argument( string_format( R"(Invalid aspect "%s" for val())", checked_value ) ); } -static double handle_min_max( dialogue &d, double input, std::optional min, - std::optional max ) +namespace { - if( min.has_value() ) { - double min_val = min.value().evaluate( d ); - input = std::max( min_val, input ); - } - if( max.has_value() ) { - double max_val = max.value().evaluate( d ); - input = std::min( max_val, input ); - } - return input; -} +std::unordered_map const f_set_vals = { + { "age", &talker::set_age }, + { "anger", &talker::set_anger }, + { "dexterity_base", &talker::set_dex_max }, + { "dexterity_bonus", &talker::set_dex_bonus }, + { "exp", &talker::set_kill_xp }, + { "fatigue", &talker::set_fatigue }, + { "friendly", &talker::set_friendly }, + { "height", &talker::set_height }, + { "intelligence_base", &talker::set_int_max }, + { "intelligence_bonus", &talker::set_int_bonus }, + { "mana", &talker::set_mana_cur }, + { "morale", &talker::set_morale }, + { "npc_anger", &talker::set_npc_anger }, + { "npc_fear", &talker::set_npc_fear }, + { "npc_trust", &talker::set_npc_trust }, + { "npc_value", &talker::set_npc_value }, + { "perception_base", &talker::set_per_max }, + { "perception_bonus", &talker::set_per_bonus }, + { "pkill", &talker::set_pkill }, + { "rad", &talker::set_rad }, + { "sleep_deprivation", &talker::set_sleep_deprivation }, + { "stamina", &talker::set_stamina }, + { "stim", &talker::set_stim }, + { "stored_kcal", &talker::set_stored_kcal }, + { "strength_base", &talker::set_str_max }, + { "strength_bonus", &talker::set_str_bonus }, + { "thirst", &talker::set_thirst }, +}; +} // namespace -template +// Consider adding new, single-purpose math functions instead of feeding this monster another else-if std::function -// NOLINTNEXTLINE(readability-function-cognitive-complexity): not my problem!! -conditional_t::get_set_dbl( const J &jo, const std::optional &min, - const std::optional &max, bool temp_var ) -{ - if( temp_var ) { - jo.allow_omitted_members(); - return [min, max]( dialogue & d, double input ) { - write_var_value( var_type::global, "temp_var", d.actor( false ), &d, - handle_min_max( d, input, min, max ) ); +conditional_t::get_set_dbl( std::string_view checked_value, char scope ) +{ + const bool is_npc = scope == 'n'; + + if( auto iter = f_set_vals.find( checked_value ); iter != f_set_vals.end() ) { + return [is_npc, func = iter->second ]( dialogue & d, double input ) { + ( d.actor( is_npc )->*func )( input ); }; - } else if( jo.has_member( "u_val" ) || jo.has_member( "npc_val" ) || - jo.has_member( "global_val" ) || jo.has_member( "faction_val" ) || jo.has_member( "party_val" ) || - jo.has_member( "context_val" ) ) { - var_type type = var_type::u; - std::string checked_value; - if( jo.has_member( "u_val" ) ) { - type = var_type::u; - checked_value = jo.get_string( "u_val" ); - } else if( jo.has_member( "npc_val" ) ) { - type = var_type::npc; - checked_value = jo.get_string( "npc_val" ); - } else if( jo.has_member( "global_val" ) ) { - type = var_type::global; - checked_value = jo.get_string( "global_val" ); - } else if( jo.has_member( "var_val" ) ) { - type = var_type::var; - checked_value = jo.get_string( "var_val" ); - } else if( jo.has_member( "context_val" ) ) { - type = var_type::context; - checked_value = jo.get_string( "context_val" ); - } else if( jo.has_member( "faction_val" ) ) { - type = var_type::faction; - checked_value = jo.get_string( "faction_val" ); - } else if( jo.has_member( "party_val" ) ) { - type = var_type::party; - checked_value = jo.get_string( "party_val" ); - } else { - jo.throw_error( "Invalid variable type." ); - } - const bool is_npc = type == var_type::npc; - if( checked_value == "strength_base" ) { - return [is_npc, min, max]( dialogue & d, double input ) { - d.actor( is_npc )->set_str_max( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "dexterity_base" ) { - return [is_npc, min, max]( dialogue & d, double input ) { - d.actor( is_npc )->set_dex_max( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "intelligence_base" ) { - return [is_npc, min, max]( dialogue & d, double input ) { - d.actor( is_npc )->set_int_max( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "perception_base" ) { - return [is_npc, min, max]( dialogue & d, double input ) { - d.actor( is_npc )->set_per_max( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "strength_bonus" ) { - return [is_npc, min, max]( dialogue & d, double input ) { - d.actor( is_npc )->set_str_max( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "dexterity_bonus" ) { - return [is_npc, min, max]( dialogue & d, double input ) { - d.actor( is_npc )->set_dex_max( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "intelligence_bonus" ) { - return [is_npc, min, max]( dialogue & d, double input ) { - d.actor( is_npc )->set_int_max( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "perception_bonus" ) { - return [is_npc, min, max]( dialogue & d, double input ) { - d.actor( is_npc )->set_per_max( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "var" ) { - dbl_or_var empty; - std::string var_name; - if constexpr( std::is_same_v ) { - var_name = get_talk_varname( jo, "var_name", false, empty ); - } - return [is_npc, var_name, type, min, max]( dialogue & d, double input ) { - write_var_value( type, var_name, d.actor( is_npc ), &d, - handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "owed" ) { - if( is_npc ) { - jo.throw_error( "owed amount not supported for NPCs. In " + jo.str() ); - } else { - return [min, max]( dialogue & d, double input ) { - d.actor( true )->add_debt( handle_min_max( d, input, min, max ) - d.actor( true )->debt() ); - }; - } - } else if( checked_value == "sold" ) { - if( is_npc ) { - jo.throw_error( "sold amount not supported for NPCs. In " + jo.str() ); - } else { - return [min, max]( dialogue & d, double input ) { - d.actor( true )->add_sold( handle_min_max( d, input, min, max ) - d.actor( true )->sold() ); - }; - } - } else if( checked_value == "pos_x" ) { - return [is_npc, min, max]( dialogue & d, double input ) { - d.actor( is_npc )->set_pos( tripoint( handle_min_max( d, input, min, max ), - d.actor( is_npc )->posy(), - d.actor( is_npc )->posz() ) ); - }; - } else if( checked_value == "pos_y" ) { - return [is_npc, min, max]( dialogue & d, double input ) { - d.actor( is_npc )->set_pos( tripoint( d.actor( is_npc )->posx(), handle_min_max( d, input, min, - max ), - d.actor( is_npc )->posz() ) ); - }; - } else if( checked_value == "pos_z" ) { - return [is_npc, min, max]( dialogue & d, double input ) { - d.actor( is_npc )->set_pos( tripoint( d.actor( is_npc )->posx(), d.actor( is_npc )->posy(), - handle_min_max( d, input, min, max ) ) ); - }; - } else if( checked_value == "power" ) { - return [is_npc, min, max]( dialogue & d, double input ) { - // Energy in milijoule - d.actor( is_npc )->set_power_cur( 1_mJ * handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "power_percentage" ) { - return [is_npc, min, max]( dialogue & d, double input ) { - // Energy in milijoule - d.actor( is_npc )->set_power_cur( ( d.actor( is_npc )->power_max() * handle_min_max( d, input, - min, - max ) ) / 100 ); - }; - } else if( checked_value == "focus" ) { - return [is_npc, min, max]( dialogue & d, double input ) { - d.actor( is_npc )->mod_focus( handle_min_max( d, input, min, - max ) - d.actor( is_npc )->focus_cur() ); - }; - } else if( checked_value == "mana" ) { - return [is_npc, min, max]( dialogue & d, double input ) { - d.actor( is_npc )->set_mana_cur( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "mana_percentage" ) { - return [is_npc, min, max]( dialogue & d, double input ) { - d.actor( is_npc )->set_mana_cur( ( d.actor( is_npc )->mana_max() * handle_min_max( d, input, min, - max ) ) / 100 ); - }; - } else if( checked_value == "thirst" ) { - return [is_npc, min, max]( dialogue & d, double input ) { - d.actor( is_npc )->set_thirst( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "stored_kcal" ) { - return [is_npc, min, max]( dialogue & d, double input ) { - d.actor( is_npc )->set_stored_kcal( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "stored_kcal_percentage" ) { - // 100% is 55'000 kcal, which is considered healthy. - return [is_npc, min, max]( dialogue & d, double input ) { - d.actor( is_npc )->set_stored_kcal( handle_min_max( d, input, min, max ) * 5500 ); - }; - } else if( checked_value == "stim" ) { - return [is_npc, min, max]( dialogue & d, double input ) { - d.actor( is_npc )->set_stim( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "pkill" ) { - return [is_npc, min, max]( dialogue & d, double input ) { - d.actor( is_npc )->set_pkill( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "rad" ) { - return [is_npc, min, max]( dialogue & d, double input ) { - d.actor( is_npc )->set_rad( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "fatigue" ) { - return [is_npc, min, max]( dialogue & d, double input ) { - d.actor( is_npc )->set_fatigue( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "stamina" ) { - return [is_npc, min, max]( dialogue & d, double input ) { - d.actor( is_npc )->set_stamina( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "sleep_deprivation" ) { - return [is_npc, min, max]( dialogue & d, double input ) { - d.actor( is_npc )->set_sleep_deprivation( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "anger" ) { - return [is_npc, min, max]( dialogue & d, double input ) { - d.actor( is_npc )->set_anger( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "morale" ) { - return [is_npc, min, max]( dialogue & d, double input ) { - d.actor( is_npc )->set_morale( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "friendly" ) { - return [is_npc, min, max]( dialogue & d, double input ) { - d.actor( is_npc )->set_friendly( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "exp" ) { - return [is_npc, min, max]( dialogue & d, double input ) { - d.actor( is_npc )->set_kill_xp( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "age" ) { - return [is_npc, min, max]( dialogue & d, double input ) { - d.actor( is_npc )->set_age( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "height" ) { - return [is_npc, min, max]( dialogue & d, double input ) { - d.actor( is_npc )->set_height( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "npc_trust" ) { - return [is_npc, min, max]( dialogue & d, double input ) { - d.actor( is_npc )->set_npc_trust( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "npc_fear" ) { - return [is_npc, min, max]( dialogue & d, double input ) { - d.actor( is_npc )->set_npc_fear( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "npc_value" ) { - return [is_npc, min, max]( dialogue & d, double input ) { - d.actor( is_npc )->set_npc_value( handle_min_max( d, input, min, max ) ); - }; - } else if( checked_value == "npc_anger" ) { - return [is_npc, min, max]( dialogue & d, double input ) { - d.actor( is_npc )->set_npc_anger( handle_min_max( d, input, min, max ) ); - }; - } + } else if( checked_value == "owed" ) { + return [is_npc]( dialogue & d, double input ) { + d.actor( is_npc )->add_debt( input - d.actor( is_npc )->debt() ); + }; + } else if( checked_value == "sold" ) { + return [is_npc]( dialogue & d, double input ) { + d.actor( is_npc )->add_sold( input - d.actor( is_npc )->sold() ); + }; + } else if( checked_value == "pos_x" ) { + return [is_npc]( dialogue & d, double input ) { + tripoint const tr = d.actor( is_npc )->pos(); + d.actor( is_npc )->set_pos( tripoint( input, tr.y, tr.z ) ); + }; + } else if( checked_value == "pos_y" ) { + return [is_npc]( dialogue & d, double input ) { + tripoint const tr = d.actor( is_npc )->pos(); + d.actor( is_npc )->set_pos( tripoint( tr.x, input, tr.z ) ); + }; + } else if( checked_value == "pos_z" ) { + return [is_npc]( dialogue & d, double input ) { + tripoint const tr = d.actor( is_npc )->pos(); + d.actor( is_npc )->set_pos( tripoint( tr.xy(), input ) ); + }; + } else if( checked_value == "power" ) { + return [is_npc]( dialogue & d, double input ) { + // Energy in milijoule + d.actor( is_npc )->set_power_cur( 1_mJ * input ); + }; + } else if( checked_value == "power_percentage" ) { + return [is_npc]( dialogue & d, double input ) { + // Energy in milijoule + d.actor( is_npc )->set_power_cur( ( d.actor( is_npc )->power_max() * input ) / 100 ); + }; + } else if( checked_value == "focus" ) { + return [is_npc]( dialogue & d, double input ) { + d.actor( is_npc )->mod_focus( input - d.actor( is_npc )->focus_cur() ); + }; + } else if( checked_value == "mana_percentage" ) { + return [is_npc]( dialogue & d, double input ) { + d.actor( is_npc )->set_mana_cur( ( d.actor( is_npc )->mana_max() * input ) / 100 ); + }; + } else if( checked_value == "stored_kcal_percentage" ) { + // 100% is 55'000 kcal, which is considered healthy. + return [is_npc]( dialogue & d, double input ) { + d.actor( is_npc )->set_stored_kcal( input * 5500 ); + }; } - jo.throw_error( "error setting double destination in " + jo.str() ); - return []( dialogue const &, double ) {}; + throw std::invalid_argument( string_format( R"(Invalid aspect "%s" for val())", checked_value ) ); } void eoc_math::_validate_type( JsonArray const &objects, type_t type_ ) const @@ -2849,16 +2501,3 @@ const std::unordered_set &dialogue_data::complex_conds() } return ret; } - -template std::function -conditional_t::get_get_dbl<>( kwargs_shim const & ); - -template std::function -conditional_t::get_set_dbl<>( const kwargs_shim &, - const std::optional &, - const std::optional &, bool ); - -template std::function -conditional_t::get_set_dbl<>( const JsonObject &, - const std::optional &, - const std::optional &, bool ); diff --git a/src/condition.h b/src/condition.h index d49cb102fc16e..6f45ab96a318c 100644 --- a/src/condition.h +++ b/src/condition.h @@ -9,7 +9,6 @@ #include "dialogue.h" #include "dialogue_helpers.h" -#include "math_parser_shim.h" #include "mission.h" class JsonObject; @@ -79,12 +78,10 @@ struct conditional_t { static std::function get_get_string( const JsonObject &jo ); static std::function get_get_translation( const JsonObject &jo ); - template - static std::function get_get_dbl( J const &jo ); - template + static std::function get_get_dbl( std::string_view checked_value, + char scope ); std::function - static get_set_dbl( const J &jo, const std::optional &min, - const std::optional &max, bool temp_var ); + static get_set_dbl( std::string_view checked_value, char scope ); bool operator()( dialogue &d ) const { if( !condition ) { return false; @@ -96,12 +93,4 @@ struct conditional_t { func condition; }; -extern template std::function -conditional_t::get_get_dbl<>( kwargs_shim const & ); - -extern template std::function -conditional_t::get_set_dbl<>( const kwargs_shim &, - const std::optional &, - const std::optional &, bool ); - #endif // CATA_SRC_CONDITION_H diff --git a/src/math_parser_diag.cpp b/src/math_parser_diag.cpp index f853b8cb5040f..58c282c9b5990 100644 --- a/src/math_parser_diag.cpp +++ b/src/math_parser_diag.cpp @@ -12,7 +12,6 @@ #include "magic.h" #include "map.h" #include "math_parser_diag_value.h" -#include "math_parser_shim.h" #include "mtype.h" #include "options.h" #include "string_input_popup.h" @@ -88,27 +87,13 @@ T _read_from_string( std::string_view s, const std::vector u_val( char scope, std::vector const ¶ms, diag_kwargs const &/* kwargs */ ) { - kwargs_shim const shim( params, scope ); - try { - return conditional_t::get_get_dbl( shim ); - } catch( std::exception const &e ) { - debugmsg( "shim failed: %s", e.what() ); - return []( dialogue const & ) { - return 0; - }; - } + return conditional_t::get_get_dbl( params[0].str(), scope ); } std::function u_val_ass( char scope, std::vector const ¶ms, diag_kwargs const &/* kwargs */ ) { - kwargs_shim const shim( params, scope ); - try { - return conditional_t::get_set_dbl( shim, {}, {}, false ); - } catch( std::exception const &e ) { - debugmsg( "shim failed: %s", e.what() ); - return []( dialogue const &, double ) {}; - } + return conditional_t::get_set_dbl( params[0].str(), scope ); } std::function option_eval( char /* scope */, @@ -1285,7 +1270,7 @@ std::map const dialogue_eval_f{ { "time_since", { "g", 1, time_since_eval } }, { "time_until_eoc", { "g", 1, time_until_eoc_eval } }, { "proficiency", { "un", 1, proficiency_eval } }, - { "val", { "un", -1, u_val } }, + { "val", { "un", 1, u_val } }, { "value_or", { "g", 2, value_or_eval } }, { "vitamin", { "un", 1, vitamin_eval } }, { "warmth", { "un", 1, warmth_eval } }, @@ -1308,7 +1293,7 @@ std::map const dialogue_assign_f{ { "spell_level_adjustment", { "un", 1, spell_level_adjustment_ass } }, { "time", { "g", 1, time_ass } }, { "proficiency", { "un", 1, proficiency_ass } }, - { "val", { "un", -1, u_val_ass } }, + { "val", { "un", 1, u_val_ass } }, { "vitamin", { "un", 1, vitamin_ass } }, { "weather", { "g", 1, weather_ass } }, }; diff --git a/src/math_parser_shim.cpp b/src/math_parser_shim.cpp deleted file mode 100644 index d12eb39a1e6b6..0000000000000 --- a/src/math_parser_shim.cpp +++ /dev/null @@ -1,95 +0,0 @@ -#include "math_parser_shim.h" - -#include "math_parser_diag_value.h" -#include "math_parser_func.h" - -#include -#include - -#include "json_loader.h" -#include "string_formatter.h" - -kwargs_shim::kwargs_shim( std::vector const &tokens, char scope ) -{ - bool positional = false; - for( diag_value const &token : tokens ) { - std::vector parts = tokenize( token.str(), ":", false ); - if( parts.size() == 1 && !positional ) { - kwargs.emplace( - // NOLINTNEXTLINE(cata-translate-string-literal): not a user-visible string - string_format( "%s_val", scope == 'g' ? "global" : scope == 'n' ? "npc" : std::string( 1, scope ) ), - parts[0] ); - positional = true; - } else if( parts.size() == 2 ) { - kwargs.emplace( parts[0], parts[1] ); - } else { - debugmsg( "Too many parts in token %s", token.str() ); - } - } -} - -std::string kwargs_shim::str() const -{ - std::string ret; - for( decltype( kwargs )::value_type const &e : kwargs ) { - // NOLINTNEXTLINE(cata-translate-string-literal): not a user-visible string - ret += string_format( "'%.*s: %.*s', ", e.first.size(), e.first.data(), e.second.size(), - e.second.data() ); - } - return ret; -} - -JsonValue kwargs_shim::get_member( std::string_view key ) const -{ - auto const it = kwargs.find( key ); - bool const valid = it != kwargs.end(); - std::string const temp = - valid ? string_format( R"("%.*s")", it->second.size(), it->second.data() ) - : R"("dummy")"; - return json_loader::from_string( temp ); -} - -std::string kwargs_shim::get_string( std::string_view key ) const -{ - std::optional get = _get_string( key ); - if( get ) { - return *get; - } - return {}; -} - -namespace -{ -template -T _ret_t( std::optional const &get, T def ) -{ - if( !get || get->empty() ) { - return def; - } - try { - return std::stod( *get ); - } catch( std::invalid_argument const &/* ex */ ) { - return def; - } -} -} // namespace - -double kwargs_shim::get_float( std::string_view key, double def ) const -{ - return _ret_t( _get_string( key ), def ); -} - -int kwargs_shim::get_int( std::string_view key, int def ) const -{ - return _ret_t( _get_string( key ), def ); -} - -std::optional kwargs_shim::_get_string( std::string_view key ) const -{ - auto itkw = kwargs.find( key ); - if( itkw != kwargs.end() ) { - return { std::string{ itkw->second } }; - } - - return std::nullopt; -} diff --git a/src/math_parser_shim.h b/src/math_parser_shim.h deleted file mode 100644 index a148c07f9f9f2..0000000000000 --- a/src/math_parser_shim.h +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once -#ifndef CATA_SRC_MATH_PARSER_SHIM_H -#define CATA_SRC_MATH_PARSER_SHIM_H - -#include -#include -#include -#include -#include -#include - -#include "flexbuffer_json.h" - -struct diag_value; - -// temporary shim that pretends to be a JsonObject for the purpose of reusing code between the new -// "math" and the old "arithmetic"/"compare_num"/"u_val" -class kwargs_shim -{ - public: - explicit kwargs_shim( std::vector const &tokens, char scope ); - - std::string get_string( std::string_view key ) const; - double get_float( std::string_view key, double def = 0 ) const; - int get_int( std::string_view key, int def = 0 ) const; - - bool has_string( std::string_view key ) const { - return _get_string( key ).has_value(); - } - bool has_int( std::string_view key ) const { - constexpr int min_int = std::numeric_limits::min(); - return get_int( key, min_int ) != min_int; - } - bool has_member( std::string_view key ) const { - return has_string( key ); - } - - JsonValue get_member( std::string_view key ) const; - static bool has_object( std::string_view /* key */ ) { - return false; - } - static JsonObject get_object( std::string_view /* key */ ) { - return {}; - } - static bool has_array( std::string_view /* key */ ) { - return false; - } - static JsonArray get_array( std::string_view /* key */ ) { - return {}; - } - - static void throw_error( std::string_view message ) { - debugmsg( message.data() ); - } - std::string str() const; - void allow_omitted_members() const {} - - private: - std::map> kwargs; - std::optional _get_string( std::string_view key ) const; -}; - -#endif // CATA_SRC_MATH_PARSER_SHIM_H diff --git a/src/npctalk.cpp b/src/npctalk.cpp index f2db1d367c6c6..377bb84ca5dda 100644 --- a/src/npctalk.cpp +++ b/src/npctalk.cpp @@ -5415,26 +5415,23 @@ talk_effect_fun_t::func f_queue_eoc_with( const JsonObject &jo, std::string_view talk_effect_fun_t::func f_weighted_list_eocs( const JsonObject &jo, std::string_view member ) { - std::vector>> eoc_pairs; + std::vector> eoc_pairs; for( JsonArray ja : jo.get_array( member ) ) { JsonValue eoc = ja.next_value(); + dbl_or_var eoc_weight; if( ja.test_int() ) { - int weight = ja.next_int(); - eoc_pairs.emplace_back( effect_on_conditions::load_inline_eoc( eoc, - "" ), [weight]( dialogue const & ) { - return weight; - } ); + eoc_weight.min = dbl_or_var_part{ ja.next_int() }; } else { - JsonObject weight = ja.next_object(); - eoc_pairs.emplace_back( effect_on_conditions::load_inline_eoc( eoc, "" ), - conditional_t::get_get_dbl( weight ) ); + eoc_weight.min = get_dbl_or_var_part( ja.next_value(), member ); } + eoc_pairs.emplace_back( effect_on_conditions::load_inline_eoc( eoc, "" ), + eoc_weight ); } return [eoc_pairs]( dialogue & d ) { weighted_int_list eocs; - for( const std::pair> &eoc_pair : + for( const std::pair &eoc_pair : eoc_pairs ) { - eocs.add( eoc_pair.first, eoc_pair.second( d ) ); + eocs.add( eoc_pair.first, eoc_pair.second.evaluate( d ) ); } effect_on_condition_id picked_eoc = *eocs.pick(); dialogue newDialog( d ); @@ -5464,8 +5461,7 @@ talk_effect_fun_t::func f_if( const JsonObject &jo, std::string_view member ) talk_effect_fun_t::func f_switch( const JsonObject &jo, std::string_view member ) { - std::function eoc_switch = - conditional_t::get_get_dbl( jo.get_object( member ) ); + dbl_or_var eoc_switch = get_dbl_or_var( jo, member ); std::vector> case_pairs; for( const JsonValue jv : jo.get_array( "cases" ) ) { JsonObject array_case = jv.get_object(); @@ -5474,7 +5470,7 @@ talk_effect_fun_t::func f_switch( const JsonObject &jo, std::string_view member case_pairs.emplace_back( get_dbl_or_var( array_case, "case" ), case_effect ); } return [eoc_switch, case_pairs]( dialogue & d ) { - const double switch_int = eoc_switch( d ); + const double switch_int = eoc_switch.evaluate( d ); talk_effect_t case_effect; for( const std::pair &case_pair : case_pairs ) {