Skip to content

Commit

Permalink
Display ordered NPC path on map, move TALK_GOTO_LOCATION to top-level…
Browse files Browse the repository at this point in the history
… menu (CleverRaven#74093)

* Display ordered NPC path on map (WIP implementation)

* Only advance one tile per redraw, even if it's slow

* Update the goto talk topic and move it to top-level menu

* Display path for tiles overmap too, not just ascii

* Display ETA and distance for destination

+don't overrun our iterators

* Please the clang with pass-by-reference
  • Loading branch information
RenechCDDA authored Jun 26, 2024
1 parent c77385e commit 8ed7912
Show file tree
Hide file tree
Showing 11 changed files with 95 additions and 14 deletions.
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() ) {
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

0 comments on commit 8ed7912

Please sign in to comment.