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

Creatures above cast shadows onto tiles below #66730

Merged
merged 11 commits into from
Jul 10, 2023
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
3 changes: 3 additions & 0 deletions doc/TILESET.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ Bashing animations (handle_action.cpp):
`bash_effective` Bash effective but target not yet destroyed.
`bash_ineffective` Bash not effective.

Shadows (cata_tiles.cpp):
`shadow` Drawn when creature present in tiles above.

#### Complex IDs

Special prefixes that are used include:
Expand Down
76 changes: 76 additions & 0 deletions src/cata_tiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1695,6 +1695,12 @@ void cata_tiles::draw( const point &dest, const tripoint &center, int width, int
}
};

// Skip drawing shadow of critters above if there is no shadow sprite
bool do_draw_shadow = false;
if( find_tile_looks_like( "shadow", TILE_CATEGORY::NONE, "" ) ) {
do_draw_shadow = true;
}

if( max_draw_depth <= 0 ) {
// Legacy draw mode
for( int row = min_row; row < max_row; row ++ ) {
Expand Down Expand Up @@ -1762,6 +1768,13 @@ void cata_tiles::draw( const point &dest, const tripoint &center, int width, int
// If no vpart drawn, revert height_3d changes
p.com.height_3d = temp_height_3d;
}
} else if( f == &cata_tiles::draw_critter_at ) {
// Draw
if( !( this->*f )( draw_loc, var->ll, p.com.height_3d, var->invisible, false ) && do_draw_shadow &&
cur_zlevel == p.com.draw_min_z ) {
// Draw shadow of flying critters on bottom-most tile if no other critter drawn
draw_critter_above( draw_loc, var->ll, p.com.height_3d, var->invisible );
}
} else {
// Draw
( this->*f )( draw_loc, var->ll, p.com.height_3d, var->invisible, false );
Expand Down Expand Up @@ -4042,6 +4055,69 @@ bool cata_tiles::draw_critter_at( const tripoint &p, lit_level ll, int &height_3
return result;
}

bool cata_tiles::draw_critter_above( const tripoint &p, lit_level ll, int &height_3d,
const std::array<bool, 5> & )
{
tripoint scan_p( p.xy(), p.z + 1 );
map &here = get_map();
Character &you = get_player_character();
const Creature *pcritter = nullptr;
// Search for a creature above
while( pcritter == nullptr && !here.dont_draw_lower_floor( scan_p ) &&
scan_p.z - you.pos().z <= fov_3d_z_range ) {
pcritter = get_creature_tracker().creature_at( scan_p, true );
scan_p.z++;
}

// Abort if no creature found
if( pcritter == nullptr ) {
return false;
}
const Creature &critter = *pcritter;

// Draw shadow
if( draw_from_id_string( "shadow", TILE_CATEGORY::NONE, empty_string, p,
0, 0, ll, false, height_3d ) && scan_p.z - 1 > you.pos().z && you.sees( critter ) ) {

bool is_player = false;
bool sees_player = false;
Creature::Attitude attitude = Creature::Attitude::ANY;

// Get critter status disposition if monster
const monster *m = dynamic_cast<const monster *>( &critter );
if( m != nullptr ) {
sees_player = m->sees( you );
attitude = m->attitude_to( you );
}

// Get critter status disposition if character
const Character *pl = dynamic_cast<const Character *>( &critter );
if( pl != nullptr ) {
if( pl->is_avatar() ) {
is_player = true;
} else {
sees_player = pl->sees( you );
attitude = pl->attitude_to( you );
}
}

// Draw overlay for shadow owner
if( !is_player ) {
std::string draw_id = "overlay_" + Creature::attitude_raw_string( attitude );
if( sees_player && !you.has_trait( trait_INATTENTIVE ) ) {
draw_id += "_sees_player";
}
if( tileset_ptr->find_tile_type( draw_id ) ) {
draw_from_id_string( draw_id, TILE_CATEGORY::NONE, empty_string, p, 0, 0,
lit_level::LIT, false, height_3d );
}
}
return true;
} else {
return false;
}
}

bool cata_tiles::draw_zone_mark( const tripoint &p, lit_level ll, int &height_3d,
const std::array<bool, 5> &invisible, const bool memorize_only )
{
Expand Down
2 changes: 2 additions & 0 deletions src/cata_tiles.h
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,8 @@ class cata_tiles
const std::array<bool, 5> &invisible, bool memorize_only );
bool draw_critter_at_below( const tripoint &p, lit_level ll, int &height_3d,
const std::array<bool, 5> &invisible, bool memorize_only );
bool draw_critter_above( const tripoint &p, lit_level ll, int &height_3d,
const std::array<bool, 5> &invisible );
bool draw_zone_mark( const tripoint &p, lit_level ll, int &height_3d,
const std::array<bool, 5> &invisible, bool memorize_only );
bool draw_zombie_revival_indicators( const tripoint &pos, lit_level ll, int &height_3d,
Expand Down
47 changes: 43 additions & 4 deletions src/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3799,8 +3799,6 @@ void game::draw_async_anim_curses()
const nc_color nccol = anim.second.second;

mvwprintz( w_terrain, p.xy(), nccol, ncstr );
//shared_ptr_fast<game::draw_callback_t> hit_cb = make_shared_fast<game::draw_callback_t>( [&]() { mvwprintz( w_terrain, p.xy(), nccol, ncstr ); } );
//g->add_draw_callback( hit_cb );
}
}

Expand All @@ -3809,6 +3807,37 @@ void game::void_async_anim_curses()
async_anim_layer_curses.clear();
}

void game::init_draw_blink_curses( const tripoint &p, const std::string &ncstr,
const nc_color &nccol )
{
std::pair <std::string, nc_color> anim( ncstr, nccol );
blink_layer_curses[p] = anim;
}

void game::draw_blink_curses()
{
// game::draw_blink_curses can be called multiple times, storing each animation to be played in blink_layer_curses
// Iterate through every animation in async_anim_layer
for( const auto &anim : blink_layer_curses ) {
const tripoint p = anim.first - u.view_offset + tripoint( POSX - u.posx(), POSY - u.posy(),
-u.posz() );
const std::string ncstr = anim.second.first;
const nc_color nccol = anim.second.second;

mvwprintz( w_terrain, p.xy(), nccol, ncstr );
}
}

void game::void_blink_curses()
{
blink_layer_curses.clear();
}

bool game::has_blink_curses()
{
return !blink_layer_curses.empty();
}

void game::draw( ui_adaptor &ui )
{
if( test_mode ) {
Expand All @@ -3821,6 +3850,7 @@ void game::draw( ui_adaptor &ui )
m.update_visibility_cache( ter_view_p.z );

werase( w_terrain );
void_blink_curses();
draw_ter();
for( auto it = draw_callbacks.begin(); it != draw_callbacks.end(); ) {
shared_ptr_fast<draw_callback_t> cb = it->lock();
Expand All @@ -3832,6 +3862,10 @@ void game::draw( ui_adaptor &ui )
}
}
draw_async_anim_curses();
// Only draw blinking symbols when in active phase
if( blink_active_phase ) {
draw_blink_curses();
}
wnoutrefresh( w_terrain );

draw_panels( true );
Expand Down Expand Up @@ -3938,8 +3972,13 @@ void game::draw_critter( const Creature &critter, const tripoint &center )
// Monster is below
// TODO: Make this show something more informative than just green 'v'
// TODO: Allow looking at this mon with look command
// TODO: Redraw this after weather etc. animations
mvwputch( w_terrain, point( mx, my ), c_green_cyan, 'v' );
init_draw_blink_curses( tripoint( critter.pos().xy(), center.z ), "v", c_green_cyan );
}
if( critter.posz() == center.z + 1 &&
( debug_mode || u.sees( critter ) ) &&
m.valid_move( critter.pos(), critter.pos() + tripoint_below, false, true ) ) {
// Monster is above
init_draw_blink_curses( tripoint( critter.pos().xy(), center.z ), "^", c_green_cyan );
}
return;
}
Expand Down
11 changes: 11 additions & 0 deletions src/game.h
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,17 @@ class game
std::map<tripoint, std::pair <std::string, nc_color>>
async_anim_layer_curses; // NOLINT(cata-serialize)

public:
void init_draw_blink_curses( const tripoint &p, const std::string &ncstr,
const nc_color &nccol );
void draw_blink_curses();
void void_blink_curses();
bool has_blink_curses();
bool blink_active_phase = true; // NOLINT(cata-serialize)
protected:
std::map<tripoint, std::pair <std::string, nc_color>>
blink_layer_curses; // NOLINT(cata-serialize)

public:
// when force_redraw is true, redraw all panel instead of just animated panels
// mostly used after UI updates
Expand Down
18 changes: 18 additions & 0 deletions src/handle_action.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,18 @@ class user_turn
return elapsed_ms.count() > get_option<int>( "ANIMATION_DELAY" );
}

std::chrono::steady_clock::time_point last_blink_transition = std::chrono::steady_clock::now();
bool blink_timeout() {
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
std::chrono::milliseconds elapsed_ms =
std::chrono::duration_cast<std::chrono::milliseconds>( now - last_blink_transition );
if( elapsed_ms.count() > get_option<int>( "BLINK_SPEED" ) ) {
last_blink_transition = now;
return true;
}
return false;
}

};

input_context game::get_player_input( std::string &action )
Expand Down Expand Up @@ -397,6 +409,12 @@ input_context game::get_player_input( std::string &action )
#endif
}

if( g->has_blink_curses() && current_turn.blink_timeout() ) {
// Toggle blink phase and redraw
g->blink_active_phase = !g->blink_active_phase;
g->invalidate_main_ui_adaptor();
}

ui_manager::redraw_invalidated();
} while( handle_mouseview( ctxt, action ) && uquit != QUIT_WATCH
&& ( action != "TIMEOUT" || !current_turn.has_timeout_elapsed() ) );
Expand Down
1 change: 1 addition & 0 deletions tools/json_tools/generate_overlay_ids.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
'infrared_creature',
'run_nw', 'run_n', 'run_ne', 'run_w', 'run_e', 'run_sw', 'run_s', 'run_se',
'bash_complete', 'bash_effective', 'bash_ineffective',
'shadow',
)
ATTITUDES = ('hostile', 'neutral', 'friendly', 'other')

Expand Down