Permalink
Cannot retrieve contributors at this time
Join GitHub today
GitHub is home to over 36 million developers working together to host and review code, manage projects, and build software together.
Sign up
Fetching contributors…
| #include "player.h" | |
| #include "action.h" | |
| #include "game.h" | |
| #include "map.h" | |
| #include "debug.h" | |
| #include "rng.h" | |
| #include "input.h" | |
| #include "item.h" | |
| #include "bionics.h" | |
| #include "line.h" | |
| #include "json.h" | |
| #include "messages.h" | |
| #include "overmapbuffer.h" | |
| #include "sounds.h" | |
| #include "translations.h" | |
| #include "catacharset.h" | |
| #include "input.h" | |
| #include "monster.h" | |
| #include "overmap.h" | |
| #include "itype.h" | |
| #include "vehicle.h" | |
| #include "field.h" | |
| #include "weather_gen.h" | |
| #include "weather.h" | |
| #include "cata_utility.h" | |
| #include <math.h> //sqrt | |
| #include <algorithm> //std::min | |
| #include <sstream> | |
| // '!', '-' and '=' are uses as default bindings in the menu | |
| const invlet_wrapper bionic_chars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\"#&()*+./:;@[\\]^_{|}"); | |
| const skill_id skilll_electronics( "electronics" ); | |
| const skill_id skilll_firstaid( "firstaid" ); | |
| const skill_id skilll_mechanics( "mechanics" ); | |
| const efftype_id effect_adrenaline( "adrenaline" ); | |
| const efftype_id effect_adrenaline_mycus( "adrenaline_mycus" ); | |
| const efftype_id effect_bleed( "bleed" ); | |
| const efftype_id effect_bloodworms( "bloodworms" ); | |
| const efftype_id effect_brainworms( "brainworms" ); | |
| const efftype_id effect_cig( "cig" ); | |
| const efftype_id effect_datura( "datura" ); | |
| const efftype_id effect_dermatik( "dermatik" ); | |
| const efftype_id effect_drunk( "drunk" ); | |
| const efftype_id effect_fungus( "fungus" ); | |
| const efftype_id effect_hallu( "hallu" ); | |
| const efftype_id effect_high( "high" ); | |
| const efftype_id effect_iodine( "iodine" ); | |
| const efftype_id effect_meth( "meth" ); | |
| const efftype_id effect_paincysts( "paincysts" ); | |
| const efftype_id effect_pblue( "pblue" ); | |
| const efftype_id effect_pkill1( "pkill1" ); | |
| const efftype_id effect_pkill2( "pkill2" ); | |
| const efftype_id effect_pkill3( "pkill3" ); | |
| const efftype_id effect_pkill_l( "pkill_l" ); | |
| const efftype_id effect_poison( "poison" ); | |
| const efftype_id effect_stung( "stung" ); | |
| const efftype_id effect_tapeworm( "tapeworm" ); | |
| const efftype_id effect_teleglow( "teleglow" ); | |
| const efftype_id effect_tetanus( "tetanus" ); | |
| const efftype_id effect_took_flumed( "took_flumed" ); | |
| const efftype_id effect_took_prozac( "took_prozac" ); | |
| const efftype_id effect_took_xanax( "took_xanax" ); | |
| const efftype_id effect_visuals( "visuals" ); | |
| const efftype_id effect_weed_high( "weed_high" ); | |
| namespace { | |
| std::map<std::string, bionic_data> bionics; | |
| std::vector<std::string> faulty_bionics; | |
| } //namespace | |
| bool is_valid_bionic(std::string const& id) | |
| { | |
| return !!bionics.count(id); | |
| } | |
| bionic_data const& bionic_info(std::string const &id) | |
| { | |
| auto const it = bionics.find(id); | |
| if (it != bionics.end()) { | |
| return it->second; | |
| } | |
| debugmsg("bad bionic id"); | |
| static bionic_data const null_value {"bad bionic", false, false, 0, 0, 0, 0, 0, "bad_bionic", false}; | |
| return null_value; | |
| } | |
| void bionics_install_failure(player *u, int difficulty, int success); | |
| bionic_data::bionic_data(std::string nname, bool ps, bool tog, int pac, int pad, int pot, | |
| int ct, int cap, std::string desc, bool fault | |
| ) : name(std::move(nname)), description(std::move(desc)), power_activate(pac), | |
| power_deactivate(pad), power_over_time(pot), charge_time(ct), capacity(cap), faulty(fault), | |
| power_source(ps), activated(tog || pac || ct), toggled(tog) | |
| { | |
| } | |
| void show_bionics_titlebar(WINDOW *window, player *p, std::string menu_mode) | |
| { | |
| werase(window); | |
| std::string caption = _("BIONICS -"); | |
| int cap_offset = utf8_width(caption) + 1; | |
| mvwprintz(window, 0, 0, c_blue, "%s", caption.c_str()); | |
| std::stringstream pwr; | |
| pwr << string_format(_("Power: %i/%i"), int(p->power_level), int(p->max_power_level)); | |
| int pwr_length = utf8_width(pwr.str()) + 1; | |
| mvwprintz(window, 0, getmaxx(window) - pwr_length, c_white, "%s", pwr.str().c_str()); | |
| std::string desc; | |
| int desc_length = getmaxx(window) - cap_offset - pwr_length; | |
| if(menu_mode == "reassigning") { | |
| desc = _("Reassigning.\nSelect a bionic to reassign or press SPACE to cancel."); | |
| } else if(menu_mode == "activating") { | |
| desc = _("<color_green>Activating</color> <color_yellow>!</color> to examine, <color_yellow>-</color> to remove, <color_yellow>=</color> to reassign, <color_yellow>TAB</color> to switch tabs."); | |
| } else if(menu_mode == "removing") { | |
| desc = _("<color_red>Removing</color> <color_yellow>!</color> to activate, <color_yellow>-</color> to remove, <color_yellow>=</color> to reassign, <color_yellow>TAB</color> to switch tabs."); | |
| } else if(menu_mode == "examining") { | |
| desc = _("<color_ltblue>Examining</color> <color_yellow>!</color> to activate, <color_yellow>-</color> to remove, <color_yellow>=</color> to reassign, <color_yellow>TAB</color> to switch tabs."); | |
| } | |
| fold_and_print(window, 0, cap_offset, desc_length, c_white, desc); | |
| wrefresh(window); | |
| } | |
| //builds the power usage string of a given bionic | |
| std::string build_bionic_poweronly_string(bionic const &bio) | |
| { | |
| std::ostringstream power_desc; | |
| bool hasPreviousText = false; | |
| if (bionics[bio.id].power_over_time > 0 && bionics[bio.id].charge_time > 0) { | |
| power_desc << ( | |
| bionics[bio.id].charge_time == 1 | |
| ? string_format(_("%d PU / turn"), | |
| bionics[bio.id].power_over_time) | |
| : string_format(_("%d PU / %d turns"), | |
| bionics[bio.id].power_over_time, | |
| bionics[bio.id].charge_time)); | |
| hasPreviousText = true; | |
| } | |
| if (bionics[bio.id].power_activate > 0 && !bionics[bio.id].charge_time) { | |
| if(hasPreviousText){ | |
| power_desc << ", "; | |
| } | |
| power_desc << string_format(_("%d PU act"), | |
| bionics[bio.id].power_activate); | |
| hasPreviousText = true; | |
| } | |
| if (bionics[bio.id].power_deactivate > 0 && !bionics[bio.id].charge_time) { | |
| if(hasPreviousText){ | |
| power_desc << ", "; | |
| } | |
| power_desc << string_format(_("%d PU deact"), | |
| bionics[bio.id].power_deactivate); | |
| hasPreviousText = true; | |
| } | |
| if (bionics[bio.id].toggled) { | |
| if(hasPreviousText){ | |
| power_desc << ", "; | |
| } | |
| power_desc << (bio.powered ? _("ON") : _("OFF")); | |
| } | |
| return power_desc.str(); | |
| } | |
| //generates the string that show how much power a bionic uses | |
| std::string build_bionic_powerdesc_string(bionic const &bio) | |
| { | |
| std::ostringstream power_desc; | |
| std::string power_string = build_bionic_poweronly_string(bio); | |
| power_desc << bionics[bio.id].name; | |
| if(power_string.length()>0){ | |
| power_desc << ", " << power_string; | |
| } | |
| return power_desc.str(); | |
| } | |
| //get a text color depending on the power/powering state of the bionic | |
| nc_color get_bionic_text_color(bionic const &bio, bool const isHighlightedBionic) | |
| { | |
| nc_color type = c_white; | |
| if(bionics[bio.id].activated){ | |
| if(isHighlightedBionic){ | |
| if (bio.powered && !bionics[bio.id].power_source) { | |
| type = h_red; | |
| } else if (bionics[bio.id].power_source && !bio.powered) { | |
| type = h_ltcyan; | |
| } else if (bionics[bio.id].power_source && bio.powered) { | |
| type = h_ltgreen; | |
| } else { | |
| type = h_ltred; | |
| } | |
| }else{ | |
| if (bio.powered && !bionics[bio.id].power_source) { | |
| type = c_red; | |
| } else if (bionics[bio.id].power_source && !bio.powered) { | |
| type = c_ltcyan; | |
| } else if (bionics[bio.id].power_source && bio.powered) { | |
| type = c_ltgreen; | |
| } else { | |
| type = c_ltred; | |
| } | |
| } | |
| }else{ | |
| if(isHighlightedBionic){ | |
| if (bionics[bio.id].power_source) { | |
| type = h_ltcyan; | |
| } else { | |
| type = h_cyan; | |
| } | |
| }else{ | |
| if (bionics[bio.id].power_source) { | |
| type = c_ltcyan; | |
| } else { | |
| type = c_cyan; | |
| } | |
| } | |
| } | |
| return type; | |
| } | |
| void player::power_bionics() | |
| { | |
| std::vector <bionic *> passive; | |
| std::vector <bionic *> active; | |
| bionic *bio_last = NULL; | |
| std::string tab_mode = "TAB_ACTIVE"; | |
| for( auto &elem : my_bionics ) { | |
| if( !bionics[elem.id].activated ) { | |
| passive.push_back( &elem ); | |
| } else { | |
| active.push_back( &elem ); | |
| } | |
| } | |
| // maximal number of rows in both columns | |
| int active_bionic_count = active.size(); | |
| int passive_bionic_count = passive.size(); | |
| int bionic_count = std::max(passive_bionic_count, active_bionic_count); | |
| //added title_tab_height for the tabbed bionic display | |
| int TITLE_HEIGHT = 2; | |
| int TITLE_TAB_HEIGHT = 3; | |
| // Main window | |
| /** Total required height is: | |
| * top frame line: + 1 | |
| * height of title window: + TITLE_HEIGHT | |
| * height of tabs: + TITLE_TAB_HEIGHT | |
| * height of the biggest list of active/passive bionics: + bionic_count | |
| * bottom frame line: + 1 | |
| * TOTAL: TITLE_HEIGHT + TITLE_TAB_HEIGHT + bionic_count + 2 | |
| */ | |
| int HEIGHT = std::min(TERMY, std::max(FULL_SCREEN_HEIGHT, | |
| TITLE_HEIGHT + TITLE_TAB_HEIGHT + bionic_count + 2)); | |
| int WIDTH = FULL_SCREEN_WIDTH + (TERMX - FULL_SCREEN_WIDTH) / 2; | |
| int START_X = (TERMX - WIDTH) / 2; | |
| int START_Y = (TERMY - HEIGHT) / 2; | |
| //wBio is the entire bionic window | |
| WINDOW *wBio = newwin(HEIGHT, WIDTH, START_Y, START_X); | |
| WINDOW_PTR wBioptr( wBio ); | |
| int LIST_HEIGHT = HEIGHT - TITLE_HEIGHT - TITLE_TAB_HEIGHT - 2; | |
| int DESCRIPTION_WIDTH = WIDTH - 2 - 40; | |
| int DESCRIPTION_START_Y = START_Y + TITLE_HEIGHT + TITLE_TAB_HEIGHT + 1; | |
| int DESCRIPTION_START_X = START_X + 1 + 40; | |
| //w_description is the description panel that is controlled with ! key | |
| WINDOW *w_description = newwin(LIST_HEIGHT, DESCRIPTION_WIDTH, | |
| DESCRIPTION_START_Y, DESCRIPTION_START_X); | |
| WINDOW_PTR w_descriptionptr( w_description ); | |
| // Title window | |
| int TITLE_START_Y = START_Y + 1; | |
| int HEADER_LINE_Y = TITLE_HEIGHT + TITLE_TAB_HEIGHT + 1; // + lines with text in titlebar, local | |
| WINDOW *w_title = newwin(TITLE_HEIGHT, WIDTH - 2, TITLE_START_Y, START_X + 1); | |
| WINDOW_PTR w_titleptr( w_title ); | |
| int TAB_START_Y = TITLE_START_Y + 2; | |
| //w_tabs is the tab bar for passive and active bionic groups | |
| WINDOW *w_tabs = newwin(TITLE_TAB_HEIGHT, WIDTH - 2, TAB_START_Y, START_X + 1); | |
| WINDOW_PTR w_tabsptr( w_tabs ); | |
| int scroll_position = 0; | |
| int cursor = 0; | |
| //generate the tab title string and a count of the bionics owned | |
| std::string menu_mode = "activating"; | |
| std::ostringstream tabname; | |
| tabname << _("ACTIVE"); | |
| if(active_bionic_count>0){ | |
| tabname << "(" << active_bionic_count << ")"; | |
| } | |
| std::string active_tab_name = tabname.str(); | |
| tabname.str(""); | |
| tabname << _("PASSIVE"); | |
| if(passive_bionic_count > 0){ | |
| tabname << "(" << passive_bionic_count << ")"; | |
| } | |
| std::string passive_tab_name = tabname.str(); | |
| const int tabs_start = 1; | |
| const int tab_step = 3; | |
| // offset for display: bionic with index i is drawn at y=list_start_y+i | |
| // drawing the bionics starts with bionic[scroll_position] | |
| const int list_start_y = HEADER_LINE_Y;// - scroll_position; | |
| int half_list_view_location = LIST_HEIGHT / 2; | |
| int max_scroll_position = std::max(0, (tab_mode == "TAB_ACTIVE" ? active_bionic_count : passive_bionic_count) - LIST_HEIGHT); | |
| input_context ctxt("BIONICS"); | |
| ctxt.register_updown(); | |
| ctxt.register_action("ANY_INPUT"); | |
| ctxt.register_action("TOGGLE_EXAMINE"); | |
| ctxt.register_action("REASSIGN"); | |
| ctxt.register_action("REMOVE"); | |
| ctxt.register_action("NEXT_TAB"); | |
| ctxt.register_action("PREV_TAB"); | |
| ctxt.register_action("CONFIRM"); | |
| ctxt.register_action("HELP_KEYBINDINGS"); | |
| bool recalc = false; | |
| bool redraw = true; | |
| for (;;) { | |
| if(recalc) { | |
| active.clear(); | |
| passive.clear(); | |
| for( auto &elem : my_bionics ) { | |
| if( !bionics[elem.id].activated ) { | |
| passive.push_back( &elem ); | |
| } else { | |
| active.push_back( &elem ); | |
| } | |
| } | |
| active_bionic_count = active.size(); | |
| passive_bionic_count = passive.size(); | |
| bionic_count = std::max(passive_bionic_count, active_bionic_count); | |
| if(active_bionic_count == 0 && passive_bionic_count > 0){ | |
| tab_mode = "TAB_PASSIVE"; | |
| } | |
| max_scroll_position = std::max(0, (tab_mode == "TAB_ACTIVE" ? active_bionic_count : passive_bionic_count) - LIST_HEIGHT); | |
| if(--cursor < 0) { | |
| cursor = 0; | |
| } | |
| if(scroll_position > max_scroll_position && cursor - scroll_position < LIST_HEIGHT - half_list_view_location) { | |
| scroll_position--; | |
| } | |
| recalc = false; | |
| } | |
| //track which list we are looking at | |
| std::vector<bionic*> *current_bionic_list = (tab_mode == "TAB_ACTIVE" ? &active : &passive); | |
| if(redraw) { | |
| redraw = false; | |
| werase(wBio); | |
| draw_border(wBio); | |
| // Draw symbols to connect additional lines to border | |
| mvwputch(wBio, HEADER_LINE_Y - 1, 0, BORDER_COLOR, LINE_XXXO); // |- | |
| mvwputch(wBio, HEADER_LINE_Y - 1, WIDTH - 1, BORDER_COLOR, LINE_XOXX); // -| | |
| nc_color type; | |
| if(tab_mode == "TAB_PASSIVE"){ | |
| if (passive.empty()) { | |
| mvwprintz(wBio, list_start_y + 1, 2, c_ltgray, _("No passive bionics installed.")); | |
| } else { | |
| for (size_t i = scroll_position; i < passive.size(); i++) { | |
| if (list_start_y + static_cast<int>(i) - scroll_position == HEIGHT - 1) { | |
| break; | |
| } | |
| bool isHighlighted = false; | |
| if(cursor == static_cast<int>(i)){ | |
| isHighlighted = true; | |
| } | |
| type = get_bionic_text_color(*passive[i], isHighlighted); | |
| mvwprintz(wBio, list_start_y + i - scroll_position, 2, type, "%c %s", passive[i]->invlet, | |
| bionics[passive[i]->id].name.c_str()); | |
| } | |
| } | |
| } | |
| if(tab_mode == "TAB_ACTIVE"){ | |
| if (active.empty()) { | |
| mvwprintz(wBio, list_start_y + 1, 2, c_ltgray, _("No activatable bionics installed.")); | |
| } else { | |
| for (size_t i = scroll_position; i < active.size(); i++) { | |
| if (list_start_y + static_cast<int>(i) - scroll_position == HEIGHT - 1) { | |
| break; | |
| } | |
| bool isHighlighted = false; | |
| if(cursor == static_cast<int>(i)){ | |
| isHighlighted = true; | |
| } | |
| type = get_bionic_text_color(*active[i], isHighlighted); | |
| mvwputch(wBio, list_start_y + i - scroll_position, 2, type, active[i]->invlet); | |
| mvwputch(wBio, list_start_y + i - scroll_position, 3, type, ' '); | |
| std::string power_desc = build_bionic_powerdesc_string(*active[i]); | |
| std::string tmp = utf8_truncate(power_desc, WIDTH - 3); | |
| mvwprintz(wBio, list_start_y + i - scroll_position, 2 + 2, type, tmp.c_str()); | |
| } | |
| } | |
| } | |
| // Scrollbar | |
| if(scroll_position > 0) { | |
| mvwputch(wBio, HEADER_LINE_Y, 0, c_ltgreen, '^'); | |
| } | |
| if(scroll_position < max_scroll_position && max_scroll_position > 0) { | |
| mvwputch(wBio, HEIGHT - 1 - 1, | |
| 0, c_ltgreen, 'v'); | |
| } | |
| } | |
| wrefresh(wBio); | |
| //handle tab drawing after main window is refreshed | |
| werase(w_tabs); | |
| int width = getmaxx(w_tabs); | |
| for (int i = 0; i < width; i++) { | |
| mvwputch(w_tabs, 2, i, BORDER_COLOR, LINE_OXOX); | |
| } | |
| int tab_x = tabs_start; | |
| draw_tab(w_tabs, tab_x, active_tab_name, tab_mode == "TAB_ACTIVE"); | |
| tab_x += tab_step + utf8_width(active_tab_name); | |
| draw_tab(w_tabs, tab_x, passive_tab_name, tab_mode != "TAB_ACTIVE"); | |
| wrefresh(w_tabs); | |
| show_bionics_titlebar(w_title, this, menu_mode); | |
| // Description | |
| if(menu_mode == "examining" && current_bionic_list->size() > 0){ | |
| werase(w_description); | |
| std::ostringstream power_only_desc; | |
| std::string poweronly_string; | |
| std::string bionic_name; | |
| if(tab_mode == "TAB_ACTIVE"){ | |
| bionic_name = bionics[active[cursor]->id].name; | |
| poweronly_string = build_bionic_poweronly_string(*active[cursor]); | |
| }else{ | |
| bionic_name = bionics[passive[cursor]->id].name; | |
| poweronly_string = build_bionic_poweronly_string(*passive[cursor]); | |
| } | |
| int ypos = 0; | |
| ypos += fold_and_print(w_description, ypos, 0, DESCRIPTION_WIDTH, c_white, bionic_name); | |
| if(poweronly_string.length() > 0){ | |
| power_only_desc << _("Power usage: ") << poweronly_string; | |
| ypos += fold_and_print(w_description, ypos, 0, DESCRIPTION_WIDTH, c_ltgray, power_only_desc.str()); | |
| } | |
| ypos += fold_and_print(w_description, ypos, 0, DESCRIPTION_WIDTH, c_ltblue, bionics[(*current_bionic_list)[cursor]->id].description); | |
| wrefresh(w_description); | |
| } | |
| const std::string action = ctxt.handle_input(); | |
| const long ch = ctxt.get_raw_input().get_first_input(); | |
| bionic *tmp = NULL; | |
| bool confirmCheck = false; | |
| if (menu_mode == "reassigning") { | |
| menu_mode = "activating"; | |
| tmp = bionic_by_invlet(ch); | |
| if(tmp == nullptr) { | |
| // Selected an non-existing bionic (or escape, or ...) | |
| continue; | |
| } | |
| redraw = true; | |
| const long newch = popup_getkey(_("%s; enter new letter."), | |
| bionics[tmp->id].name.c_str()); | |
| wrefresh(wBio); | |
| if(newch == ch || newch == ' ' || newch == KEY_ESCAPE) { | |
| continue; | |
| } | |
| if( !bionic_chars.valid( newch ) ) { | |
| popup( _("Invlid bionic letter. Only those characters are valid:\n\n%s"), | |
| bionic_chars.get_allowed_chars().c_str() ); | |
| continue; | |
| } | |
| bionic *otmp = bionic_by_invlet(newch); | |
| if(otmp != nullptr) { | |
| std::swap(tmp->invlet, otmp->invlet); | |
| } else { | |
| tmp->invlet = newch; | |
| } | |
| // TODO: show a message like when reassigning a key to an item? | |
| } else if (action == "NEXT_TAB") { | |
| redraw = true; | |
| scroll_position = 0; | |
| cursor = 0; | |
| if(tab_mode == "TAB_ACTIVE"){ | |
| tab_mode = "TAB_PASSIVE"; | |
| }else{ | |
| tab_mode = "TAB_ACTIVE"; | |
| } | |
| } else if (action == "PREV_TAB") { | |
| redraw = true; | |
| scroll_position = 0; | |
| cursor = 0; | |
| if(tab_mode == "TAB_PASSIVE"){ | |
| tab_mode = "TAB_ACTIVE"; | |
| }else{ | |
| tab_mode = "TAB_PASSIVE"; | |
| } | |
| } else if (action == "DOWN") { | |
| redraw = true; | |
| if(static_cast<size_t>(cursor)<current_bionic_list->size()-1){ | |
| cursor++; | |
| } | |
| if(scroll_position < max_scroll_position && cursor - scroll_position > LIST_HEIGHT - half_list_view_location) { | |
| scroll_position++; | |
| } | |
| } else if (action == "UP") { | |
| redraw = true; | |
| if(cursor>0){ | |
| cursor--; | |
| } | |
| if(scroll_position > 0 && cursor - scroll_position < half_list_view_location) { | |
| scroll_position--; | |
| } | |
| } else if (action == "REASSIGN") { | |
| menu_mode = "reassigning"; | |
| } else if (action == "TOGGLE_EXAMINE") { // switches between activation and examination | |
| menu_mode = menu_mode == "activating" ? "examining" : "activating"; | |
| redraw = true; | |
| } else if (action == "REMOVE") { | |
| menu_mode = "removing"; | |
| redraw = true; | |
| } else if (action == "HELP_KEYBINDINGS") { | |
| redraw = true; | |
| } else if (action == "CONFIRM"){ | |
| confirmCheck = true; | |
| } else { | |
| confirmCheck = true; | |
| } | |
| //confirmation either occurred by pressing enter where the bionic cursor is, or the hotkey was selected | |
| if(confirmCheck){ | |
| auto& bio_list = tab_mode == "TAB_ACTIVE" ? active : passive; | |
| if(action == "CONFIRM" && current_bionic_list->size() > 0){ | |
| tmp = bio_list[cursor]; | |
| }else{ | |
| tmp = bionic_by_invlet(ch); | |
| if(tmp && tmp != bio_last) { | |
| // new bionic selected, update cursor and scroll position | |
| int temp_cursor = 0; | |
| for(temp_cursor = 0; temp_cursor < (int)bio_list.size(); temp_cursor++) { | |
| if(bio_list[temp_cursor] == tmp) { | |
| break; | |
| } | |
| } | |
| // if bionic is not found in current list, ignore the attempt to view/activate | |
| if(temp_cursor >= (int)bio_list.size()) { | |
| continue; | |
| } | |
| //relocate cursor to the bionic that was found | |
| cursor = temp_cursor; | |
| scroll_position = 0; | |
| while(scroll_position < max_scroll_position && cursor - scroll_position > LIST_HEIGHT - half_list_view_location) { | |
| scroll_position++; | |
| } | |
| } | |
| } | |
| if(!tmp) { | |
| // entered a key that is not mapped to any bionic, | |
| // -> leave screen | |
| break; | |
| } | |
| bio_last = tmp; | |
| const std::string &bio_id = tmp->id; | |
| const bionic_data &bio_data = bionics[bio_id]; | |
| if (menu_mode == "removing") { | |
| if (uninstall_bionic(bio_id)) { | |
| recalc = true; | |
| redraw = true; | |
| continue; | |
| } | |
| } | |
| if (menu_mode == "activating") { | |
| if (bio_data.activated) { | |
| int b = tmp - &my_bionics[0]; | |
| if (tmp->powered) { | |
| deactivate_bionic(b); | |
| } else { | |
| activate_bionic(b); | |
| } | |
| // update message log and the menu | |
| g->refresh_all(); | |
| redraw = true; | |
| continue; | |
| } else { | |
| popup(_("You can not activate %s!\n" | |
| "To read a description of %s, press '!', then '%c'."), bio_data.name.c_str(), bio_data.name.c_str(), tmp->invlet); | |
| redraw = true; | |
| } | |
| } else if (menu_mode == "examining") { // Describing bionics, allow user to jump to description key | |
| redraw = true; | |
| if(action != "CONFIRM"){ | |
| for(size_t i = 0; i < active.size(); i++){ | |
| if(active[i] == tmp){ | |
| tab_mode = "TAB_ACTIVE"; | |
| cursor = static_cast<int>(i); | |
| int max_scroll_check = std::max(0, active_bionic_count - LIST_HEIGHT); | |
| if(static_cast<int>(i) > max_scroll_check){ | |
| scroll_position = max_scroll_check; | |
| }else{ | |
| scroll_position = i; | |
| } | |
| break; | |
| } | |
| } | |
| for(size_t i = 0; i < passive.size(); i++){ | |
| if(passive[i] == tmp){ | |
| tab_mode = "TAB_PASSIVE"; | |
| cursor = static_cast<int>(i); | |
| int max_scroll_check = std::max(0, passive_bionic_count - LIST_HEIGHT); | |
| if(static_cast<int>(i) > max_scroll_check){ | |
| scroll_position = max_scroll_check; | |
| }else{ | |
| scroll_position = i; | |
| } | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| void draw_exam_window(WINDOW *win, int border_line, bool examination) | |
| { | |
| int width = getmaxx(win); | |
| if (examination) { | |
| for (int i = 1; i < width - 1; i++) { | |
| mvwputch(win, border_line, i, BORDER_COLOR, LINE_OXOX); // Draw line above description | |
| } | |
| mvwputch(win, border_line, 0, BORDER_COLOR, LINE_XXXO); // |- | |
| mvwputch(win, border_line, width - 1, BORDER_COLOR, LINE_XOXX); // -| | |
| } else { | |
| for (int i = 1; i < width - 1; i++) { | |
| mvwprintz(win, border_line, i, c_black, " "); // Erase line | |
| } | |
| mvwputch(win, border_line, 0, BORDER_COLOR, LINE_XOXO); // | | |
| mvwputch(win, border_line, width, BORDER_COLOR, LINE_XOXO); // | | |
| } | |
| } | |
| // Why put this in a Big Switch? Why not let bionics have pointers to | |
| // functions, much like monsters and items? | |
| // | |
| // Well, because like diseases, which are also in a Big Switch, bionics don't | |
| // share functions.... | |
| bool player::activate_bionic(int b, bool eff_only) | |
| { | |
| bionic &bio = my_bionics[b]; | |
| // Special compatibility code for people who updated saves with their claws out | |
| if ((weapon.type->id == "bio_claws_weapon" && bio.id == "bio_claws_weapon") || | |
| (weapon.type->id == "bio_blade_weapon" && bio.id == "bio_blade_weapon")) { | |
| return deactivate_bionic(b); | |
| } | |
| // eff_only means only do the effect without messing with stats or displaying messages | |
| if (!eff_only) { | |
| if (bio.powered) { | |
| // It's already on! | |
| return false; | |
| } | |
| if (power_level < bionics[bio.id].power_activate) { | |
| add_msg(m_info, _("You don't have the power to activate your %s."), bionics[bio.id].name.c_str()); | |
| return false; | |
| } | |
| //We can actually activate now, do activation-y things | |
| charge_power(-bionics[bio.id].power_activate); | |
| if (bionics[bio.id].toggled || bionics[bio.id].charge_time > 0) { | |
| bio.powered = true; | |
| } | |
| if (bionics[bio.id].charge_time > 0) { | |
| bio.charge = bionics[bio.id].charge_time; | |
| } | |
| add_msg(m_info, _("You activate your %s."), bionics[bio.id].name.c_str()); | |
| } | |
| std::vector<std::string> good; | |
| std::vector<std::string> bad; | |
| tripoint dirp = pos(); | |
| int &dirx = dirp.x; | |
| int &diry = dirp.y; | |
| item tmp_item; | |
| w_point const weatherPoint = g->weather_gen->get_weather( global_square_location(), calendar::turn ); | |
| // On activation effects go here | |
| if(bio.id == "bio_painkiller") { | |
| mod_pain( -2 ); | |
| mod_painkiller( 6 ); | |
| if ( get_painkiller() > get_pain() ) { | |
| set_painkiller( get_pain() ); | |
| } | |
| } else if (bio.id == "bio_ears" && has_active_bionic("bio_earplugs")) { | |
| for (auto &i : my_bionics) { | |
| if (i.id == "bio_earplugs") { | |
| i.powered = false; | |
| add_msg(m_info, _("Your %s automatically turn off."), bionics[i.id].name.c_str()); | |
| } | |
| } | |
| } else if (bio.id == "bio_earplugs" && has_active_bionic("bio_ears")) { | |
| for (auto &i : my_bionics) { | |
| if (i.id == "bio_ears") { | |
| i.powered = false; | |
| add_msg(m_info, _("Your %s automatically turns off."), bionics[i.id].name.c_str()); | |
| } | |
| } | |
| } else if (bio.id == "bio_tools") { | |
| invalidate_crafting_inventory(); | |
| } else if (bio.id == "bio_cqb") { | |
| if (!pick_style()) { | |
| bio.powered = false; | |
| add_msg(m_info, _("You change your mind and turn it off.")); | |
| return false; | |
| } | |
| } else if (bio.id == "bio_nanobots") { | |
| remove_effect( effect_bleed ); | |
| healall(4); | |
| } else if (bio.id == "bio_resonator") { | |
| //~Sound of a bionic sonic-resonator shaking the area | |
| sounds::sound( pos(), 30, _("VRRRRMP!")); | |
| for (int i = posx() - 1; i <= posx() + 1; i++) { | |
| for (int j = posy() - 1; j <= posy() + 1; j++) { | |
| tripoint bashpoint( i, j, posz() ); | |
| g->m.bash( bashpoint, 110 ); | |
| g->m.bash( bashpoint, 110 ); // Multibash effect, so that doors &c will fall | |
| g->m.bash( bashpoint, 110 ); | |
| } | |
| } | |
| } else if (bio.id == "bio_time_freeze") { | |
| moves += power_level; | |
| power_level = 0; | |
| add_msg(m_good, _("Your speed suddenly increases!")); | |
| if (one_in(3)) { | |
| add_msg(m_bad, _("Your muscles tear with the strain.")); | |
| apply_damage( nullptr, bp_arm_l, rng( 5, 10 ) ); | |
| apply_damage( nullptr, bp_arm_r, rng( 5, 10 ) ); | |
| apply_damage( nullptr, bp_leg_l, rng( 7, 12 ) ); | |
| apply_damage( nullptr, bp_leg_r, rng( 7, 12 ) ); | |
| apply_damage( nullptr, bp_torso, rng( 5, 15 ) ); | |
| } | |
| if (one_in(5)) { | |
| add_effect( effect_teleglow, rng( 50, 400 ) ); | |
| } | |
| } else if (bio.id == "bio_teleport") { | |
| g->teleport(); | |
| add_effect( effect_teleglow, 300 ); | |
| // TODO: More stuff here (and bio_blood_filter) | |
| } else if(bio.id == "bio_blood_anal") { | |
| WINDOW *w = newwin(20, 40, 3 + ((TERMY > 25) ? (TERMY - 25) / 2 : 0), | |
| 10 + ((TERMX > 80) ? (TERMX - 80) / 2 : 0)); | |
| draw_border(w); | |
| if (has_effect( effect_fungus )) { | |
| bad.push_back(_("Fungal Parasite")); | |
| } | |
| if (has_effect( effect_dermatik )) { | |
| bad.push_back(_("Insect Parasite")); | |
| } | |
| if (has_effect( effect_stung )) { | |
| bad.push_back(_("Stung")); | |
| } | |
| if (has_effect( effect_poison )) { | |
| bad.push_back(_("Poison")); | |
| } | |
| if (radiation > 0) { | |
| bad.push_back(_("Irradiated")); | |
| } | |
| if (has_effect( effect_pkill1 )) { | |
| good.push_back(_("Minor Painkiller")); | |
| } | |
| if (has_effect( effect_pkill2 )) { | |
| good.push_back(_("Moderate Painkiller")); | |
| } | |
| if (has_effect( effect_pkill3 )) { | |
| good.push_back(_("Heavy Painkiller")); | |
| } | |
| if (has_effect( effect_pkill_l )) { | |
| good.push_back(_("Slow-Release Painkiller")); | |
| } | |
| if (has_effect( effect_drunk )) { | |
| good.push_back(_("Alcohol")); | |
| } | |
| if (has_effect( effect_cig )) { | |
| good.push_back(_("Nicotine")); | |
| } | |
| if (has_effect( effect_meth )) { | |
| good.push_back(_("Methamphetamines")); | |
| } | |
| if (has_effect( effect_high )) { | |
| good.push_back(_("Intoxicant: Other")); | |
| } | |
| if (has_effect( effect_weed_high )) { | |
| good.push_back(_("THC Intoxication")); | |
| } | |
| if (has_effect( effect_hallu ) || has_effect( effect_visuals )) { | |
| bad.push_back(_("Hallucinations")); | |
| } | |
| if (has_effect( effect_pblue )) { | |
| good.push_back(_("Prussian Blue")); | |
| } | |
| if (has_effect( effect_iodine )) { | |
| good.push_back(_("Potassium Iodide")); | |
| } | |
| if (has_effect( effect_datura )) { | |
| good.push_back(_("Anticholinergic Tropane Alkaloids")); | |
| } | |
| if (has_effect( effect_took_xanax )) { | |
| good.push_back(_("Xanax")); | |
| } | |
| if (has_effect( effect_took_prozac )) { | |
| good.push_back(_("Prozac")); | |
| } | |
| if (has_effect( effect_took_flumed )) { | |
| good.push_back(_("Antihistamines")); | |
| } | |
| if (has_effect( effect_adrenaline )) { | |
| good.push_back(_("Adrenaline Spike")); | |
| } | |
| if (has_effect( effect_adrenaline_mycus )) { | |
| good.push_back(_("Mycal Spike")); | |
| } | |
| if (has_effect( effect_tapeworm )) { // This little guy is immune to the blood filter though, as he lives in your bowels. | |
| good.push_back(_("Intestinal Parasite")); | |
| } | |
| if (has_effect( effect_bloodworms )) { | |
| good.push_back(_("Hemolytic Parasites")); | |
| } | |
| if (has_effect( effect_brainworms )) { // These little guys are immune to the blood filter too, as they live in your brain. | |
| good.push_back(_("Intracranial Parasite")); | |
| } | |
| if (has_effect( effect_paincysts )) { // These little guys are immune to the blood filter too, as they live in your muscles. | |
| good.push_back(_("Intramuscular Parasites")); | |
| } | |
| if (has_effect( effect_tetanus )) { // Tetanus infection. | |
| good.push_back(_("Clostridium Tetani Infection")); | |
| } | |
| if (good.empty() && bad.empty()) { | |
| mvwprintz(w, 1, 1, c_white, _("No effects.")); | |
| } else { | |
| for (unsigned line = 1; line < 39 && line <= good.size() + bad.size(); line++) { | |
| if (line <= bad.size()) { | |
| mvwprintz(w, line, 1, c_red, "%s", bad[line - 1].c_str()); | |
| } else { | |
| mvwprintz(w, line, 1, c_green, "%s", good[line - 1 - bad.size()].c_str()); | |
| } | |
| } | |
| } | |
| wrefresh(w); | |
| refresh(); | |
| getch(); | |
| delwin(w); | |
| } else if(bio.id == "bio_blood_filter") { | |
| remove_effect( effect_fungus ); | |
| remove_effect( effect_dermatik ); | |
| remove_effect( effect_bloodworms ); | |
| remove_effect( effect_tetanus ); | |
| remove_effect( effect_poison ); | |
| remove_effect( effect_stung ); | |
| remove_effect( effect_pkill1 ); | |
| remove_effect( effect_pkill2 ); | |
| remove_effect( effect_pkill3 ); | |
| remove_effect( effect_pkill_l ); | |
| remove_effect( effect_drunk ); | |
| remove_effect( effect_cig ); | |
| remove_effect( effect_high ); | |
| remove_effect( effect_hallu ); | |
| remove_effect( effect_visuals ); | |
| remove_effect( effect_pblue ); | |
| remove_effect( effect_iodine ); | |
| remove_effect( effect_datura ); | |
| remove_effect( effect_took_xanax ); | |
| remove_effect( effect_took_prozac ); | |
| remove_effect( effect_took_flumed ); | |
| remove_effect( effect_adrenaline ); | |
| remove_effect( effect_meth ); | |
| set_painkiller( 0 ); | |
| stim = 0; | |
| } else if(bio.id == "bio_evap") { | |
| item water = item("water_clean", 0); | |
| int humidity = weatherPoint.humidity; | |
| int water_charges = (humidity * 3.0) / 100.0 + 0.5; | |
| // At 50% relative humidity or more, the player will draw 2 units of water | |
| // At 16% relative humidity or less, the player will draw 0 units of water | |
| water.charges = water_charges; | |
| if (water_charges == 0) { | |
| add_msg_if_player(m_bad, _("There was not enough moisture in the air from which to draw water!")); | |
| } else if (g->handle_liquid(water, true, false)) { | |
| moves -= 100; | |
| } else { | |
| water.charges -= drink_from_hands( water ); | |
| if( water.charges == water_charges ) { | |
| charge_power(bionics["bio_evap"].power_activate); | |
| } | |
| } | |
| } else if(bio.id == "bio_lighter") { | |
| g->refresh_all(); | |
| if(!choose_adjacent(_("Start a fire where?"), dirp) || | |
| (!g->m.add_field(dirp, fd_fire, 1, 0))) { | |
| add_msg_if_player(m_info, _("You can't light a fire there.")); | |
| charge_power(bionics["bio_lighter"].power_activate); | |
| } | |
| } else if(bio.id == "bio_leukocyte") { | |
| set_healthy(std::min(100, get_healthy() + 2)); | |
| mod_healthy_mod(20, 100); | |
| } else if(bio.id == "bio_geiger") { | |
| add_msg(m_info, _("Your radiation level: %d"), radiation); | |
| } else if(bio.id == "bio_radscrubber") { | |
| if (radiation > 4) { | |
| radiation -= 5; | |
| } else { | |
| radiation = 0; | |
| } | |
| } else if(bio.id == "bio_adrenaline") { | |
| if (has_effect( effect_adrenaline )) { | |
| add_effect( effect_adrenaline, 50); | |
| } else { | |
| add_effect( effect_adrenaline, 200); | |
| } | |
| } else if(bio.id == "bio_blaster") { | |
| tmp_item = weapon; | |
| weapon = item("bio_blaster_gun", 0); | |
| g->refresh_all(); | |
| g->plfire(false); | |
| if(weapon.charges == 1) { // not fired | |
| charge_power(bionics[bio.id].power_activate); | |
| } | |
| weapon = tmp_item; | |
| } else if (bio.id == "bio_laser") { | |
| tmp_item = weapon; | |
| weapon = item("bio_laser_gun", 0); | |
| g->refresh_all(); | |
| g->plfire(false); | |
| if(weapon.charges == 1) { // not fired | |
| charge_power(bionics[bio.id].power_activate); | |
| } | |
| weapon = tmp_item; | |
| } else if(bio.id == "bio_chain_lightning") { | |
| tmp_item = weapon; | |
| weapon = item("bio_lightning", 0); | |
| g->refresh_all(); | |
| g->plfire(false); | |
| if(weapon.charges == 1) { // not fired | |
| charge_power(bionics[bio.id].power_activate); | |
| } | |
| weapon = tmp_item; | |
| } else if (bio.id == "bio_emp") { | |
| g->refresh_all(); | |
| if(choose_adjacent(_("Create an EMP where?"), dirx, diry)) { | |
| g->emp_blast( tripoint( dirx, diry, posz() ) ); | |
| } else { | |
| charge_power(bionics["bio_emp"].power_activate); | |
| } | |
| } else if (bio.id == "bio_hydraulics") { | |
| add_msg(m_good, _("Your muscles hiss as hydraulic strength fills them!")); | |
| // Sound of hissing hydraulic muscle! (not quite as loud as a car horn) | |
| sounds::sound( pos(), 19, _("HISISSS!")); | |
| } else if (bio.id == "bio_water_extractor") { | |
| bool extracted = false; | |
| for( auto it = g->m.i_at(pos()).begin(); | |
| it != g->m.i_at(pos()).end(); ++it) { | |
| if( it->is_corpse() ) { | |
| const int avail = it->get_var( "remaining_water", it->volume() / 2 ); | |
| if(avail > 0 && query_yn(_("Extract water from the %s"), it->tname().c_str())) { | |
| item water = item("water_clean", 0); | |
| water.charges = avail; | |
| if (g->handle_liquid(water, true, false)) { | |
| moves -= 100; | |
| } else { | |
| water.charges -= drink_from_hands( water ); | |
| } | |
| if( water.charges != avail ) { | |
| extracted = true; | |
| it->set_var( "remaining_water", static_cast<int>( water.charges ) ); | |
| } | |
| break; | |
| } | |
| } | |
| } | |
| if (!extracted) { | |
| charge_power(bionics["bio_water_extractor"].power_activate); | |
| } | |
| } else if(bio.id == "bio_magnet") { | |
| std::vector<tripoint> traj; | |
| for (int i = posx() - 10; i <= posx() + 10; i++) { | |
| for (int j = posy() - 10; j <= posy() + 10; j++) { | |
| if (g->m.i_at(i, j).size() > 0) { | |
| traj = g->m.find_clear_path( {i, j, posz()}, pos() ); | |
| } | |
| traj.insert(traj.begin(), {i, j, posz()}); | |
| if( g->m.has_flag( "SEALED", i, j ) ) { | |
| continue; | |
| } | |
| for (unsigned k = 0; k < g->m.i_at(i, j).size(); k++) { | |
| tmp_item = g->m.i_at(i, j)[k]; | |
| if( (tmp_item.made_of("iron") || tmp_item.made_of("steel")) && | |
| tmp_item.weight() < weight_capacity() ) { | |
| g->m.i_rem(i, j, k); | |
| std::vector<tripoint>::iterator it; | |
| for (it = traj.begin(); it != traj.end(); ++it) { | |
| int index = g->mon_at(*it); | |
| if (index != -1) { | |
| g->zombie(index).apply_damage( this, bp_torso, tmp_item.weight() / 225 ); | |
| g->zombie(index).check_dead_state(); | |
| g->m.add_item_or_charges(it->x, it->y, tmp_item); | |
| break; | |
| } else if (g->m.impassable(it->x, it->y)) { | |
| if (it != traj.begin()) { | |
| g->m.bash( tripoint( it->x, it->y, posz() ), tmp_item.weight() / 225 ); | |
| if (g->m.impassable(it->x, it->y)) { | |
| g->m.add_item_or_charges((it - 1)->x, (it - 1)->y, tmp_item); | |
| break; | |
| } | |
| } else { | |
| g->m.bash( *it, tmp_item.weight() / 225 ); | |
| if (g->m.impassable(it->x, it->y)) { | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| if (it == traj.end()) { | |
| g->m.add_item_or_charges(pos(), tmp_item); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| moves -= 100; | |
| } else if(bio.id == "bio_lockpick") { | |
| tmp_item = item( "pseuso_bio_picklock", 0 ); | |
| g->refresh_all(); | |
| if( invoke_item( &tmp_item ) == 0 ) { | |
| if (tmp_item.charges > 0) { | |
| // restore the energy since CBM wasn't used | |
| charge_power(bionics[bio.id].power_activate); | |
| } | |
| return true; | |
| } | |
| if( tmp_item.damage > 0 ) { | |
| // TODO: damage the player / their bionics | |
| } | |
| } else if(bio.id == "bio_flashbang") { | |
| g->flashbang( pos(), true); | |
| } else if(bio.id == "bio_shockwave") { | |
| g->shockwave( pos(), 3, 4, 2, 8, true ); | |
| add_msg_if_player(m_neutral, _("You unleash a powerful shockwave!")); | |
| } else if(bio.id == "bio_meteorologist") { | |
| // Calculate local wind power | |
| int vpart = -1; | |
| vehicle *veh = g->m.veh_at( pos(), vpart ); | |
| int vehwindspeed = 0; | |
| if( veh != nullptr ) { | |
| vehwindspeed = abs(veh->velocity / 100); // vehicle velocity in mph | |
| } | |
| const oter_id &cur_om_ter = overmap_buffer.ter( global_omt_location() ); | |
| std::string omtername = otermap[cur_om_ter].name; | |
| /* windpower defined in internal velocity units (=.01 mph) */ | |
| double windpower = 100.0f * get_local_windpower( weatherPoint.windpower + vehwindspeed, | |
| omtername, g->is_sheltered( g->u.pos() ) ); | |
| add_msg_if_player( m_info, _( "Temperature: %s." ), | |
| print_temperature( g->get_temperature() ).c_str() ); | |
| add_msg_if_player( m_info, _( "Relative Humidity: %s." ), | |
| print_humidity( | |
| get_local_humidity( weatherPoint.humidity, g->weather, | |
| g->is_sheltered( g->u.pos() ) ) ).c_str() ); | |
| add_msg_if_player( m_info, _( "Pressure: %s."), | |
| print_pressure( (int)weatherPoint.pressure ).c_str() ); | |
| add_msg_if_player( m_info, _( "Wind Speed: %.1f %s." ), | |
| convert_velocity( int( windpower ), VU_WIND ), | |
| velocity_units( VU_WIND ) ); | |
| add_msg_if_player( m_info, _( "Feels Like: %s." ), | |
| print_temperature( | |
| get_local_windchill( weatherPoint.temperature, weatherPoint.humidity, | |
| windpower ) + g->get_temperature() ).c_str() ); | |
| } else if(bio.id == "bio_claws") { | |
| if (weapon.has_flag ("NO_UNWIELD")) { | |
| add_msg(m_info, _("Deactivate your %s first!"), | |
| weapon.tname().c_str()); | |
| charge_power(bionics[bio.id].power_activate); | |
| bio.powered = false; | |
| return false; | |
| } else if(weapon.type->id != "null") { | |
| add_msg(m_warning, _("Your claws extend, forcing you to drop your %s."), | |
| weapon.tname().c_str()); | |
| g->m.add_item_or_charges(pos(), weapon); | |
| weapon = item("bio_claws_weapon", 0); | |
| weapon.invlet = '#'; | |
| } else { | |
| add_msg(m_neutral, _("Your claws extend!")); | |
| weapon = item("bio_claws_weapon", 0); | |
| weapon.invlet = '#'; | |
| } | |
| } else if(bio.id == "bio_blade") { | |
| if (weapon.has_flag ("NO_UNWIELD")) { | |
| add_msg(m_info, _("Deactivate your %s first!"), | |
| weapon.tname().c_str()); | |
| charge_power(bionics[bio.id].power_activate); | |
| bio.powered = false; | |
| return false; | |
| } else if(weapon.type->id != "null") { | |
| add_msg(m_warning, _("Your blade extends, forcing you to drop your %s."), | |
| weapon.tname().c_str()); | |
| g->m.add_item_or_charges(pos(), weapon); | |
| weapon = item("bio_blade_weapon", 0); | |
| weapon.invlet = '#'; | |
| } else { | |
| add_msg(m_neutral, _("You extend your blade!")); | |
| weapon = item("bio_blade_weapon", 0); | |
| weapon.invlet = '#'; | |
| } | |
| } else if( bio.id == "bio_remote" ) { | |
| int choice = menu( true, _("Perform which function:"), _("Nothing"), | |
| _("Control vehicle"), _("RC radio"), NULL ); | |
| if( choice >= 2 && choice <= 3 ) { | |
| item ctr; | |
| if( choice == 2 ) { | |
| ctr = item( "remotevehcontrol", 0 ); | |
| } else { | |
| ctr = item( "radiocontrol", 0 ); | |
| } | |
| ctr.charges = power_level; | |
| int power_use = invoke_item( &ctr ); | |
| charge_power(-power_use); | |
| bio.powered = ctr.active; | |
| } else { | |
| bio.powered = g->remoteveh() != nullptr || get_value( "remote_controlling" ) != ""; | |
| } | |
| } else if (bio.id == "bio_plutdump") { | |
| if (query_yn(_("WARNING: Purging all fuel is likely to result in radiation! Purge anyway?"))) { | |
| slow_rad += (tank_plut + reactor_plut); | |
| tank_plut = 0; | |
| reactor_plut = 0; | |
| } | |
| } | |
| // Recalculate stats (strength, mods from pain etc.) that could have been affected | |
| reset(); | |
| return true; | |
| } | |
| bool player::deactivate_bionic(int b, bool eff_only) | |
| { | |
| bionic &bio = my_bionics[b]; | |
| // Just do the effect, no stat changing or messages | |
| if (!eff_only) { | |
| if (!bio.powered) { | |
| // It's already off! | |
| return false; | |
| } | |
| if (!bionics[bio.id].toggled) { | |
| // It's a fire-and-forget bionic, we can't turn it off but have to wait for it to run out of charge | |
| add_msg(m_info, _("You can't deactivate your %s manually!"), bionics[bio.id].name.c_str()); | |
| return false; | |
| } | |
| if (power_level < bionics[bio.id].power_deactivate) { | |
| add_msg(m_info, _("You don't have the power to deactivate your %s."), bionics[bio.id].name.c_str()); | |
| return false; | |
| } | |
| //We can actually deactivate now, do deactivation-y things | |
| charge_power(-bionics[bio.id].power_deactivate); | |
| bio.powered = false; | |
| add_msg(m_neutral, _("You deactivate your %s."), bionics[bio.id].name.c_str()); | |
| } | |
| // Deactivation effects go here | |
| if (bio.id == "bio_cqb") { | |
| // check if player knows current style naturally, otherwise drop them back to style_none | |
| if( style_selected != matype_id( "style_none" ) ) { | |
| bool has_style = false; | |
| for( auto &elem : ma_styles ) { | |
| if( elem == style_selected ) { | |
| has_style = true; | |
| } | |
| } | |
| if (!has_style) { | |
| style_selected = matype_id( "style_none" ); | |
| } | |
| } | |
| } else if(bio.id == "bio_claws") { | |
| if (weapon.type->id == "bio_claws_weapon") { | |
| add_msg(m_neutral, _("You withdraw your claws.")); | |
| weapon = ret_null; | |
| } | |
| } else if(bio.id == "bio_blade") { | |
| if (weapon.type->id == "bio_blade_weapon") { | |
| add_msg(m_neutral, _("You retract your blade.")); | |
| weapon = ret_null; | |
| } | |
| } else if( bio.id == "bio_remote" ) { | |
| if( g->remoteveh() != nullptr && !has_active_item( "remotevehcontrol" ) ) { | |
| g->setremoteveh( nullptr ); | |
| } else if( get_value( "remote_controlling" ) != "" && !has_active_item( "radiocontrol" ) ) { | |
| set_value( "remote_controlling", "" ); | |
| } | |
| } else if( bio.id == "bio_tools" ) { | |
| invalidate_crafting_inventory(); | |
| } | |
| // Recalculate stats (strength, mods from pain etc.) that could have been affected | |
| reset(); | |
| return true; | |
| } | |
| void player::process_bionic(int b) | |
| { | |
| bionic &bio = my_bionics[b]; | |
| if (!bio.powered) { | |
| // Only powered bionics should be processed | |
| return; | |
| } | |
| if (bio.charge > 0) { | |
| // Units already with charge just lose charge | |
| bio.charge--; | |
| } else { | |
| if (bionics[bio.id].charge_time > 0) { | |
| // Try to recharge our bionic if it is made for it | |
| if (bionics[bio.id].power_over_time > 0) { | |
| if (power_level < bionics[bio.id].power_over_time) { | |
| // No power to recharge, so deactivate | |
| bio.powered = false; | |
| add_msg(m_neutral, _("Your %s powers down."), bionics[bio.id].name.c_str()); | |
| // This purposely bypasses the deactivation cost | |
| deactivate_bionic(b, true); | |
| return; | |
| } else { | |
| // Pay the recharging cost | |
| charge_power(-bionics[bio.id].power_over_time); | |
| // We just spent our first turn of charge, so -1 here | |
| bio.charge = bionics[bio.id].charge_time - 1; | |
| } | |
| // Some bionics are a 1-shot activation so they just deactivate at 0 charge. | |
| } else { | |
| bio.powered = false; | |
| add_msg(m_neutral, _("Your %s powers down."), bionics[bio.id].name.c_str()); | |
| // This purposely bypasses the deactivation cost | |
| deactivate_bionic(b, true); | |
| return; | |
| } | |
| } | |
| } | |
| // Bionic effects on every turn they are active go here. | |
| if( bio.id == "bio_night" ) { | |
| if( calendar::once_every(5) ) { | |
| add_msg(m_neutral, _("Artificial night generator active!")); | |
| } | |
| } else if( bio.id == "bio_remote" ) { | |
| if( g->remoteveh() == nullptr && get_value( "remote_controlling" ) == "" ) { | |
| bio.powered = false; | |
| add_msg( m_warning, _("Your %s has lost connection and is turning off."), | |
| bionics[bio.id].name.c_str() ); | |
| } | |
| } else if (bio.id == "bio_hydraulics") { | |
| // Sound of hissing hydraulic muscle! (not quite as loud as a car horn) | |
| sounds::sound( pos(), 19, _("HISISSS!")); | |
| } | |
| } | |
| void bionics_uninstall_failure(player *u) | |
| { | |
| switch (rng(1, 5)) { | |
| case 1: | |
| add_msg(m_neutral, _("You flub the removal.")); | |
| break; | |
| case 2: | |
| add_msg(m_neutral, _("You mess up the removal.")); | |
| break; | |
| case 3: | |
| add_msg(m_neutral, _("The removal fails.")); | |
| break; | |
| case 4: | |
| add_msg(m_neutral, _("The removal is a failure.")); | |
| break; | |
| case 5: | |
| add_msg(m_neutral, _("You screw up the removal.")); | |
| break; | |
| } | |
| add_msg(m_bad, _("Your body is severely damaged!")); | |
| u->hurtall(rng(30, 80), u); // stop hurting yourself! | |
| } | |
| // bionic manipulation chance of success | |
| int bionic_manip_cos(int p_int, int s_electronics, int s_firstaid, int s_mechanics, | |
| int bionic_difficulty) | |
| { | |
| int pl_skill = p_int * 4 + | |
| s_electronics * 4 + | |
| s_firstaid * 3 + | |
| s_mechanics * 1; | |
| // Medical residents have some idea what they're doing | |
| if (g->u.has_trait("PROF_MED")) { | |
| pl_skill += 3; | |
| add_msg(m_neutral, _("You prep yourself to begin surgery.")); | |
| } | |
| // for chance_of_success calculation, shift skill down to a float between ~0.4 - 30 | |
| float adjusted_skill = float (pl_skill) - std::min( float (40), | |
| float (pl_skill) - float (pl_skill) / float (10.0)); | |
| // we will base chance_of_success on a ratio of skill and difficulty | |
| // when skill=difficulty, this gives us 1. skill < difficulty gives a fraction. | |
| float skill_difficulty_parameter = float(adjusted_skill / (4.0 * bionic_difficulty)); | |
| // when skill == difficulty, chance_of_success is 50%. Chance of success drops quickly below that | |
| // to reserve bionics for characters with the appropriate skill. For more difficult bionics, the | |
| // curve flattens out just above 80% | |
| int chance_of_success = int((100 * skill_difficulty_parameter) / | |
| (skill_difficulty_parameter + sqrt( 1 / skill_difficulty_parameter))); | |
| return chance_of_success; | |
| } | |
| bool player::uninstall_bionic(std::string const &b_id, int skill_level) | |
| { | |
| // malfunctioning bionics don't have associated items and get a difficulty of 12 | |
| int difficulty = 12; | |
| if( item::type_is_defined( b_id ) ) { | |
| auto type = item::find_type( b_id ); | |
| if( type->bionic ) { | |
| difficulty = type->bionic->difficulty; | |
| } | |
| } | |
| if (!has_bionic(b_id)) { | |
| popup(_("You don't have this bionic installed.")); | |
| return false; | |
| } | |
| //If you are paying the doctor to do it, shouldn't use your supplies | |
| if (!(has_items_with_quality("CUT", 1, 1) && has_amount("1st_aid", 1)) && skill_level == -1) { | |
| popup(_("Removing bionics requires a cutting tool and a first aid kit.")); | |
| return false; | |
| } | |
| if ( b_id == "bio_blaster" ) { | |
| popup(_("Removing your Fusion Blaster Arm would leave you with a useless stump.")); | |
| return false; | |
| } | |
| if (( b_id == "bio_reactor" ) || ( b_id == "bio_advreactor" )) { | |
| if (!query_yn(_("WARNING: Removing a reactor may leave radioactive material! Remove anyway?"))) { | |
| return false; | |
| } | |
| } else if (b_id == "bio_plutdump") { | |
| popup(_("You must remove your reactor to remove the Plutonium Purger.")); | |
| } | |
| if ( b_id == "bio_earplugs") { | |
| popup(_("You must remove the Enhanced Hearing bionic to remove the Sound Dampeners.")); | |
| return false; | |
| } | |
| if( b_id == "bio_eye_optic" ) { | |
| popup(_("The Telescopic Lenses are part of your eyes now. Removing them would leave you blind.") ); | |
| return false; | |
| } | |
| if( b_id == "bio_blindfold" ) { | |
| popup(_("You must remove the Anti-glare Compensators bionic to remove the Optical Dampers.") ); | |
| return false; | |
| } | |
| // removal of bionics adds +2 difficulty over installation | |
| int chance_of_success; | |
| if (skill_level != -1){ | |
| chance_of_success = bionic_manip_cos(skill_level, | |
| skill_level, | |
| skill_level, | |
| skill_level, | |
| difficulty + 2); | |
| } else { | |
| ///\EFFECT_INT increases chance of success removing bionics with unspecified skil level | |
| chance_of_success = bionic_manip_cos(int_cur, | |
| skillLevel( skilll_electronics ), | |
| skillLevel( skilll_firstaid ), | |
| skillLevel( skilll_mechanics ), | |
| difficulty + 2); | |
| } | |
| if (!query_yn(_("WARNING: %i percent chance of failure and SEVERE bodily damage! Remove anyway?"), | |
| 100 - chance_of_success)) { | |
| return false; | |
| } | |
| // surgery is imminent, retract claws or blade if active | |
| if (has_bionic("bio_claws") && skill_level == -1 ) { | |
| if (weapon.type->id == "bio_claws_weapon") { | |
| add_msg(m_neutral, _("You withdraw your claws.")); | |
| weapon = ret_null; | |
| } | |
| } | |
| if (has_bionic("bio_blade") && skill_level == -1 ) { | |
| if (weapon.type->id == "bio_blade_weapon") { | |
| add_msg(m_neutral, _("You retract your blade.")); | |
| weapon = ret_null; | |
| } | |
| } | |
| //If you are paying the doctor to do it, shouldn't use your supplies | |
| if (skill_level == -1) | |
| use_charges("1st_aid", 1); | |
| practice( skilll_electronics, int((100 - chance_of_success) * 1.5) ); | |
| practice( skilll_firstaid, int((100 - chance_of_success) * 1.0) ); | |
| practice( skilll_mechanics, int((100 - chance_of_success) * 0.5) ); | |
| int success = chance_of_success - rng(1, 100); | |
| if (success > 0) { | |
| add_memorial_log(pgettext("memorial_male", "Removed bionic: %s."), | |
| pgettext("memorial_female", "Removed bionic: %s."), | |
| bionics[b_id].name.c_str()); | |
| // until bionics can be flagged as non-removable | |
| add_msg(m_neutral, _("You jiggle your parts back into their familiar places.")); | |
| add_msg(m_good, _("Successfully removed %s."), bionics[b_id].name.c_str()); | |
| // remove power bank provided by bionic | |
| max_power_level -= bionics[b_id].capacity; | |
| remove_bionic(b_id); | |
| if (b_id == "bio_reactor" || b_id == "bio_advreactor") { | |
| remove_bionic("bio_plutdump"); | |
| } | |
| g->m.spawn_item(pos(), "burnt_out_bionic", 1); | |
| } else { | |
| add_memorial_log(pgettext("memorial_male", "Removed bionic: %s."), | |
| pgettext("memorial_female", "Removed bionic: %s."), | |
| bionics[b_id].name.c_str()); | |
| bionics_uninstall_failure(this); | |
| } | |
| g->refresh_all(); | |
| return true; | |
| } | |
| bool player::install_bionics(const itype &type, int skill_level) | |
| { | |
| if( type.bionic.get() == nullptr ) { | |
| debugmsg("Tried to install NULL bionic"); | |
| return false; | |
| } | |
| const std::string &bioid = type.bionic->bionic_id; | |
| if( bionics.count( bioid ) == 0 ) { | |
| popup("invalid / unknown bionic id %s", bioid.c_str()); | |
| return false; | |
| } | |
| if (bioid == "bio_reactor" || bioid == "bio_advreactor") { | |
| if (has_bionic("bio_furnace") && has_bionic("bio_storage")) { | |
| popup(_("Your internal storage and furnace take up too much room!")); | |
| return false; | |
| } else if (has_bionic("bio_furnace")) { | |
| popup(_("Your internal furnace takes up too much room!")); | |
| return false; | |
| } else if (has_bionic("bio_storage")) { | |
| popup(_("Your internal storage takes up too much room!")); | |
| return false; | |
| } | |
| } | |
| if ((bioid == "bio_furnace" ) || (bioid == "bio_storage") || (bioid == "bio_reactor") || (bioid == "bio_advreactor")) { | |
| if (has_bionic("bio_reactor") || has_bionic("bio_advreactor")) { | |
| popup(_("Your installed reactor leaves no room!")); | |
| return false; | |
| } | |
| } | |
| if (bioid == "bio_reactor_upgrade" ){ | |
| if (!has_bionic("bio_reactor")) { | |
| popup(_("There is nothing to upgrade!")); | |
| return false; | |
| } | |
| } | |
| if( has_bionic( bioid ) ) { | |
| if( !( bioid == "bio_power_storage" || bioid == "bio_power_storage_mkII" ) ) { | |
| popup(_("You have already installed this bionic.")); | |
| return false; | |
| } | |
| } | |
| const int difficult = type.bionic->difficulty; | |
| int chance_of_success; | |
| if (skill_level != -1){ | |
| chance_of_success = bionic_manip_cos(skill_level, | |
| skill_level, | |
| skill_level, | |
| skill_level, | |
| difficult); | |
| } else { | |
| ///\EFFECT_INT increases chance of success installing bionics with unspecified skill level | |
| chance_of_success = bionic_manip_cos(int_cur, | |
| skillLevel( skilll_electronics ), | |
| skillLevel( skilll_firstaid ), | |
| skillLevel( skilll_mechanics ), | |
| difficult); | |
| } | |
| if (!query_yn( | |
| _("WARNING: %i percent chance of genetic damage, blood loss, or damage to existing bionics! Install anyway?"), | |
| 100 - chance_of_success)) { | |
| return false; | |
| } | |
| practice( skilll_electronics, int((100 - chance_of_success) * 1.5) ); | |
| practice( skilll_firstaid, int((100 - chance_of_success) * 1.0) ); | |
| practice( skilll_mechanics, int((100 - chance_of_success) * 0.5) ); | |
| int success = chance_of_success - rng(0, 99); | |
| if (success > 0) { | |
| add_memorial_log(pgettext("memorial_male", "Installed bionic: %s."), | |
| pgettext("memorial_female", "Installed bionic: %s."), | |
| bionics[bioid].name.c_str()); | |
| add_msg(m_good, _("Successfully installed %s."), bionics[bioid].name.c_str()); | |
| add_bionic(bioid); | |
| if( bioid == "bio_eye_optic" && has_trait( "HYPEROPIC" ) ) { | |
| remove_mutation( "HYPEROPIC" ); | |
| } | |
| if( bioid == "bio_eye_optic" && has_trait( "MYOPIC" ) ) { | |
| remove_mutation( "MYOPIC" ); | |
| } else if( bioid == "bio_ears" ) { | |
| add_bionic( "bio_earplugs" ); // automatically add the earplugs, they're part of the same bionic | |
| } else if( bioid == "bio_sunglasses" ) { | |
| add_bionic( "bio_blindfold" ); // automatically add the Optical Dampers, they're part of the same bionic | |
| } else if( bioid == "bio_reactor_upgrade" ) { | |
| remove_bionic( "bio_reactor" ); | |
| remove_bionic( "bio_reactor_upgrade" ); | |
| add_bionic( "bio_advreactor" ); | |
| } else if( bioid == "bio_reactor" || bioid == "bio_advreactor" ) { | |
| add_bionic( "bio_plutdump" ); | |
| } | |
| } else{ | |
| add_memorial_log(pgettext("memorial_male", "Installed bionic: %s."), | |
| pgettext("memorial_female", "Installed bionic: %s."), | |
| bionics[bioid].name.c_str()); | |
| bionics_install_failure(this, difficult, success); | |
| } | |
| g->refresh_all(); | |
| return true; | |
| } | |
| void bionics_install_failure(player *u, int difficulty, int success) | |
| { | |
| // "success" should be passed in as a negative integer representing how far off we | |
| // were for a successful install. We use this to determine consequences for failing. | |
| success = abs(success); | |
| // it would be better for code reuse just to pass in skill as an argument from install_bionic | |
| // pl_skill should be calculated the same as in install_bionics | |
| ///\EFFECT_INT randomly decreases severity of bionics installation failure | |
| int pl_skill = u->int_cur * 4 + | |
| u->skillLevel( skilll_electronics ) * 4 + | |
| u->skillLevel( skilll_firstaid ) * 3 + | |
| u->skillLevel( skilll_mechanics ) * 1; | |
| // Medical residents get a substantial assist here | |
| if (u->has_trait("PROF_MED")) { | |
| pl_skill += 6; | |
| } | |
| // for failure_level calculation, shift skill down to a float between ~0.4 - 30 | |
| float adjusted_skill = float (pl_skill) - std::min( float (40), | |
| float (pl_skill) - float (pl_skill) / float (10.0)); | |
| // failure level is decided by how far off the character was from a successful install, and | |
| // this is scaled up or down by the ratio of difficulty/skill. At high skill levels (or low | |
| // difficulties), only minor consequences occur. At low skill levels, severe consequences | |
| // are more likely. | |
| int failure_level = int(sqrt(success * 4.0 * difficulty / float (adjusted_skill))); | |
| int fail_type = (failure_level > 5 ? 5 : failure_level); | |
| if (fail_type <= 0) { | |
| add_msg(m_neutral, _("The installation fails without incident.")); | |
| return; | |
| } | |
| switch (rng(1, 5)) { | |
| case 1: | |
| add_msg(m_neutral, _("You flub the installation.")); | |
| break; | |
| case 2: | |
| add_msg(m_neutral, _("You mess up the installation.")); | |
| break; | |
| case 3: | |
| add_msg(m_neutral, _("The installation fails.")); | |
| break; | |
| case 4: | |
| add_msg(m_neutral, _("The installation is a failure.")); | |
| break; | |
| case 5: | |
| add_msg(m_neutral, _("You screw up the installation.")); | |
| break; | |
| } | |
| if (u->has_trait("PROF_MED")) { | |
| //~"Complications" is USian medical-speak for "unintended damage from a medical procedure". | |
| add_msg(m_neutral, _("Your training helps you minimize the complications.")); | |
| // In addition to the bonus, medical residents know enough OR protocol to avoid botching. | |
| // Take MD and be immune to faulty bionics. | |
| if (fail_type == 5) { | |
| fail_type = rng(1,3); | |
| } | |
| } | |
| if (fail_type == 3 && u->num_bionics() == 0) { | |
| fail_type = 2; // If we have no bionics, take damage instead of losing some | |
| } | |
| switch (fail_type) { | |
| case 1: | |
| if (!(u->has_trait("NOPAIN"))) { | |
| add_msg(m_bad, _("It really hurts!")); | |
| u->mod_pain( rng(failure_level * 3, failure_level * 6) ); | |
| } | |
| break; | |
| case 2: | |
| add_msg(m_bad, _("Your body is damaged!")); | |
| u->hurtall(rng(failure_level, failure_level * 2), u); // you hurt yourself | |
| break; | |
| case 3: | |
| if (u->num_bionics() <= failure_level && u->max_power_level == 0) { | |
| add_msg(m_bad, _("All of your existing bionics are lost!")); | |
| } else { | |
| add_msg(m_bad, _("Some of your existing bionics are lost!")); | |
| } | |
| for (int i = 0; i < failure_level && u->remove_random_bionic(); i++) { | |
| ; | |
| } | |
| break; | |
| case 4: | |
| add_msg(m_mixed, _("You do damage to your genetics, causing mutation!")); | |
| while (failure_level > 0) { | |
| u->mutate(); | |
| failure_level -= rng(1, failure_level + 2); | |
| } | |
| break; | |
| case 5: { | |
| add_msg(m_bad, _("The installation is faulty!")); | |
| std::vector<std::string> valid; | |
| std::copy_if(begin(faulty_bionics), end(faulty_bionics), std::back_inserter(valid), | |
| [&](std::string const &id) { return !u->has_bionic(id); }); | |
| if (valid.empty()) { // We've got all the bad bionics! | |
| if (u->max_power_level > 0) { | |
| int old_power = u->max_power_level; | |
| add_msg(m_bad, _("You lose power capacity!")); | |
| u->max_power_level = rng(0, u->max_power_level - 25); | |
| u->add_memorial_log(pgettext("memorial_male", "Lost %d units of power capacity."), | |
| pgettext("memorial_female", "Lost %d units of power capacity."), | |
| old_power - u->max_power_level); | |
| } | |
| // TODO: What if we can't lose power capacity? No penalty? | |
| } else { | |
| const std::string& id = random_entry( valid ); | |
| u->add_bionic( id ); | |
| u->add_memorial_log(pgettext("memorial_male", "Installed bad bionic: %s."), | |
| pgettext("memorial_female", "Installed bad bionic: %s."), | |
| bionics[ id ].name.c_str()); | |
| } | |
| } | |
| break; | |
| } | |
| } | |
| void player::add_bionic( std::string const &b ) | |
| { | |
| if( has_bionic( b ) ) { | |
| debugmsg( "Tried to install bionic %s that is already installed!", b.c_str() ); | |
| return; | |
| } | |
| char newinv = ' '; | |
| for( auto &inv_char : bionic_chars ) { | |
| if( bionic_by_invlet( inv_char ) == nullptr ) { | |
| newinv = inv_char; | |
| break; | |
| } | |
| } | |
| int pow_up = bionics[b].capacity; | |
| max_power_level += pow_up; | |
| if ( b == "bio_power_storage" || b == "bio_power_storage_mkII" ) { | |
| add_msg_if_player(m_good, _("Increased storage capacity by %i."), pow_up); | |
| // Power Storage CBMs are not real bionic units, so return without adding it to my_bionics | |
| return; | |
| } | |
| my_bionics.push_back( bionic( b, newinv ) ); | |
| if ( b == "bio_tools" || b == "bio_ears" ) { | |
| activate_bionic(my_bionics.size() -1); | |
| } | |
| recalc_sight_limits(); | |
| } | |
| void player::remove_bionic(std::string const &b) { | |
| std::vector<bionic> new_my_bionics; | |
| for(auto &i : my_bionics) { | |
| if (b == i.id) { | |
| continue; | |
| } | |
| // Ears and earplugs and sunglasses and blindfold go together like peanut butter and jelly. | |
| // Therefore, removing one, should remove the other. | |
| if ((b == "bio_ears" && i.id == "bio_earplugs") || | |
| (b == "bio_earplugs" && i.id == "bio_ears")) { | |
| continue; | |
| } else if ((b == "bio_sunglasses" && i.id == "bio_blindfold") || | |
| (b == "bio_blindfold" && i.id == "bio_sunglasses")) { | |
| continue; | |
| } | |
| new_my_bionics.push_back(bionic(i.id, i.invlet)); | |
| } | |
| my_bionics = new_my_bionics; | |
| recalc_sight_limits(); | |
| } | |
| int player::num_bionics() const | |
| { | |
| return my_bionics.size(); | |
| } | |
| std::pair<int, int> player::amount_of_storage_bionics() const | |
| { | |
| int lvl = max_power_level; | |
| // exclude amount of power capacity obtained via non-power-storage CBMs | |
| for( auto it : my_bionics ) { | |
| lvl -= bionics[it.id].capacity; | |
| } | |
| std::pair<int, int> results (0, 0); | |
| if (lvl <= 0) { | |
| return results; | |
| } | |
| int pow_mkI = bionics["bio_power_storage"].capacity; | |
| int pow_mkII = bionics["bio_power_storage_mkII"].capacity; | |
| while (lvl >= std::min(pow_mkI, pow_mkII)) { | |
| if ( one_in(2) ) { | |
| if (lvl >= pow_mkI) { | |
| results.first++; | |
| lvl -= pow_mkI; | |
| } | |
| } else { | |
| if (lvl >= pow_mkII) { | |
| results.second++; | |
| lvl -= pow_mkII; | |
| } | |
| } | |
| } | |
| return results; | |
| } | |
| bionic& player::bionic_at_index(int i) | |
| { | |
| return my_bionics[i]; | |
| } | |
| bionic* player::bionic_by_invlet( const long ch ) { | |
| for( auto &elem : my_bionics ) { | |
| if( elem.invlet == ch ) { | |
| return &elem; | |
| } | |
| } | |
| return nullptr; | |
| } | |
| // Returns true if a bionic was removed. | |
| bool player::remove_random_bionic() { | |
| const int numb = num_bionics(); | |
| if (numb) { | |
| int rem = rng(0, num_bionics() - 1); | |
| const auto bionic = my_bionics[rem]; | |
| remove_bionic(bionic.id); | |
| recalc_sight_limits(); | |
| } | |
| return numb; | |
| } | |
| void reset_bionics() | |
| { | |
| bionics.clear(); | |
| faulty_bionics.clear(); | |
| } | |
| void load_bionic(JsonObject &jsobj) | |
| { | |
| std::string id = jsobj.get_string("id"); | |
| std::string name = _(jsobj.get_string("name").c_str()); | |
| std::string description = _(jsobj.get_string("description").c_str()); | |
| int on_cost = jsobj.get_int("act_cost", 0); | |
| bool toggled = jsobj.get_bool("toggled", false); | |
| // Requires ability to toggle | |
| int off_cost = jsobj.get_int("deact_cost", 0); | |
| int time = jsobj.get_int("time", 0); | |
| // Requires a non-zero time | |
| int react_cost = jsobj.get_int("react_cost", 0); | |
| int capacity = jsobj.get_int("capacity", 0); | |
| bool faulty = jsobj.get_bool("faulty", false); | |
| bool power_source = jsobj.get_bool("power_source", false); | |
| if (faulty) { | |
| faulty_bionics.push_back(id); | |
| } | |
| auto const result = bionics.insert(std::make_pair(std::move(id), | |
| bionic_data(std::move(name), power_source, toggled, on_cost, off_cost, react_cost, time, | |
| capacity, std::move(description), faulty))); | |
| if (!result.second) { | |
| debugmsg("duplicate bionic id"); | |
| } | |
| } | |
| void bionic::serialize(JsonOut &json) const | |
| { | |
| json.start_object(); | |
| json.member("id", id); | |
| json.member("invlet", (int)invlet); | |
| json.member("powered", powered); | |
| json.member("charge", charge); | |
| json.end_object(); | |
| } | |
| void bionic::deserialize(JsonIn &jsin) | |
| { | |
| JsonObject jo = jsin.get_object(); | |
| id = jo.get_string("id"); | |
| invlet = jo.get_int("invlet"); | |
| powered = jo.get_bool("powered"); | |
| charge = jo.get_int("charge"); | |
| } |