void player::activate_mutation( const std::string &mut ) { const auto &mdata = mutation_branch::get( mut ); auto &tdata = my_mutations[mut]; int cost = mdata.cost; // You can take yourself halfway to Near Death levels of hunger/thirst. // Fatigue can go to Exhausted. if ((mdata.hunger && get_hunger() >= 700) || (mdata.thirst && get_thirst() >= 260) || (mdata.fatigue && get_fatigue() >= EXHAUSTED)) { // Insufficient Foo to *maintain* operation is handled in player::suffer add_msg_if_player(m_warning, _("You feel like using your %s would kill you!"), mdata.name.c_str()); return; } if (tdata.powered && tdata.charge > 0) { // Already-on units just lose a bit of charge tdata.charge--; } else { // Not-on units, or those with zero charge, have to pay the power cost if (mdata.cooldown > 0) { tdata.charge = mdata.cooldown - 1; } if (mdata.hunger){ mod_hunger(cost); } if (mdata.thirst){ mod_thirst(cost); } if (mdata.fatigue){ mod_fatigue(cost); } tdata.powered = true; // Handle stat changes from activation apply_mods(mut, true); recalc_sight_limits(); } if( mut == "WEB_WEAVER" ) { g->m.add_field(pos(), fd_web, 1, 0); add_msg_if_player(_("You start spinning web with your spinnerets!")); } else if (mut == "BURROW"){ if( is_underwater() ) { add_msg_if_player(m_info, _("You can't do that while underwater.")); tdata.powered = false; return; } tripoint dirp; if (!choose_adjacent(_("Burrow where?"), dirp)) { tdata.powered = false; return; } if( dirp == pos() ) { add_msg_if_player(_("You've got places to go and critters to beat.")); add_msg_if_player(_("Let the lesser folks eat their hearts out.")); tdata.powered = false; return; } int turns; if (g->m.is_bashable(dirp) && g->m.has_flag("SUPPORTS_ROOF", dirp) && g->m.ter(dirp) != t_tree) { // Takes about 100 minutes (not quite two hours) base time. // Being better-adapted to the task means that skillful Survivors can do it almost twice as fast. ///\EFFECT_CARPENTRY speeds up burrowing turns = (100000 - 5000 * skillLevel( skill_id( "carpentry" ) )); } else if (g->m.move_cost(dirp) == 2 && g->get_levz() == 0 && g->m.ter(dirp) != t_dirt && g->m.ter(dirp) != t_grass) { turns = 18000; } else { add_msg_if_player(m_info, _("You can't burrow there.")); tdata.powered = false; return; } assign_activity(ACT_BURROW, turns, -1, 0); activity.placement = dirp; add_msg_if_player(_("You tear into the %s with your teeth and claws."), g->m.tername(dirp).c_str()); tdata.powered = false; return; // handled when the activity finishes } else if (mut == "SLIMESPAWNER") { std::vector<tripoint> valid; for (int x = posx() - 1; x <= posx() + 1; x++) { for (int y = posy() - 1; y <= posy() + 1; y++) { tripoint dest(x, y, posz()); if (g->is_empty(dest)) { valid.push_back( dest ); } } } // Oops, no room to divide! if (valid.size() == 0) { add_msg_if_player(m_bad, _("You focus, but are too hemmed in to birth a new slimespring!")); tdata.powered = false; return; } add_msg_if_player(m_good, _("You focus, and with a pleasant splitting feeling, birth a new slimespring!")); int numslime = 1; for (int i = 0; i < numslime && !valid.empty(); i++) { const tripoint target = random_entry_removed( valid ); if (g->summon_mon(mtype_id( "mon_player_blob" ), target)) { monster *slime = g->monster_at( target ); slime->friendly = -1; } } if (one_in(3)) { //~ Usual enthusiastic slimespring small voices! :D add_msg_if_player(m_good, _("wow! you look just like me! we should look out for each other!")); } else if (one_in(2)) { //~ Usual enthusiastic slimespring small voices! :D add_msg_if_player(m_good, _("come on, big me, let's go!")); } else { //~ Usual enthusiastic slimespring small voices! :D add_msg_if_player(m_good, _("we're a team, we've got this!")); } tdata.powered = false; return; } else if ((mut == "NAUSEA") || (mut == "VOMITOUS") ){ vomit(); tdata.powered = false; return; } else if (mut == "M_FERTILE"){ spores(); tdata.powered = false; return; } else if (mut == "M_BLOOM"){ blossoms(); tdata.powered = false; return; } else if (mut == "VINES3"){ item newit( "vine_30", calendar::turn ); if (!can_pickVolume(newit.volume())) { //Accounts for result_mult add_msg_if_player(_("You detach a vine but don't have room to carry it, so you drop it.")); g->m.add_item_or_charges(pos(), newit); } else if (!can_pickWeight(newit.weight(), !OPTIONS["DANGEROUS_PICKUPS"])) { add_msg_if_player(_("Your freshly-detached vine is too heavy to carry, so you drop it.")); g->m.add_item_or_charges(pos(), newit); } else { inv.assign_empty_invlet(newit); newit = i_add(newit); add_msg_if_player(m_info, "%c - %s", newit.invlet == 0 ? ' ' : newit.invlet, newit.tname().c_str()); } tdata.powered = false; return; } else if( mut == "SELFAWARE" ) { print_health(); tdata.powered = false; return; } }
void player::activate_mutation( const trait_id &mut ) { const mutation_branch &mdata = mut.obj(); auto &tdata = my_mutations[mut]; int cost = mdata.cost; // You can take yourself halfway to Near Death levels of hunger/thirst. // Fatigue can go to Exhausted. if ((mdata.hunger && get_hunger() >= 700) || (mdata.thirst && get_thirst() >= 260) || (mdata.fatigue && get_fatigue() >= EXHAUSTED)) { // Insufficient Foo to *maintain* operation is handled in player::suffer add_msg_if_player(m_warning, _("You feel like using your %s would kill you!"), mdata.name.c_str()); return; } if (tdata.powered && tdata.charge > 0) { // Already-on units just lose a bit of charge tdata.charge--; } else { // Not-on units, or those with zero charge, have to pay the power cost if (mdata.cooldown > 0) { tdata.charge = mdata.cooldown - 1; } if (mdata.hunger){ mod_hunger(cost); } if (mdata.thirst){ mod_thirst(cost); } if (mdata.fatigue){ mod_fatigue(cost); } tdata.powered = true; // Handle stat changes from activation apply_mods(mut, true); recalc_sight_limits(); } if( mut == trait_WEB_WEAVER ) { g->m.add_field( pos(), fd_web, 1 ); add_msg_if_player(_("You start spinning web with your spinnerets!")); } else if (mut == "BURROW"){ if( is_underwater() ) { add_msg_if_player(m_info, _("You can't do that while underwater.")); tdata.powered = false; return; } tripoint dirp; if (!choose_adjacent(_("Burrow where?"), dirp)) { tdata.powered = false; return; } if( dirp == pos() ) { add_msg_if_player(_("You've got places to go and critters to beat.")); add_msg_if_player(_("Let the lesser folks eat their hearts out.")); tdata.powered = false; return; } time_duration time_to_do = 0_turns; if (g->m.is_bashable(dirp) && g->m.has_flag("SUPPORTS_ROOF", dirp) && g->m.ter(dirp) != t_tree) { // Being better-adapted to the task means that skillful Survivors can do it almost twice as fast. time_to_do = 30_minutes; } else if (g->m.move_cost(dirp) == 2 && g->get_levz() == 0 && g->m.ter(dirp) != t_dirt && g->m.ter(dirp) != t_grass) { time_to_do = 10_minutes; } else { add_msg_if_player(m_info, _("You can't burrow there.")); tdata.powered = false; return; } assign_activity( activity_id( "ACT_BURROW" ), to_moves<int>( time_to_do ), -1, 0 ); activity.placement = dirp; add_msg_if_player(_("You tear into the %s with your teeth and claws."), g->m.tername(dirp).c_str()); tdata.powered = false; return; // handled when the activity finishes } else if( mut == trait_SLIMESPAWNER ) { std::vector<tripoint> valid; for( const tripoint &dest : g->m.points_in_radius( pos(), 1 ) ) { if (g->is_empty(dest)) { valid.push_back( dest ); } } // Oops, no room to divide! if( valid.empty() ) { add_msg_if_player(m_bad, _("You focus, but are too hemmed in to birth a new slimespring!")); tdata.powered = false; return; } add_msg_if_player(m_good, _("You focus, and with a pleasant splitting feeling, birth a new slimespring!")); int numslime = 1; for (int i = 0; i < numslime && !valid.empty(); i++) { const tripoint target = random_entry_removed( valid ); if( monster * const slime = g->summon_mon( mtype_id( "mon_player_blob" ), target ) ) { slime->friendly = -1; } } if (one_in(3)) { //~ Usual enthusiastic slimespring small voices! :D add_msg_if_player(m_good, _("wow! you look just like me! we should look out for each other!")); } else if (one_in(2)) { //~ Usual enthusiastic slimespring small voices! :D add_msg_if_player(m_good, _("come on, big me, let's go!")); } else { //~ Usual enthusiastic slimespring small voices! :D add_msg_if_player(m_good, _("we're a team, we've got this!")); } tdata.powered = false; return; } else if( mut == trait_NAUSEA || mut == trait_VOMITOUS ) { vomit(); tdata.powered = false; return; } else if( mut == trait_M_FERTILE ) { spores(); tdata.powered = false; return; } else if( mut == trait_M_BLOOM ) { blossoms(); tdata.powered = false; return; } else if( mut == trait_SELFAWARE ) { print_health(); tdata.powered = false; return; } else if( !mdata.spawn_item.empty() ) { item tmpitem( mdata.spawn_item ); i_add_or_drop( tmpitem ); add_msg_if_player( _( mdata.spawn_item_message.c_str() ) ); tdata.powered = false; return; } }
void player::power_mutations() { if( !is_player() ) { // TODO: Implement NPCs activating muts return; } std::vector <std::string> passive; std::vector <std::string> active; for( auto &mut : my_mutations ) { if (!mutation_branch::get( mut.first ).activated) { passive.push_back(mut.first); } else { active.push_back(mut.first); } // New mutations are initialized with no key at all, so we have to do this here. if( mut.second.key == ' ' ) { for( const auto &letter : mutation_chars ) { if( trait_by_invlet( letter ).empty() ) { mut.second.key = letter; break; } } } } // maximal number of rows in both columns const int mutations_count = std::max(passive.size(), active.size()); int TITLE_HEIGHT = 2; int DESCRIPTION_HEIGHT = 5; // Main window /** Total required height is: * top frame line: + 1 * height of title window: + TITLE_HEIGHT * line after the title: + 1 * line with active/passive mutation captions: + 1 * height of the biggest list of active/passive mutations: + mutations_count * line before mutation description: + 1 * height of description window: + DESCRIPTION_HEIGHT * bottom frame line: + 1 * TOTAL: TITLE_HEIGHT + mutations_count + DESCRIPTION_HEIGHT + 5 */ int HEIGHT = std::min(TERMY, std::max(FULL_SCREEN_HEIGHT, TITLE_HEIGHT + mutations_count + DESCRIPTION_HEIGHT + 5)); int WIDTH = FULL_SCREEN_WIDTH + (TERMX - FULL_SCREEN_WIDTH) / 2; int START_X = (TERMX - WIDTH) / 2; int START_Y = (TERMY - HEIGHT) / 2; WINDOW *wBio = newwin(HEIGHT, WIDTH, START_Y, START_X); // Description window @ the bottom of the bio window int DESCRIPTION_START_Y = START_Y + HEIGHT - DESCRIPTION_HEIGHT - 1; int DESCRIPTION_LINE_Y = DESCRIPTION_START_Y - START_Y - 1; WINDOW *w_description = newwin(DESCRIPTION_HEIGHT, WIDTH - 2, DESCRIPTION_START_Y, START_X + 1); // Title window int TITLE_START_Y = START_Y + 1; int HEADER_LINE_Y = TITLE_HEIGHT + 1; // + lines with text in titlebar, local WINDOW *w_title = newwin(TITLE_HEIGHT, WIDTH - 2, TITLE_START_Y, START_X + 1); int scroll_position = 0; int second_column = 32 + (TERMX - FULL_SCREEN_WIDTH) / 4; // X-coordinate of the list of active mutations input_context ctxt("MUTATIONS"); ctxt.register_updown(); ctxt.register_action("ANY_INPUT"); ctxt.register_action("TOGGLE_EXAMINE"); ctxt.register_action("REASSIGN"); ctxt.register_action("HELP_KEYBINDINGS"); bool redraw = true; std::string menu_mode = "activating"; while(true) { // offset for display: mutation with index i is drawn at y=list_start_y+i // drawing the mutation starts with mutation[scroll_position] const int list_start_y = HEADER_LINE_Y + 2 - scroll_position; int max_scroll_position = HEADER_LINE_Y + 2 + mutations_count - ((menu_mode == "examining") ? DESCRIPTION_LINE_Y : (HEIGHT - 1)); if(redraw) { redraw = false; werase(wBio); draw_border(wBio); // Draw line under title mvwhline(wBio, HEADER_LINE_Y, 1, LINE_OXOX, WIDTH - 2); // Draw symbols to connect additional lines to border mvwputch(wBio, HEADER_LINE_Y, 0, BORDER_COLOR, LINE_XXXO); // |- mvwputch(wBio, HEADER_LINE_Y, WIDTH - 1, BORDER_COLOR, LINE_XOXX); // -| // Captions mvwprintz(wBio, HEADER_LINE_Y + 1, 2, c_ltblue, _("Passive:")); mvwprintz(wBio, HEADER_LINE_Y + 1, second_column, c_ltblue, _("Active:")); draw_exam_window(wBio, DESCRIPTION_LINE_Y, menu_mode == "examining"); nc_color type; if (passive.empty()) { mvwprintz(wBio, list_start_y, 2, c_ltgray, _("None")); } else { for (size_t i = scroll_position; i < passive.size(); i++) { const auto &md = mutation_branch::get( passive[i] ); const auto &td = my_mutations[passive[i]]; if (list_start_y + static_cast<int>(i) == (menu_mode == "examining" ? DESCRIPTION_LINE_Y : HEIGHT - 1)) { break; } type = c_cyan; mvwprintz(wBio, list_start_y + i, 2, type, "%c %s", td.key, md.name.c_str()); } } if (active.empty()) { mvwprintz(wBio, list_start_y, second_column, c_ltgray, _("None")); } else { for (size_t i = scroll_position; i < active.size(); i++) { const auto &md = mutation_branch::get( active[i] ); const auto &td = my_mutations[active[i]]; if (list_start_y + static_cast<int>(i) == (menu_mode == "examining" ? DESCRIPTION_LINE_Y : HEIGHT - 1)) { break; } if (!td.powered) { type = c_red; }else if (td.powered) { type = c_ltgreen; } else { type = c_ltred; } // TODO: track resource(s) used and specify mvwputch( wBio, list_start_y + i, second_column, type, td.key ); std::stringstream mut_desc; mut_desc << md.name; if ( md.cost > 0 && md.cooldown > 0 ) { mut_desc << string_format( _(" - %d RU / %d turns"), md.cost, md.cooldown ); } else if ( md.cost > 0 ) { mut_desc << string_format( _(" - %d RU"), md.cost ); } else if ( md.cooldown > 0 ) { mut_desc << string_format( _(" - %d turns"), md.cooldown ); } if ( td.powered ) { mut_desc << _(" - Active"); } mvwprintz( wBio, list_start_y + i, second_column + 2, type, mut_desc.str().c_str() ); } } // Scrollbar if(scroll_position > 0) { mvwputch(wBio, HEADER_LINE_Y + 2, 0, c_ltgreen, '^'); } if(scroll_position < max_scroll_position && max_scroll_position > 0) { mvwputch(wBio, (menu_mode == "examining" ? DESCRIPTION_LINE_Y : HEIGHT - 1) - 1, 0, c_ltgreen, 'v'); } } wrefresh(wBio); show_mutations_titlebar(w_title, this, menu_mode); const std::string action = ctxt.handle_input(); const long ch = ctxt.get_raw_input().get_first_input(); if (menu_mode == "reassigning") { menu_mode = "activating"; const auto mut_id = trait_by_invlet( ch ); if( mut_id.empty() ) { // Selected an non-existing mutation (or escape, or ...) continue; } redraw = true; const long newch = popup_getkey(_("%s; enter new letter."), mutation_branch::get_name( mut_id ).c_str()); wrefresh(wBio); if(newch == ch || newch == ' ' || newch == KEY_ESCAPE) { continue; } if( !mutation_chars.valid( newch ) ) { popup( _("Invalid mutation letter. Only those characters are valid:\n\n%s"), mutation_chars.get_allowed_chars().c_str() ); continue; } const auto other_mut_id = trait_by_invlet( newch ); if( !other_mut_id.empty() ) { std::swap(my_mutations[mut_id].key, my_mutations[other_mut_id].key); } else { my_mutations[mut_id].key = newch; } // TODO: show a message like when reassigning a key to an item? } else if (action == "DOWN") { if(scroll_position < max_scroll_position) { scroll_position++; redraw = true; } } else if (action == "UP") { if(scroll_position > 0) { scroll_position--; redraw = true; } } else if (action == "REASSIGN") { menu_mode = "reassigning"; } else if (action == "TOGGLE_EXAMINE") { // switches between activation and examination menu_mode = menu_mode == "activating" ? "examining" : "activating"; werase(w_description); draw_exam_window(wBio, DESCRIPTION_LINE_Y, false); redraw = true; }else if (action == "HELP_KEYBINDINGS") { redraw = true; } else { const auto mut_id = trait_by_invlet( ch ); if( mut_id.empty() ) { // entered a key that is not mapped to any mutation, // -> leave screen break; } const auto &mut_data = mutation_branch::get( mut_id ); if (menu_mode == "activating") { if (mut_data.activated) { if (my_mutations[mut_id].powered) { add_msg_if_player(m_neutral, _("You stop using your %s."), mut_data.name.c_str()); deactivate_mutation( mut_id ); delwin(w_title); delwin(w_description); delwin(wBio); // Action done, leave screen break; } else if( (!mut_data.hunger || get_hunger() <= 400) && (!mut_data.thirst || get_thirst() <= 400) && (!mut_data.fatigue || get_fatigue() <= 400) ) { // this will clear the mutations menu for targeting purposes werase(wBio); wrefresh(wBio); delwin(w_title); delwin(w_description); delwin(wBio); g->draw(); add_msg_if_player( m_neutral, _("You activate your %s."), mut_data.name.c_str() ); activate_mutation( mut_id ); // Action done, leave screen break; } else { popup( _( "You don't have enough in you to activate your %s!" ), mut_data.name.c_str() ); redraw = true; continue; } } else { popup(_("\ You cannot activate %s! To read a description of \ %s, press '!', then '%c'."), mut_data.name.c_str(), mut_data.name.c_str(), my_mutations[mut_id].key ); redraw = true; } } if (menu_mode == "examining") { // Describing mutations, not activating them! draw_exam_window(wBio, DESCRIPTION_LINE_Y, true); // Clear the lines first werase(w_description); fold_and_print(w_description, 0, 0, WIDTH - 2, c_ltblue, mut_data.description); wrefresh(w_description); } } } //if we activated a mutation, already killed the windows if(!(menu_mode == "activating")) { werase(wBio); wrefresh(wBio); delwin(w_title); delwin(w_description); delwin(wBio); } }