mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 08:15:59 -10:00
This introduces a new mechanism for handling bit flags. Instead of using an int (or two, or three) to write bits into, this uses the std::bitset in a wrapper class that accepts an enum class as input. This means that certain classes of errors we've had a few of in the engine, where a coder used the wrong set of flags or checked them against the wrong variable (SF_STEALTH vs SIF_STEALTH, for example) can now be detected at a compiler level rather than not at all.
2909 lines
75 KiB
C++
2909 lines
75 KiB
C++
/*
|
|
* 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 "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"
|
|
|
|
/**
|
|
* 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<size_t> > _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];
|
|
}
|
|
}
|
|
}
|
|
|
|
Assertion(false, "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;
|
|
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;
|
|
bool quit_mission_popup_shown = 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
|
|
};
|
|
|
|
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,
|
|
CYCLE_PRIMARY_WEAPON_SEQUENCE
|
|
};
|
|
|
|
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 >= static_cast<int>(Ship_info.size()) ){
|
|
si_index = 0;
|
|
}
|
|
if ( si_index < 0 ){
|
|
si_index = static_cast<int>(Ship_info.size() - 1);
|
|
}
|
|
sip = &Ship_info[si_index];
|
|
if ( sip->flags[Ship::Info_Flags::Player_ship] ){
|
|
break;
|
|
}
|
|
|
|
// just in case
|
|
sanity++;
|
|
if ( sanity >= static_cast<int>(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 >= static_cast<int>(Ship_info.size()) )
|
|
si_index = 0;
|
|
if ( si_index < 0 )
|
|
si_index = static_cast<int>(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 >= static_cast<int>(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)
|
|
{
|
|
int index;
|
|
ship *shipp = &Ships[objp->instance];
|
|
ship_info *sip = &Ship_info[shipp->ship_info_index];
|
|
ship_weapon *swp = &shipp->weapons;
|
|
|
|
for ( index = 0; index < MAX_SHIP_SECONDARY_BANKS; index++ )
|
|
{
|
|
swp->secondary_bank_ammo[index] = sip->secondary_bank_ammo_capacity[index];
|
|
}
|
|
}
|
|
|
|
void debug_max_primary_weapons(object *objp) // Goober5000
|
|
{
|
|
Assert(objp); // Goober5000
|
|
|
|
int index;
|
|
ship *shipp = &Ships[objp->instance];
|
|
ship_info *sip = &Ship_info[shipp->ship_info_index];
|
|
ship_weapon *swp = &shipp->weapons;
|
|
weapon_info *wip;
|
|
|
|
if (sip->flags[Ship::Info_Flags::Ballistic_primaries])
|
|
{
|
|
for ( index = 0; index < MAX_SHIP_PRIMARY_BANKS; index++ )
|
|
{
|
|
wip = &Weapon_info[swp->primary_bank_weapons[index]];
|
|
if (wip->wi_flags2 & WIF2_BALLISTIC)
|
|
{
|
|
float capacity, size;
|
|
capacity = (float) sip->primary_bank_ammo_capacity[index];
|
|
size = (float) wip->cargo_size;
|
|
swp->primary_bank_ammo[index] = fl2i((capacity / size)+0.5f);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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, XSTR( "Event music is not playing", 3));
|
|
}
|
|
}
|
|
|
|
extern void hud_target_asteroid();
|
|
extern int Framerate_delay;
|
|
|
|
extern void snd_stop_any_sound();
|
|
|
|
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;
|
|
|
|
for (i = 1; i < Num_weapon_types; i++)
|
|
{
|
|
new_index = (Num_weapon_types + current_weapon - i) % Num_weapon_types;
|
|
|
|
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;
|
|
|
|
for (i = 1; i < Num_weapon_types; i++)
|
|
{
|
|
new_index = (current_weapon + i) % Num_weapon_types;
|
|
|
|
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 knocked 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].ship_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, XSTR( "Weapon energy and missile count will always be at full ALL SHIPS!", 15));
|
|
else
|
|
HUD_sourced_printf(HUD_SOURCE_HIDDEN, 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, 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].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].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, 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].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].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, XSTR( "Event music disabled", 20));
|
|
|
|
} else {
|
|
event_music_enable();
|
|
HUD_sourced_printf(HUD_SOURCE_HIDDEN, 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", -1), Ships[obj_to_rearm->instance].ship_name);
|
|
ai_issue_rearm_request(obj_to_rearm);
|
|
}
|
|
else
|
|
{
|
|
HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR("Cannot issue rearm request for %s", -1), Ships[obj_to_rearm->instance].ship_name);
|
|
}
|
|
|
|
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, XSTR( "Frame Rate test started", 23));
|
|
|
|
break;
|
|
}
|
|
#endif
|
|
case KEY_DEBUGGED + KEY_A: {
|
|
|
|
HUD_printf("frame rate currently is %0.2 FPS", 1/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, 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;
|
|
|
|
} // 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].ship_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; i<count; i++)
|
|
if (check_control(list[i], key))
|
|
button_info_set(&Player->bi, 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 {
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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();
|
|
snd_stop_all();
|
|
|
|
quit_mission_popup_shown = true;
|
|
|
|
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;
|
|
|
|
default:
|
|
break; // do nothing
|
|
}
|
|
quit_mission_popup_shown = false;
|
|
|
|
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->orders_accepted = (1<<NUM_COMM_ORDER_ITEMS)-1;
|
|
|
|
// Goober5000 - stolen from support ship creation
|
|
// create a name for the ship. use "Volition Bravos #". look for collisions until one isn't found anymore
|
|
ship_idx = 1;
|
|
do {
|
|
sprintf(name, NOX("Volition Bravos %d"), ship_idx);
|
|
if ( (ship_name_lookup(name) == -1) && (ship_find_exited_ship_by_name(name) == -1) )
|
|
{
|
|
strcpy_s(shipp->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 |= SW_FLAG_BEAM_FREE;
|
|
ptr->turret_next_fire_stamp = timestamp((int) frand_range(50.0f, 4000.0f));
|
|
}
|
|
}
|
|
|
|
// 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; i<NUM_BUTTON_FIELDS; i++)
|
|
Player->bi.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, XSTR( "This ship has no secondary weapons", 33));
|
|
gamesnd_play_iface(SND_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, XSTR( "Secondary weapon set to normal fire mode", 34));
|
|
snd_play( &Snds[ship_get_sound(Player_obj, SND_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, XSTR( "Secondary weapon set to dual fire mode", 35));
|
|
snd_play( &Snds[ship_get_sound(Player_obj, SND_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( &Snds[SND_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:
|
|
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( &Snds[SND_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( &Snds[SND_TARGET_FAIL] );
|
|
}
|
|
ret = 1;
|
|
break;
|
|
|
|
case VIEW_EXTERNAL:
|
|
control_used(VIEW_EXTERNAL);
|
|
if(!Perspective_locked)
|
|
{
|
|
Viewer_mode ^= VM_EXTERNAL;
|
|
Viewer_mode &= ~VM_EXTERNAL_CAMERA_LOCKED; // reset camera lock when leaving/entering external view
|
|
}
|
|
else
|
|
{
|
|
snd_play( &Snds[SND_TARGET_FAIL] );
|
|
}
|
|
ret = 1;
|
|
break;
|
|
|
|
case VIEW_TOPDOWN:
|
|
control_used(VIEW_TOPDOWN);
|
|
if(!Perspective_locked)
|
|
{
|
|
Viewer_mode ^= VM_TOPDOWN;
|
|
}
|
|
else
|
|
{
|
|
snd_play( &Snds[SND_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_EXTERNAL_CAMERA_LOCKED;
|
|
if ( Viewer_mode & VM_EXTERNAL_CAMERA_LOCKED ) {
|
|
HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "External camera is locked, controls will move ship", 36));
|
|
} else {
|
|
HUD_sourced_printf(HUD_SOURCE_HIDDEN, 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( &Snds[SND_TARGET_FAIL] );
|
|
} else {
|
|
if ( Objects[Player_ai->target_objnum].type != OBJ_SHIP ) {
|
|
snd_play( &Snds[SND_TARGET_FAIL] );
|
|
} else {
|
|
Viewer_mode ^= VM_OTHER_SHIP;
|
|
}
|
|
}
|
|
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:
|
|
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(&Snds[SND_SHIELD_XFER_OK], 1.0f);
|
|
if ( !(Player->flags & PLAYER_FLAGS_MATCH_TARGET) ) {
|
|
player_match_target_speed();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
snd_play(&Snds[SND_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)
|
|
|| (Ship_info[Ships[Player_obj->instance].ship_info_index].warpout_type == WT_HYPERSPACE
|
|
&& collide_predict_large_ship(Player_obj, 100000.0f)))
|
|
{
|
|
gamesnd_play_iface(SND_GENERAL_FAIL);
|
|
HUD_printf(XSTR( "** WARNING ** Collision danger. Subspace drive not activated.", 39));
|
|
} else if (!ship_engine_ok_to_warp(Player_ship)) {
|
|
gamesnd_play_iface(SND_GENERAL_FAIL);
|
|
HUD_printf(XSTR("Engine failure. Cannot engage subspace drive.", 40));
|
|
} else if (!ship_navigation_ok_to_warp(Player_ship)) {
|
|
gamesnd_play_iface(SND_GENERAL_FAIL);
|
|
HUD_printf(XSTR("Navigation failure. Cannot engage subspace drive.", 1596));
|
|
} else if ( (Player_obj != NULL) && object_get_gliding(Player_obj)) {
|
|
gamesnd_play_iface(SND_GENERAL_FAIL);
|
|
HUD_printf(XSTR("Cannot engage subspace drive while gliding.", 1597));
|
|
} 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(SND_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(SND_USER_SELECT);
|
|
hud_toggle_draw();
|
|
break;
|
|
|
|
case HUD_TARGETBOX_TOGGLE_WIREFRAME:
|
|
if (!Lock_targetbox_mode) {
|
|
gamesnd_play_iface(SND_USER_SELECT);
|
|
hud_targetbox_switch_wireframe_mode();
|
|
} else {
|
|
gamesnd_play_iface(SND_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(SND_GENERAL_FAIL);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NAV_CYCLE:
|
|
if (!Sel_NextNav())
|
|
gamesnd_play_iface(SND_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(&Snds[SND_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(&Snds[SND_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(&Snds[SND_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(&Snds[SND_TARGET_FAIL]);
|
|
}
|
|
}
|
|
break;
|
|
|
|
// stop targeting ship
|
|
case STOP_TARGETING_SHIP:
|
|
hud_cease_targeting();
|
|
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)
|
|
|| (Ship_info[Ships[Player_obj->instance].ship_info_index].warpout_type == WT_HYPERSPACE
|
|
&& collide_predict_large_ship(Player_obj, 100000.0f)))
|
|
{
|
|
gamesnd_play_iface(SND_GENERAL_FAIL);
|
|
HUD_printf(XSTR( "** WARNING ** Collision danger. Subspace drive not activated.", 39));
|
|
} else if (!ship_engine_ok_to_warp(Player_ship)) {
|
|
gamesnd_play_iface(SND_GENERAL_FAIL);
|
|
HUD_printf(XSTR("Engine failure. Cannot engage subspace drive.", 40));
|
|
} else if (!ship_navigation_ok_to_warp(Player_ship)) {
|
|
gamesnd_play_iface(SND_GENERAL_FAIL);
|
|
HUD_printf(XSTR("Navigation failure. Cannot engage subspace drive.", 1572));
|
|
} else if (Player_obj != NULL && object_get_gliding(Player_obj)) {
|
|
gamesnd_play_iface(SND_GENERAL_FAIL);
|
|
HUD_printf(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; i<NUM_BUTTON_FIELDS; i++) {
|
|
bi->status[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<Non_critical_key_set_size;idx++){
|
|
button_info_unset(bi,Non_critical_key_set[idx]);
|
|
}
|
|
}
|