diff --git a/data/json/ascii_art/graveyard.json b/data/json/ascii_art/graveyard.json new file mode 100644 index 0000000000000..e23da83c1129d --- /dev/null +++ b/data/json/ascii_art/graveyard.json @@ -0,0 +1,113 @@ +[ + { + "type": "ascii_art", + "id": "ascii_rip_cross", + "picture": [ + " _______ ___", + " < `/ |", + " > _ _ (", + " | |_) | |_) |", + " | | \\ | | |", + " ______.__%_| |________ __", + " _/ \\| |", + "| <", + "| |", + "| |", + "|_____.-._____ _/|_________|", + " | |", + " | |", + " | <", + " | |", + " | _ |", + " |__/ |", + " % / `--. |%", + " * .%%| -< @%%%", + " `\\%`@| |@@%@%%", + " .%%%@@@|% ` % @@@%%@%%%%", + " _.%%%%%%@@@@@@%%%__/\\%@@%%@@@@@@@%%%%%%" + ] + }, + { + "type": "ascii_art", + "id": "ascii_rip_inverted_cross", + "picture": [ + " _______ ___", + " | \\/ |", + " | |", + " | |", + " | |", + " | |", + " | |", + " | |", + " | <", + " | _ |", + " |__/ |", + " _____.__%_| |_________ _", + " _/ \\| \\", + "| <", + "| |", + "| |", + "|____.-._______ __/|__________|", + " % / `_-. _ |%", + " * .%%| |_) | |_)< @%%%", + " `\\%`@| | \\ | | |@@%@%%", + " .%%%@@@|% ` % @@@%%@%%%%", + " _.%%%%%%@@@@@@%%%__/\\%@@%%@@@@@@@%%%%%%" + ] + }, + { + "type": "ascii_art", + "id": "ascii_tombstone", + "picture": [ + " _________ ____ ", + " _/ `/ \\_ ", + " _/ _ _ \\_. ", + " _%\\ |_) | |_) \\_ ", + " _/ \\/ | \\ | | \\_ ", + " _/ \\_ ", + "| |", + " ) < ", + "| |", + "| |", + "| _ |", + "|__/ |", + " / `--. |", + "| ( ", + "| |", + "| |", + "| % . |", + "| @` %% |", + "| %@%@%\\ * %`%@%|", + "%%@@@.%@%\\%% `\\ %%.%%@@%@", + "@%@@%%%%%@@@@@@%%%%%%%%@@%%@@@%%%@%%@" + ] + }, + { + "type": "ascii_art", + "id": "ascii_portal_storm_win", + "picture": [ + " ", + " | ", + " -.- ", + " | ", + " ", + " ", + " | ", + " _._ ", + " | ", + " ", + " ", + " ", + " | ", + " -.- ", + " | ", + " ", + " | ", + " _._ ", + " | ", + " ", + " ", + " " + ] + } +] diff --git a/data/json/effects.json b/data/json/effects.json index d806b66883750..d855214df75e6 100644 --- a/data/json/effects.json +++ b/data/json/effects.json @@ -4993,5 +4993,11 @@ "desc": [ "Applied by leap special attack of burned frog zombies, conditional for related spell." ], "max_duration": "5 m", "show_in_info": true + }, + { + "type": "effect_type", + "id": "a_winner_is_u", + "name": [ "Winning!" ], + "//": "Used to display the winning screen upon winning like a winner." } ] diff --git a/data/json/end_screen.json b/data/json/end_screen.json new file mode 100644 index 0000000000000..4189a56a228bf --- /dev/null +++ b/data/json/end_screen.json @@ -0,0 +1,64 @@ +[ + { + "type": "end_screen", + "id": "death_tombstone", + "priority": 0, + "picture_id": "ascii_tombstone", + "condition": { "not": "u_is_alive" }, + "added_info": [ + [ [ 6, 8 ], "In memory of: " ], + [ [ 13, 9 ], "Survived: " ], + [ [ 15, 10 ], "Kills: " ] + ], + "last_words_label": "Last Words:" + }, + { + "type": "end_screen", + "id": "death_cross", + "priority": 100, + "picture_id": "ascii_rip_cross", + "condition": { + "and": [ + { "not": "u_is_alive" }, + { + "or": [ { "u_has_item": "holybook_bible1" }, { "u_has_item": "holybook_bible2" }, { "u_has_item": "holybook_bible3" } ] + }, + { "not": { "and": [ { "u_has_trait": "CANNIBAL" }, { "u_has_trait": "PSYCHOPATH" } ] } } + ] + }, + "added_info": [ + [ [ 8, 8 ], "In memory of: " ], + [ [ 15, 9 ], "Survived: " ], + [ [ 17, 10 ], "Kills: " ] + ], + "last_words_label": "Last Words:" + }, + { + "type": "end_screen", + "id": "death_inverted_cross", + "priority": 100, + "picture_id": "ascii_rip_inverted_cross", + "condition": { "and": [ { "not": "u_is_alive" }, { "or": [ { "u_has_trait": "CANNIBAL" }, { "u_has_trait": "PSYCHOPATH" } ] } ] }, + "added_info": [ + [ [ 8, 14 ], "In memory of: " ], + [ [ 15, 15 ], "Survived: " ], + [ [ 17, 16 ], "Kills: " ] + ], + "last_words_label": "Last Words:" + }, + { + "type": "end_screen", + "id": "portal_storm_win", + "priority": 200, + "picture_id": "ascii_portal_storm_win", + "condition": { "and": [ { "not": "u_is_alive" }, { "u_has_effect": "a_winner_is_u" } ] }, + "added_info": [ + [ [ 6, 6 ], "You won" ], + [ [ 8, 7 ], "..." ], + [ [ 4, 10 ], "..., who's that?" ], + [ [ 15, 11 ], "Not your problem anymore." ], + [ [ 9, 15 ], "What a success." ], + [ [ 4, 21 ], "Something struggled for: " ] + ] + } +] diff --git a/data/json/npcs/EOC_talkers/portal_storm.json b/data/json/npcs/EOC_talkers/portal_storm.json index 6a2b1c9a54466..a7e7eb62c9a49 100644 --- a/data/json/npcs/EOC_talkers/portal_storm.json +++ b/data/json/npcs/EOC_talkers/portal_storm.json @@ -109,6 +109,12 @@ "type": "talk_topic", "id": "TALK_PORTAL_STORM_WILL_IT", "dynamic_line": "&You do not exist anymore. Something else exists elsewhere, and vaguely remembers being you. Its (torment?/joy?/confusion?/freedom?) will never truly end.", - "responses": [ { "text": "An ending? The cataclysm was bad but was this better?", "topic": "TALK_DONE", "effect": [ "u_die" ] } ] + "responses": [ + { + "text": "An ending? The cataclysm was bad but was this better?", + "topic": "TALK_DONE", + "effect": [ "u_die", { "u_add_effect": "a_winner_is_u", "duration": "PERMANENT" } ] + } + ] } ] diff --git a/doc/JSON_INFO.md b/doc/JSON_INFO.md index ae5fc10e484ac..8d8a5cccdba08 100644 --- a/doc/JSON_INFO.md +++ b/doc/JSON_INFO.md @@ -39,6 +39,7 @@ Use the `Home` key to return to the top. - [Damage Info Ordering](#damage-info-ordering) - [Dreams](#dreams) - [Disease](#disease) + - [End Screen](#end-screen) - [Emitters](#emitters) - [Item Groups](#item-groups) - [Item Category](#item-category) @@ -1512,6 +1513,41 @@ Using `damage_info_order` we can reorder how these are shown, and even determine } ``` +### End Screen + +| Identifier | Description +|--- |--- +| `id` | (_mandatory_) Unique ID. Must be one continuous word, use underscores if necessary. +| `priority` | (_mandatory_) Int used to chose among several end sreens with valid conditions, higher value have higher priority. Priority 0 is the default tombstone end. +| `picture_id` | (_mandatory_) ID of an ascii art, see #Ascii_arts. +| `condition` | (_mandatory_) Conditions necessary to display this end screen. See the "Dialogue conditions" section of [NPCs](NPCs.md) for the full syntax. +| `added_info` | (_optional_) Vector of pairs of a pair of int character offset and Line number and a string to be written on the end screen. The string can use talk tags, see the "Special Custom Entries" section of [NPCs](NPCs.md) for the full syntax. +| `last_words_label` | (_optional_) String used to label the last word input prompt. If left empty no prompt will be displayed. + +```json + { + "type": "end_screen", + "id": "death_cross", + "priority": 1, + "picture_id": "ascii_rip_cross", + "condition": { + "and": [ + { "not": "u_is_alive" }, + { + "or": [ { "u_has_item": "holybook_bible1" }, { "u_has_item": "holybook_bible2" }, { "u_has_item": "holybook_bible3" } ] + }, + { "not": { "and": [ { "u_has_trait": "CANNIBAL" }, { "u_has_trait": "PSYCHOPATH" } ] } } + ] + }, + "added_info": [ + [ [ 8, 8 ], "In memory of: " ], + [ [ 15, 9 ], "Survived: " ], + [ [ 17, 10 ], "Kills: " ] + ], + "last_words_label": "Last Words:" + } +``` + ### Emitters Emitters randomly place [fields](#field-types) around their positions - every turn for monster emissions, every ten seconds for furniture/terrain. diff --git a/doc/NPCs.md b/doc/NPCs.md index d2a910c2f70a8..c7aeb7570fc57 100644 --- a/doc/NPCs.md +++ b/doc/NPCs.md @@ -356,6 +356,8 @@ Field | Used for... `` | displays npc's current activity `` | displays a random punctuation from: `.`, `…`, `!` `` | displays npc's pronoun +`` | total kills of the Player +`` | time since start of the game `` | referenced item `` | referenced item unit price `` | TODO Add diff --git a/lang/string_extractor/parser.py b/lang/string_extractor/parser.py index 24ff1470e1237..589e204481c61 100644 --- a/lang/string_extractor/parser.py +++ b/lang/string_extractor/parser.py @@ -17,6 +17,7 @@ from .parsers.effect import parse_effect_on_condition from .parsers.effect_type import parse_effect_type from .parsers.enchant import parse_enchant +from .parsers.end_screen import parse_end_screen from .parsers.event_statistic import parse_event_statistic from .parsers.faction import parse_faction from .parsers.fault import parse_fault @@ -137,6 +138,7 @@ def dummy_parser(json, origin): "effect_type": parse_effect_type, "emit": dummy_parser, "enchantment": parse_enchant, + "end_screen": parse_end_screen, "engine": parse_generic, "event_statistic": parse_event_statistic, "event_transformation": dummy_parser, diff --git a/lang/string_extractor/parsers/end_screen.py b/lang/string_extractor/parsers/end_screen.py new file mode 100644 index 0000000000000..db3935de0f187 --- /dev/null +++ b/lang/string_extractor/parsers/end_screen.py @@ -0,0 +1,8 @@ +from ..write_text import write_text + + +def parse_end_screen(json, origin): + if "last_words_label" in json: + write_text(json["last_words_label"], origin, + comment="String used to label the last word input prompt." + "ex: \"Last Words:\")") diff --git a/src/end_screen.cpp b/src/end_screen.cpp new file mode 100644 index 0000000000000..3d1c6507b4b17 --- /dev/null +++ b/src/end_screen.cpp @@ -0,0 +1,42 @@ +#include "assign.h" +#include "condition.h" +#include "end_screen.h" +#include "generic_factory.h" + +namespace +{ +generic_factory end_screen_factory( "end_screen" ); +} // namespace + +template<> +const end_screen &string_id::obj()const +{ + return end_screen_factory.obj( *this ); +} + +template<> +bool string_id::is_valid() const +{ + return end_screen_factory.is_valid( *this ); +} + +void end_screen::load_end_screen( const JsonObject &jo, const std::string &src ) +{ + end_screen_factory.load( jo, src ); +} + +void end_screen::load( const JsonObject &jo, std::string_view ) +{ + mandatory( jo, was_loaded, "id", id ); + mandatory( jo, was_loaded, "picture_id", picture_id ); + mandatory( jo, was_loaded, "priority", priority ); + read_condition( jo, "condition", condition, false ); + + optional( jo, was_loaded, "added_info", added_info ); + optional( jo, was_loaded, "last_words_label", last_words_label ); +} + +const std::vector &end_screen::get_all() +{ + return end_screen_factory.get_all(); +} diff --git a/src/end_screen.h b/src/end_screen.h new file mode 100644 index 0000000000000..78d092c3da818 --- /dev/null +++ b/src/end_screen.h @@ -0,0 +1,30 @@ +#pragma once +#ifndef CATA_SRC_DEATH_SCREEN_H +#define CATA_SRC_DEATH_SCREEN_H + +#include +#include + +#include "ascii_art.h" +#include "effect_on_condition.h" +#include "type_id.h" + +class JsonObject; + +struct end_screen { + public: + static void load_end_screen( const JsonObject &jo, const std::string &src ); + + void load( const JsonObject &jo, std::string_view ); + static const std::vector &get_all(); + bool was_loaded = false; + + end_screen_id id; + ascii_art_id picture_id; + std::function condition; + int priority; + std::vector, std::string>> added_info; + std::string last_words_label; +}; + +#endif // CATA_SRC_DEATH_SCREEN_H diff --git a/src/game.cpp b/src/game.cpp index 9401dc66a2dbc..aa7d4d1044cd0 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -41,6 +41,7 @@ #include "activity_actor_definitions.h" #include "activity_handlers.h" #include "activity_type.h" +#include "ascii_art.h" #include "auto_note.h" #include "auto_pickup.h" #include "avatar.h" @@ -52,6 +53,7 @@ #include "bodypart.h" #include "butchery_requirements.h" #include "cached_options.h" +#include "cata_imgui.h" #include "cata_path.h" #include "cata_scope_helpers.h" #include "cata_utility.h" @@ -86,6 +88,7 @@ #include "editmap.h" #include "effect.h" #include "effect_on_condition.h" +#include "end_screen.h" #include "enums.h" #include "event.h" #include "event_bus.h" @@ -105,6 +108,7 @@ #include "get_version.h" #include "harvest.h" #include "iexamine.h" +#include "imgui/imgui.h" #include "init.h" #include "input.h" #include "input_context.h" @@ -226,6 +230,8 @@ static const activity_id ACT_TRAIN_TEACHER( "ACT_TRAIN_TEACHER" ); static const activity_id ACT_TRAVELLING( "ACT_TRAVELLING" ); static const activity_id ACT_VIEW_RECIPE( "ACT_VIEW_RECIPE" ); +static const ascii_art_id ascii_tombstone( "ascii_tombstone" ); + static const bionic_id bio_jointservo( "bio_jointservo" ); static const bionic_id bio_probability_travel( "bio_probability_travel" ); static const bionic_id bio_remote( "bio_remote" ); @@ -285,9 +291,6 @@ static const itype_id fuel_type_animal( "animal" ); static const itype_id itype_battery( "battery" ); static const itype_id itype_disassembly( "disassembly" ); static const itype_id itype_grapnel( "grapnel" ); -static const itype_id itype_holybook_bible1( "holybook_bible1" ); -static const itype_id itype_holybook_bible2( "holybook_bible2" ); -static const itype_id itype_holybook_bible3( "holybook_bible3" ); static const itype_id itype_manhole_cover( "manhole_cover" ); static const itype_id itype_remotevehcontrol( "remotevehcontrol" ); static const itype_id itype_rope_30( "rope_30" ); @@ -342,7 +345,6 @@ static const ter_str_id ter_t_pit( "t_pit" ); static const ter_str_id ter_t_pit_shallow( "t_pit_shallow" ); static const trait_id trait_BADKNEES( "BADKNEES" ); -static const trait_id trait_CANNIBAL( "CANNIBAL" ); static const trait_id trait_CENOBITE( "CENOBITE" ); static const trait_id trait_ILLITERATE( "ILLITERATE" ); static const trait_id trait_INATTENTIVE( "INATTENTIVE" ); @@ -355,7 +357,6 @@ static const trait_id trait_M_IMMUNE( "M_IMMUNE" ); static const trait_id trait_NPC_STARTING_NPC( "NPC_STARTING_NPC" ); static const trait_id trait_NPC_STATIC_NPC( "NPC_STATIC_NPC" ); static const trait_id trait_PROF_CHURL( "PROF_CHURL" ); -static const trait_id trait_PSYCHOPATH( "PSYCHOPATH" ); static const trait_id trait_THICKSKIN( "THICKSKIN" ); static const trait_id trait_VINES2( "VINES2" ); static const trait_id trait_VINES3( "VINES3" ); @@ -2798,192 +2799,112 @@ bool game::is_game_over() return false; } -void game::bury_screen() const -{ - avatar &u = get_avatar(); +class end_screen_data; - std::vector vRip; - - int iMaxWidth = 0; - int iNameLine = 0; - int iInfoLine = 0; - - if( u.has_amount( itype_holybook_bible1, 1 ) || u.has_amount( itype_holybook_bible2, 1 ) || - u.has_amount( itype_holybook_bible3, 1 ) ) { - if( !( u.has_trait( trait_CANNIBAL ) || u.has_trait( trait_PSYCHOPATH ) ) ) { - vRip.emplace_back( " _______ ___" ); - vRip.emplace_back( " < `/ |" ); - vRip.emplace_back( " > _ _ (" ); - vRip.emplace_back( " | |_) | |_) |" ); - vRip.emplace_back( " | | \\ | | |" ); - vRip.emplace_back( " ______.__%_| |_________ __" ); - vRip.emplace_back( " _/ \\| |" ); - iNameLine = vRip.size(); - vRip.emplace_back( "| <" ); - vRip.emplace_back( "| |" ); - iMaxWidth = utf8_width( vRip.back() ); - vRip.emplace_back( "| |" ); - vRip.emplace_back( "|_____.-._____ __/|_________|" ); - vRip.emplace_back( " | |" ); - iInfoLine = vRip.size(); - vRip.emplace_back( " | |" ); - vRip.emplace_back( " | <" ); - vRip.emplace_back( " | |" ); - vRip.emplace_back( " | _ |" ); - vRip.emplace_back( " |__/ |" ); - vRip.emplace_back( " % / `--. |%" ); - vRip.emplace_back( " * .%%| -< @%%%" ); // NOLINT(cata-text-style) - vRip.emplace_back( " `\\%`@| |@@%@%%" ); - vRip.emplace_back( " .%%%@@@|% ` % @@@%%@%%%%" ); - vRip.emplace_back( " _.%%%%%%@@@@@@%%%__/\\%@@%%@@@@@@@%%%%%%" ); +class end_screen_data +{ + friend class end_screen_ui_impl; + public: + void draw_end_screen_ui(); +}; - } else { - vRip.emplace_back( " _______ ___" ); - vRip.emplace_back( " | \\/ |" ); - vRip.emplace_back( " | |" ); - vRip.emplace_back( " | |" ); - iInfoLine = vRip.size(); - vRip.emplace_back( " | |" ); - vRip.emplace_back( " | |" ); - vRip.emplace_back( " | |" ); - vRip.emplace_back( " | |" ); - vRip.emplace_back( " | <" ); - vRip.emplace_back( " | _ |" ); - vRip.emplace_back( " |__/ |" ); - vRip.emplace_back( " ______.__%_| |__________ _" ); - vRip.emplace_back( " _/ \\| \\" ); - iNameLine = vRip.size(); - vRip.emplace_back( "| <" ); - vRip.emplace_back( "| |" ); - iMaxWidth = utf8_width( vRip.back() ); - vRip.emplace_back( "| |" ); - vRip.emplace_back( "|_____.-._______ __/|__________|" ); - vRip.emplace_back( " % / `_-. _ |%" ); - vRip.emplace_back( " * .%%| |_) | |_)< @%%%" ); // NOLINT(cata-text-style) - vRip.emplace_back( " `\\%`@| | \\ | | |@@%@%%" ); - vRip.emplace_back( " .%%%@@@|% ` % @@@%%@%%%%" ); - vRip.emplace_back( " _.%%%%%%@@@@@@%%%__/\\%@@%%@@@@@@@%%%%%%" ); +class end_screen_ui_impl : public cataimgui::window +{ + public: + std::array text; + explicit end_screen_ui_impl() : cataimgui::window( _( "The End" ) ) { } - } else { - vRip.emplace_back( R"( _________ ____ )" ); - vRip.emplace_back( R"( _/ `/ \_ )" ); - vRip.emplace_back( R"( _/ _ _ \_. )" ); - vRip.emplace_back( R"( _%\ |_) | |_) \_ )" ); - vRip.emplace_back( R"( _/ \/ | \ | | \_ )" ); - vRip.emplace_back( R"( _/ \_ )" ); - vRip.emplace_back( R"(| |)" ); - iNameLine = vRip.size(); - vRip.emplace_back( R"( ) < )" ); - vRip.emplace_back( R"(| |)" ); - vRip.emplace_back( R"(| |)" ); - vRip.emplace_back( R"(| _ |)" ); - vRip.emplace_back( R"(|__/ |)" ); - iMaxWidth = utf8_width( vRip.back() ); - vRip.emplace_back( R"( / `--. |)" ); - vRip.emplace_back( R"(| ( )" ); - iInfoLine = vRip.size(); - vRip.emplace_back( R"(| |)" ); - vRip.emplace_back( R"(| |)" ); - vRip.emplace_back( R"(| % . |)" ); - vRip.emplace_back( R"(| @` %% |)" ); - vRip.emplace_back( R"(| %@%@%\ * %`%@%|)" ); - vRip.emplace_back( R"(%%@@@.%@%\%% `\ %%.%%@@%@)" ); - vRip.emplace_back( R"(@%@@%%%%%@@@@@@%%%%%%%%@@%%@@@%%%@%%@)" ); - } - - const point iOffset( TERMX > FULL_SCREEN_WIDTH ? ( TERMX - FULL_SCREEN_WIDTH ) / 2 : 0, - TERMY > FULL_SCREEN_HEIGHT ? ( TERMY - FULL_SCREEN_HEIGHT ) / 2 : 0 ); - - catacurses::window w_rip = catacurses::newwin( FULL_SCREEN_HEIGHT, FULL_SCREEN_WIDTH, - iOffset ); - draw_border( w_rip ); + protected: + void draw_controls() override; +}; - sfx::do_player_death_hurt( get_player_character(), true ); - sfx::fade_audio_group( sfx::group::weather, 2000 ); - sfx::fade_audio_group( sfx::group::time_of_day, 2000 ); - sfx::fade_audio_group( sfx::group::context_themes, 2000 ); - sfx::fade_audio_group( sfx::group::sleepiness, 2000 ); +void end_screen_data::draw_end_screen_ui() +{ + input_context ctxt; + ctxt.register_action( "TEXT.CONFIRM" ); +#if defined(WIN32) || defined(TILES) + ctxt.set_timeout( 50 ); +#endif + end_screen_ui_impl p_impl; - for( size_t iY = 0; iY < vRip.size(); ++iY ) { - size_t iX = 0; - const char *str = vRip[iY].data(); - for( int slen = vRip[iY].size(); slen > 0; ) { - const uint32_t cTemp = UTF8_getch( &str, &slen ); - if( cTemp != U' ' ) { - nc_color ncColor = c_light_gray; + while( true ) { + ui_manager::redraw_invalidated(); + std::string action = ctxt.handle_input(); + if( action == "TEXT.CONFIRM" || !p_impl.get_is_open() ) { + break; + } + } + avatar &u = get_avatar(); + const bool is_suicide = g->uquit == QUIT_SUICIDE; + get_event_bus().send( u.getID(), u.name, u.male, is_suicide, + std::string( p_impl.text.data() ) ); +} - if( cTemp == U'%' ) { - ncColor = c_green; +void end_screen_ui_impl::draw_controls() +{ + text[0] = '\0'; + avatar &u = get_avatar(); + ascii_art_id art = ascii_tombstone; + dialogue d( get_talker_for( u ), nullptr ); + std::string input_label; + std::vector, std::string>> added_info; + + //Sort end_screens in order of decreasing priority + std::vector sorted_screens = end_screen::get_all(); + std::sort( sorted_screens.begin(), sorted_screens.end(), []( end_screen const & a, + end_screen const & b ) { + return a.priority > b.priority; + } ); - } else if( cTemp == U'_' || cTemp == U'|' ) { - ncColor = c_white; + for( const end_screen &e_screen : sorted_screens ) { + if( e_screen.condition( d ) ) { + art = e_screen.picture_id; + if( !e_screen.added_info.empty() ) { + added_info = e_screen.added_info; + } + if( !e_screen.last_words_label.empty() ) { + input_label = e_screen.last_words_label; + } + break; + } + } - } else if( cTemp == U'@' ) { - ncColor = c_brown; + if( art.is_valid() ) { + int row = 1; + for( const std::string &line : art->picture ) { + draw_colored_text( line ); - } else if( cTemp == U'*' ) { - ncColor = c_red; + for( std::pair, std::string> info : added_info ) { + if( row == info.first.second ) { + parse_tags( info.second, u, u ); + ImGui::SameLine( str_width_to_pixels( info.first.first ), 0 ); + draw_colored_text( info.second ); } - - mvwputch( w_rip, point( iX + FULL_SCREEN_WIDTH / 2 - ( iMaxWidth / 2 ), iY + 1 ), ncColor, - cTemp ); } - iX += mk_wcwidth( cTemp ); + row++; } } - std::string sTemp; - - center_print( w_rip, iInfoLine++, c_white, _( "Survived:" ) ); - - const time_duration survived = calendar::turn - calendar::start_of_game; - const int minutes = to_minutes( survived ) % 60; - const int hours = to_hours( survived ) % 24; - const int days = to_days( survived ); - - if( days > 0 ) { - // NOLINTNEXTLINE(cata-translate-string-literal) - sTemp = string_format( "%dd %dh %dm", days, hours, minutes ); - } else if( hours > 0 ) { - // NOLINTNEXTLINE(cata-translate-string-literal) - sTemp = string_format( "%dh %dm", hours, minutes ); - } else { - // NOLINTNEXTLINE(cata-translate-string-literal) - sTemp = string_format( "%dm", minutes ); + if( !input_label.empty() ) { + ImGui::NewLine(); + draw_colored_text( input_label ); + ImGui::SameLine( str_width_to_pixels( input_label.size() + 2 ), 0 ); + ImGui::InputText( "##LAST_WORD_BOX", text.data(), text.size() ); + ImGui::SetKeyboardFocusHere( -1 ); } - center_print( w_rip, iInfoLine++, c_white, sTemp ); - - const int iTotalKills = g->get_kill_tracker().monster_kill_count(); - - sTemp = _( "Kills:" ); - mvwprintz( w_rip, point( FULL_SCREEN_WIDTH / 2 - 5, 1 + iInfoLine++ ), c_light_gray, - ( sTemp + " " ) ); - wprintz( w_rip, c_magenta, "%d", iTotalKills ); - - sTemp = _( "In memory of:" ); - mvwprintz( w_rip, point( FULL_SCREEN_WIDTH / 2 - utf8_width( sTemp ) / 2, iNameLine++ ), - c_light_gray, - sTemp ); - - sTemp = u.get_name(); - mvwprintz( w_rip, point( FULL_SCREEN_WIDTH / 2 - utf8_width( sTemp ) / 2, iNameLine++ ), c_white, - sTemp ); - - sTemp = _( "Last Words:" ); - mvwprintz( w_rip, point( FULL_SCREEN_WIDTH / 2 - utf8_width( sTemp ) / 2, iNameLine++ ), - c_light_gray, - sTemp ); +} - int iStartX = FULL_SCREEN_WIDTH / 2 - ( ( iMaxWidth - 4 ) / 2 ); - std::string sLastWords = string_input_popup() - .window( w_rip, point( iStartX, iNameLine ), iStartX + iMaxWidth - 4 - 1 ) - .max_length( iMaxWidth - 4 - 1 ) - .query_string(); +void game::bury_screen() const +{ + end_screen_data new_instance; + new_instance.draw_end_screen_ui(); - const bool is_suicide = uquit == QUIT_SUICIDE; - get_event_bus().send( u.getID(), u.name, u.male, is_suicide, - sLastWords ); + sfx::do_player_death_hurt( get_player_character(), true ); + sfx::fade_audio_group( sfx::group::weather, 2000 ); + sfx::fade_audio_group( sfx::group::time_of_day, 2000 ); + sfx::fade_audio_group( sfx::group::context_themes, 2000 ); + sfx::fade_audio_group( sfx::group::sleepiness, 2000 ); } void game::death_screen() diff --git a/src/init.cpp b/src/init.cpp index 0bb49026b1952..e13963ea6c0af 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -37,6 +37,7 @@ #include "effect.h" #include "effect_on_condition.h" #include "emit.h" +#include "end_screen.h" #include "event_statistics.h" #include "faction.h" #include "fault.h" @@ -298,6 +299,7 @@ void DynamicDataLoader::initialize() add( "scent_type", &scent_type::load_scent_type ); add( "disease_type", &disease_type::load_disease_type ); add( "ascii_art", &ascii_art::load_ascii_art ); + add( "end_screen", &end_screen::load_end_screen ); // json/colors.json would be listed here, but it's loaded before the others (see init_colors()) // Non Static Function Access diff --git a/src/npctalk.cpp b/src/npctalk.cpp index 53b5ecfd4f196..cb519edafe114 100644 --- a/src/npctalk.cpp +++ b/src/npctalk.cpp @@ -48,6 +48,7 @@ #include "item.h" #include "item_category.h" #include "itype.h" +#include "kill_tracker.h" #include "line.h" #include "magic.h" #include "map.h" @@ -2281,6 +2282,26 @@ void parse_tags( std::string &phrase, const talker &u, const talker &me, const d cityname = c->name; } phrase.replace( fa, l, cityname ); + } else if( tag.find( "" ) == 0 ) { + std::string time_survived; + const time_duration survived = calendar::turn - calendar::start_of_game; + const int minutes = to_minutes( survived ) % 60; + const int hours = to_hours( survived ) % 24; + const int days = to_days( survived ); + if( days > 0 ) { + // NOLINTNEXTLINE(cata-translate-string-literal) + time_survived = string_format( "%dd %dh %dm", days, hours, minutes ); + } else if( hours > 0 ) { + // NOLINTNEXTLINE(cata-translate-string-literal) + time_survived = string_format( "%dh %dm", hours, minutes ); + } else { + // NOLINTNEXTLINE(cata-translate-string-literal) + time_survived = string_format( "%dm", minutes ); + } + phrase.replace( fa, l, time_survived ); + } else if( tag.find( "" ) == 0 ) { + const std::string total_kills = std::to_string( g->get_kill_tracker().monster_kill_count() ); + phrase.replace( fa, l, total_kills ); } else if( !tag.empty() ) { debugmsg( "Bad tag. '%s' (%d - %d)", tag.c_str(), fa, fb ); phrase.replace( fa, fb - fa + 1, "????" ); diff --git a/src/type_id.h b/src/type_id.h index bfbe45db19c4c..d3164eee1d3b6 100644 --- a/src/type_id.h +++ b/src/type_id.h @@ -88,6 +88,9 @@ using emit_id = string_id; class enchantment; using enchantment_id = string_id; +struct end_screen; +using end_screen_id = string_id; + class event_statistic; using event_statistic_id = string_id;