From d0d3619c53246b413651a0e7d582a7e1d835d5a3 Mon Sep 17 00:00:00 2001 From: Jason Jones Date: Mon, 26 Aug 2019 23:00:48 -0800 Subject: [PATCH] Add new mission/talk effect u_learn_recipe (#33574) * Add u_learn_recipe effect and u_know_recipe condition * Add new mission MISSION_LEARN_ABOUT_CATTAIL_JELLY --- data/json/npcs/missiondef.json | 41 ++++++++++++++++++++++++++++++++++ doc/NPCs.md | 2 ++ src/condition.cpp | 14 ++++++++++++ src/condition.h | 3 ++- src/dialogue.h | 1 + src/npctalk.cpp | 13 +++++++++++ 6 files changed, 73 insertions(+), 1 deletion(-) diff --git a/data/json/npcs/missiondef.json b/data/json/npcs/missiondef.json index a26ca6adb8533..ccd30f67a9885 100644 --- a/data/json/npcs/missiondef.json +++ b/data/json/npcs/missiondef.json @@ -771,5 +771,46 @@ "success_lie": "I know time is relative and all that.", "failure": "I'm not quite sure how you failed to survive AND are talking to me." } + }, + { + "id": "MISSION_LEARN_ABOUT_CATTAIL_JELLY", + "type": "mission_definition", + "name": "Gather cattail stalks to create cattail jelly", + "description": "Gather 80 cattail stalks from the swamp and bring them back to learn how to craft cattail jelly. Raise your survival skill to at least 1 by harvesting cattail stalks. Bring back the provided bag as well.", + "goal": "MGOAL_CONDITION", + "goal_condition": { + "and": [ + { "u_has_item": "duffelbag" }, + { "u_has_items": { "item": "cattail_stalk", "count": 80 } }, + { "u_has_skill": { "skill": "survival", "level": 1 } } + ] + }, + "difficulty": 1, + "value": 0, + "origins": [ "ORIGIN_OPENER_NPC", "ORIGIN_ANY_NPC" ], + "start": { + "effect": [ { "u_buy_item": "duffelbag" } ], + "assign_mission_target": { "om_terrain": "forest_water", "reveal_radius": 3 } + }, + "end": { + "effect": [ + { "u_sell_item": "duffelbag" }, + { "u_sell_item": "cattail_stalk", "count": 80 }, + { "npc_consume_item": "cattail_stalk", "count": 80 }, + { "u_learn_recipe": "cattail_jelly" }, + { "u_buy_item": "cattail_jelly", "container": "bag_zipper", "count": 7 } + ] + }, + "dialogue": { + "describe": "Medical services are a little sparse following , but people have been surviving using their wits and the bounty of Mother Nature for a long time. Care to learn a little?", + "offer": "Did you know that cattails are a source of a jelly that works as an antiseptic and analgesic? Something like that is likely to be mighty helpful out here. I want you to take this bag, head to the nearest swamp, collect 80 cattail stalks, and bring them back here. In exchange, I'll show you how to harvest the jelly.", + "accepted": "Great! This bag should be big enough to hold all of the stalks we'll need. Don't forget to bring it back.", + "rejected": "Your loss.", + "advice": "The cattails grow in the fresh water in swamps. You can't miss them.", + "inquire": "Got those cattail stalks yet? Don't forget to bring back my bag and improve your survival skill to at least 1 as well.", + "success": "Great! Hand them over and I'll show you how this works.", + "success_lie": "OK, then hand them over.", + "failure": "Well, that's a shame." + } } ] diff --git a/doc/NPCs.md b/doc/NPCs.md index 12d389e6dda2c..b0192abb88cd2 100644 --- a/doc/NPCs.md +++ b/doc/NPCs.md @@ -430,6 +430,7 @@ Effect | Description `u_lose_var`, `npc_lose_var`: `var_name`, `type: type_str`, `context: context_str` | Your character or the NPC will clear any stored variable that has the same `var_name`, `type_str`, and `context_str`. `barber_hair` | Opens a menu allowing the player to choose a new hair style. `barber_beard` | Opens a menu allowing the player to choose a new beard style. +`u_learn_recipe: recipe_string` | Your character will learn and memorize the recipe `recipe_string`. #### Trade / Items @@ -564,6 +565,7 @@ Condition | Type | Description `"u_has_weapon"`
`"npc_has_weapon"` | simple string | `true` if the player character or NPC is wielding a weapon. `"u_driving"`
`"npc_driving"` | simple string | `true` if the player character or NPC is operating a vehicle. Note NPCs cannot currently operate vehicles. `"u_has_skill"`
`"npc_has_skill"` | dictionary | `u_has_skill` or `npc_has_skill` must be a dictionary with a `skill` string and a `level` int.
`true` if the player character or NPC has at least the value of `level` in `skill`. +`"u_know_recipe"` | string | `true` if the player character knows the recipe specified in `u_know_recipe`. It only counts as known if it is actually memorized--holding a book with the recipe in it will not count. #### Player Only conditions diff --git a/src/condition.cpp b/src/condition.cpp index 9b173a3841100..1d475a58b2c45 100644 --- a/src/condition.cpp +++ b/src/condition.cpp @@ -21,6 +21,7 @@ #include "mission.h" #include "npc.h" #include "overmapbuffer.h" +#include "recipe.h" #include "string_id.h" #include "type_id.h" #include "vehicle.h" @@ -800,6 +801,17 @@ void conditional_t::set_has_skill( JsonObject &jo, const std::string &member, } } +template +void conditional_t::set_u_know_recipe( JsonObject &jo, const std::string &member ) +{ + const std::string &known_recipe_id = jo.get_string( member ); + condition = [known_recipe_id]( const T & d ) { + player *actor = d.alpha; + const recipe &r = recipe_id( known_recipe_id ).obj(); + return actor->knows_recipe( &r ); + }; +} + template conditional_t::conditional_t( JsonObject jo ) { @@ -967,6 +979,8 @@ conditional_t::conditional_t( JsonObject jo ) set_has_skill( jo, "u_has_skill" ); } else if( jo.has_member( "npc_has_skill" ) ) { set_has_skill( jo, "npc_has_skill", is_npc ); + } else if( jo.has_member( "u_know_recipe" ) ) { + set_u_know_recipe( jo, "u_know_recipe" ); } else { for( const std::string &sub_member : dialogue_data::simple_string_conds ) { if( jo.has_string( sub_member ) ) { diff --git a/src/condition.h b/src/condition.h index a31bc7986e38a..17e2487c0f4ba 100644 --- a/src/condition.h +++ b/src/condition.h @@ -43,7 +43,7 @@ const std::unordered_set complex_conds = { { "npc_aim_rule", "npc_engagement_rule", "npc_rule", "npc_override", "npc_cbm_reserve_rule", "npc_cbm_recharge_rule", "days_since_cataclysm", "is_season", "mission_goal", "u_has_var", "npc_has_var", - "u_has_skill", "npc_has_skill" + "u_has_skill", "npc_has_skill", "u_know_recipe" } }; } // namespace dialogue_data @@ -131,6 +131,7 @@ struct conditional_t { void set_has_reason(); void set_is_gender( bool is_male, bool is_npc = false ); void set_has_skill( JsonObject &jo, const std::string &member, bool is_npc = false ); + void set_u_know_recipe( JsonObject &jo, const std::string &member ); bool operator()( const T &d ) const { if( !condition ) { diff --git a/src/dialogue.h b/src/dialogue.h index bee5ed09bd8b4..c3d10cf4860be 100644 --- a/src/dialogue.h +++ b/src/dialogue.h @@ -123,6 +123,7 @@ struct talk_effect_fun_t { void set_add_mission( std::string mission_id ); void set_u_buy_monster( const std::string &monster_type_id, int cost, int count, bool pacified, const translation &name ); + void set_u_learn_recipe( const std::string &learned_recipe_id ); void operator()( const dialogue &d ) const { if( !function ) { diff --git a/src/npctalk.cpp b/src/npctalk.cpp index d770539843cb8..e1ffefe48a7fc 100644 --- a/src/npctalk.cpp +++ b/src/npctalk.cpp @@ -44,6 +44,7 @@ #include "npctrade.h" #include "output.h" #include "overmapbuffer.h" +#include "recipe.h" #include "rng.h" #include "skill.h" #include "sounds.h" @@ -2147,6 +2148,15 @@ void talk_effect_fun_t::set_u_buy_monster( const std::string &monster_type_id, i }; } +void talk_effect_fun_t::set_u_learn_recipe( const std::string &learned_recipe_id ) +{ + function = [learned_recipe_id]( const dialogue & d ) { + const recipe &r = recipe_id( learned_recipe_id ).obj(); + d.alpha->learn_recipe( &r ); + popup( _( "You learn how to craft %s." ), r.result_name() ); + }; +} + void talk_effect_t::set_effect_consequence( const talk_effect_fun_t &fun, dialogue_consequence con ) { effects.push_back( fun ); @@ -2351,6 +2361,9 @@ void talk_effect_t::parse_sub_effect( JsonObject jo ) translation name; jo.read( "name", name ); subeffect_fun.set_u_buy_monster( monster_type_id, cost, count, pacified, name ); + } else if( jo.has_string( "u_learn_recipe" ) ) { + const std::string recipe_id = jo.get_string( "u_learn_recipe" ); + subeffect_fun.set_u_learn_recipe( recipe_id ); } else { jo.throw_error( "invalid sub effect syntax :" + jo.str() ); }