/* * Copyright (C) Volition, Inc. 1999. All rights reserved. * * All source code herein is the property of Volition, Inc. You may not sell * or otherwise commercially exploit the source or things you created based on the * source. * */ #include "globalincs/pstypes.h" #include "globalincs/globals.h" #include "globalincs/linklist.h" #include "io/key.h" #include "io/joy.h" #include "io/timer.h" #include "ship/ship.h" #include "ship/ship_flags.h" #include "playerman/player.h" #include "weapon/weapon.h" #include "hud/hud.h" #include "gamesequence/gamesequence.h" #include "mission/missiongoals.h" #include "hud/hudets.h" #include "gamesnd/gamesnd.h" #include "hud/hudsquadmsg.h" #include "gamesnd/eventmusic.h" #include "freespace.h" #include "mission/missionhotkey.h" #include "hud/hudescort.h" #include "hud/hudshield.h" #include "io/keycontrol.h" #include "ship/shiphit.h" #include "ship/shipfx.h" #include "mission/missionlog.h" #include "hud/hudtargetbox.h" #include "popup/popup.h" #include "object/objcollide.h" #include "object/object.h" #include "hud/hudconfig.h" #include "hud/hudmessage.h" #include "network/multi_pmsg.h" #include "starfield/supernova.h" #include "mission/missionmessage.h" #include "menuui/mainhallmenu.h" #include "missionui/missionpause.h" #include "hud/hudgauges.h" #include "freespace.h" //For time compression stuff #include "species_defs/species_defs.h" #include "asteroid/asteroid.h" #include "iff_defs/iff_defs.h" #include "network/multi.h" #include "network/multiutil.h" #include "network/multimsgs.h" #include "network/multi_pause.h" #include "network/multi_observer.h" #include "network/multi_endgame.h" #include "autopilot/autopilot.h" #include "cmdline/cmdline.h" #include "object/objectshield.h" #include "sound/audiostr.h" /** * Natural number factor lookup class. */ class factor_table { public: /** * Constructor. * * @param size The desired initial size of the lookup table. */ factor_table(size_t size = 6); /** * Destructor. */ ~factor_table(); /** * Returns the next natural number factor for the given number and current factor. * * @param maximum The number to lookup the next natural number factor for. * @param current The current natural number factor. * @return A natural number factor. */ size_t getNext(size_t n, size_t current); private: SCP_vector< SCP_vector > _lookup; /** * Grow the internal lookup table based on the desired size. * * @param size The desired size. */ void resize(size_t size); /** * Returns true if the given factor is a natural number factor for the given value. * * @param factor The factor. * @param n The value. */ static bool isNaturalNumberFactor(size_t factor, size_t n); }; factor_table::factor_table(size_t size) { resize(size); } factor_table::~factor_table() {} size_t factor_table::getNext(size_t n, size_t current) { Assertion(n >= 1, "factor_table::getNext() called with " SIZE_T_ARG ", when only natural numbers make sense; get a coder!\n", n); // Resize lookup table if the value is greater than the current size if (n > _lookup.size()) resize(n); size_t index = n - 1; for (size_t i = 0; i < _lookup[index].size(); ++i) { if (_lookup[index][i] == current) { if (_lookup[index].size() == i + 1) { // Overflow back to 1 return 1; } else { // Next factor in the table return _lookup[index][i + 1]; } } } UNREACHABLE("For some reason, factor_table::getNext() was unable to locate the current factor. This should never happen; get a coder!\n"); return 1; } void factor_table::resize(size_t size) { size_t oldSize = _lookup.size(); _lookup.resize(size); // Fill lookup table for the missing values for (size_t i = oldSize; i < size; ++i) { for (size_t j = 1; j <= i + 1; ++j) { if (isNaturalNumberFactor(j, i + 1)) { _lookup[i].push_back(j); } } } } bool factor_table::isNaturalNumberFactor(size_t factor, size_t n) { return ((float)n / (float)factor) == n / factor; } // Natural number factor lookup table factor_table ftables; // -------------------------------------------------------------- // Global to file // -------------------------------------------------------------- // time compression/dilation values - Goober5000 // (Volition sez "can't compress below 0.25"... not sure if // this is arbitrary or dictated by code) #define MAX_TIME_MULTIPLIER 64 #define MAX_TIME_DIVIDER 4 #define CHEAT_BUFFER_LEN 17 char CheatBuffer[CHEAT_BUFFER_LEN+1]; enum cheatCode { CHEAT_CODE_NONE = 0, CHEAT_CODE_FREESPACE, CHEAT_CODE_FISH, CHEAT_CODE_HEADZ, CHEAT_CODE_TOOLED, CHEAT_CODE_PIRATE, CHEAT_CODE_SKIP }; struct Cheat { cheatCode code; const char* data; }; static struct Cheat cheatsTable[] = { { CHEAT_CODE_FREESPACE, "www.freespace2.com" }, { CHEAT_CODE_FISH, "vasudanswuvfishes" }, { CHEAT_CODE_HEADZ, "humanheadsinside." }, { CHEAT_CODE_TOOLED, "tooledworkedowned" }, { CHEAT_CODE_PIRATE, "arrrrwalktheplank" }, { CHEAT_CODE_SKIP, "skipmemymissionyo" } }; #define CHEATS_TABLE_LEN 6 int Tool_enabled = 0; bool Perspective_locked=false; extern int AI_watch_object; extern int Countermeasures_enabled; extern float do_subobj_hit_stuff(object *ship_obj, object *other_obj, vec3d *hitpos, int submodel_num, float damage, bool *hull_should_apply_armor); extern void mission_goal_mark_all_true( int type ); int Normal_key_set[] = { TARGET_NEXT, TARGET_PREV, TARGET_NEXT_CLOSEST_HOSTILE, TARGET_PREV_CLOSEST_HOSTILE, TARGET_NEXT_CLOSEST_FRIENDLY, TARGET_PREV_CLOSEST_FRIENDLY, TARGET_TARGETS_TARGET, TARGET_SHIP_IN_RETICLE, TARGET_LAST_TRANMISSION_SENDER, TARGET_CLOSEST_SHIP_ATTACKING_TARGET, TARGET_CLOSEST_SHIP_ATTACKING_SELF, STOP_TARGETING_SHIP, TOGGLE_AUTO_TARGETING, TARGET_SUBOBJECT_IN_RETICLE, TARGET_PREV_SUBOBJECT, TARGET_NEXT_SUBOBJECT, STOP_TARGETING_SUBSYSTEM, TARGET_NEXT_UNINSPECTED_CARGO, TARGET_PREV_UNINSPECTED_CARGO, TARGET_NEWEST_SHIP, TARGET_NEXT_LIVE_TURRET, TARGET_PREV_LIVE_TURRET, TARGET_NEXT_BOMB, TARGET_PREV_BOMB, ATTACK_MESSAGE, DISARM_MESSAGE, DISABLE_MESSAGE, ATTACK_SUBSYSTEM_MESSAGE, CAPTURE_MESSAGE, ENGAGE_MESSAGE, FORM_MESSAGE, PROTECT_MESSAGE, COVER_MESSAGE, WARP_MESSAGE, REARM_MESSAGE, IGNORE_MESSAGE, SQUADMSG_MENU, VIEW_CHASE, VIEW_OTHER_SHIP, VIEW_TOPDOWN, VIEW_TRACK_TARGET, SHOW_GOALS, END_MISSION, ADD_REMOVE_ESCORT, ESCORT_CLEAR, TARGET_NEXT_ESCORT_SHIP, XFER_SHIELD, XFER_LASER, INCREASE_SHIELD, INCREASE_WEAPON, INCREASE_ENGINE, DECREASE_SHIELD, DECREASE_WEAPON, DECREASE_ENGINE, ETS_EQUALIZE, SHIELD_EQUALIZE, SHIELD_XFER_TOP, SHIELD_XFER_BOTTOM, SHIELD_XFER_RIGHT, SHIELD_XFER_LEFT, CYCLE_NEXT_PRIMARY, CYCLE_PREV_PRIMARY, CYCLE_SECONDARY, CYCLE_NUM_MISSLES, RADAR_RANGE_CYCLE, MATCH_TARGET_SPEED, TOGGLE_AUTO_MATCH_TARGET_SPEED, VIEW_EXTERNAL, VIEW_EXTERNAL_TOGGLE_CAMERA_LOCK, LAUNCH_COUNTERMEASURE, ONE_THIRD_THROTTLE, TWO_THIRDS_THROTTLE, PLUS_5_PERCENT_THROTTLE, MINUS_5_PERCENT_THROTTLE, ZERO_THROTTLE, MAX_THROTTLE, TARGET_CLOSEST_REPAIR_SHIP, MULTI_MESSAGE_ALL, MULTI_MESSAGE_FRIENDLY, MULTI_MESSAGE_HOSTILE, MULTI_MESSAGE_TARGET, MULTI_OBSERVER_ZOOM_TO, TIME_SPEED_UP, TIME_SLOW_DOWN, TOGGLE_HUD_CONTRAST, MULTI_TOGGLE_NETINFO, MULTI_SELF_DESTRUCT, TOGGLE_HUD, HUD_TARGETBOX_TOGGLE_WIREFRAME, AUTO_PILOT_TOGGLE, NAV_CYCLE, TOGGLE_GLIDING, CYCLE_PRIMARY_WEAPON_SEQUENCE, CUSTOM_CONTROL_1, CUSTOM_CONTROL_2, CUSTOM_CONTROL_3, CUSTOM_CONTROL_4, CUSTOM_CONTROL_5 }; int Dead_key_set[] = { TARGET_NEXT, TARGET_PREV, TARGET_NEXT_CLOSEST_HOSTILE, TARGET_PREV_CLOSEST_HOSTILE, TARGET_NEXT_CLOSEST_FRIENDLY, TARGET_PREV_CLOSEST_FRIENDLY, TARGET_TARGETS_TARGET, TARGET_CLOSEST_SHIP_ATTACKING_TARGET, STOP_TARGETING_SHIP, TOGGLE_AUTO_TARGETING, TARGET_SUBOBJECT_IN_RETICLE, TARGET_PREV_SUBOBJECT, TARGET_NEXT_SUBOBJECT, STOP_TARGETING_SUBSYSTEM, TARGET_NEWEST_SHIP, TARGET_NEXT_LIVE_TURRET, TARGET_PREV_LIVE_TURRET, TARGET_NEXT_BOMB, TARGET_PREV_BOMB, VIEW_CHASE, VIEW_OTHER_SHIP, VIEW_TOPDOWN, SHOW_GOALS, ADD_REMOVE_ESCORT, ESCORT_CLEAR, TARGET_NEXT_ESCORT_SHIP, TARGET_CLOSEST_REPAIR_SHIP, MULTI_MESSAGE_ALL, MULTI_MESSAGE_FRIENDLY, MULTI_MESSAGE_HOSTILE, MULTI_MESSAGE_TARGET, MULTI_OBSERVER_ZOOM_TO, TIME_SPEED_UP, TIME_SLOW_DOWN }; int Critical_key_set[] = { CYCLE_NEXT_PRIMARY, CYCLE_PREV_PRIMARY, CYCLE_SECONDARY, CYCLE_NUM_MISSLES, INCREASE_WEAPON, DECREASE_WEAPON, INCREASE_SHIELD, DECREASE_SHIELD, INCREASE_ENGINE, DECREASE_ENGINE, ETS_EQUALIZE, SHIELD_EQUALIZE, SHIELD_XFER_TOP, SHIELD_XFER_BOTTOM, SHIELD_XFER_LEFT, SHIELD_XFER_RIGHT, XFER_SHIELD, XFER_LASER, }; int Non_critical_key_set[] = { MATCH_TARGET_SPEED, TOGGLE_AUTO_MATCH_TARGET_SPEED, TARGET_NEXT, TARGET_PREV, TARGET_NEXT_CLOSEST_HOSTILE, TARGET_PREV_CLOSEST_HOSTILE, TOGGLE_AUTO_TARGETING, TARGET_NEXT_CLOSEST_FRIENDLY, TARGET_PREV_CLOSEST_FRIENDLY, TARGET_SHIP_IN_RETICLE, TARGET_LAST_TRANMISSION_SENDER, TARGET_CLOSEST_REPAIR_SHIP, TARGET_CLOSEST_SHIP_ATTACKING_TARGET, STOP_TARGETING_SHIP, TARGET_CLOSEST_SHIP_ATTACKING_SELF, TARGET_TARGETS_TARGET, TARGET_SUBOBJECT_IN_RETICLE, TARGET_PREV_SUBOBJECT, TARGET_NEXT_SUBOBJECT, STOP_TARGETING_SUBSYSTEM, TARGET_NEXT_BOMB, TARGET_PREV_BOMB, TARGET_NEXT_UNINSPECTED_CARGO, TARGET_PREV_UNINSPECTED_CARGO, TARGET_NEWEST_SHIP, TARGET_NEXT_LIVE_TURRET, TARGET_PREV_LIVE_TURRET, ATTACK_MESSAGE, DISARM_MESSAGE, DISABLE_MESSAGE, ATTACK_SUBSYSTEM_MESSAGE, CAPTURE_MESSAGE, ENGAGE_MESSAGE, FORM_MESSAGE, PROTECT_MESSAGE, COVER_MESSAGE, WARP_MESSAGE, IGNORE_MESSAGE, REARM_MESSAGE, VIEW_CHASE, VIEW_EXTERNAL, VIEW_EXTERNAL_TOGGLE_CAMERA_LOCK, VIEW_OTHER_SHIP, VIEW_TOPDOWN, VIEW_TRACK_TARGET, RADAR_RANGE_CYCLE, SQUADMSG_MENU, SHOW_GOALS, END_MISSION, ADD_REMOVE_ESCORT, ESCORT_CLEAR, TARGET_NEXT_ESCORT_SHIP, MULTI_MESSAGE_ALL, MULTI_MESSAGE_FRIENDLY, MULTI_MESSAGE_HOSTILE, MULTI_MESSAGE_TARGET, MULTI_OBSERVER_ZOOM_TO, TOGGLE_HUD_CONTRAST, MULTI_TOGGLE_NETINFO, MULTI_SELF_DESTRUCT, TOGGLE_HUD, HUD_TARGETBOX_TOGGLE_WIREFRAME, AUTO_PILOT_TOGGLE, NAV_CYCLE, TOGGLE_GLIDING, CUSTOM_CONTROL_1, CUSTOM_CONTROL_2, CUSTOM_CONTROL_3, CUSTOM_CONTROL_4, CUSTOM_CONTROL_5 }; int Ignored_keys[CCFG_MAX]; // set sizes of the key sets automatically int Normal_key_set_size = sizeof(Normal_key_set) / sizeof(int); int Dead_key_set_size = sizeof(Dead_key_set) / sizeof(int); int Critical_key_set_size = sizeof(Critical_key_set) / sizeof(int); int Non_critical_key_set_size = sizeof(Non_critical_key_set) / sizeof(int); // -------------------------------------------------------------- // routine to process keys used only for debugging // -------------------------------------------------------------- void debug_cycle_player_ship(int delta) { if ( Player_obj == NULL ) return; int si_index = Ships[Player_obj->instance].ship_info_index; int sanity = 0; ship_info *sip; while ( TRUE ) { si_index += delta; if ( si_index >= ship_info_size() ){ si_index = 0; } if ( si_index < 0 ){ si_index = static_cast(Ship_info.size() - 1); } sip = &Ship_info[si_index]; if ( sip->flags[Ship::Info_Flags::Player_ship] ){ break; } // just in case sanity++; if ( sanity >= ship_info_size() ){ break; } } change_ship_type(Player_obj->instance, si_index); HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Player ship changed to %s", 0), Ship_info[si_index].name); } /** * Cycle targeted ship to next ship in that species * @param delta Increment */ void debug_cycle_targeted_ship(int delta) { object *objp; ship_info *sip; int si_index, species; char name[NAME_LENGTH]; if ( Player_ai->target_objnum == -1 ) return; objp = &Objects[Player_ai->target_objnum]; if ( objp->type != OBJ_SHIP ) return; si_index = Ships[objp->instance].ship_info_index; Assert(si_index != -1 ); species = Ship_info[si_index].species; int sanity = 0; while ( TRUE ) { si_index += delta; if ( si_index >= ship_info_size() ) si_index = 0; if ( si_index < 0 ) si_index = static_cast(Ship_info.size() - 1); sip = &Ship_info[si_index]; // if it has test in the name, jump over it strcpy_s(name, sip->name); strlwr(name); if ( strstr(name,NOX("test")) != NULL ) continue; if (sip->species == species && (sip->is_fighter_bomber() || sip->flags[Ship::Info_Flags::Transport])) break; // just in case sanity++; if ( sanity >= ship_info_size() ) break; } change_ship_type(objp->instance, si_index); HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Changed player target to %s", 1), Ship_info[si_index].name); } void debug_max_secondary_weapons(object *objp) { Assert(objp); ship *shipp = &Ships[objp->instance]; ship_info *sip = &Ship_info[shipp->ship_info_index]; ship_weapon *swp = &shipp->weapons; for (int index = 0; index < MAX_SHIP_SECONDARY_BANKS; ++index) { if (swp->secondary_bank_weapons[index] >= 0) { weapon_info *wip = &Weapon_info[swp->secondary_bank_weapons[index]]; float capacity = (float)sip->secondary_bank_ammo_capacity[index]; float size = (float)wip->cargo_size; Assertion(size > 0.0f, "Weapon cargo size for %s must be greater than 0!", wip->name); swp->secondary_bank_ammo[index] = (int)std::lround(capacity / size); } } } void debug_max_primary_weapons(object *objp) // Goober5000 { Assert(objp); ship *shipp = &Ships[objp->instance]; ship_info *sip = &Ship_info[shipp->ship_info_index]; ship_weapon *swp = &shipp->weapons; for (int index = 0; index < MAX_SHIP_PRIMARY_BANKS; ++index) { if (swp->primary_bank_weapons[index] >= 0) { weapon_info *wip = &Weapon_info[swp->primary_bank_weapons[index]]; if (wip->wi_flags[Weapon::Info_Flags::Ballistic]) { float capacity = (float)sip->primary_bank_ammo_capacity[index]; float size = (float)wip->cargo_size; Assertion(size > 0.0f, "Weapon cargo size for %s must be greater than 0!", wip->name); swp->primary_bank_ammo[index] = (int)std::lround(capacity / size); } } } } void debug_change_song(int delta) { char buf[256]; if ( event_music_next_soundtrack(delta) != -1 ) { event_music_get_soundtrack_name(buf); HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Soundtrack changed to: %s", 2), buf); } else { HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR( "Event music is not playing", 3)); } } extern int Framerate_delay; extern vec3d Eye_position; extern matrix Eye_matrix; extern void g3_set_view_matrix(const vec3d *view_pos, const matrix *view_matrix, float zoom); extern int Show_cpu; int get_prev_weapon_looped(int current_weapon, int subtype) { int i, new_index; int size = weapon_info_size(); for (i = 1; i < size; i++) { new_index = (size + current_weapon - i) % size; if (Weapon_info[new_index].subtype == subtype) { return new_index; } } return current_weapon; } int get_next_weapon_looped(int current_weapon, int subtype) { int i, new_index; int size = weapon_info_size(); for (i = 1; i < size; i++) { new_index = (current_weapon + i) % size; if (Weapon_info[new_index].subtype == subtype) { return new_index; } } return current_weapon; } void process_debug_keys(int k) { // Kazan -- NO CHEATS IN MULTI if (Game_mode & GM_MULTIPLAYER) { Cheats_enabled = 0; return; } switch (k) { case KEY_DEBUGGED + KEY_Q: case KEY_DEBUGGED1 + KEY_Q: Snapshot_all_events = true; break; case KEY_DEBUGGED + KEY_H: hud_target_toggle_hidden_from_sensors(); break; case KEY_DEBUGGED + KEY_F: extern int wacky_scheme; if(wacky_scheme == 3){ wacky_scheme = 0; } else { wacky_scheme++; } break; case KEY_DEBUGGED + KEY_ALTED + KEY_F: Framerate_delay += 10; HUD_printf(XSTR( "Framerate delay increased to %i milliseconds per frame.", 4), Framerate_delay); break; case KEY_DEBUGGED + KEY_ALTED + KEY_SHIFTED + KEY_F: Framerate_delay -= 10; if (Framerate_delay < 0) Framerate_delay = 0; HUD_printf(XSTR( "Framerate delay decreased to %i milliseconds per frame.", 5), Framerate_delay); break; case KEY_DEBUGGED + KEY_X: case KEY_DEBUGGED1 + KEY_X: HUD_printf("Cloaking has been disabled, thank you for playing fs2_open, %s", Player->callsign); break; case KEY_DEBUGGED + KEY_C: case KEY_DEBUGGED1 + KEY_C: if(Player_obj->flags[Object::Object_Flags::Collides]){ obj_set_flags(Player_obj, Player_obj->flags - Object::Object_Flags::Collides); HUD_sourced_printf(HUD_SOURCE_HIDDEN, "Player no longer collides"); } else { obj_set_flags(Player_obj, Player_obj->flags + Object::Object_Flags::Collides); HUD_sourced_printf(HUD_SOURCE_HIDDEN, "Player collides"); } break; case KEY_DEBUGGED + KEY_SHIFTED + KEY_C: case KEY_DEBUGGED1 + KEY_SHIFTED + KEY_C: Countermeasures_enabled = !Countermeasures_enabled; HUD_printf(XSTR( "Countermeasure firing: %s", 6), Countermeasures_enabled ? XSTR( "ENABLED", 7) : XSTR( "DISABLED", 8)); break; // Goober5000: this should only be compiled in debug builds, since it crashes release #ifndef NDEBUG case KEY_DEBUGGED + KEY_E: gameseq_post_event(GS_EVENT_EVENT_DEBUG); break; #endif // Goober5000: handle time dilation in cheat section case KEY_DEBUGGED + KEY_SHIFTED + KEY_COMMA: case KEY_DEBUGGED1 + KEY_SHIFTED + KEY_COMMA: if ( Game_mode & GM_NORMAL ) { if ( Game_time_compression > (F1_0/MAX_TIME_DIVIDER) ) { change_time_compression(0.5); } else { gamesnd_play_error_beep(); } } else { gamesnd_play_error_beep(); } break; // Goober5000: handle as normal here case KEY_DEBUGGED + KEY_SHIFTED + KEY_PERIOD: case KEY_DEBUGGED1 + KEY_SHIFTED + KEY_PERIOD: if ( Game_mode & GM_NORMAL ) { if ( Game_time_compression < (F1_0*MAX_TIME_MULTIPLIER) ) { change_time_compression(2); } else { gamesnd_play_error_beep(); } } else { gamesnd_play_error_beep(); } break; // Kill! the currently targeted ship. case KEY_DEBUGGED + KEY_K: case KEY_DEBUGGED1 + KEY_K: if (Player_ai->target_objnum != -1) { object *objp = &Objects[Player_ai->target_objnum]; switch (objp->type) { case OBJ_SHIP: // remove guardian flag -- kazan Ships[objp->instance].ship_guardian_threshold = 0; ship_apply_local_damage( objp, Player_obj, &objp->pos, 100000.0f, MISS_SHIELDS, CREATE_SPARKS); ship_apply_local_damage( objp, Player_obj, &objp->pos, 1.0f, MISS_SHIELDS, CREATE_SPARKS); break; case OBJ_WEAPON: Weapons[objp->instance].lifeleft = 0.01f; break; } } break; // play the next mission message case KEY_DEBUGGED + KEY_V: extern int Message_debug_index; extern int Num_messages_playing; // stop any other messages if(Num_messages_playing){ message_kill_all(1); } // next message if(Message_debug_index >= Num_messages - 1){ Message_debug_index = Num_builtin_messages; } else { Message_debug_index++; } // play the message message_send_unique_to_player( Messages[Message_debug_index].name, Message_waves[Messages[Message_debug_index].wave_info.index].name, MESSAGE_SOURCE_SPECIAL, MESSAGE_PRIORITY_HIGH, 0, 0 ); if (Messages[Message_debug_index].avi_info.index == -1) { HUD_printf("No anim set for message \"%s\"; None will play!", Messages[Message_debug_index].name); } break; // play the previous mission message case KEY_DEBUGGED + KEY_SHIFTED + KEY_V: extern int Message_debug_index; extern int Num_messages_playing; // stop any other messages if(Num_messages_playing){ message_kill_all(1); } // go maybe go down one if(Message_debug_index == Num_builtin_messages - 1){ Message_debug_index = Num_builtin_messages; } else if(Message_debug_index > Num_builtin_messages){ Message_debug_index--; } // play the message message_send_unique_to_player( Messages[Message_debug_index].name, Message_waves[Messages[Message_debug_index].wave_info.index].name, MESSAGE_SOURCE_SPECIAL, MESSAGE_PRIORITY_HIGH, 0, 0 ); if (Messages[Message_debug_index].avi_info.index == -1) { HUD_printf("No avi associated with this message; None will play!"); } break; // reset to the beginning of mission messages case KEY_DEBUGGED + KEY_ALTED + KEY_V: extern int Message_debug_index; Message_debug_index = Num_builtin_messages - 1; HUD_printf("Resetting to first mission message"); break; // Kill! the currently targeted ship. case KEY_DEBUGGED + KEY_ALTED + KEY_SHIFTED + KEY_K: case KEY_DEBUGGED1 + KEY_ALTED + KEY_SHIFTED + KEY_K: if (Player_ai->target_objnum != -1) { object *objp = &Objects[Player_ai->target_objnum]; if (objp->type == OBJ_SHIP) { ship_apply_local_damage( objp, Player_obj, &objp->pos, Ships[objp->instance].ship_max_hull_strength * 0.1f + 10.0f, MISS_SHIELDS, CREATE_SPARKS); } } break; // Kill the currently targeted subsystem. case KEY_DEBUGGED + KEY_SHIFTED + KEY_K: case KEY_DEBUGGED1 + KEY_SHIFTED + KEY_K: if ((Player_ai->target_objnum != -1) && (Player_ai->targeted_subsys != NULL)) { object *objp = &Objects[Player_ai->target_objnum]; if ( objp->type == OBJ_SHIP ) { ship *sp = &Ships[objp->instance]; vec3d g_subobj_pos; get_subsystem_world_pos(objp, Player_ai->targeted_subsys, &g_subobj_pos); do_subobj_hit_stuff(objp, Player_obj, &g_subobj_pos, Player_ai->targeted_subsys->system_info->subobj_num, (float) -Player_ai->targeted_subsys->system_info->type, NULL); //100.0f); if ( sp->subsys_info[SUBSYSTEM_ENGINE].aggregate_current_hits <= 0.0f ) { mission_log_add_entry(LOG_SHIP_DISABLED, sp->ship_name, NULL ); sp->flags.set(Ship::Ship_Flags::Disabled); // add the disabled flag } if ( sp->subsys_info[SUBSYSTEM_TURRET].aggregate_current_hits <= 0.0f ) { mission_log_add_entry(LOG_SHIP_DISARMED, sp->ship_name, NULL ); } } } break; case KEY_DEBUGGED + KEY_ALTED + KEY_K: case KEY_DEBUGGED1 + KEY_ALTED + KEY_K: { float shield, integrity; vec3d pos, randvec; vm_vec_rand_vec_quick(&randvec); vm_vec_scale_add(&pos, &Player_obj->pos, &randvec, Player_obj->radius); ship_apply_local_damage(Player_obj, Player_obj, &pos, 25.0f, MISS_SHIELDS, CREATE_SPARKS); hud_get_target_strength(Player_obj, &shield, &integrity); HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "You whacked yourself down to %7.3f percent hull.\n", 9), 100.0f * integrity); break; } // Whack down the player's shield and hull by a little more than 50% // Select next object to be viewed by AI. case KEY_DEBUGGED + KEY_I: case KEY_DEBUGGED1 + KEY_I: Player_obj->flags.toggle(Object::Object_Flags::Invulnerable); HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "You are %s", 10), Player_obj->flags[Object::Object_Flags::Invulnerable] ? XSTR( "now INVULNERABLE!", 11) : XSTR( "no longer invulnerable...", 12)); break; case KEY_DEBUGGED + KEY_SHIFTED + KEY_I: case KEY_DEBUGGED1 + KEY_SHIFTED + KEY_I: if (Player_ai->target_objnum != -1) { object *objp = &Objects[Player_ai->target_objnum]; objp->flags.toggle(Object::Object_Flags::Invulnerable); HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Player's target [%s] is %s", 13), Ships[objp->instance].get_display_name(), objp->flags[Object::Object_Flags::Invulnerable] ? XSTR( "now INVULNERABLE!", 11) : XSTR( "no longer invulnerable...", 12)); } break; case KEY_DEBUGGED + KEY_N: AI_watch_object++; HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Spewing debug info about object #%d", 14), AI_watch_object); break; case KEY_DEBUGGED + KEY_O: case KEY_DEBUGGED1 + KEY_O: toggle_player_object(); break; case KEY_DEBUGGED + KEY_SHIFTED + KEY_O: extern int Debug_octant; if(Debug_octant == 7){ Debug_octant = -1; } else { Debug_octant++; } nprintf(("General", "Debug_octant == %d\n", Debug_octant)); break; case KEY_DEBUGGED + KEY_P: supernova_start(20); break; case KEY_DEBUGGED + KEY_W: case KEY_DEBUGGED1 + KEY_W: case KEY_DEBUGGED + KEY_SHIFTED + KEY_W: case KEY_DEBUGGED1 + KEY_SHIFTED + KEY_W: // temp code for testing purposes, toggles weapon energy cheat Weapon_energy_cheat = !Weapon_energy_cheat; if (Weapon_energy_cheat) { if (k & KEY_SHIFTED) HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR( "Weapon energy and missile count will always be at full ALL SHIPS!", 15)); else HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR( "Weapon energy and missile count will always be at full for player", 16)); debug_max_secondary_weapons(Player_obj); debug_max_primary_weapons(Player_obj); if (k & KEY_SHIFTED) { object *objp; for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) if (objp->type == OBJ_SHIP) { debug_max_secondary_weapons(objp); debug_max_primary_weapons(objp); } } } else HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR( "Normal weapon energy system / missile count restored", 17)); break; case KEY_DEBUGGED + KEY_G: case KEY_DEBUGGED1 + KEY_G: mission_goal_mark_all_true( PRIMARY_GOAL ); break; case KEY_DEBUGGED + KEY_G + KEY_SHIFTED: case KEY_DEBUGGED1 + KEY_G + KEY_SHIFTED: mission_goal_mark_all_true( SECONDARY_GOAL ); break; case KEY_DEBUGGED + KEY_G + KEY_ALTED: case KEY_DEBUGGED1 + KEY_G + KEY_ALTED: mission_goal_mark_all_true( BONUS_GOAL ); break; case KEY_DEBUGGED + KEY_9: { case KEY_DEBUGGED1 + KEY_9: ship* shipp; shipp = &Ships[Player_obj->instance]; int *weap = &shipp->weapons.secondary_bank_weapons[shipp->weapons.current_secondary_bank]; *weap = get_next_weapon_looped(*weap, WP_MISSILE); HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Secondary Weapon forced to %s", 18), Weapon_info[*weap].get_display_name()); break; } case KEY_DEBUGGED + KEY_SHIFTED + KEY_9: { case KEY_DEBUGGED1 + KEY_SHIFTED + KEY_9: ship* shipp; shipp = &Ships[Player_obj->instance]; int *weap = &shipp->weapons.secondary_bank_weapons[shipp->weapons.current_secondary_bank]; *weap = get_prev_weapon_looped(*weap, WP_MISSILE); HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Secondary Weapon forced to %s", 18), Weapon_info[*weap].get_display_name()); break; } case KEY_DEBUGGED + KEY_U: { case KEY_DEBUGGED1 + KEY_U: // launch asteroid object *asteroid_create(asteroid_field *asfieldp, int asteroid_type, int subtype); object *objp = asteroid_create(&Asteroid_field, 0, 0); if(objp == NULL) { break; } vec3d vel; vm_vec_copy_scale(&vel, &Player_obj->orient.vec.fvec, 50.0f); objp->phys_info.vel = vel; objp->phys_info.desired_vel = vel; objp->pos = Player_obj->pos; HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR( "Asteroid launched", 1595)); break; } case KEY_DEBUGGED + KEY_0: { case KEY_DEBUGGED1 + KEY_0: ship* shipp; shipp = &Ships[Player_obj->instance]; int *weap = &shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]; *weap = get_next_weapon_looped(*weap, WP_LASER); HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Primary Weapon forced to %s", 19), Weapon_info[*weap].get_display_name()); break; } case KEY_DEBUGGED + KEY_SHIFTED + KEY_0: { case KEY_DEBUGGED1 + KEY_SHIFTED + KEY_0: ship* shipp; shipp = &Ships[Player_obj->instance]; int *weap = &shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]; *weap = get_prev_weapon_looped(*weap, WP_LASER); HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Primary Weapon forced to %s", 19), Weapon_info[*weap].get_display_name()); break; } case KEY_DEBUGGED + KEY_J: { int new_pattern = event_music_return_current_pattern(); new_pattern++; if ( new_pattern >= MAX_PATTERNS ) new_pattern = 0; event_music_change_pattern(new_pattern); break; } case KEY_DEBUGGED + KEY_M: { if ( Event_music_enabled ) { event_music_disable(); HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR( "Event music disabled", 20)); } else { event_music_enable(); HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR( "Event music enabled", 21)); } break; } case KEY_DEBUGGED + KEY_R: case KEY_DEBUGGED1 + KEY_R: { // rearm the target, if we have one object *obj_to_rearm = (Player_ai->target_objnum >= 0) ? &Objects[Player_ai->target_objnum] : Player_obj; if (is_support_allowed(obj_to_rearm)) { HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR("Issuing rearm request for %s", 1610), Ships[obj_to_rearm->instance].get_display_name()); ai_issue_rearm_request(obj_to_rearm); } else { HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR("Cannot issue rearm request for %s", 1611), Ships[obj_to_rearm->instance].get_display_name()); } break; } // Goober5000 case KEY_DEBUGGED + KEY_SHIFTED + KEY_R: case KEY_DEBUGGED1 + KEY_SHIFTED + KEY_R: { // toggle support for this mission if (The_mission.support_ships.max_support_ships == 0) { HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR("Setting maximum number of support ships to infinite.", 1643)); The_mission.support_ships.max_support_ships = -1; } else { HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR("Setting maximum number of support ships to zero.", 1644)); The_mission.support_ships.max_support_ships = 0; } break; } // Goober5000 case KEY_DEBUGGED + KEY_SHIFTED + KEY_U: case KEY_DEBUGGED1 + KEY_SHIFTED + KEY_U: { // toggle the current target between scanned and unscanned if (Player_ai->target_objnum >= 0) { object *objp = &Objects[Player_ai->target_objnum]; if (objp->type == OBJ_SHIP) { ship *targeted_shipp = &Ships[objp->instance]; if (Player_ai->targeted_subsys == nullptr) { if (targeted_shipp->flags[Ship::Ship_Flags::Cargo_revealed]) ship_do_cargo_hidden(targeted_shipp); else ship_do_cargo_revealed(targeted_shipp); } else { if (Player_ai->targeted_subsys->flags[Ship::Subsystem_Flags::Cargo_revealed]) ship_do_cap_subsys_cargo_hidden(targeted_shipp, Player_ai->targeted_subsys); else ship_do_cap_subsys_cargo_revealed(targeted_shipp, Player_ai->targeted_subsys); } } } break; } case KEY_DEBUGGED + KEY_SHIFTED + KEY_UP: Game_detail_level++; HUD_printf( XSTR( "Detail level set to %+d\n", 22), Game_detail_level ); break; case KEY_DEBUGGED + KEY_SHIFTED + KEY_DOWN: Game_detail_level--; HUD_printf( XSTR( "Detail level set to %+d\n", 22), Game_detail_level ); break; #ifndef NDEBUG case KEY_DEBUGGED + KEY_SHIFTED + KEY_T: { extern int Test_begin; if ( Test_begin == 1 ) break; Test_begin = 1; HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR( "Frame Rate test started", 23)); break; } #endif case KEY_DEBUGGED + KEY_A: { HUD_printf("frame rate currently is %0.2f FPS", 1.f/flFrametime); break; } case KEY_DEBUGGED + KEY_D: extern int OO_update_index; if(MULTIPLAYER_MASTER){ do { OO_update_index++; } while((OO_update_index < (MAX_PLAYERS-1)) && !MULTI_CONNECTED(Net_players[OO_update_index])); if(OO_update_index >= MAX_PLAYERS-1){ OO_update_index = -1; } } else { if(OO_update_index < 0){ OO_update_index = MY_NET_PLAYER_NUM; } else { OO_update_index = -1; } } break; // change player ship to next flyable type case KEY_DEBUGGED + KEY_RIGHT: debug_cycle_player_ship(1); break; // change player ship to previous flyable ship case KEY_DEBUGGED + KEY_LEFT: debug_cycle_player_ship(-1); break; // cycle target to ship case KEY_DEBUGGED + KEY_SHIFTED + KEY_RIGHT: debug_cycle_targeted_ship(1); break; // cycle target to previous ship case KEY_DEBUGGED + KEY_SHIFTED + KEY_LEFT: debug_cycle_targeted_ship(-1); break; // change species of the targeted ship case KEY_DEBUGGED + KEY_S: { if ( Player_ai->target_objnum < 0 ) break; object *objp; ship_info *sip; objp = &Objects[Player_ai->target_objnum]; if ( objp->type != OBJ_SHIP ) return; sip = &Ship_info[Ships[objp->instance].ship_info_index]; sip->species++; if (sip->species >= (int)Species_info.size()) sip->species = 0; HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Species of target changed to: %s", 24), Species_info[sip->species].species_name); break; } case KEY_DEBUGGED + KEY_SHIFTED + KEY_S: game_increase_skill_level(); HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Skill level set to %s.", 25), Skill_level_names(Game_skill_level)); break; case KEY_DEBUGGED + KEY_T: { char buf[256]; event_music_get_info(buf); HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", buf); break; } case KEY_DEBUGGED + KEY_UP: case KEY_DEBUGGED1 + KEY_UP: debug_change_song(1); break; case KEY_DEBUGGED + KEY_DOWN: case KEY_DEBUGGED1 + KEY_DOWN: debug_change_song(-1); break; case KEY_PADMINUS: { int init_flag = 0; if ( keyd_pressed[KEY_1] ) { init_flag = 1; HUD_color_red -= 4; } if ( keyd_pressed[KEY_2] ) { init_flag = 1; HUD_color_green -= 4; } if ( keyd_pressed[KEY_3] ) { init_flag = 1; HUD_color_blue -= 4; } if (init_flag) HUD_init_colors(); break; } case KEY_DEBUGGED + KEY_Y: extern int tst; tst = 2; break; case KEY_PADPLUS: { int init_flag = 0; if ( keyd_pressed[KEY_1] ) { init_flag = 1; HUD_color_red += 4; } if ( keyd_pressed[KEY_2] ) { init_flag = 1; HUD_color_green += 4; } if ( keyd_pressed[KEY_3] ) { init_flag = 1; HUD_color_blue += 4; } if (init_flag) HUD_init_colors(); break; } case KEY_DEBUGGED + KEY_ALTED + KEY_EQUAL: case KEY_DEBUGGED1 + KEY_ALTED + KEY_EQUAL: { camera *cam = Main_camera.getCamera(); if(cam == NULL) { HUD_sourced_printf(HUD_SOURCE_HIDDEN, "Couldn't get camera FOV"); break; } cam->set_fov(cam->get_fov() + 0.1f); HUD_sourced_printf(HUD_SOURCE_HIDDEN, "Camera fov raised to %0.2f" , cam->get_fov()); } break; case KEY_DEBUGGED + KEY_ALTED + KEY_MINUS: case KEY_DEBUGGED1 + KEY_ALTED + KEY_MINUS: { camera *cam = Main_camera.getCamera(); if(cam == NULL) { HUD_sourced_printf(HUD_SOURCE_HIDDEN, "Couldn't get camera FOV"); break; } cam->set_fov(cam->get_fov() - 0.1f); HUD_sourced_printf(HUD_SOURCE_HIDDEN, "Camera fov lowered to %0.2f" , cam->get_fov()); } break; case KEY_DEBUGGED + KEY_Z: case KEY_DEBUGGED1 + KEY_Z: { Show_cpu = !Show_cpu; } break; case KEY_DEBUGGED + KEY_B: case KEY_DEBUGGED1 + KEY_B: { Cmdline_bmpman_usage = !Cmdline_bmpman_usage; } } // end switch } void ppsk_hotkeys(int k) { // use k to check for keys that can have Shift,Ctrl,Alt,Del status int hotkey_set; #ifndef NDEBUG k &= ~KEY_DEBUGGED; // since hitting F11 will set this bit #endif switch (k) { case KEY_F5: case KEY_F6: case KEY_F7: case KEY_F8: case KEY_F9: case KEY_F10: case KEY_F11: case KEY_F12: hotkey_set = mission_hotkey_get_set_num(k); if ( !(Players[Player_num].flags & PLAYER_FLAGS_MSG_MODE) ) hud_target_hotkey_select( hotkey_set ); else hud_squadmsg_hotkey_select( hotkey_set ); break; case KEY_F5 + KEY_SHIFTED: case KEY_F6 + KEY_SHIFTED: case KEY_F7 + KEY_SHIFTED: case KEY_F8 + KEY_SHIFTED: case KEY_F9 + KEY_SHIFTED: case KEY_F10 + KEY_SHIFTED: case KEY_F11 + KEY_SHIFTED: case KEY_F12 + KEY_SHIFTED: hotkey_set = mission_hotkey_get_set_num(k&(~KEY_SHIFTED)); mprintf(("Adding to set %d\n", hotkey_set+1)); if ( Player_ai->target_objnum == -1) HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "No target to add/remove from set %d.", 26), hotkey_set+1); else { hud_target_hotkey_add_remove( hotkey_set, &Objects[Player_ai->target_objnum], HOTKEY_USER_ADDED); HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "%s added to set %d. (F%d)", 27), Ships[Objects[Player_ai->target_objnum].instance].get_display_name(), hotkey_set, 4+hotkey_set+1); } break; case KEY_F5 + KEY_SHIFTED + KEY_ALTED: case KEY_F6 + KEY_SHIFTED + KEY_ALTED: case KEY_F7 + KEY_SHIFTED + KEY_ALTED: case KEY_F8 + KEY_SHIFTED + KEY_ALTED: case KEY_F9 + KEY_SHIFTED + KEY_ALTED: case KEY_F10 + KEY_SHIFTED + KEY_ALTED: case KEY_F11 + KEY_SHIFTED + KEY_ALTED: case KEY_F12 + KEY_SHIFTED + KEY_ALTED: hotkey_set = mission_hotkey_get_set_num(k & (~(KEY_SHIFTED+KEY_ALTED))); hud_target_hotkey_clear( hotkey_set ); break; case KEY_SHIFTED + KEY_MINUS: if ( HUD_color_alpha > HUD_COLOR_ALPHA_USER_MIN ) { HUD_color_alpha--; HUD_init_colors(); } break; case KEY_SHIFTED + KEY_EQUAL: if ( HUD_color_alpha < HUD_COLOR_ALPHA_USER_MAX ) { HUD_color_alpha++; HUD_init_colors(); } break; } // end switch } /** * Check keypress 'key' against a set of valid controls and mark the match in the * player's button info bitfield. Also checks joystick controls in the set. * * @param key Scancode (plus modifiers). * @param count Total size of the list * @param list List of ::Control_config struct action indices to check for */ void process_set_of_keys(int key, int count, int *list) { int i; for (i=0; ibi, list[i]); } /** * Routine to process keys used for player ship stuff (*not* ship movement). */ void process_player_ship_keys(int k) { int masked_k; masked_k = k & ~KEY_CTRLED; // take out CTRL modifier only // moved this line to beginning of function since hotkeys now encompass // F5 - F12. We can return after using F11 as a hotkey. ppsk_hotkeys(masked_k); if (keyd_pressed[KEY_DEBUG_KEY]){ return; } // if we're in supernova mode. do nothing if(Player->control_mode == PCM_SUPERNOVA){ return; } // pass the key to the squadmate messaging code. If the messaging code took the key, then return // from here immediately since we don't want to do further key processing. if ( hud_squadmsg_read_key(k) ) return; if ( Player->control_mode == PCM_NORMAL ) { // The following things are not legal to do while dead. if ( !(Game_mode & GM_DEAD) ) { process_set_of_keys(masked_k, Normal_key_set_size, Normal_key_set); } else { process_set_of_keys(masked_k, Dead_key_set_size, Dead_key_set); } if (lua_game_control & LGC_B_POLL_ALL) { // first clear all button_info_clear(&Player->lua_bi_full); // then check the keys. int i; for(i = 0; i < CCFG_MAX; i++) { if (check_control(i, masked_k)) button_info_set(&Player->lua_bi_full, i); } } } else { } } //Cyborg17 - Linking this function to restart sound if needed. This is to avoid having to //check for subspace every frame. extern void game_start_subspace_ambient_sound(); /** * Handler for when player hits 'ESC' during the game */ void game_do_end_mission_popup() { int pf_flags, choice; // do the multiplayer version of this if(Game_mode & GM_MULTIPLAYER){ multi_quit_game(PROMPT_ALL); } else { // single player version.... // do housekeeping things. game_stop_time(); game_stop_looped_sounds(); audiostream_pause_all(); snd_stop_all(); pf_flags = PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON; choice = popup(pf_flags, 3, POPUP_NO, XSTR( "&Yes, Quit", 28), XSTR( "Yes, &Restart", 29), XSTR( "Do you really want to end the mission?", 30)); switch (choice) { case 1: gameseq_post_event(GS_EVENT_END_GAME); break; case 2: gameseq_post_event(GS_EVENT_ENTER_GAME); break; // do nothing; resume game default: // Cyborg17 - best place to check for this *one* looping sound. if (Game_subspace_effect) { game_start_subspace_ambient_sound(); } audiostream_unpause_all(); break; } game_start_time(); game_flush(); } } /** * Handle pause keypress */ void game_process_pause_key() { // special processing for multiplayer if (Game_mode & GM_MULTIPLAYER) { if(Multi_pause_status){ multi_pause_request(0); } else { multi_pause_request(1); } } else { gameseq_post_event( GS_EVENT_PAUSE_GAME ); } } /** * Process cheat codes */ void game_process_cheats(int k) { size_t i; if ( k == 0 ){ return; } // no cheats in multiplayer, ever if(Game_mode & GM_MULTIPLAYER){ Cheats_enabled = 0; return; } for (i = 0; i < CHEAT_BUFFER_LEN; i++){ CheatBuffer[i]=CheatBuffer[i+1]; } CheatBuffer[CHEAT_BUFFER_LEN - 1] = (char)key_to_ascii(k); cheatCode detectedCheatCode = CHEAT_CODE_NONE; for(i=0; i < CHEATS_TABLE_LEN; i++) { Cheat cheat = cheatsTable[i]; if(!strncmp(cheat.data, CheatBuffer, CHEAT_BUFFER_LEN)){ detectedCheatCode = cheat.code; break; } } if(detectedCheatCode == CHEAT_CODE_FREESPACE){ Cheats_enabled = 1; // cheating allows the changing of weapons so we have to grab anything // that we don't already have loaded, just in case extern void weapons_page_in_cheats(); weapons_page_in_cheats(); HUD_printf("Cheats enabled"); } if(detectedCheatCode == CHEAT_CODE_FISH){ // only enable in the Vasudan main hall if ((gameseq_get_state() == GS_STATE_MAIN_MENU) && main_hall_is_vasudan()) { extern void fishtank_start(); fishtank_start(); } } if(detectedCheatCode == CHEAT_CODE_HEADZ){ // only enable in the Vasudan main hall if ((gameseq_get_state() == GS_STATE_MAIN_MENU) && main_hall_is_vasudan()) { main_hall_vasudan_funny(); } } if(detectedCheatCode == CHEAT_CODE_SKIP && (gameseq_get_state() == GS_STATE_MAIN_MENU)){ extern void main_hall_campaign_cheat(); main_hall_campaign_cheat(); } if(detectedCheatCode == CHEAT_CODE_TOOLED && (Game_mode & GM_IN_MISSION)){ Tool_enabled = 1; HUD_printf("Prepare to be taken to school"); } if(detectedCheatCode == CHEAT_CODE_PIRATE && (Game_mode & GM_IN_MISSION) && (Player_obj != NULL)){ extern void prevent_spawning_collision(object *new_obj); ship_subsys *ptr; char name[NAME_LENGTH]; int ship_idx, ship_class; // if not found, then don't create it :( ship_class = ship_info_lookup("Volition Bravos"); if (ship_class < 0) return; HUD_printf(NOX("Walk the plank")); vec3d pos = Player_obj->pos; matrix orient = Player_obj->orient; pos.xyz.x += frand_range(-700.0f, 700.0f); pos.xyz.y += frand_range(-700.0f, 700.0f); pos.xyz.z += frand_range(-700.0f, 700.0f); int objnum = ship_create(&orient, &pos, ship_class); if (objnum < 0) return; ship *shipp = &Ships[Objects[objnum].instance]; shipp->ship_name[0] = '\0'; shipp->display_name.clear(); shipp->orders_accepted = (1<ship_name, name); break; } ship_idx++; } while(1); shipp->flags.set(Ship::Ship_Flags::Escort); shipp->escort_priority = 1000 - ship_idx; // now make sure we're not colliding with anyone prevent_spawning_collision(&Objects[objnum]); // Goober5000 - beam free for (ptr = GET_FIRST(&shipp->subsys_list); ptr != END_OF_LIST(&shipp->subsys_list); ptr = GET_NEXT(ptr)) { // mark all turrets as beam free if (ptr->system_info->type == SUBSYSTEM_TURRET) { ptr->weapons.flags.set(Ship::Weapon_Flags::Beam_Free); ptr->turret_next_fire_stamp = timestamp((int) frand_range(50.0f, 4000.0f)); } } // Cyborg17 to prevent a nullptr... ship_set_warp_effects(&Objects[objnum]); // warpin shipfx_warpin_start(&Objects[objnum]); } } void game_process_keys() { int k; button_info_clear(&Player->bi); // clear out the button info struct for the player do { k = game_poll(); if ( Game_mode & GM_DEAD_BLEW_UP ) { continue; } game_process_cheats( k ); process_player_ship_keys(k); process_debug_keys(k); switch (k) { case 0: // No key break; case KEY_ESC: if ( Player->control_mode != PCM_NORMAL ) { if ( Player->control_mode == PCM_WARPOUT_STAGE1 ) { gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_STOP ); } else { // too late to abort warp out! } } else { // let the ESC key break out of messaging mode if ( Players[Player_num].flags & PLAYER_FLAGS_MSG_MODE ) { hud_squadmsg_toggle(); break; } //If topdown view in non-2D mission, go back to cockpit view. if ( (Viewer_mode & VM_TOPDOWN) && !(The_mission.flags[Mission::Mission_Flags::Mission_2d]) && !(Perspective_locked) ) { Viewer_mode &= ~VM_TOPDOWN; break; } // if in external view or chase view, go back to cockpit view if ( (Viewer_mode & (VM_EXTERNAL|VM_CHASE|VM_OTHER_SHIP)) && !(Perspective_locked) ) { Viewer_mode &= ~(VM_EXTERNAL|VM_CHASE|VM_OTHER_SHIP); break; } if (!(Game_mode & GM_DEAD_DIED)) game_do_end_mission_popup(); } break; case KEY_Y: break; case KEY_N: break; case KEY_DEBUGGED | KEY_PAUSE: gameseq_post_event( GS_EVENT_DEBUG_PAUSE_GAME ); break; case KEY_ALTED + KEY_PAUSE: if( Game_mode & GM_DEAD_BLEW_UP || Game_mode & GM_DEAD_DIED) { break; } pause_set_type(PAUSE_TYPE_VIEWER); game_process_pause_key(); break; case KEY_PAUSE: if( Game_mode & GM_DEAD_BLEW_UP || Game_mode & GM_DEAD_DIED) { break; } pause_set_type(PAUSE_TYPE_NORMAL); game_process_pause_key(); break; } // end switch } while (k); // lua button command override goes here!! if (lua_game_control & LGC_B_OVERRIDE) { button_info temp = Player->bi; Player->bi = Player->lua_bi; Player->lua_bi = temp; } else if (lua_game_control & LGC_B_ADDITIVE) { // add the lua commands to current commands int i; for (i=0; ibi.status[i] |= Player->lua_bi.status[i]; Player->lua_bi = Player->bi; } else { // just copy over the values Player->lua_bi = Player->bi; } // there.. wasnt that bad hack was it? button_info_do(&Player->bi); // call functions based on status of button_info bit vectors } int button_function_critical(int n, net_player *p = NULL) { object *objp; player *pl; net_player *npl; int at_self; // flag indicating the object is local (for hud messages, etc) Assert(n >= 0); // multiplayer clients should leave critical button bits alone and pass them to the server instead if (MULTIPLAYER_CLIENT) { // if this flag is set, we should apply the button itself (came from the server) if (!Multi_button_info_ok){ return 0; } } // in single player mode make sure we're using the player object and the player himself, otherwise use the object and // player pertaining to the passed net_player npl = NULL; if (p == NULL) { objp = Player_obj; pl = Player; if(Game_mode & GM_MULTIPLAYER){ npl = Net_player; // if we're the server in multiplayer and we're an observer, don't process our own critical button functions if((Net_player->flags & NETINFO_FLAG_AM_MASTER) && (Net_player->flags & NETINFO_FLAG_OBSERVER)){ return 0; } } at_self = 1; } else { objp = &Objects[p->m_player->objnum]; pl = p->m_player; npl = p; at_self = 0; if ( NETPLAYER_IS_DEAD(npl) || (Ships[Objects[pl->objnum].instance].flags[Ship::Ship_Flags::Dying]) ) return 0; } switch (n) { // cycle num primaries to fire at once case CYCLE_PRIMARY_WEAPON_SEQUENCE: { int count; ship * shipp = &Ships[objp->instance]; ship_weapon *swp = &shipp->weapons; ship_info *sip = &Ship_info[shipp->ship_info_index]; if (sip->flags[Ship::Info_Flags::Dyn_primary_linking]) { polymodel *pm = model_get( sip->model_num ); count = (int)ftables.getNext( pm->gun_banks[ swp->current_primary_bank ].num_slots, swp->primary_bank_slot_count[ swp->current_primary_bank ] ); swp->primary_bank_slot_count[ swp->current_primary_bank ] = count; shipp->last_fired_point[ swp->current_primary_bank ] += count - ( shipp->last_fired_point[ swp->current_primary_bank ] % count); shipp->last_fired_point[ swp->current_primary_bank ] -= 1; shipp->last_fired_point[ swp->current_primary_bank ] %= swp->primary_bank_slot_count[ swp->current_primary_bank ]; } } break; // cycle to next primary weapon case CYCLE_NEXT_PRIMARY: if (at_self) { control_used(CYCLE_NEXT_PRIMARY); } hud_gauge_popup_start(HUD_WEAPONS_GAUGE); if (ship_select_next_primary(objp, CYCLE_PRIMARY_NEXT)) { ship* shipp = &Ships[objp->instance]; if ( timestamp_elapsed(shipp->weapons.next_primary_fire_stamp[shipp->weapons.current_primary_bank]) ) { shipp->weapons.next_primary_fire_stamp[shipp->weapons.current_primary_bank] = timestamp(250); // 1/4 second delay until can fire } // multiplayer server should maintain bank/link status here if ( MULTIPLAYER_MASTER ) { Assert(npl != NULL); multi_server_update_player_weapons(npl,shipp); } } break; // cycle to previous primary weapon case CYCLE_PREV_PRIMARY: if (at_self) { control_used(CYCLE_PREV_PRIMARY); } hud_gauge_popup_start(HUD_WEAPONS_GAUGE); if (ship_select_next_primary(objp, CYCLE_PRIMARY_PREV)) { ship* shipp = &Ships[objp->instance]; if ( timestamp_elapsed(shipp->weapons.next_primary_fire_stamp[shipp->weapons.current_primary_bank]) ) { shipp->weapons.next_primary_fire_stamp[shipp->weapons.current_primary_bank] = timestamp(250); // 1/4 second delay until can fire } // multiplayer server should maintain bank/link status here if ( MULTIPLAYER_MASTER ) { Assert(npl != NULL); multi_server_update_player_weapons(npl,shipp); } } break; // cycle to next secondary weapon case CYCLE_SECONDARY: if(at_self) control_used(CYCLE_SECONDARY); hud_gauge_popup_start(HUD_WEAPONS_GAUGE); if (ship_select_next_secondary(objp)) { ship* shipp = &Ships[objp->instance]; if ( timestamp_elapsed(shipp->weapons.next_secondary_fire_stamp[shipp->weapons.current_secondary_bank]) ) { shipp->weapons.next_secondary_fire_stamp[shipp->weapons.current_secondary_bank] = timestamp(250); // 1/4 second delay until can fire } // multiplayer server should maintain bank/link status here if( MULTIPLAYER_MASTER ){ Assert(npl != NULL); multi_server_update_player_weapons(npl,shipp); } } break; // cycle number of missiles case CYCLE_NUM_MISSLES: { if(at_self) control_used(CYCLE_NUM_MISSLES); if ( objp == Player_obj ) { if ( Player_ship->weapons.num_secondary_banks <= 0 ) { HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR( "This ship has no secondary weapons", 33)); gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL); break; } } polymodel *pm = model_get(Ship_info[Ships[objp->instance].ship_info_index].model_num); int firepoints = pm->missile_banks[Ships[objp->instance].weapons.current_secondary_bank].num_slots; if (Ships[objp->instance].flags[Ship::Ship_Flags::Secondary_dual_fire] || firepoints < 2) { Ships[objp->instance].flags.remove(Ship::Ship_Flags::Secondary_dual_fire); if(at_self) { HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR( "Secondary weapon set to normal fire mode", 34)); snd_play( gamesnd_get_game_sound(ship_get_sound(Player_obj, GameSounds::SECONDARY_CYCLE)) ); hud_gauge_popup_start(HUD_WEAPONS_GAUGE); } } else { Ships[objp->instance].flags.set(Ship::Ship_Flags::Secondary_dual_fire); if(at_self) { HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR( "Secondary weapon set to dual fire mode", 35)); snd_play( gamesnd_get_game_sound(ship_get_sound(Player_obj, GameSounds::SECONDARY_CYCLE)) ); hud_gauge_popup_start(HUD_WEAPONS_GAUGE); } } // multiplayer server should maintain bank/link status here if( MULTIPLAYER_MASTER ){ Assert(npl != NULL); multi_server_update_player_weapons(npl,&Ships[objp->instance]); } break; } // increase weapon recharge rate case INCREASE_WEAPON: if(at_self) control_used(INCREASE_WEAPON); increase_recharge_rate(objp, WEAPONS); // multiplayer server should maintain bank/link status here if( MULTIPLAYER_MASTER ){ Assert(npl != NULL); multi_server_update_player_weapons(npl,&Ships[objp->instance]); } break; // decrease weapon recharge rate case DECREASE_WEAPON: if(at_self) control_used(DECREASE_WEAPON); decrease_recharge_rate(objp, WEAPONS); // multiplayer server should maintain bank/link status here if( MULTIPLAYER_MASTER ){ Assert(npl != NULL); multi_server_update_player_weapons(npl,&Ships[objp->instance]); } break; // increase shield recharge rate case INCREASE_SHIELD: if(at_self) control_used(INCREASE_SHIELD); increase_recharge_rate(objp, SHIELDS); // multiplayer server should maintain bank/link status here if( MULTIPLAYER_MASTER ){ Assert(npl != NULL); multi_server_update_player_weapons(npl,&Ships[objp->instance]); } break; // decrease shield recharge rate case DECREASE_SHIELD: if(at_self) control_used(DECREASE_SHIELD); decrease_recharge_rate(objp, SHIELDS); // multiplayer server should maintain bank/link status here if( MULTIPLAYER_MASTER ){ Assert(npl != NULL); multi_server_update_player_weapons(npl,&Ships[objp->instance]); } break; // increase energy to engines case INCREASE_ENGINE: if(at_self) control_used(INCREASE_ENGINE); increase_recharge_rate(objp, ENGINES); // multiplayer server should maintain bank/link status here if( MULTIPLAYER_MASTER ){ Assert(npl != NULL); multi_server_update_player_weapons(npl,&Ships[objp->instance]); } break; // decrease energy to engines case DECREASE_ENGINE: if(at_self) control_used(DECREASE_ENGINE); decrease_recharge_rate(objp, ENGINES); // multiplayer server should maintain bank/link status here if( MULTIPLAYER_MASTER ){ Assert(npl != NULL); multi_server_update_player_weapons(npl,&Ships[objp->instance]); } break; // equalize recharge rates case ETS_EQUALIZE: if (at_self) { control_used(ETS_EQUALIZE); } set_default_recharge_rates(objp); snd_play( gamesnd_get_game_sound(GameSounds::ENERGY_TRANS) ); // multiplayer server should maintain bank/link status here if( MULTIPLAYER_MASTER ){ Assert(npl != NULL); multi_server_update_player_weapons(npl,&Ships[objp->instance]); } break; // equalize shield energy to all quadrants case SHIELD_EQUALIZE: if(at_self){ control_used(SHIELD_EQUALIZE); } hud_shield_equalize(objp, pl); break; // transfer shield energy to front case SHIELD_XFER_TOP: if(at_self){ control_used(SHIELD_XFER_TOP); } hud_augment_shield_quadrant(objp, FRONT_QUAD); break; // transfer shield energy to rear case SHIELD_XFER_BOTTOM: if(at_self) control_used(SHIELD_XFER_BOTTOM); hud_augment_shield_quadrant(objp, REAR_QUAD); break; // transfer shield energy to left case SHIELD_XFER_LEFT: if(at_self) control_used(SHIELD_XFER_LEFT); hud_augment_shield_quadrant(objp, LEFT_QUAD); break; // transfer shield energy to right case SHIELD_XFER_RIGHT: if(at_self) control_used(SHIELD_XFER_RIGHT); hud_augment_shield_quadrant(objp, RIGHT_QUAD); break; // transfer energy to shield from weapons case XFER_SHIELD: if(at_self) control_used(XFER_SHIELD); transfer_energy_to_shields(objp); break; // transfer energy to weapons from shield case XFER_LASER: if(at_self) control_used(XFER_LASER); transfer_energy_to_weapons(objp); break; // following are not handled here, but we need to bypass the Int3() case LAUNCH_COUNTERMEASURE: case VIEW_SLEW: case VIEW_EXTERNAL: case VIEW_EXTERNAL_TOGGLE_CAMERA_LOCK: case VIEW_TRACK_TARGET: case ONE_THIRD_THROTTLE: case TWO_THIRDS_THROTTLE: case MINUS_5_PERCENT_THROTTLE: case PLUS_5_PERCENT_THROTTLE: case ZERO_THROTTLE: case MAX_THROTTLE: case TOGGLE_GLIDING: case CUSTOM_CONTROL_1: case CUSTOM_CONTROL_2: case CUSTOM_CONTROL_3: case CUSTOM_CONTROL_4: case CUSTOM_CONTROL_5: return 0; default : Int3(); // bad bad bad break; } return 1; } /** * Execute function corresponding to action n * Basically, these are actions which don't affect demo playback at all * @param n Action number */ int button_function_demo_valid(int n) { // by default, we'll return "not processed". ret will get set to 1, if this is one of the keys which is always allowed, even in demo // playback. int ret = 0; // No keys, not even targeting keys, when player in death roll. He can press keys after he blows up. if (Game_mode & GM_DEAD_DIED){ return 0; } // any of these buttons are valid switch(n){ case VIEW_CHASE: control_used(VIEW_CHASE); if(!Perspective_locked) { Viewer_mode ^= VM_CHASE; } else { snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL) ); } ret = 1; break; case VIEW_TRACK_TARGET: // Target padlock mode toggle (Swifty) control_used(VIEW_TRACK_TARGET); if (!Perspective_locked) { if(Viewer_mode & VM_TRACK) { chase_slew_angles.h = 0; chase_slew_angles.p = 0; } Viewer_mode ^= VM_TRACK; } else { snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL) ); } ret = 1; break; case VIEW_EXTERNAL: control_used(VIEW_EXTERNAL); if(!Perspective_locked) { Viewer_mode ^= VM_EXTERNAL; Viewer_mode &= ~VM_CAMERA_LOCKED; // reset camera lock when leaving/entering external view // reset external camera distance if we're external if (Viewer_mode & VM_EXTERNAL) { if (Viewer_mode & VM_OTHER_SHIP) Viewer_external_info.preferred_distance = 2 * Objects[Player_ai->target_objnum].radius; else Viewer_external_info.preferred_distance = 2 * Player_obj->radius; } } else { snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL) ); } ret = 1; break; case VIEW_TOPDOWN: control_used(VIEW_TOPDOWN); if(!Perspective_locked) { Viewer_mode ^= VM_TOPDOWN; } else { snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL) ); } ret = 1; break; case VIEW_EXTERNAL_TOGGLE_CAMERA_LOCK: control_used(VIEW_EXTERNAL_TOGGLE_CAMERA_LOCK); if ( Viewer_mode & VM_EXTERNAL ) { Viewer_mode ^= VM_CAMERA_LOCKED; if ( Viewer_mode & VM_CAMERA_LOCKED ) { HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR( "External camera is locked, controls will move ship", 36)); } else { HUD_sourced_printf(HUD_SOURCE_HIDDEN, "%s", XSTR( "External camera is free, controls will move the camera, not the ship", 37)); } } ret = 1; break; case VIEW_OTHER_SHIP: control_used(VIEW_OTHER_SHIP); if ( Player_ai->target_objnum < 0 || Perspective_locked) { snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL) ); } else { if ( Objects[Player_ai->target_objnum].type != OBJ_SHIP ) { snd_play( gamesnd_get_game_sound(GameSounds::TARGET_FAIL) ); } else { Viewer_mode ^= VM_OTHER_SHIP; // reset external camera distance if we're external if (Viewer_mode & VM_EXTERNAL) { if (Viewer_mode & VM_OTHER_SHIP) Viewer_external_info.preferred_distance = 2 * Objects[Player_ai->target_objnum].radius; else Viewer_external_info.preferred_distance = 2 * Player_obj->radius; } } } ret = 1; break; case TIME_SLOW_DOWN: if ( Game_mode & GM_NORMAL ) { // Goober5000 - time dilation only available in cheat mode (see above); // now you can do it with or without pressing the tilde, per Kazan's request if ( ((Game_time_compression > F1_0) || (Cheats_enabled && (Game_time_compression > (F1_0/MAX_TIME_DIVIDER)))) && !Time_compression_locked) { change_time_compression(0.5f); } else { gamesnd_play_error_beep(); } } else { gamesnd_play_error_beep(); } ret = 1; break; case TIME_SPEED_UP: if ( Game_mode & GM_NORMAL ) { if ( (Game_time_compression < (F1_0*MAX_TIME_MULTIPLIER)) && !Time_compression_locked ) { change_time_compression(2.0f); } else { gamesnd_play_error_beep(); } } else { gamesnd_play_error_beep(); } ret = 1; break; } // done return ret; } bool key_is_targeting(int n) { switch(n) { case TARGET_NEXT: case TARGET_PREV: case TARGET_NEXT_CLOSEST_HOSTILE: case TARGET_PREV_CLOSEST_HOSTILE: case TARGET_NEXT_CLOSEST_FRIENDLY: case TARGET_PREV_CLOSEST_FRIENDLY: case TARGET_SHIP_IN_RETICLE: case TARGET_LAST_TRANMISSION_SENDER: case TARGET_CLOSEST_SHIP_ATTACKING_TARGET: case TARGET_CLOSEST_SHIP_ATTACKING_SELF: case TARGET_TARGETS_TARGET: case TARGET_SUBOBJECT_IN_RETICLE: case TARGET_PREV_SUBOBJECT: case TARGET_NEXT_SUBOBJECT: return true; default: return false; } } /** * Execute function corresponding to action n (BUTTON_ from KeyControl.h) * @return 1 when action was taken */ int button_function(int n) { Assert(n >= 0); if (Control_config[n].disabled) return 0; // check if the button has been set to be ignored by a SEXP if (Ignored_keys[n]) { if (Ignored_keys[n] > 0) { Ignored_keys[n]--; } return 0; } // No keys, not even targeting keys, when player in death roll. He can press keys after he blows up. if (Game_mode & GM_DEAD_DIED){ return 0; } // Goober5000 - if the ship doesn't have subspace drive, jump key doesn't work: so test and exit early if (Player_ship->flags[Ship::Ship_Flags::No_subspace_drive]) { switch(n) { case END_MISSION: control_used(n); // set the timestamp for when we used the control, in case we need it return 1; // pretend we took the action: if we return 0, strange stuff may happen } } // Goober5000 - if we have primitive sensors, some keys don't work: so test and exit early if (Player_ship->flags[Ship::Ship_Flags::Primitive_sensors]) { switch (n) { case MATCH_TARGET_SPEED: case TOGGLE_AUTO_MATCH_TARGET_SPEED: case TOGGLE_AUTO_TARGETING: case TARGET_NEXT: case TARGET_PREV: case TARGET_NEXT_CLOSEST_HOSTILE: case TARGET_PREV_CLOSEST_HOSTILE: case TARGET_NEXT_CLOSEST_FRIENDLY: case TARGET_PREV_CLOSEST_FRIENDLY: case TARGET_SHIP_IN_RETICLE: case TARGET_LAST_TRANMISSION_SENDER: case TARGET_CLOSEST_REPAIR_SHIP: case TARGET_CLOSEST_SHIP_ATTACKING_TARGET: case STOP_TARGETING_SHIP: case TARGET_CLOSEST_SHIP_ATTACKING_SELF: case TARGET_TARGETS_TARGET: case TARGET_SUBOBJECT_IN_RETICLE: case TARGET_NEXT_SUBOBJECT: case TARGET_PREV_SUBOBJECT: case STOP_TARGETING_SUBSYSTEM: case TARGET_NEXT_BOMB: case TARGET_PREV_BOMB: case TARGET_NEXT_UNINSPECTED_CARGO: case TARGET_PREV_UNINSPECTED_CARGO: case TARGET_NEWEST_SHIP: case TARGET_NEXT_LIVE_TURRET: case TARGET_PREV_LIVE_TURRET: case TARGET_NEXT_ESCORT_SHIP: control_used(n); // set the timestamp for when we used the control, in case we need it return 1; // pretend we took the action: if we return 0, strange stuff may happen } } switch(n) { // following are not handled here, but we need to bypass the Int3() case LAUNCH_COUNTERMEASURE: case VIEW_SLEW: case VIEW_TRACK_TARGET: case ONE_THIRD_THROTTLE: case TWO_THIRDS_THROTTLE: case MINUS_5_PERCENT_THROTTLE: case PLUS_5_PERCENT_THROTTLE: case ZERO_THROTTLE: case MAX_THROTTLE: case TOGGLE_GLIDING: case GLIDE_WHEN_PRESSED: case CUSTOM_CONTROL_1: case CUSTOM_CONTROL_2: case CUSTOM_CONTROL_3: case CUSTOM_CONTROL_4: case CUSTOM_CONTROL_5: return 0; } /** * This switch handles the critical buttons * * button_function_critical is also called from network */ switch (n) { case CYCLE_PRIMARY_WEAPON_SEQUENCE: case CYCLE_NEXT_PRIMARY: // cycle to next primary weapon case CYCLE_PREV_PRIMARY: // cycle to previous primary weapon case CYCLE_SECONDARY: // cycle to next secondary weapon case CYCLE_NUM_MISSLES: // cycle number of missiles fired from secondary bank case SHIELD_EQUALIZE: // equalize shield energy to all quadrants case SHIELD_XFER_TOP: // transfer shield energy to front case SHIELD_XFER_BOTTOM: // transfer shield energy to rear case SHIELD_XFER_LEFT: // transfer shield energy to left case SHIELD_XFER_RIGHT: // transfer shield energy to right case XFER_SHIELD: // transfer energy to shield from weapons case XFER_LASER: // transfer energy to weapons from shield return button_function_critical(n); break; case INCREASE_WEAPON: // increase weapon recharge rate case DECREASE_WEAPON: // decrease weapon recharge rate case INCREASE_SHIELD: // increase shield recharge rate case DECREASE_SHIELD: // decrease shield recharge rate case INCREASE_ENGINE: // increase energy to engines case DECREASE_ENGINE: // decrease energy to engines case ETS_EQUALIZE: if ((Player_ship->flags[Ship::Ship_Flags::No_ets]) == 0) { hud_gauge_popup_start(HUD_ETS_GAUGE); return button_function_critical(n); } return 1; break; } /** * Assume the switches below will catch the key, if not, set to FALSE in default * * Below, you must not use return in cases, * else the check for invalid keys will fail */ int keyHasBeenUsed = TRUE; switch(n) { // message all netplayers button case MULTI_MESSAGE_ALL: multi_msg_key_down(MULTI_MSG_ALL); break; // message all friendlies button case MULTI_MESSAGE_FRIENDLY: multi_msg_key_down(MULTI_MSG_FRIENDLY); break; // message all hostiles button case MULTI_MESSAGE_HOSTILE: multi_msg_key_down(MULTI_MSG_HOSTILE); break; // message targeted ship (if player) case MULTI_MESSAGE_TARGET: multi_msg_key_down(MULTI_MSG_TARGET); break; // undefined in multiplayer for clients right now // toggle auto-match target speed case TOGGLE_AUTO_MATCH_TARGET_SPEED: // multiplayer observers can't match target speed if((Game_mode & GM_MULTIPLAYER) && (Net_player != NULL) && ((Net_player->flags & NETINFO_FLAG_OBSERVER) || (Player_obj->type == OBJ_OBSERVER)) ){ break; } Player->flags ^= PLAYER_FLAGS_AUTO_MATCH_SPEED; control_used(TOGGLE_AUTO_MATCH_TARGET_SPEED); hud_gauge_popup_start(HUD_AUTO_SPEED); if ( Players[Player_num].flags & PLAYER_FLAGS_AUTO_MATCH_SPEED ) { snd_play(gamesnd_get_game_sound(GameSounds::SHIELD_XFER_OK), 1.0f); if ( !(Player->flags & PLAYER_FLAGS_MATCH_TARGET) ) { player_match_target_speed(); } } else { snd_play(gamesnd_get_game_sound(GameSounds::SHIELD_XFER_OK), 1.0f); player_match_target_speed(); } break; case TARGET_NEXT_UNINSPECTED_CARGO: hud_target_uninspected_object(1); break; case TARGET_PREV_UNINSPECTED_CARGO: hud_target_uninspected_object(0); break; case TARGET_NEWEST_SHIP: hud_target_newest_ship(); break; case TARGET_NEXT_LIVE_TURRET: hud_target_live_turret(1); break; case TARGET_PREV_LIVE_TURRET: hud_target_live_turret(0); break; // end the mission case END_MISSION: // in multiplayer, all end mission requests should go through the server if (Game_mode & GM_MULTIPLAYER) { multi_handle_end_mission_request(); break; } control_used(END_MISSION); if ( collide_predict_large_ship(Player_obj, 200.0f) || (Warp_params[Ships[Player_obj->instance].warpout_params_index].warp_type == WT_HYPERSPACE && collide_predict_large_ship(Player_obj, 100000.0f)) ) { gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL); HUD_printf("%s", XSTR( "** WARNING ** Collision danger. Subspace drive not activated.", 39)); } else if (!ship_engine_ok_to_warp(Player_ship)) { gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL); HUD_printf("%s", XSTR("Engine failure. Cannot engage subspace drive.", 40)); } else if (!ship_navigation_ok_to_warp(Player_ship)) { gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL); HUD_printf("%s", XSTR("Navigation failure. Cannot engage subspace drive.", 1572)); } else if ((Player_obj != nullptr) && object_get_gliding(Player_obj)) { gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL); HUD_printf("%s", XSTR("Cannot engage subspace drive while gliding.", 1573)); } else { gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_START ); } break; case ADD_REMOVE_ESCORT: if ( Player_ai->target_objnum >= 0 ) { control_used(ADD_REMOVE_ESCORT); hud_add_remove_ship_escort(Player_ai->target_objnum); } break; // if i'm an observer, zoom to my targeted object case MULTI_OBSERVER_ZOOM_TO: multi_obs_zoom_to_target(); break; // toggle between high and low HUD contrast case TOGGLE_HUD_CONTRAST: gamesnd_play_iface(InterfaceSounds::USER_SELECT); hud_toggle_contrast(); break; // toggle network info case MULTI_TOGGLE_NETINFO: extern int Multi_display_netinfo; Multi_display_netinfo = !Multi_display_netinfo; break; // self destruct (multiplayer only) case MULTI_SELF_DESTRUCT: if (!(Game_mode & GM_MULTIPLAYER)) { break; } // bogus netplayer if ( (Net_player == NULL) || (Net_player->m_player == NULL) ) { break; } // blow myself up, if I'm the server if (Net_player->flags & NETINFO_FLAG_AM_MASTER) { if ( (Net_player->m_player->objnum >= 0) && (Net_player->m_player->objnum < MAX_OBJECTS) && (Objects[Net_player->m_player->objnum].type == OBJ_SHIP) && (Objects[Net_player->m_player->objnum].instance >= 0) && (Objects[Net_player->m_player->objnum].instance < MAX_SHIPS) ) { ship_self_destruct(&Objects[Net_player->m_player->objnum]); } } else { // otherwise send a packet to the server send_self_destruct_packet(); } break; case TOGGLE_HUD: gamesnd_play_iface(InterfaceSounds::USER_SELECT); hud_toggle_draw(); break; case HUD_TARGETBOX_TOGGLE_WIREFRAME: if (!Lock_targetbox_mode) { gamesnd_play_iface(InterfaceSounds::USER_SELECT); hud_targetbox_switch_wireframe_mode(); } else { gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL); } break; // Autopilot key control case AUTO_PILOT_TOGGLE: if (!(The_mission.flags[Mission::Mission_Flags::Deactivate_ap])) { if (AutoPilotEngaged) { if (Cmdline_autopilot_interruptable == 1) //allow WCS to disable autopilot interrupt via commandline EndAutoPilot(); } else { if (!StartAutopilot()) gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL); } } break; case NAV_CYCLE: if (!Sel_NextNav()) gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL); break; default: keyHasBeenUsed = FALSE; break; } /** * The key has been handled, return early before the timestamp is set */ if (keyHasBeenUsed) { return 1; } /** * Update the last used timestamp of this key */ control_used(n); if ( hud_sensors_ok(Player_ship) ) { keyHasBeenUsed = TRUE; switch(n) { // target next case TARGET_NEXT: hud_target_next(); break; // target previous case TARGET_PREV: hud_target_prev(); break; // target the next hostile target case TARGET_NEXT_CLOSEST_HOSTILE: hud_target_next_list(); break; // target the previous closest hostile case TARGET_PREV_CLOSEST_HOSTILE: hud_target_next_list(1,0); break; // target the next friendly ship case TARGET_NEXT_CLOSEST_FRIENDLY: hud_target_next_list(0); break; // target the closest friendly ship case TARGET_PREV_CLOSEST_FRIENDLY: hud_target_next_list(0,0); break; // target ship closest to center of reticle case TARGET_SHIP_IN_RETICLE: hud_target_in_reticle_new(); break; case TARGET_LAST_TRANMISSION_SENDER: hud_target_last_transmit(); break; // target the closest ship attacking current target case TARGET_CLOSEST_SHIP_ATTACKING_TARGET: if (Player_ai->target_objnum < 0) { snd_play(gamesnd_get_game_sound(GameSounds::TARGET_FAIL)); break; } hud_target_closest(iff_get_attacker_mask(obj_team(&Objects[Player_ai->target_objnum])), Player_ai->target_objnum); break; // target closest ship that is attacking player case TARGET_CLOSEST_SHIP_ATTACKING_SELF: hud_target_next_list(1, 0, iff_get_attacker_mask(Player_ship->team), OBJ_INDEX(Player_obj), TRUE, 0, 1); break; // target your target's target case TARGET_TARGETS_TARGET: hud_target_targets_target(); break; // target ships subsystem in reticle case TARGET_SUBOBJECT_IN_RETICLE: hud_target_subsystem_in_reticle(); break; case TARGET_PREV_SUBOBJECT: hud_target_prev_subobject(); break; // target next subsystem on current target case TARGET_NEXT_SUBOBJECT: hud_target_next_subobject(); break; default: keyHasBeenUsed = FALSE; break; }; if (keyHasBeenUsed) { return 1; } } else { //if sensors are gone, and the passed key is one of the targeting keys, we need to exit here before we hit the Int3() later in this function if (key_is_targeting(n)) { return 1; } } keyHasBeenUsed = TRUE; switch(n) { // undefined in multiplayer for clients right now // match target speed case MATCH_TARGET_SPEED: // If player is auto-matching, break auto-match speed if ( Player->flags & PLAYER_FLAGS_AUTO_MATCH_SPEED ) { Player->flags &= ~PLAYER_FLAGS_AUTO_MATCH_SPEED; } player_match_target_speed(); break; // toggle auto-targeting case TOGGLE_AUTO_TARGETING: hud_gauge_popup_start(HUD_AUTO_TARGET); Players[Player_num].flags ^= PLAYER_FLAGS_AUTO_TARGETING; if ( Players[Player_num].flags & PLAYER_FLAGS_AUTO_TARGETING ) { if (hud_sensors_ok(Player_ship)) { hud_target_closest(iff_get_attackee_mask(Player_ship->team), -1, FALSE, TRUE ); snd_play(gamesnd_get_game_sound(GameSounds::SHIELD_XFER_OK), 1.0f); //HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Auto targeting activated", -1)); } else { Players[Player_num].flags ^= PLAYER_FLAGS_AUTO_TARGETING; } } else { snd_play(gamesnd_get_game_sound(GameSounds::SHIELD_XFER_OK), 1.0f); //HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Auto targeting deactivated", -1)); } break; // target the closest repair ship case TARGET_CLOSEST_REPAIR_SHIP: // AL: Try to find the closest repair ship coming to repair the player... if no support // ships are coming to rearm the player, just try for the closest repair ship if ( hud_target_closest_repair_ship(OBJ_INDEX(Player_obj)) == 0 ) { if ( hud_target_closest_repair_ship() == 0 ) { snd_play(gamesnd_get_game_sound(GameSounds::TARGET_FAIL)); } } break; // stop targeting ship case STOP_TARGETING_SHIP: hud_cease_targeting(true); break; // stop targeting subsystems on ship case STOP_TARGETING_SUBSYSTEM: hud_cease_subsystem_targeting(); break; case TARGET_NEXT_BOMB: hud_target_missile(Player_obj, 1); break; case TARGET_PREV_BOMB: hud_target_missile(Player_obj, 0); break; // wingman message: attack current target case ATTACK_MESSAGE: hud_squadmsg_shortcut( ATTACK_TARGET_ITEM ); break; // wingman message: disarm current target case DISARM_MESSAGE: hud_squadmsg_shortcut( DISARM_TARGET_ITEM ); break; // wingman message: disable current target case DISABLE_MESSAGE: hud_squadmsg_shortcut( DISABLE_TARGET_ITEM ); break; // wingman message: disable current target case ATTACK_SUBSYSTEM_MESSAGE: hud_squadmsg_shortcut( DISABLE_SUBSYSTEM_ITEM ); break; // wingman message: capture current target case CAPTURE_MESSAGE: hud_squadmsg_shortcut( CAPTURE_TARGET_ITEM ); break; // wingman message: engage enemy case ENGAGE_MESSAGE: hud_squadmsg_shortcut( ENGAGE_ENEMY_ITEM ); break; // wingman message: form on my wing case FORM_MESSAGE: hud_squadmsg_shortcut( FORMATION_ITEM ); break; // wingman message: protect current target case PROTECT_MESSAGE: hud_squadmsg_shortcut( PROTECT_TARGET_ITEM ); break; // wingman message: cover me case COVER_MESSAGE: hud_squadmsg_shortcut( COVER_ME_ITEM ); break; // wingman message: warp out case WARP_MESSAGE: hud_squadmsg_shortcut( DEPART_ITEM ); break; case IGNORE_MESSAGE: hud_squadmsg_shortcut( IGNORE_TARGET_ITEM ); break; // rearm message case REARM_MESSAGE: hud_squadmsg_rearm_shortcut(); break; // cycle to next radar range case RADAR_RANGE_CYCLE: HUD_config.rp_dist++; if ( HUD_config.rp_dist >= RR_MAX_RANGES ) HUD_config.rp_dist = 0; HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Radar range set to %s", 38), Radar_range_text(HUD_config.rp_dist)); break; // toggle the squadmate messaging menu case SQUADMSG_MENU: hud_squadmsg_toggle(); // leave the details to the messaging code!!! break; // show the mission goals screen case SHOW_GOALS: gameseq_post_event( GS_EVENT_SHOW_GOALS ); break; // end the mission case END_MISSION: // in multiplayer, all end mission requests should go through the server if(Game_mode & GM_MULTIPLAYER){ multi_handle_end_mission_request(); break; } control_used(END_MISSION); if ( collide_predict_large_ship(Player_obj, 200.0f) || (Warp_params[Ships[Player_obj->instance].warpout_params_index].warp_type == WT_HYPERSPACE && collide_predict_large_ship(Player_obj, 100000.0f)) ) { gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL); HUD_printf("%s", XSTR( "** WARNING ** Collision danger. Subspace drive not activated.", 39)); } else if (!ship_engine_ok_to_warp(Player_ship)) { gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL); HUD_printf("%s", XSTR("Engine failure. Cannot engage subspace drive.", 40)); } else if (!ship_navigation_ok_to_warp(Player_ship)) { gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL); HUD_printf("%s", XSTR("Navigation failure. Cannot engage subspace drive.", 1572)); } else if ((Player_obj != nullptr) && object_get_gliding(Player_obj)) { gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL); HUD_printf("%s", XSTR("Cannot engage subspace drive while gliding.", 1573)); } else { gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_START ); } break; case ADD_REMOVE_ESCORT: if ( Player_ai->target_objnum >= 0 ) { control_used(ADD_REMOVE_ESCORT); hud_add_remove_ship_escort(Player_ai->target_objnum); } break; case ESCORT_CLEAR: hud_escort_clear_all(true); break; case TARGET_NEXT_ESCORT_SHIP: hud_escort_target_next(); break; default: keyHasBeenUsed = FALSE; break; }; if (keyHasBeenUsed) { return 1; } /** * All keys should have been handled above, if not panic */ mprintf(("Unknown key %d at %s:%u\n", n, __FILE__, __LINE__)); Int3(); return 1; } /** * Calls multiple event handlers for each active button * @param bi currently active buttons */ void button_info_do(button_info *bi) { for (int i = 0; i < CCFG_MAX; i++) { if( button_info_query(bi, i) ) { int keyHasBeenUsed = FALSE; if( !keyHasBeenUsed ) { keyHasBeenUsed = button_function_demo_valid(i); } if( !keyHasBeenUsed ) { keyHasBeenUsed = button_function(i); } if( keyHasBeenUsed ) { button_info_unset(bi, i); } } } } /** * Set the bit for the corresponding action n (BUTTON_ from KeyControl.h) */ void button_info_set(button_info *bi, int n) { int field_num, bit_num; field_num = n / 32; bit_num = n % 32; bi->status[field_num] |= (1 << bit_num); } /** * Unset the bit for the corresponding action n (BUTTON_ from KeyControl.h) */ void button_info_unset(button_info *bi, int n) { int field_num, bit_num; field_num = n / 32; bit_num = n % 32; bi->status[field_num] &= ~(1 << bit_num); } int button_info_query(button_info *bi, int n) { return bi->status[n / 32] & (1 << (n % 32)); } /** * Clear out the ::button_info struct */ void button_info_clear(button_info *bi) { int i; for (i=0; istatus[i] = 0; } } /** * Strip out all noncritical keys from the ::button_info struct */ void button_strip_noncritical_keys(button_info *bi) { int idx; // clear out all noncritical keys for(idx=0;idx