diff --git a/data/json/npcs/common_chat/TALK_COMMON_ALLY.json b/data/json/npcs/common_chat/TALK_COMMON_ALLY.json index c2f99bbef60d8..1b6f1bfd0d8df 100644 --- a/data/json/npcs/common_chat/TALK_COMMON_ALLY.json +++ b/data/json/npcs/common_chat/TALK_COMMON_ALLY.json @@ -44,8 +44,7 @@ "no": { "has_reason": { "use_reason": true }, "no": "What is it, friend?" } }, "responses": [ - { "text": "I want to give you some commands for combat.", "topic": "TALK_COMBAT_COMMANDS" }, - { "text": "I want to set some miscellaneous rules.", "topic": "TALK_MISC_RULES" }, + { "text": "[rules] Let's talk about how we'll work together.", "topic": "TALK_DONE", "effect": "npc_rules_menu" }, { "text": "I'd like to know a bit more about your abilities.", "topic": "TALK_FRIEND", @@ -234,421 +233,6 @@ { "text": "Let's go.", "topic": "TALK_DONE" } ] }, - { - "id": "TALK_COMBAT_COMMANDS", - "type": "talk_topic", - "dynamic_line": { - "concatenate": [ - { - "npc_engagement_rule": "ENGAGE_NONE", - "no": { - "npc_engagement_rule": "ENGAGE_CLOSE", - "no": { - "npc_engagement_rule": "ENGAGE_WEAK", - "no": { - "npc_engagement_rule": "ENGAGE_HIT", - "no": { - "npc_engagement_rule": "ENGAGE_FREE_FIRE", - "no": { - "npc_engagement_rule": "ENGAGE_NO_MOVE", - "no": "*will engage all enemies.", - "yes": "*will engage enemies close enough to attack without moving." - }, - "yes": "*will engage distant enemies without moving." - }, - "yes": "*will engage enemies you attack." - }, - "yes": "*will engage weak enemies." - }, - "yes": "*will engage nearby enemies." - }, - "yes": "*will not engage enemies." - }, - { - "concatenate": [ - { "npc_override": "follow_close", "yes": " OVERRIDE: ", "no": " " }, - { - "npc_rule": "follow_close", - "yes": "", - "no": "" - } - ] - }, - { - "concatenate": [ - { "npc_override": "follow_distance_2", "yes": " OVERRIDE: ", "no": " " }, - { - "npc_rule": "follow_distance_2", - "yes": "", - "no": "" - } - ] - }, - { - "concatenate": [ - { "npc_override": "use_guns", "yes": " OVERRIDE: ", "no": " " }, - { - "npc_rule": "use_guns", - "yes": { - "concatenate": [ - { "npc_override": "use_silent", "yes": " OVERRIDE: ", "no": "" }, - { - "npc_rule": "use_silent", - "yes": "", - "no": "" - } - ] - }, - "no": "" - } - ] - }, - { - "concatenate": [ - { "npc_override": "use_grenades", "yes": " OVERRIDE: ", "no": " " }, - { - "npc_rule": "use_grenades", - "yes": "", - "no": "" - } - ] - }, - { - "concatenate": [ - { "npc_override": "avoid_friendly_fire", "yes": " OVERRIDE: ", "no": " " }, - { - "npc_rule": "avoid_friendly_fire", - "yes": "", - "no": "" - } - ] - }, - { - "concatenate": [ - { "npc_override": "hold_the_line", "yes": " OVERRIDE: ", "no": " " }, - { - "npc_rule": "hold_the_line", - "yes": "", - "no": "" - } - ] - }, - " What should do?" - ] - }, - "responses": [ - { "text": "Change your engagement rules…", "topic": "TALK_COMBAT_ENGAGEMENT" }, - { "text": "Change your aiming rules…", "topic": "TALK_AIM_RULES" }, - { - "text": "Change your bionic power reserve rules…", - "topic": "TALK_CBM_RESERVE_RULES", - "condition": { "npc_has_bionics": "ANY" } - }, - { - "text": "Change your bionic power recharge rules…", - "topic": "TALK_CBM_RECHARGE_RULES", - "condition": { "npc_has_bionics": "ANY" } - }, - { - "truefalsetext": { - "condition": { "npc_rule": "follow_close" }, - "true": "Move freely as you need to.", - "false": "Stick close to me, no matter what." - }, - "topic": "TALK_COMBAT_COMMANDS", - "effect": { "toggle_npc_rule": "follow_close" } - }, - { - "truefalsetext": { - "condition": { "npc_rule": "follow_distance_2" }, - "false": "", - "true": "" - }, - "topic": "TALK_COMBAT_COMMANDS", - "effect": { "toggle_npc_rule": "follow_distance_2" } - }, - { - "truefalsetext": { - "condition": { "npc_rule": "use_guns" }, - "true": "Don't use ranged weapons anymore.", - "false": "You can use ranged weapons." - }, - "topic": "TALK_COMBAT_COMMANDS", - "effect": { "toggle_npc_rule": "use_guns" } - }, - { - "truefalsetext": { "condition": { "npc_rule": "use_silent" }, "true": "Don't worry about noise.", "false": "Use only silent weapons." }, - "topic": "TALK_COMBAT_COMMANDS", - "effect": { "toggle_npc_rule": "use_silent" } - }, - { - "truefalsetext": { "condition": { "npc_rule": "use_grenades" }, "true": "Don't use grenades anymore.", "false": "You can use grenades." }, - "topic": "TALK_COMBAT_COMMANDS", - "effect": { "toggle_npc_rule": "use_grenades" } - }, - { - "truefalsetext": { - "condition": { "npc_rule": "avoid_friendly_fire" }, - "true": "Don't worry about shooting an ally.", - "false": "Don't shoot unless you're sure you won't hit an ally." - }, - "topic": "TALK_COMBAT_COMMANDS", - "effect": { "toggle_npc_rule": "avoid_friendly_fire" } - }, - { - "truefalsetext": { - "condition": { "npc_rule": "hold_the_line" }, - "true": "Move wherever you need to go to fight an enemy.", - "false": "Hold the line: don't move onto obstacles adjacent to me." - }, - "topic": "TALK_COMBAT_COMMANDS", - "effect": { "toggle_npc_rule": "hold_the_line" } - }, - { "text": "Never mind.", "topic": "TALK_NONE" } - ] - }, - { - "id": "TALK_COMBAT_ENGAGEMENT", - "type": "talk_topic", - "dynamic_line": { - "concatenate": [ - { - "npc_engagement_rule": "ENGAGE_NONE", - "no": { - "npc_engagement_rule": "ENGAGE_CLOSE", - "no": { - "npc_engagement_rule": "ENGAGE_WEAK", - "no": { - "npc_engagement_rule": "ENGAGE_HIT", - "no": { - "npc_engagement_rule": "ENGAGE_FREE_FIRE", - "no": { - "npc_engagement_rule": "ENGAGE_NO_MOVE", - "no": "*will engage all enemies.", - "yes": "*will engage enemies close enough to attack without moving." - }, - "yes": "*will engage distant enemies without moving." - }, - "yes": "*will engage enemies you attack." - }, - "yes": "*will engage weak enemies." - }, - "yes": "*will engage nearby enemies." - }, - "yes": "*will not engage enemies." - }, - " What should do?" - ] - }, - "responses": [ - { - "text": "Don't fight unless your life depends on it.", - "topic": "TALK_NONE", - "condition": { "not": { "npc_engagement_rule": "ENGAGE_NONE" } }, - "effect": { "set_npc_engagement_rule": "ENGAGE_NONE" } - }, - { - "text": "Attack enemies that get too close.", - "topic": "TALK_NONE", - "condition": { "not": { "npc_engagement_rule": "ENGAGE_CLOSE" } }, - "effect": { "set_npc_engagement_rule": "ENGAGE_CLOSE" } - }, - { - "text": "Attack enemies that you can kill easily.", - "topic": "TALK_NONE", - "condition": { "not": { "npc_engagement_rule": "ENGAGE_WEAK" } }, - "effect": { "set_npc_engagement_rule": "ENGAGE_WEAK" } - }, - { - "text": "Attack only enemies that I attack first.", - "topic": "TALK_NONE", - "condition": { "not": { "npc_engagement_rule": "ENGAGE_HIT" } }, - "effect": { "set_npc_engagement_rule": "ENGAGE_HIT" } - }, - { - "text": "Attack only enemies you can reach without moving.", - "topic": "TALK_NONE", - "condition": { "not": { "npc_engagement_rule": "ENGAGE_NO_MOVE" } }, - "effect": { "set_npc_engagement_rule": "ENGAGE_NO_MOVE" } - }, - { - "text": "Attack any enemy within range of your ranged weapon, but don't move.", - "topic": "TALK_NONE", - "condition": { "not": { "npc_engagement_rule": "ENGAGE_FREE_FIRE" } }, - "effect": { "set_npc_engagement_rule": "ENGAGE_FREE_FIRE" } - }, - { - "text": "Attack anything you want.", - "topic": "TALK_NONE", - "condition": { "not": { "npc_engagement_rule": "ENGAGE_ALL" } }, - "effect": { "set_npc_engagement_rule": "ENGAGE_ALL" } - }, - { "text": "Never mind.", "topic": "TALK_NONE" } - ] - }, - { - "id": "TALK_CBM_RESERVE_RULES", - "type": "talk_topic", - "dynamic_line": { - "concatenate": [ - { - "npc_cbm_reserve_rule": "CBM_RESERVE_ALL", - "no": { - "npc_cbm_reserve_rule": "CBM_RESERVE_MOST", - "no": { - "npc_cbm_reserve_rule": "CBM_RESERVE_SOME", - "no": { - "npc_cbm_reserve_rule": "CBM_RESERVE_LITTLE", - "no": "*will not reserve any power for defense or utility CBMs.", - "yes": "*will reserve 25% of CBM power for defense or utility CBMs." - }, - "yes": "*will reserve 50% of CBM power for defense or utility CBMs." - }, - "yes": "*will reserve 75% of CBM power for defense or utility CBMs." - }, - "yes": "*will reserve 100% of CBM power for defense or utility CBMs." - }, - " How should much power should reserve for defense or utility CBMs?" - ] - }, - "responses": [ - { - "text": "Don't use any CBM weapons. Save all power for defense or utility CBMs.", - "topic": "TALK_NONE", - "condition": { "not": { "npc_cbm_reserve_rule": "CBM_RESERVE_ALL" } }, - "effect": { "set_npc_cbm_reserve_rule": "CBM_RESERVE_ALL" } - }, - { - "text": "Use CBM weapons, but save 75% of total power for defense or utility CBMs.", - "topic": "TALK_NONE", - "condition": { "not": { "npc_cbm_reserve_rule": "CBM_RESERVE_MOST" } }, - "effect": { "set_npc_cbm_reserve_rule": "CBM_RESERVE_MOST" } - }, - { - "text": "Use CBM weapons, but save 50% of total power for defense or utility CBMs.", - "topic": "TALK_NONE", - "condition": { "not": { "npc_cbm_reserve_rule": "CBM_RESERVE_SOME" } }, - "effect": { "set_npc_cbm_reserve_rule": "CBM_RESERVE_SOME" } - }, - { - "text": "Use CBM weapons, but save 25% of total power for defense or utility CBMs.", - "topic": "TALK_NONE", - "condition": { "not": { "npc_cbm_reserve_rule": "CBM_RESERVE_LITTLE" } }, - "effect": { "set_npc_cbm_reserve_rule": "CBM_RESERVE_LITTLE" } - }, - { - "text": "Go wild with CBM weapons. Don't reserve any power for defense or utility CBMs.", - "topic": "TALK_NONE", - "condition": { "not": { "npc_cbm_reserve_rule": "CBM_RESERVE_NONE" } }, - "effect": { "set_npc_cbm_reserve_rule": "CBM_RESERVE_NONE" } - }, - { "text": "Never mind.", "topic": "TALK_NONE" } - ] - }, - { - "id": "TALK_CBM_RECHARGE_RULES", - "type": "talk_topic", - "dynamic_line": { - "concatenate": [ - { - "npc_cbm_recharge_rule": "CBM_RECHARGE_ALL", - "no": { - "npc_cbm_recharge_rule": "CBM_RECHARGE_MOST", - "no": { - "npc_cbm_recharge_rule": "CBM_RECHARGE_SOME", - "no": { - "npc_cbm_recharge_rule": "CBM_RECHARGE_LITTLE", - "no": "*will recharge power CBMs until has 10% of total power.", - "yes": "*will recharge power CBMs until has 25% of total power." - }, - "yes": "*will recharge power CBMs until has 50% of total power." - }, - "yes": "*will recharge power CBMs until has 75% of total power." - }, - "yes": "*will recharge power CBMs until has 90% of total power." - }, - " When should consume supplies to recharge power CBMs?" - ] - }, - "responses": [ - { - "text": "We have plenty of supplies. Recharge until you have 90% of total power.", - "topic": "TALK_NONE", - "condition": { "not": { "npc_cbm_recharge_rule": "CBM_RECHARGE_ALL" } }, - "effect": { "set_npc_cbm_recharge_rule": "CBM_RECHARGE_ALL" } - }, - { - "text": "We have supplies. Recharge until you have 75% of total power.", - "topic": "TALK_NONE", - "condition": { "not": { "npc_cbm_recharge_rule": "CBM_RECHARGE_MOST" } }, - "effect": { "set_npc_cbm_recharge_rule": "CBM_RECHARGE_MOST" } - }, - { - "text": "We have some supplies. Recharge until you have 50% of total power.", - "topic": "TALK_NONE", - "condition": { "not": { "npc_cbm_recharge_rule": "CBM_RECHARGE_SOME" } }, - "effect": { "set_npc_cbm_recharge_rule": "CBM_RECHARGE_SOME" } - }, - { - "text": "We're running low on supplies. Recharge until you have 25% of total power.", - "topic": "TALK_NONE", - "condition": { "not": { "npc_cbm_recharge_rule": "CBM_RECHARGE_LITTLE" } }, - "effect": { "set_npc_cbm_recharge_rule": "CBM_RECHARGE_LITTLE" } - }, - { - "text": "We're almost out of supplies. Recharge until you have 10% of total power.", - "topic": "TALK_NONE", - "condition": { "not": { "npc_cbm_recharge_rule": "CBM_RECHARGE_NONE" } }, - "effect": { "set_npc_cbm_recharge_rule": "CBM_RECHARGE_NONE" } - }, - { "text": "Never mind.", "topic": "TALK_NONE" } - ] - }, - { - "id": "TALK_AIM_RULES", - "type": "talk_topic", - "dynamic_line": { - "concatenate": [ - { - "npc_aim_rule": "AIM_WHEN_CONVENIENT", - "no": { - "npc_aim_rule": "AIM_STRICTLY_PRECISE", - "no": { "npc_aim_rule": "AIM_PRECISE", "no": "*will not bother to aim at all.", "yes": "*will take time and aim carefully." }, - "yes": "*will only shoot after taking a long time to aim." - }, - "yes": "*will aim when it's convenient." - }, - " How should aim?" - ] - }, - "responses": [ - { - "text": "Aim when it's convenient.", - "topic": "TALK_NONE", - "condition": { "not": { "npc_aim_rule": "AIM_WHEN_CONVENIENT" } }, - "effect": { "set_npc_aim_rule": "AIM_WHEN_CONVENIENT" } - }, - { - "text": "Go wild, you don't need to aim much.", - "topic": "TALK_NONE", - "condition": { "not": { "npc_aim_rule": "AIM_SPRAY" } }, - "effect": { "set_npc_aim_rule": "AIM_SPRAY" } - }, - { - "text": "Take your time, aim carefully.", - "topic": "TALK_NONE", - "condition": { "not": { "npc_aim_rule": "AIM_PRECISE" } }, - "effect": { "set_npc_aim_rule": "AIM_PRECISE" } - }, - { - "text": "Don't shoot if you can't aim really well.", - "topic": "TALK_NONE", - "condition": { "not": { "npc_aim_rule": "AIM_STRICTLY_PRECISE" } }, - "effect": { "set_npc_aim_rule": "AIM_STRICTLY_PRECISE" } - }, - { "text": "Never mind.", "topic": "TALK_NONE" } - ] - }, { "id": "TALK_TRAIN_PERSUADE", "type": "talk_topic", @@ -679,201 +263,6 @@ { "text": "Forget it, let's get going.", "topic": "TALK_DONE" } ] }, - { - "id": "TALK_MISC_RULES", - "type": "talk_topic", - "dynamic_line": { - "concatenate": [ - { - "concatenate": [ - { "npc_override": "allow_pick_up", "yes": "OVERRIDE: " }, - { - "npc_rule": "allow_pick_up", - "yes": { "has_pickup_list": "", "no": "" }, - "no": "" - } - ] - }, - { - "concatenate": [ - { "npc_override": "allow_bash", "yes": " OVERRIDE: ", "no": " " }, - { - "npc_rule": "allow_bash", - "yes": "", - "no": "" - } - ] - }, - { - "concatenate": [ - { "npc_override": "allow_sleep", "yes": " OVERRIDE: ", "no": " " }, - { - "npc_rule": "allow_sleep", - "yes": "", - "no": "" - } - ] - }, - { - "concatenate": [ - { "npc_override": "allow_complain", "yes": " OVERRIDE: ", "no": " " }, - { - "npc_rule": "allow_complain", - "yes": "", - "no": "" - } - ] - }, - { - "concatenate": [ - { "npc_override": "allow_pulp", "yes": " OVERRIDE: ", "no": " " }, - { - "npc_rule": "allow_pulp", - "yes": "", - "no": "" - } - ] - }, - { - "concatenate": [ - { "npc_override": "close_doors", "yes": " OVERRIDE: ", "no": " " }, - { - "npc_rule": "close_doors", - "yes": "", - "no": "" - } - ] - }, - { - "concatenate": [ - { "npc_override": "lock_doors", "yes": " OVERRIDE: ", "no": " " }, - { - "npc_rule": "lock_doors", - "yes": "", - "no": "" - } - ] - }, - { - "concatenate": [ - { "npc_override": "ignore_noise", "yes": " OVERRIDE: ", "no": " " }, - { - "npc_rule": "ignore_noise", - "yes": "", - "no": "" - } - ] - }, - { - "concatenate": [ - { "npc_override": "avoid_doors", "yes": " OVERRIDE: ", "no": " " }, - { - "npc_rule": "avoid_doors", - "yes": "", - "no": "" - } - ] - }, - { - "concatenate": [ - { "npc_override": "avoid_locks", "yes": " OVERRIDE: ", "no": " " }, - { - "npc_rule": "avoid_locks", - "yes": "", - "no": "" - } - ] - }, - { - "concatenate": [ - { "npc_override": "forbid_engage", "yes": " OVERRIDE: ", "no": " " }, - { - "npc_rule": "forbid_engage", - "yes": "", - "no": "" - } - ] - } - ] - }, - "responses": [ - { - "text": "Follow same rules as this follower.", - "topic": "TALK_MISC_RULES", - "condition": { "npc_allies": 2 }, - "effect": "copy_npc_rules" - }, - { - "truefalsetext": { "condition": { "npc_rule": "allow_pick_up" }, "true": "Don't pick up items.", "false": "You can pick up items now." }, - "topic": "TALK_MISC_RULES", - "effect": { "toggle_npc_rule": "allow_pick_up" } - }, - { - "truefalsetext": { "condition": { "npc_rule": "allow_bash" }, "true": "Don't bash obstacles.", "false": "You can bash obstacles." }, - "topic": "TALK_MISC_RULES", - "effect": { "toggle_npc_rule": "allow_bash" } - }, - { - "truefalsetext": { "condition": { "npc_rule": "allow_sleep" }, "true": "Stay awake.", "false": "Sleep when you feel tired." }, - "topic": "TALK_MISC_RULES", - "effect": { "toggle_npc_rule": "allow_sleep" } - }, - { - "truefalsetext": { "condition": { "npc_rule": "allow_complain" }, "true": "Stay quiet.", "false": "Tell me when you need something." }, - "topic": "TALK_MISC_RULES", - "effect": { "toggle_npc_rule": "allow_complain" } - }, - { - "truefalsetext": { "condition": { "npc_rule": "allow_pulp" }, "true": "Leave corpses alone.", "false": "Smash zombie corpses." }, - "topic": "TALK_MISC_RULES", - "effect": { "toggle_npc_rule": "allow_pulp" } - }, - { - "truefalsetext": { "condition": { "npc_rule": "close_doors" }, "true": "Leave doors open.", "false": "Close the doors." }, - "topic": "TALK_MISC_RULES", - "effect": { "toggle_npc_rule": "close_doors" } - }, - { - "truefalsetext": { - "condition": { "npc_rule": "lock_doors" }, - "true": "Leave doors unlocked.", - "false": "Lock any doors that you close." - }, - "topic": "TALK_MISC_RULES", - "effect": { "toggle_npc_rule": "lock_doors" } - }, - { - "truefalsetext": { - "condition": { "npc_rule": "avoid_doors" }, - "true": "Open doors to get where you're going.", - "false": "Don't walk through closed doors." - }, - "topic": "TALK_MISC_RULES", - "effect": { "toggle_npc_rule": "avoid_doors" } - }, - { - "truefalsetext": { - "condition": { "npc_rule": "avoid_locks" }, - "true": "Unlock doors to get where you're going.", - "false": "Don't unlock locked doors." - }, - "topic": "TALK_MISC_RULES", - "effect": { "toggle_npc_rule": "avoid_locks" } - }, - { - "truefalsetext": { - "condition": { "npc_rule": "ignore_noise" }, - "true": "If you hear a noise but can't see the source, move to investigate it.", - "false": "Don't investigate noises that you can't see." - }, - "topic": "TALK_MISC_RULES", - "effect": { "toggle_npc_rule": "ignore_noise" } - }, - { "text": "Set up pickup rules.", "topic": "TALK_MISC_RULES", "effect": "set_npc_pickup" }, - { "text": "Clear all overrides.", "topic": "TALK_MISC_RULES", "effect": "clear_overrides" }, - { "text": "Never mind.", "topic": "TALK_NONE" } - ] - }, { "id": "TALK_LEAVE", "type": "talk_topic", diff --git a/data/json/npcs/talk_tags.json b/data/json/npcs/talk_tags.json index 8ca24d48fc183..54800efe728a4 100644 --- a/data/json/npcs/talk_tags.json +++ b/data/json/npcs/talk_tags.json @@ -2107,6 +2107,114 @@ "it's a million miles away!" ] }, + { + "type": "snippet", + "category": "", + "text": " will recharge power CBMs until has 90% of total power." + }, + { + "type": "snippet", + "category": "", + "text": " will recharge power CBMs until has 75% of total power." + }, + { + "type": "snippet", + "category": "", + "text": " will recharge power CBMs until has 50% of total power." + }, + { + "type": "snippet", + "category": "", + "text": " will recharge power CBMs until has 25% of total power." + }, + { + "type": "snippet", + "category": "", + "text": " will recharge power CBMs until has 10% of total power." + }, + { + "type": "snippet", + "category": "", + "text": " will reserve 100% of CBM power for defense or utility CBMs." + }, + { + "type": "snippet", + "category": "", + "text": " will reserve 75% of CBM power for defense or utility CBMs." + }, + { + "type": "snippet", + "category": "", + "text": " will reserve 50% of CBM power for defense or utility CBMs." + }, + { + "type": "snippet", + "category": "", + "text": " will reserve 25% of CBM power for defense or utility CBMs." + }, + { + "type": "snippet", + "category": "", + "text": " will not reserve any power for defense or utility CBMs." + }, + { + "type": "snippet", + "category": "", + "text": " will aim when it's convenient." + }, + { + "type": "snippet", + "category": "", + "text": " will not bother to aim at all." + }, + { + "type": "snippet", + "category": "", + "text": " will take time and aim carefully." + }, + { + "type": "snippet", + "category": "", + "text": " will only shoot after taking a long time to aim." + }, + { + "type": "snippet", + "category": "", + "text": " won't fight unless their life depends on it." + }, + { + "type": "snippet", + "category": "", + "text": " will attack enemies that get too close." + }, + { + "type": "snippet", + "category": "", + "text": " will attack enemies that they can kill easily." + }, + { + "type": "snippet", + "category": "", + "text": " will attack only enemies that you attack first." + }, + { + "type": "snippet", + "category": "", + "text": [ + { "text": " will attack anything they want.", "weight": 999 }, + { "text": " has permission to go ABSOLUTELY AXE CRAZY!", "weight": 1 } + ] + }, + { + "type": "snippet", + "category": "", + "text": " will attack any enemy within range of their ranged weapon, but won't move." + }, + { + "type": "snippet", + "category": "", + "text": " will attack only enemies they can reach without moving." + }, { "type": "snippet", "category": "", diff --git a/src/mission_ui.cpp b/src/mission_ui.cpp index ae7402d497496..68ad4fb11238f 100644 --- a/src/mission_ui.cpp +++ b/src/mission_ui.cpp @@ -104,7 +104,7 @@ void mission_ui::draw_mission_ui() p_impl.last_action = ctxt.handle_input(); - if( p_impl.last_action == "QUIT" ) { + if( p_impl.last_action == "QUIT" || !p_impl.get_is_open() ) { break; } } diff --git a/src/npc.cpp b/src/npc.cpp index 1cc75e0d45673..b4c5ad1cb2fa4 100644 --- a/src/npc.cpp +++ b/src/npc.cpp @@ -3766,6 +3766,11 @@ void npc_follower_rules::clear_overrides() override_enable = ally_rule::DEFAULT; } +void npc_follower_rules::clear_flags() +{ + flags = ally_rule::DEFAULT; +} + int npc::get_thirst() const { return Character::get_thirst() - units::to_milliliter( stomach.get_water() ) / 5; diff --git a/src/npc.h b/src/npc.h index b8a35e9961128..2586a091f1db9 100644 --- a/src/npc.h +++ b/src/npc.h @@ -532,6 +532,7 @@ struct npc_follower_rules { void clear_override( ally_rule clearit ); void set_danger_overrides(); + void clear_flags(); void clear_overrides(); }; diff --git a/src/npctalk.cpp b/src/npctalk.cpp index 982a70338d26c..3d1bb6d07d919 100644 --- a/src/npctalk.cpp +++ b/src/npctalk.cpp @@ -60,6 +60,7 @@ #include "mutation.h" #include "npc.h" #include "npctalk.h" +#include "npctalk_rules.h" #include "npctrade.h" #include "output.h" #include "overmapbuffer.h" @@ -3480,6 +3481,14 @@ talk_effect_fun_t::func f_add_debt( const JsonObject &jo, std::string_view membe }; } +talk_effect_fun_t::func f_npc_rules_menu() +{ + return []( dialogue const & d ) { + follower_rules_ui new_ui; + new_ui.draw_follower_rules_ui( d.actor( true )->get_npc() ); + }; +} + talk_effect_fun_t::func f_toggle_npc_rule( const JsonObject &jo, std::string_view member ) { str_or_var rule = get_str_or_var( jo.get_member( member ), member, true ); @@ -6674,6 +6683,10 @@ void talk_effect_t::parse_string_effect( const std::string &effect_id, const Jso set_effect( talk_effect_fun_t( talk_effect_fun::f_wants_to_talk( true ) ) ); return; } + if( effect_id == "npc_rules_menu" ) { + set_effect( talk_effect_fun_t( talk_effect_fun::f_npc_rules_menu() ) ); + return; + } jo.throw_error_at( effect_id, "unknown effect string" ); } diff --git a/src/npctalk_rules.cpp b/src/npctalk_rules.cpp new file mode 100644 index 0000000000000..01013b37d9321 --- /dev/null +++ b/src/npctalk_rules.cpp @@ -0,0 +1,467 @@ +#include "npctalk_rules.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cata_imgui.h" +#include "dialogue.h" +#include "game.h" +#include "npctalk.h" +#include "ui_manager.h" +#include "imgui/imgui.h" +#include "imgui/imgui_internal.h" + +static std::map recharge_map = { + {cbm_recharge_rule::CBM_RECHARGE_ALL, "" }, + {cbm_recharge_rule::CBM_RECHARGE_MOST, "" }, + {cbm_recharge_rule::CBM_RECHARGE_SOME, "" }, + {cbm_recharge_rule::CBM_RECHARGE_LITTLE, "" }, + {cbm_recharge_rule::CBM_RECHARGE_NONE, "" }, +}; + +static std::map reserve_map = { + {cbm_reserve_rule::CBM_RESERVE_ALL, "" }, + {cbm_reserve_rule::CBM_RESERVE_MOST, "" }, + {cbm_reserve_rule::CBM_RESERVE_SOME, "" }, + {cbm_reserve_rule::CBM_RESERVE_LITTLE, "" }, + {cbm_reserve_rule::CBM_RESERVE_NONE, "" }, +}; + +static std::map engagement_rules = { + {combat_engagement::NONE, "" }, + {combat_engagement::CLOSE, "" }, + {combat_engagement::WEAK, "" }, + {combat_engagement::HIT, "" }, + {combat_engagement::ALL, "" }, + {combat_engagement::FREE_FIRE, "" }, + {combat_engagement::NO_MOVE, "" }, +}; + +static std::map aim_rule_map = { + {aim_rule::WHEN_CONVENIENT, "" }, + {aim_rule::SPRAY, "" }, + {aim_rule::PRECISE, "" }, + {aim_rule::STRICTLY_PRECISE, "" }, +}; + +void follower_rules_ui::draw_follower_rules_ui( npc *guy ) +{ + input_context ctxt; + follower_rules_ui_impl p_impl; + p_impl.set_npc_pointer_to( guy ); + p_impl.input_ptr = &ctxt; + + ctxt.register_navigate_ui_list(); + ctxt.register_leftright(); + ctxt.register_action( "MOUSE_MOVE" ); + ctxt.register_action( "CONFIRM", to_translation( "Set, toggle, or reset selected rule" ) ); + ctxt.register_action( "HELP_KEYBINDINGS" ); + ctxt.register_action( "QUIT" ); + ctxt.register_action( "ANY_INPUT" ); + // This is still bizarrely necessary for imgui + ctxt.set_timeout( 10 ); + + while( true ) { + ui_manager::redraw_invalidated(); + + + p_impl.last_action = ctxt.handle_input(); + + if( p_impl.last_action == "QUIT" || !p_impl.get_is_open() ) { + break; + } + } +} + +template +void follower_rules_ui_impl::multi_rule_header( const std::string &id, T &rule, + const std::map &rule_map, bool should_advance ) +{ + if( ImGui::InvisibleButton( id.c_str(), ImVec2( 1.0, 1.0 ) ) || should_advance ) { + if( rule_map.upper_bound( rule ) == rule_map.end() ) { + // Then we have the *last* entry the map, so wrap to the first element + rule = rule_map.begin()->first; + } else { + // Assign the next possible value contained in the map. + rule = rule_map.upper_bound( rule )->first; + } + int offset = distance( rule_map.begin(), rule_map.find( rule ) ); + ImGui::SetKeyboardFocusHere( offset ); + } +} + +bool follower_rules_ui_impl::setup_button( int &button_num, std::string &label, bool should_color ) +{ + bool was_clicked = false; + ImGui::PushID( button_num ); + button_num++; + if( should_color ) { + ImGui::PushStyleColor( ImGuiCol_Button, c_green ); + } + if( ImGui::Button( label.c_str() ) ) { + was_clicked = true; + } + ImGui::PopID(); + if( should_color ) { + ImGui::PopStyleColor(); + } + return was_clicked; +} + +bool follower_rules_ui_impl::setup_table_button( int &button_num, std::string &label, + bool should_color ) +{ + ImGui::TableNextColumn(); + return setup_button( button_num, label, should_color ); +} + +void follower_rules_ui_impl::set_npc_pointer_to( npc *new_guy ) +{ + guy = new_guy; +} + +std::string follower_rules_ui_impl::get_parsed( std::string initial_string ) +{ + std::string string_reference = std::move( initial_string ); + parse_tags( string_reference, get_player_character(), *guy ); + return string_reference; + +} + +void follower_rules_ui_impl::print_hotkey( input_event &hotkey ) +{ + // Padding spaces intentional, so it's obvious that the fake "Hotkey:" header refers to these. + // TODO: Just reimplement everything as a table...? Would avoid this sort of thing. + // But surely not *everything* needs to be a table... + draw_colored_text( string_format( " %s ", static_cast( hotkey.sequence.front() ) ), + c_green ); + ImGui::SameLine(); +} + +void follower_rules_ui_impl::rules_transfer_popup( bool &exporting_rules, bool &still_in_popup ) +{ + std::string toggle_label; + if( exporting_rules ) { + draw_colored_text( string_format( _( "Exporting rules from %s" ), + guy->disp_name() ), c_blue ); + //~Label for a button used to copy NPC follower rules to another NPC + toggle_label = _( "Export" ); + } else { + draw_colored_text( string_format( _( "Importing rules to %s" ), + guy->disp_name() ), c_yellow ); + //~Label for a button used to copy NPC follower rules from another NPC + toggle_label = _( "Import" ); + } + // Not ideal but leaves plenty of room for even verbose languages. Really anywhere top-right-ish is fine + ImGui::SameLine( ImGui::GetWindowWidth() * 0.7 ); + if( ImGui::Button( _( "Return to rules settings" ) ) ) { + still_in_popup = false; + return; + } + if( g->get_follower_list().size() < 2 ) { + draw_colored_text( string_format( _( "Functions unavailable. %s is your only follower." ), + guy->disp_name() ), c_red ); + return; + } + if( ImGui::Button( _( "Toggle export/import" ) ) ) { + exporting_rules = !exporting_rules; + } + ImGui::NewLine(); + draw_colored_text( string_format( + _( "Individual settings are colored if they already match to %s." ), + guy->disp_name() ) ); + if( ImGui::BeginTable( "##SETTINGS_SWAP_TABLE", 8, ImGuiTableFlags_None, + ImVec2( window_width, window_height ) ) ) { + ImGui::TableSetupColumn( _( "Name" ), ImGuiTableColumnFlags_WidthStretch, + static_cast( window_width / 8.0f ) ); + ImGui::TableSetupColumn( _( "Behaviors" ), ImGuiTableColumnFlags_WidthStretch, + static_cast( window_width / 8.0f ) ); + ImGui::TableSetupColumn( _( "Aiming" ), ImGuiTableColumnFlags_WidthStretch, + static_cast( window_width / 8.0f ) ); + ImGui::TableSetupColumn( _( "Engagement" ), ImGuiTableColumnFlags_WidthStretch, + static_cast( window_width / 8.0f ) ); + ImGui::TableSetupColumn( _( "CBM recharge" ), ImGuiTableColumnFlags_WidthStretch, + static_cast( window_width / 8.0f ) ); + ImGui::TableSetupColumn( _( "CBM reserve" ), ImGuiTableColumnFlags_WidthStretch, + static_cast( window_width / 8.0f ) ); + ImGui::TableSetupColumn( _( "Pickup list" ), ImGuiTableColumnFlags_WidthStretch, + static_cast( window_width / 8.0f ) ); + ImGui::TableSetupColumn( _( "ALL" ), ImGuiTableColumnFlags_WidthStretch, + static_cast( window_width / 8.0f ) ); + ImGui::TableHeadersRow(); + int button_number = 0; + for( npc &role_model : g->all_npcs() ) { + if( &role_model == guy ) { + continue; // No copying settings to/from yourself + } + if( role_model.is_player_ally() ) { + bool do_flags_overrides = false; + bool do_aim = false; + bool do_engagement = false; + bool do_cbm_recharge = false; + bool do_cbm_reserve = false; + bool do_pickup = false; + bool do_all = false; + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + draw_colored_text( role_model.disp_name() ); + /*Flags and overrides*/ + do_flags_overrides = setup_table_button( button_number, toggle_label, + role_model.rules.flags == guy->rules.flags ); + /*Aim rule*/ + do_aim = setup_table_button( button_number, toggle_label, + role_model.rules.aim == guy->rules.aim ); + /*Engagement rule*/ + do_engagement = setup_table_button( button_number, toggle_label, + role_model.rules.engagement == guy->rules.engagement ); + /*CBM recharge rule*/ + do_cbm_recharge = setup_table_button( button_number, toggle_label, + role_model.rules.cbm_recharge == guy->rules.cbm_recharge ); + /*CBM reserve rule*/ + do_cbm_reserve = setup_table_button( button_number, toggle_label, + role_model.rules.cbm_reserve == guy->rules.cbm_reserve ); + /*Pickup whitelist rule*/ + + // This is not correct if both have rules and both have *different* rules, but comparison is expensive + // if( role_model.rules.pickup_whitelist->empty() == guy->rules.pickup_whitelist->empty() ) + do_pickup = setup_table_button( button_number, toggle_label, + role_model.rules.pickup_whitelist->empty() == guy->rules.pickup_whitelist->empty() ); + /*The ALL button*/ + do_all = setup_table_button( button_number, toggle_label, + role_model.rules.flags == guy->rules.flags && + role_model.rules.aim == guy->rules.aim && + role_model.rules.engagement == guy->rules.engagement && + role_model.rules.cbm_recharge == guy->rules.cbm_recharge && + role_model.rules.cbm_reserve == guy->rules.cbm_reserve && + role_model.rules.pickup_whitelist->empty() == guy->rules.pickup_whitelist->empty() ); + /*The great swap*/ + // To make this slightly easier... + npc *guy_with_same = exporting_rules ? guy : &role_model; + npc *guy_with_new = exporting_rules ? &role_model : guy; + if( do_flags_overrides || do_all ) { + guy_with_new->rules.flags = guy_with_same->rules.flags; + guy_with_new->rules.overrides = guy_with_same->rules.overrides; + guy_with_new->rules.override_enable = guy_with_same->rules.override_enable; + } + if( do_aim || do_all ) { + guy_with_new->rules.aim = guy_with_same->rules.aim; + } + if( do_engagement || do_all ) { + guy_with_new->rules.engagement = guy_with_same->rules.engagement; + } + if( do_cbm_recharge || do_all ) { + guy_with_new->rules.cbm_recharge = guy_with_same->rules.cbm_recharge; + } + if( do_cbm_reserve || do_all ) { + guy_with_new->rules.cbm_reserve = guy_with_same->rules.cbm_reserve; + } + if( do_pickup || do_all ) { + guy_with_new->rules.pickup_whitelist = guy_with_same->rules.pickup_whitelist; + } + } + } + ImGui::EndTable(); + } +} + +void follower_rules_ui_impl::draw_controls() +{ + if( !guy ) { + debugmsg( "Something has gone very wrong, can't find NPC to set rules for. Aborting." ); + last_action = "QUIT"; + return; + } + + // ImGui only captures arrow keys by default, we want to also capture the numpad and hjkl + if( last_action == "QUIT" ) { + return; + } else if( last_action == "UP" ) { + ImGui::NavMoveRequestSubmit( ImGuiDir_Up, ImGuiDir_Up, ImGuiNavMoveFlags_None, + ImGuiScrollFlags_None ); + } else if( last_action == "DOWN" ) { + ImGui::NavMoveRequestSubmit( ImGuiDir_Down, ImGuiDir_Down, ImGuiNavMoveFlags_None, + ImGuiScrollFlags_None ); + } else if( last_action == "LEFT" ) { + ImGui::NavMoveRequestSubmit( ImGuiDir_Left, ImGuiDir_Left, ImGuiNavMoveFlags_None, + ImGuiScrollFlags_None ); + } else if( last_action == "RIGHT" ) { + ImGui::NavMoveRequestSubmit( ImGuiDir_Right, ImGuiDir_Right, ImGuiNavMoveFlags_None, + ImGuiScrollFlags_None ); + } else if( last_action == "CONFIRM" ) { + ImGui::ActivateItem( ImGui::GetFocusID() ); + } + + ImGui::SetWindowSize( ImVec2( window_width, window_height ), ImGuiCond_Once ); + + static bool exporting_rules = false; + static bool in_popup = false; + if( in_popup ) { + rules_transfer_popup( exporting_rules, in_popup ); + return; + } + + ImGui::InvisibleButton( "TOP_OF_WINDOW_KB_SCROLL_SELECTABLE", ImVec2( 1.0, 1.0 ) ); + + const hotkey_queue &hotkeys = hotkey_queue::alphabets(); + input_event assigned_hotkey = input_ptr->first_unassigned_hotkey( hotkeys ); + input_event pressed_key = input_ptr->get_raw_input(); + + draw_colored_text( string_format( _( "Rules for your follower, %s" ), guy->disp_name() ) ); + ImGui::Separator(); + + if( ImGui::Button( _( "Import settings from follower" ) ) ) { + exporting_rules = false; + in_popup = true; + return; + } + if( ImGui::Button( _( "Export settings to follower" ) ) ) { + exporting_rules = true; + in_popup = true; + return; + } + + draw_colored_text( _( "Hotkey:" ) ); + ImGui::NewLine(); + + + print_hotkey( assigned_hotkey ); + if( ImGui::Button( _( "Default ALL" ) ) || pressed_key == assigned_hotkey ) { + ImGui::SetKeyboardFocusHere( -1 ); + // TODO: use query_yn here as a safeguard against fatfingering. Can't use it right now, + // it destructs the imgui instance(?!) which obviously causes us to crash + npc_follower_rules default_values; //Call the constructor and let *it* tell us what the default is + guy->rules = default_values; + } + + int rule_number = 0; + /* Handle all of our regular, boolean rules */ + for( const std::pair &rule_data : ally_rule_strs ) { + assigned_hotkey = input_ptr->next_unassigned_hotkey( hotkeys, assigned_hotkey ); + ImGui::NewLine(); + print_hotkey( assigned_hotkey ); + const ally_rule_data &this_rule = rule_data.second; + bool rule_enabled = false; + std::string rules_text; + if( guy->rules.has_flag( this_rule.rule ) ) { + rules_text = string_format( "%s", get_parsed( this_rule.rule_true_text ) ); + rule_enabled = true; + } else { + rules_text = string_format( "%s", get_parsed( this_rule.rule_false_text ) ); + } + std::string label = _( "Toggle" ); + if( setup_button( rule_number, label, rule_enabled ) || pressed_key == assigned_hotkey ) { + ImGui::SetKeyboardFocusHere( -1 ); + guy->rules.toggle_flag( this_rule.rule ); + guy->rules.toggle_specific_override_state( this_rule.rule, !rule_enabled ); + } + ImGui::SameLine(); + ImGui::PushID( rule_number ); + rule_number++; + if( ImGui::Button( _( "Default" ) ) ) { + guy->rules.clear_flag( this_rule.rule ); + guy->rules.clear_override( this_rule.rule ); + } + ImGui::SameLine(); + draw_colored_text( rules_text ); + ImGui::PopID(); + } + + // Engagement rules require their own set of buttons, each instruction is unique + ImGui::Separator(); + ImGui::NewLine(); + assigned_hotkey = input_ptr->next_unassigned_hotkey( hotkeys, assigned_hotkey ); + print_hotkey( assigned_hotkey ); + multi_rule_header( "ENGAGEMENT_RULES", guy->rules.engagement, engagement_rules, + pressed_key == assigned_hotkey ); + int engagement_rule_number = 0; + for( const std::pair &engagement_rule : engagement_rules ) { + engagement_rule_number++; + // Could use a better label for these... + std::string button_label = std::to_string( engagement_rule_number ); + ImGui::SameLine(); + if( setup_button( rule_number, button_label, guy->rules.engagement == engagement_rule.first ) ) { + guy->rules.engagement = engagement_rule.first; + } + } + ImGui::SameLine(); + draw_colored_text( _( "Engagement rules" ), c_white ); + ImGui::NewLine(); + draw_colored_text( string_format( "%s", get_parsed( engagement_rules[guy->rules.engagement] ) ) ); + + // Aiming rule also has a non-boolean set of values + ImGui::Separator(); + ImGui::NewLine(); + assigned_hotkey = input_ptr->next_unassigned_hotkey( hotkeys, assigned_hotkey ); + print_hotkey( assigned_hotkey ); + multi_rule_header( "AIMING_RULES", guy->rules.aim, aim_rule_map, pressed_key == assigned_hotkey ); + int aim_rule_number = 0; + for( const std::pair &aiming_rule : aim_rule_map ) { + aim_rule_number++; + // Could use a better label for these... + std::string button_label = std::to_string( aim_rule_number ); + ImGui::SameLine(); + if( setup_button( rule_number, button_label, guy->rules.aim == aiming_rule.first ) ) { + guy->rules.aim = aiming_rule.first; + } + } + ImGui::SameLine(); + draw_colored_text( _( "Aiming rules" ), c_white ); + ImGui::NewLine(); + draw_colored_text( string_format( "%s", get_parsed( aim_rule_map[guy->rules.aim] ) ) ); + + /* Shows CBM rules, but only if the character has bionics. Must be last because it will + only appear sometimes and we don't want hotkeys to be different depending on whether + the character has bionics. That's bad for muscle memory! */ + if( !guy->get_bionics().empty() ) { + ImGui::Separator(); + ImGui::NewLine(); + assigned_hotkey = input_ptr->next_unassigned_hotkey( hotkeys, assigned_hotkey ); + print_hotkey( assigned_hotkey ); + multi_rule_header( "RECHARGE_RULES", guy->rules.cbm_recharge, recharge_map, + pressed_key == assigned_hotkey ); + for( const std::pair &recharge_rule : recharge_map ) { + int percent = static_cast( recharge_rule.first ); + std::string button_label = std::to_string( percent ) + "%"; + ImGui::SameLine(); + if( setup_button( rule_number, button_label, guy->rules.cbm_recharge == recharge_rule.first ) ) { + guy->rules.cbm_recharge = recharge_rule.first; + } + } + ImGui::SameLine(); + draw_colored_text( _( "CBM recharging rules" ), c_white ); + ImGui::NewLine(); + draw_colored_text( string_format( "%s", get_parsed( recharge_map[guy->rules.cbm_recharge] ) ) ); + + ImGui::Separator(); + ImGui::NewLine(); + assigned_hotkey = input_ptr->next_unassigned_hotkey( hotkeys, assigned_hotkey ); + print_hotkey( assigned_hotkey ); + multi_rule_header( "RESERVE_RULES", guy->rules.cbm_reserve, reserve_map, + pressed_key == assigned_hotkey ); + for( const std::pair &reserve_rule : reserve_map ) { + int percent = static_cast( reserve_rule.first ); + std::string button_label = std::to_string( percent ) + "%"; + ImGui::SameLine(); + if( setup_button( rule_number, button_label, guy->rules.cbm_reserve == reserve_rule.first ) ) { + guy->rules.cbm_reserve = reserve_rule.first; + } + } + ImGui::SameLine(); + draw_colored_text( _( "CBM reserve rules" ), c_white ); + ImGui::NewLine(); + draw_colored_text( string_format( "%s", get_parsed( reserve_map[guy->rules.cbm_reserve] ) ) ); + } + + ImGui::InvisibleButton( "BOTTOM_OF_WINDOW_KB_SCROLL_SELECTABLE", ImVec2( 1.0, 1.0 ) ); +} diff --git a/src/npctalk_rules.h b/src/npctalk_rules.h new file mode 100644 index 0000000000000..a27704d39b267 --- /dev/null +++ b/src/npctalk_rules.h @@ -0,0 +1,64 @@ +#pragma once +#ifndef CATA_SRC_NPCTALK_RULES_H +#define CATA_SRC_NPCTALK_RULES_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cata_imgui.h" +#include "dialogue.h" +#include "npctalk.h" +#include "ui_manager.h" +#include "imgui/imgui.h" + +class follower_rules_ui +{ + friend class follower_rules_ui_impl; + public: + void draw_follower_rules_ui( npc *guy ); +}; + +class follower_rules_ui_impl : public cataimgui::window +{ + public: + std::string last_action; + input_context *input_ptr = nullptr; + explicit follower_rules_ui_impl() : cataimgui::window( _( "Rules for your follower" ), + ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove ) { + } + + void set_npc_pointer_to( npc *new_guy ); + + private: + npc *guy = nullptr; + std::string get_parsed( std::string initial_string ); + void print_hotkey( input_event &hotkey ); + void rules_transfer_popup( bool &exporting_rules, bool &still_in_popup ); + bool setup_button( int &button_num, std::string &label, bool should_color ); + bool setup_table_button( int &button_num, std::string &label, bool should_color ); + + size_t window_width = str_width_to_pixels( TERMX ) / 2; + size_t window_height = str_height_to_pixels( TERMY ) / 2; + + // Prepares for a rule option with multiple valid selections. Advances and wraps through + // those options as the hotkey is pressed. + template + void multi_rule_header( const std::string &id, T &rule, const std::map &rule_map, + bool should_advance ); + + protected: + void draw_controls() override; +}; + +#endif // CATA_SRC_NPCTALK_RULES_H \ No newline at end of file diff --git a/tests/npc_talk_test.cpp b/tests/npc_talk_test.cpp index dc4275b8d738a..93f1208d4e91c 100644 --- a/tests/npc_talk_test.cpp +++ b/tests/npc_talk_test.cpp @@ -407,25 +407,6 @@ TEST_CASE( "npc_talk_allies", "[npc_talk]" ) CHECK( d.responses[1].text == "This is a npc allies 1 test response." ); } -TEST_CASE( "npc_talk_rules", "[npc_talk]" ) -{ - dialogue d; - npc &talker_npc = prep_test( d ); - - d.add_topic( "TALK_TEST_NPC_RULES" ); - gen_response_lines( d, 1 ); - CHECK( d.responses[0].text == "This is a basic test response." ); - talker_npc.rules.engagement = combat_engagement::ALL; - talker_npc.rules.aim = aim_rule::SPRAY; - talker_npc.rules.set_flag( ally_rule::avoid_doors ); - gen_response_lines( d, 4 ); - CHECK( d.responses[0].text == "This is a basic test response." ); - CHECK( d.responses[1].text == "This is a npc engagement rule test response." ); - CHECK( d.responses[2].text == "This is a npc aim rule test response." ); - CHECK( d.responses[3].text == "This is a npc rule test response." ); - talker_npc.rules.clear_flag( ally_rule::avoid_doors ); -} - TEST_CASE( "npc_talk_needs", "[npc_talk]" ) { dialogue d; @@ -821,25 +802,6 @@ TEST_CASE( "npc_talk_items", "[npc_talk]" ) CHECK_FALSE( has_item( player_character, "beer", 1 ) ); } -TEST_CASE( "npc_talk_combat_commands", "[npc_talk]" ) -{ - dialogue d; - prep_test( d ); - - d.add_topic( "TALK_COMBAT_COMMANDS" ); - gen_response_lines( d, 10 ); - CHECK( d.responses[0].text == "Change your engagement rules…" ); - CHECK( d.responses[1].text == "Change your aiming rules…" ); - CHECK( d.responses[2].text == "Move freely as you need to." ); - CHECK( d.responses[3].text == "" ); - CHECK( d.responses[4].text == "Don't use ranged weapons anymore." ); - CHECK( d.responses[5].text == "Don't worry about noise." ); - CHECK( d.responses[6].text == "You can use grenades." ); - CHECK( d.responses[7].text == "Don't worry about shooting an ally." ); - CHECK( d.responses[8].text == "Hold the line: don't move onto obstacles adjacent to me." ); - CHECK( d.responses[9].text == "Never mind." ); -} - TEST_CASE( "npc_talk_vars", "[npc_talk]" ) { dialogue d;