Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Display ordered NPC path on map, move TALK_GOTO_LOCATION to top-level menu #74093

Merged
merged 6 commits into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 5 additions & 7 deletions data/json/npcs/common_chat/TALK_COMMON_ALLY.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"topic": "TALK_FRIEND",
"effect": "reveal_stats"
},
{ "text": "Please go to this location.", "topic": "TALK_GOTO_LOCATION", "effect": "goto_location" },
{ "text": "There's something I want you to do.", "topic": "TALK_ALLY_ORDERS" },
{ "text": "I just wanted to talk for a bit.", "topic": "TALK_ALLY_SOCIAL" },
{ "text": "Can you help me understand something? (HELP/TUTORIAL)", "topic": "TALK_ALLY_TUTORIAL" },
Expand Down Expand Up @@ -1017,7 +1018,10 @@
{
"id": "TALK_GOTO_LOCATION",
"type": "talk_topic",
"dynamic_line": "Sure thing, I'll make my way there.",
"dynamic_line": {
"npc_is_travelling": "Sure thing, I'll make my way there.",
"no": "All right, tell me if you want me to go somewhere."
},
"responses": [ { "text": "Affirmative.", "topic": "TALK_DONE" } ]
},
{
Expand All @@ -1037,12 +1041,6 @@
"topic": "TALK_FRIEND_GUARD",
"effect": "assign_camp"
},
{
"text": "Please go to this location.",
"topic": "TALK_GOTO_LOCATION",
"condition": { "or": [ "is_by_radio", "u_has_camp" ] },
"effect": "goto_location"
},
{
"text": "I want you to build a camp here.",
"topic": "TALK_HALLU_CAMP",
Expand Down
2 changes: 2 additions & 0 deletions doc/NPCs.md
Original file line number Diff line number Diff line change
Expand Up @@ -961,6 +961,8 @@ Condition | Type | Description
`"has_available_mission" or "u_has_available_mission" or "npc_has_available_mission"` | simple string | `true` if u or the NPC has one job available for the player character.
`"has_many_available_missions"` | simple string | `true` if the NPC has several jobs available for the player character.
`"mission_goal" or "npc_mission_goal" or "u_mission_goal"` | string or [variable object](#variable-object) | `true` if u or the NPC's current mission has the same goal as `mission_goal`.
`"u_has_activity" or "npc_has_activity" | simple string | `true` if the [selected talker](EFFECT_ON_CONDITION.md#alpha-and-beta-talkers) is currently performing an [activity](PLAYER_ACTIVITY.md).
`"u_is_travelling" or "npc_is_travelling" | simple string | `true` if the [selected talker](EFFECT_ON_CONDITION.md#alpha-and-beta-talkers) has a current destination. Note that this just checks the destination exists, not whether u or npc is actively moving to it.
`"mission_complete" or "npc_mission_complete" or "u_mission_complete"` | simple string | `true` if u or the NPC has completed the other's current mission.
`"mission_incomplete" or "npc_mission_incomplete" or "u_mission_incomplete"` | simple string | `true` if u or the NPC hasn't completed the other's current mission.
`"mission_failed" or "npc_mission_failed" or "u_mission_failed"` | simple string | `true` if u or the NPC has failed the other's current mission.
Expand Down
1 change: 1 addition & 0 deletions lang/string_extractor/parsers/talk_topic.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def gender_options(subject):
"npc_available", "npc_following", "npc_friend", "npc_hostile",
"npc_train_skills", "npc_train_styles", "npc_train_spells",
"at_safe_space", "is_day", "npc_has_activity",
"u_is_travelling", "npc_is_travelling",
"is_outside", "u_is_outside", "npc_is_outside", "u_has_camp",
"u_can_stow_weapon", "npc_can_stow_weapon", "u_can_drop_weapon",
"npc_can_drop_weapon", "u_has_weapon", "npc_has_weapon",
Expand Down
12 changes: 12 additions & 0 deletions src/condition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1099,6 +1099,17 @@ conditional_t::func f_npc_role_nearby( const JsonObject &jo, std::string_view me
};
}

conditional_t::func f_npc_is_travelling( bool is_npc )
{
return [is_npc]( dialogue const & d ) {
const Character *traveller = d.actor( is_npc )->get_character();
if( !traveller ) {
return false;
}
return !traveller->omt_path.empty();
};
}

conditional_t::func f_npc_allies( const JsonObject &jo, std::string_view member )
{
dbl_or_var dov = get_dbl_or_var( jo, member );
Expand Down Expand Up @@ -2487,6 +2498,7 @@ std::vector<condition_parser>
parsers_simple = {
{"u_male", "npc_male", &conditional_fun::f_is_male },
{"u_female", "npc_female", &conditional_fun::f_is_female },
{"u_is_travelling", "npc_is_travelling", &conditional_fun::f_npc_is_travelling },
{"has_no_assigned_mission", &conditional_fun::f_no_assigned_mission },
{"has_assigned_mission", &conditional_fun::f_has_assigned_mission },
{"has_many_assigned_missions", &conditional_fun::f_has_many_assigned_missions },
Expand Down
2 changes: 1 addition & 1 deletion src/do_turn.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -654,7 +654,7 @@ bool do_turn()
// consider a stripped down cache just for monsters.
m.build_map_cache( levz, true );
monmove();
if( calendar::once_every( 5_minutes ) ) {
if( calendar::once_every( time_between_npc_OM_moves ) ) {
overmap_npc_move();
}
if( calendar::once_every( 10_seconds ) ) {
Expand Down
3 changes: 3 additions & 0 deletions src/game.h
Original file line number Diff line number Diff line change
Expand Up @@ -1154,6 +1154,9 @@ class game
// show NPC pathfinding on overmap ui
bool debug_pathfinding = false; // NOLINT(cata-serialize)

//Ugly kludge to pass info to tile overmaps
npc *follower_path_to_show = nullptr; // NOLINT(cata-serialize)

/* tile overlays */
// Toggle all other overlays off and flip the given overlay on/off.
void display_toggle_overlay( action_id );
Expand Down
4 changes: 4 additions & 0 deletions src/game_constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include <map>
#include <string>
#include "calendar.h"
#include "units.h"

// Fixed window sizes.
Expand Down Expand Up @@ -145,6 +146,9 @@ constexpr int BIO_CQB_LEVEL = 5;
// Minimum size of a horde to show up on the minimap.
constexpr int HORDE_VISIBILITY_SIZE = 3;

// How often a NPC can move one tile on the overmap
constexpr time_duration time_between_npc_OM_moves = 5_minutes;

/**
* Average annual temperature in Kelvin used for climate, weather and temperature calculation.
* Average New England temperature = 43F/6C rounded to int.
Expand Down
16 changes: 15 additions & 1 deletion src/npctalk_funcs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
#include "npctrade.h"
#include "output.h"
#include "overmap.h"
#include "overmap_ui.h"
#include "overmapbuffer.h"
#include "pimpl.h"
#include "player_activity.h"
Expand Down Expand Up @@ -372,7 +373,7 @@ void talk_function::goto_location( npc &p )
camps.push_back( temp_camp );
}
for( const basecamp *iter : camps ) {
//~ %1$s: camp name, %2$d and %3$d: coordinates
//~ %1$s: camp name, %2$s: coordinates of the camp
selection_menu.addentry( i++, true, MENU_AUTOASSIGN, pgettext( "camp", "%1$s at %2$s" ),
iter->camp_name(), iter->camp_omt_pos().to_string() );
}
Expand Down Expand Up @@ -406,6 +407,19 @@ void talk_function::goto_location( npc &p )
add_msg( m_info, _( "That is not a valid destination for %s." ), p.disp_name() );
return;
}
g->follower_path_to_show = &p; // Necessary for overmap display in tiles version...
ui::omap::display_npc_path( p.global_omt_location(), p.omt_path );
g->follower_path_to_show = nullptr;
int tiles_to_travel = p.omt_path.size();
time_duration ETA = time_between_npc_OM_moves * tiles_to_travel;
ETA = ETA * rng_float( 0.8, 1.2 ); // Add +-20% variance in our estimate
if( !query_yn(
_( "Estimated time to arrival: %1$s \nTiles to travel: %2$s \nIs this path and destination acceptable?" ),
to_string_approx( ETA ), tiles_to_travel ) ) {
p.goal = npc::no_goal_point;
p.omt_path.clear();
return;
}
p.set_mission( NPC_MISSION_TRAVELLING );
p.chatbin.first_topic = p.chatbin.talk_friend_guard;
p.guard_pos = std::nullopt;
Expand Down
43 changes: 38 additions & 5 deletions src/overmap_ui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,8 @@ static bool get_and_assign_los( int &los, avatar &player_character, const tripoi
static void draw_ascii(
const catacurses::window &w, const tripoint_abs_omt &center,
const tripoint_abs_omt &orig, bool blink, bool show_explored, bool /* fast_scroll */,
input_context * /* inp_ctxt */, const draw_data_t &data )
input_context * /* inp_ctxt */, const draw_data_t &data,
const std::vector<tripoint_abs_omt> &display_path )
{

const int om_map_width = OVERMAP_WINDOW_WIDTH;
Expand Down Expand Up @@ -689,6 +690,11 @@ static void draw_ascii(
npc *npc_to_add = npc_to_get.get();
followers.push_back( npc_to_add );
}
if( !display_path.empty() ) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because this is technically passed different data than the tile overmaps, future changes could accidentally knock them out of sync.

It would be ideal to NOT have these be different, but the kludge in game.h is not something I want to rely on. That is why they are still different. But having them be different at all is another problem...

for( const tripoint_abs_omt &elem : display_path ) {
npc_path_route.insert( elem );
}
}
// get all traveling NPCs for the debug menu to show pathfinding routes.
if( g->debug_pathfinding ) {
for( auto &elem : overmap_buffer.get_npcs_near_player( 200 ) ) {
Expand Down Expand Up @@ -1257,7 +1263,8 @@ tiles_redraw_info redraw_info;
static void draw(
ui_adaptor &ui, const tripoint_abs_omt &center, const tripoint_abs_omt &orig,
bool blink, bool show_explored, bool fast_scroll,
input_context *inp_ctxt, const draw_data_t &data )
input_context *inp_ctxt, const draw_data_t &data,
const std::vector<tripoint_abs_omt> &display_path )
{
draw_om_sidebar( ui, g->w_omlegend, center, orig, blink, fast_scroll, inp_ctxt, data );
#if defined( TILES )
Expand All @@ -1269,7 +1276,8 @@ static void draw(
return;
}
#endif // TILES
draw_ascii( g->w_overmap, center, orig, blink, show_explored, fast_scroll, inp_ctxt, data );
draw_ascii( g->w_overmap, center, orig, blink, show_explored, fast_scroll, inp_ctxt, data,
display_path );
}

static void create_note( const tripoint_abs_omt &curs )
Expand Down Expand Up @@ -1773,7 +1781,7 @@ static bool try_travel_to_destination( avatar &player_character, const tripoint_
}

static tripoint_abs_omt display( const tripoint_abs_omt &orig,
const draw_data_t &data = draw_data_t() )
const draw_data_t &data = draw_data_t(), std::vector<tripoint_abs_omt> display_path = {} )
{
const int previous_zoom = g->get_zoom();
g->set_zoom( overmap_zoom_level );
Expand Down Expand Up @@ -1861,10 +1869,13 @@ static tripoint_abs_omt display( const tripoint_abs_omt &orig,
int fast_scroll_offset = get_option<int>( "FAST_SCROLL_OFFSET" );
std::optional<tripoint> mouse_pos;
std::chrono::time_point<std::chrono::steady_clock> last_blink = std::chrono::steady_clock::now();
std::chrono::time_point<std::chrono::steady_clock> last_advance = std::chrono::steady_clock::now();
auto display_path_iter = display_path.rbegin();
std::chrono::milliseconds cursor_advance_time = std::chrono::milliseconds( 0 );

ui.on_redraw( [&]( ui_adaptor & ui ) {
draw( ui, curs, orig, uistate.overmap_show_overlays,
show_explored, fast_scroll, &ictxt, data );
show_explored, fast_scroll, &ictxt, data, display_path );
} );

do {
Expand All @@ -1880,6 +1891,22 @@ static tripoint_abs_omt display( const tripoint_abs_omt &orig,
#else
action = ictxt.handle_input( get_option<int>( "BLINK_SPEED" ) );
#endif
if( !display_path.empty() ) {
std::chrono::time_point<std::chrono::steady_clock> now = std::chrono::steady_clock::now();
// We go faster per-tile the more we have to go
cursor_advance_time = std::chrono::milliseconds( 1000 ) / display_path.size();
cursor_advance_time = std::max( cursor_advance_time, std::chrono::milliseconds( 1 ) );
if( now > last_advance + cursor_advance_time ) {
if( display_path_iter != display_path.rend() ) {
curs = *display_path_iter;
last_advance = now;
display_path_iter++;
} else if( now > last_advance + cursor_advance_time * 10 ) {
action = "QUIT";
break;
}
}
}
if( const std::optional<tripoint> vec = ictxt.get_direction( action ) ) {
int scroll_d = fast_scroll ? fast_scroll_offset : 1;
curs += vec->xy() * scroll_d;
Expand Down Expand Up @@ -2053,6 +2080,12 @@ void ui::omap::display()
overmap_ui::display( get_player_character().global_omt_location(), overmap_ui::draw_data_t() );
}

void ui::omap::display_npc_path( tripoint_abs_omt starting_pos,
const std::vector<tripoint_abs_omt> &display_path )
{
overmap_ui::display( starting_pos, overmap_ui::draw_data_t(), display_path );
}

void ui::omap::display_hordes()
{
overmap_ui::draw_data_t data;
Expand Down
5 changes: 5 additions & 0 deletions src/overmap_ui.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ namespace omap
* Display overmap centered at the player's position.
*/
void display();
/**
* Display overmap centered at the given NPC's position and visually move across their intended OMT path.
*/
void display_npc_path( tripoint_abs_omt starting_pos,
const std::vector<tripoint_abs_omt> &display_path );
/**
* Display overmap like with @ref display() and display hordes.
*/
Expand Down
9 changes: 9 additions & 0 deletions src/sdltiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -979,6 +979,15 @@ void cata_tiles::draw_om( const point &dest, const tripoint_abs_omt &center_abs_
}
}

if( g->follower_path_to_show ) {
for( const tripoint_abs_omt &pos : g->follower_path_to_show->omt_path ) {
if( pos.z() == center_abs_omt.z() ) {
draw_from_id_string( "highlight", global_omt_to_draw_position( pos ), 0, 0, lit_level::LIT,
false );
}
}
}

// only draw in full tiles so it doesn't get cut off
const std::optional<std::pair<tripoint_abs_omt, std::string>> mission_arrow =
get_mission_arrow( full_om_tile_area, center_abs_omt );
Expand Down
Loading