Skip to content

Commit

Permalink
Creatures above cast shadows onto tiles below (#66730)
Browse files Browse the repository at this point in the history
* tiles

* curses

* prevent shadow sprite fallback

* optimization and formatting

also remove some rogue code comments from my previous PRs

* prevent drawing overlay twice

* blinking indicators for curses

* update docs and py

* check sees for overlay

* merge fixes

Co-Authored-By: Jianxiang Wang (王健翔) <[email protected]>

* add NOLINT

---------

Co-authored-by: Jianxiang Wang (王健翔) <[email protected]>
  • Loading branch information
Rewryte and Qrox authored Jul 10, 2023
1 parent 50471ec commit 1f5aaac
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 4 deletions.
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

0 comments on commit 1f5aaac

Please sign in to comment.