Moved to action dir

This commit is contained in:
Aaron Dean 2023-09-05 15:02:58 -04:00
parent 065e2edffe
commit c139980419
13 changed files with 6027 additions and 15 deletions

View file

@ -7,10 +7,9 @@ set(CMAKE_C_COMPILER "gcc")
set(CMAKE_CXX_COMPILER "g++")
set(GAME_SRC
${CMAKE_CURRENT_SOURCE_DIR}/action/a_team.cpp
${CMAKE_CURRENT_SOURCE_DIR}/action/a_game.cpp
${CMAKE_CURRENT_SOURCE_DIR}/action/a_match.cpp
${CMAKE_CURRENT_SOURCE_DIR}/action/a_radio.cpp
${CMAKE_CURRENT_SOURCE_DIR}/action/a_team.cpp
${CMAKE_CURRENT_SOURCE_DIR}/action/a_balancer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/action/cgf_sfx_glass.cpp
${CMAKE_CURRENT_SOURCE_DIR}/bots/bot_debug.cpp

View file

@ -0,0 +1,99 @@
#include "../g_local.h"
cvar_t *eventeams;
cvar_t *use_balancer;
edict_t *FindNewestPlayer(int team)
{
edict_t *e, *newest = NULL;
int i;
for (i = 0, e = &g_edicts[1]; i < game.maxclients; i++, e++)
{
if (!e->inuse || e->client->resp.team != team)
continue;
if (!newest || e->client->resp.joined_team > newest->client->resp.joined_team) {
newest = e;
}
}
return newest;
}
void CalculatePlayers(int *players)
{
edict_t *e;
int i;
for (i = 0, e = &g_edicts[1]; i < game.maxclients; i++, e++)
{
if (!e->inuse)
continue;
players[e->client->resp.team]++;
}
}
/* parameter can be current (dead) player or null */
bool CheckForUnevenTeams (edict_t *ent)
{
edict_t *swap_ent = NULL;
int i, other_team, players[TEAM_TOP] = {0}, leastPlayers, mostPlayers;
if(!use_balancer->value)
return false;
CalculatePlayers(players);
leastPlayers = mostPlayers = TEAM1;
for (i = TEAM1; i <= teamCount; i++) {
if (players[i] > players[mostPlayers])
mostPlayers = i;
if (players[i] < players[leastPlayers])
leastPlayers = i;
}
if (players[mostPlayers] > players[leastPlayers] + 1) {
other_team = leastPlayers;
swap_ent = FindNewestPlayer(mostPlayers);
}
if(swap_ent && (!ent || ent == swap_ent)) {
gi.Client_Print (swap_ent, PRINT_HIGH, "You have been swapped to the other team to even the game.");
gi.sound(swap_ent, CHAN_ITEM, gi.soundindex("misc/talk1.wav"), 1, ATTN_NORM, 0);
swap_ent->client->team_force = true;
JoinTeam(swap_ent, other_team, 1);
return true;
}
return false;
}
bool IsAllowedToJoin(edict_t *ent, int desired_team)
{
int i, players[TEAM_TOP] = {0}, mostPlayers;
if(ent->client->team_force) {
ent->client->team_force = false;
return true;
}
CalculatePlayers(players);
mostPlayers = 0;
for (i = TEAM1; i <= teamCount; i++) {
if (i == desired_team)
continue;
if (!mostPlayers || players[i] > players[mostPlayers])
mostPlayers = i;
}
/* can join both teams if they are even and can join if the other team has less players than current */
if (players[desired_team] < players[mostPlayers] ||
(ent->client->resp.team == NOTEAM && players[desired_team] == players[mostPlayers]))
return true;
return false;
}

1108
actionlite/action/a_game.cpp Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,60 @@
// AQ2:TNG Deathwatch - Updated the Version variables to show TNG Stuff
#ifndef VERSION
#define VERSION "0.1"
#endif
#define TNG_TITLE "AQ2: AQR"
// AQ2:TNG Deathwatch End
//AQ2:TNG Slicer This is the max players writen on last killed target
//SLIC2
#define MAX_LAST_KILLED 8
//AQ2:TNG END
extern char *map_rotation[];
extern int num_maps, cur_map, rand_map, num_allvotes; // num_allvotes added by Igor[Rock]
void ReadConfigFile ();
void ReadMOTDFile ();
void PrintMOTD (edict_t *ent);
void stuffcmd (edict_t *ent, char *s);
//void unicastSound(edict_t *ent, int soundIndex, float volume);
int KickDoor (trace_t * tr_old, edict_t * ent, vec3_t forward);
// Prototypes of base Q2 functions that weren't included in any Q2 header
//bool loc_CanSee (edict_t *, edict_t *);
void ParseSayText (edict_t *, char *, size_t size);
void AttachToEntity( edict_t *self, edict_t *onto );
bool CanBeAttachedTo( const edict_t *ent );
//PG BUND - BEGIN
//void ParseSayText(edict_t *, char *);
void GetWeaponName (edict_t * ent, char *buf);
void GetItemName (edict_t * ent, char *buf);
void GetHealth (edict_t * ent, char *buf);
void GetAmmo (edict_t * ent, char *buf);
void GetNearbyTeammates (edict_t * self, char *buf);
void ResetScores (bool playerScores);
void AddKilledPlayer (edict_t * self, edict_t * ent);
void VideoCheckClient (edict_t * ent);
//AQ2:TNG END
//TempFile
void GetLastLoss (edict_t * self, char *buf, char team);
// Firing styles (where shots originate from)
#define ACTION_FIRING_CENTER 0
#define ACTION_FIRING_CLASSIC 1
#define ACTION_FIRING_CLASSIC_HIGH 2
// maxs[2] of a player when crouching (we modify it from the normal 4)
// ...also the modified viewheight -FB 7/18/99
#define CROUCHING_MAXS2 16
#define CROUCHING_VIEWHEIGHT 8
#define STANDING_VIEWHEIGHT 22
//a_team.c
void MakeAllLivePlayersObservers( void );
//a_cmds.c
void Cmd_NextMap_f( edict_t * ent );

View file

@ -0,0 +1,732 @@
#include "../g_local.h"
void Cmd_Say_f (edict_t * ent, bool team, bool arg0,
bool partner_msg);
// Each of the possible radio messages and their length
struct radio_msg_t
{
char *msg; // the msg name
int length; // length in server frames (ie tenths of a second), rounded up
int sndIndex;
};
static radio_msg_t male_radio_msgs[] = {
{"1", 6, 0},
{"2", 6, 0},
{"3", 8, 0},
{"4", 7, 0},
{"5", 8, 0},
{"6", 9, 0},
{"7", 8, 0},
{"8", 7, 0},
{"9", 7, 0},
{"10", 6, 0},
{"back", 6, 0},
{"cover", 7, 0},
{"down", 13, 0},
{"enemyd", 10, 0},
{"enemys", 9, 0},
{"forward", 6, 0},
{"go", 6, 0},
{"im_hit", 7, 0},
{"left", 7, 0},
{"reportin", 9, 0},
{"right", 6, 0},
{"taking_f", 22, 0},
{"teamdown", 13, 0},
{"treport", 12, 0},
{"up", 4, 0}
//{"END", 0, 0} // end of list delimiter
};
static radio_msg_t female_radio_msgs[] = {
{"1", 5, 0},
{"2", 5, 0},
{"3", 5, 0},
{"4", 5, 0},
{"5", 5, 0},
{"6", 8, 0},
{"7", 7, 0},
{"8", 5, 0},
{"9", 5, 0},
{"10", 5, 0},
{"back", 6, 0},
{"cover", 5, 0},
{"down", 6, 0},
{"enemyd", 9, 0},
{"enemys", 9, 0},
{"forward", 8, 0},
{"go", 6, 0},
{"im_hit", 7, 0},
{"left", 8, 0},
{"reportin", 9, 0},
{"right", 5, 0},
{"taking_f", 22, 0},
{"teamdown", 10, 0},
{"treport", 12, 0},
{"up", 6, 0}
//{"END", 0, 0}, // end of list delimiter
};
static const int numMaleSnds = ( sizeof( male_radio_msgs ) / sizeof( male_radio_msgs[0] ) );
static const int numFemaleSnds = ( sizeof( female_radio_msgs ) / sizeof( female_radio_msgs[0] ) );
#define RADIO_MALE_DIR "radio/male/"
#define RADIO_FEMALE_DIR "radio/female/"
#define RADIO_CLICK 0
#define RADIO_DEATH_MALE 1
#define RADIO_DEATH_FEMALE 2
radio_msg_t globalRadio[] = {
{"radio/click.wav", 2, 0},
{"radio/male/rdeath.wav", 27, 0},
{"radio/female/rdeath.wav", 30, 0}
};
void PrecacheRadioSounds ()
{
int i;
char path[MAX_QPATH];
globalRadio[RADIO_CLICK].sndIndex = gi.soundindex(globalRadio[RADIO_CLICK].msg);
globalRadio[RADIO_DEATH_MALE].sndIndex = gi.soundindex(globalRadio[RADIO_DEATH_MALE].msg);
globalRadio[RADIO_DEATH_FEMALE].sndIndex = gi.soundindex(globalRadio[RADIO_DEATH_FEMALE].msg);
//male
for(i = 0; i < numMaleSnds; i++)
{
snprintf(path, sizeof(path), "%s%s.wav", RADIO_MALE_DIR, male_radio_msgs[i].msg);
male_radio_msgs[i].sndIndex = gi.soundindex(path);
}
//female
for(i = 0; i < numFemaleSnds; i++)
{
snprintf(path, sizeof(path), "%s%s.wav", RADIO_FEMALE_DIR, female_radio_msgs[i].msg);
female_radio_msgs[i].sndIndex = gi.soundindex(path);
}
}
static void DeleteRadioQueueEntry( radio_t *radio, int entry_num )
{
int i;
if (radio->queue_size <= entry_num)
{
gi.Com_PrintFmt("DeleteRadioQueueEntry: attempt to delete out of range queue entry: %i\n", entry_num);
return;
}
for (i = entry_num + 1; i < radio->queue_size; i++)
{
memcpy(&radio->queue[i - 1], &radio->queue[i], sizeof(radio_queue_entry_t));
}
radio->queue_size--;
}
// RadioThink should be called once on each player per server frame.
void RadioThink (edict_t * ent)
{
radio_t *radio = &ent->client->resp.radio;
// Try to clean things up, a bit....
if (radio->partner)
{
if (!radio->partner->inuse ||
radio->partner->client->resp.radio.partner != ent)
{
radio->partner = NULL;
}
}
if (radio->partner_last_offered_to)
{
if (!radio->partner_last_offered_to->inuse ||
radio->partner_last_offered_to->solid == SOLID_NOT)
{
radio->partner_last_offered_to = NULL;
}
}
if (radio->partner_last_denied_from)
{
if (!radio->partner_last_denied_from->inuse ||
radio->partner_last_denied_from->solid == SOLID_NOT)
{
radio->partner_last_denied_from = NULL;
}
}
// ................................
if (radio->power_off)
{
radio->queue_size = 0;
return;
}
if (radio->delay > 0)
{
radio->delay--;
if (radio->delay)
return;
}
if (radio->queue_size)
{
edict_t *from;
int check;
from = radio->queue[0].from_player;
if (!radio->queue[0].click && (!from->inuse || !IS_ALIVE(from)))
{
if (radio->queue[0].from_gender)
{
radio->queue[0].sndIndex = globalRadio[RADIO_DEATH_FEMALE].sndIndex;
radio->queue[0].length = globalRadio[RADIO_DEATH_FEMALE].length;
}
else
{
radio->queue[0].sndIndex = globalRadio[RADIO_DEATH_MALE].sndIndex;
radio->queue[0].length = globalRadio[RADIO_DEATH_MALE].length;
}
for (check = 1; check < radio->queue_size; check++)
{
if (!radio->queue[check].click && radio->queue[check].from_player == from)
{
DeleteRadioQueueEntry( radio, check );
check--;
}
}
}
// if( ! IsInIgnoreList( ent, from ) )
// {
// unicastSound( ent, radio->queue[0].sndIndex, 1.0 );
// radio->delay = radio->queue[0].length;
// }
DeleteRadioQueueEntry( radio, 0 ); //We can remove it here?
}
}
static void AppendRadioMsgToQueue( radio_t *radio, int sndIndex, int len, int click, edict_t *from_player )
{
radio_queue_entry_t *newentry;
if (radio->queue_size >= MAX_RADIO_QUEUE_SIZE)
{
gi.Com_Print("AppendRadioMsgToQueue: Maximum radio queue size exceeded\n");
return;
}
newentry = &radio->queue[radio->queue_size];
newentry->sndIndex = sndIndex;
newentry->from_player = from_player;
newentry->from_gender = from_player->client->resp.radio.gender;
newentry->length = len;
newentry->click = click;
radio->queue_size++;
}
static void InsertRadioMsgInQueueBeforeClick( radio_t *radio, int sndIndex, int len, edict_t *from_player )
{
radio_queue_entry_t *newentry;
if (radio->queue_size >= MAX_RADIO_QUEUE_SIZE)
{
gi.Com_Print("InsertRadioMsgInQueueBeforeClick: Maximum radio queue size exceeded\n");
return;
}
newentry = &radio->queue[radio->queue_size - 1];
memcpy( &radio->queue[radio->queue_size], newentry, sizeof(radio_queue_entry_t));
newentry->sndIndex = sndIndex;
newentry->from_player = from_player;
newentry->from_gender = from_player->client->resp.radio.gender;
newentry->length = len;
newentry->click = 0;
radio->queue_size++;
}
static void AddRadioMsg( radio_t *radio, int sndIndex, int len, edict_t *from_player )
{
if (radio->queue_size == 0)
{
AppendRadioMsgToQueue( radio, globalRadio[RADIO_CLICK].sndIndex, globalRadio[RADIO_CLICK].length, 1, from_player );
AppendRadioMsgToQueue( radio, sndIndex, len, 0, from_player );
AppendRadioMsgToQueue( radio, globalRadio[RADIO_CLICK].sndIndex, globalRadio[RADIO_CLICK].length, 1, from_player );
}
else // we have some msgs in it already...
{
if (radio->queue_size < MAX_RADIO_QUEUE_SIZE)
InsertRadioMsgInQueueBeforeClick( radio, sndIndex, len, from_player );
// else ignore the message...
}
}
void RadioBroadcast (edict_t * ent, int partner, const char *msg)
{
int j, i, msg_len, numSnds;
edict_t *other;
radio_msg_t *radio_msgs;
int msg_soundIndex = 0;
char msgname_num[8], filteredmsg[48];
bool found = false;
radio_t *radio;
if (!IS_ALIVE(ent))
return;
radio = &ent->client->resp.radio;
if (radio->power_off)
{
gi.Center_Print (ent, "Your radio is off!");
return;
}
if (partner && radio->partner == NULL)
{
gi.LocClient_Print (ent, PRINT_HIGH, "You don't have a partner.\n");
return;
}
if (radio->gender)
{
radio_msgs = female_radio_msgs;
numSnds = numFemaleSnds;
}
else
{
radio_msgs = male_radio_msgs;
numSnds = numMaleSnds;
}
i = found = 0;
msg_len = 0;
Q_strlcpy(filteredmsg, msg, sizeof(filteredmsg));
for(i = 0; i < numSnds; i++)
{
if (!strcmp(radio_msgs[i].msg, filteredmsg))
{
found = true;
msg_soundIndex = radio_msgs[i].sndIndex;
msg_len = radio_msgs[i].length;
break;
}
}
if (!found)
{
gi.LocCenter_Print (ent, "'%s' is not a valid radio message", filteredmsg);
return;
}
if (radiolog->value)
{
gi.LocClient_Print (NULL, PRINT_CHAT, "[%s RADIO] %s: %s\n",
partner ? "PARTNER" : "TEAM", ent->client->pers.netname, filteredmsg);
}
//TempFile BEGIN
if (strcmp (filteredmsg, "enemyd") == 0)
{
if (ent->client->radio_num_kills > 1 && ent->client->radio_num_kills <= 10)
{
// If we are reporting enemy down, add the number of kills.
sprintf( msgname_num, "%i", ent->client->radio_num_kills );
ent->client->radio_num_kills = 0; // prevent from getting into an endless loop
RadioBroadcast(ent, partner, msgname_num); // Now THAT'S recursion! =)
}
ent->client->radio_num_kills = 0;
}
//TempFile END
//AQ2:TNG Slicer
if (radio_repeat->value)
{ //SLIC2 Optimization
if (CheckForRepeat (ent, i) == false)
return;
}
if (radio_max->value)
{
if (CheckForFlood (ent) == false)
return;
}
//AQ2:TNG END
for (j = 1; j <= game.maxclients; j++)
{
other = &g_edicts[j];
if (!other->inuse)
continue;
if (!other->client)
continue;
if (!OnSameTeam(ent, other))
continue;
if (partner && other != radio->partner)
continue;
AddRadioMsg( &other->client->resp.radio, msg_soundIndex, msg_len, ent );
}
}
void Cmd_Radio_f (edict_t * ent)
{
RadioBroadcast(ent, ent->client->resp.radio.partner_mode, gi.args());
}
void Cmd_Radiopartner_f (edict_t * ent)
{
RadioBroadcast(ent, 1, gi.args());
}
void Cmd_Radioteam_f (edict_t * ent)
{
RadioBroadcast(ent, 0, gi.args());
}
void Cmd_Radiogender_f (edict_t * ent)
{
const char *arg;
radio_t *radio;
radio = &ent->client->resp.radio;
arg = gi.args();
if (arg == NULL || !*arg)
{
if (radio->gender)
gi.LocClient_Print (ent, PRINT_HIGH, "Radio gender currently set to female\n");
else
gi.LocClient_Print (ent, PRINT_HIGH, "Radio gender currently set to male\n");
return;
}
if (!strcmp(arg, "male"))
{
gi.LocClient_Print (ent, PRINT_HIGH, "Radio gender set to male\n");
radio->gender = 0;
}
else if (!strcmp(arg, "female"))
{
gi.LocClient_Print (ent, PRINT_HIGH, "Radio gender set to female\n");
radio->gender = 1;
}
else
{
gi.LocClient_Print (ent, PRINT_HIGH, "Invalid gender selection, try 'male' or 'female'\n");
}
}
void Cmd_Radio_power_f (edict_t * ent)
{
radio_t *radio;
radio = &ent->client->resp.radio;
radio->power_off = !radio->power_off;
gi.LocCenter_Print(ent, "Radio switched %s", (radio->power_off) ? "off" : "on");
gi.sound(ent, CHAN_VOICE, globalRadio[RADIO_CLICK].sndIndex, 1, ATTN_NORM, 1.0);
//unicastSound(ent, globalRadio[RADIO_CLICK].sndIndex, 1.0);
}
void Cmd_Channel_f (edict_t * ent)
{
radio_t *radio;
radio = &ent->client->resp.radio;
radio->partner_mode = !radio->partner_mode;
if (radio->partner_mode)
{
gi.Center_Print (ent, "Channel set to 1, partner channel");
}
else
{
gi.Center_Print (ent, "Channel set to 0, team channel");
}
}
edict_t *DetermineViewedPlayer(edict_t *ent, bool teammate);
void Cmd_Partner_f (edict_t * ent)
{
edict_t *target;
const char *genderstr;
radio_t *radio, *tRadio;
if (!IS_ALIVE(ent))
return;
radio = &ent->client->resp.radio;
if (radio->partner) {
if (radio->partner->inuse) {
gi.LocCenter_Print( ent, "You already have a partner, %s", radio->partner->client->pers.netname );
return;
}
// just in case RadioThink hasn't caught it yet... avoid any problems
radio->partner = NULL;
}
target = DetermineViewedPlayer(ent, true);
if (target == NULL) {
gi.Center_Print (ent, "No potential partner selected");
return;
}
tRadio = &target->client->resp.radio;
if (tRadio->partner) {
gi.LocCenter_Print (ent, "%s already has a partner", target->client->pers.netname);
return;
}
if (tRadio->partner_last_offered_to == ent &&
radio->partner_last_offered_from == target)
{
gi.LocCenter_Print (ent, "%s is now your partner", target->client->pers.netname);
gi.LocCenter_Print (target, "%s is now your partner", ent->client->pers.netname);
radio->partner = target;
tRadio->partner = ent;
radio->partner_last_offered_from = NULL;
tRadio->partner_last_offered_to = NULL;
return;
}
if (tRadio->partner_last_denied_from == ent)
{
gi.LocCenter_Print (ent, "%s has already denied you", target->client->pers.netname);
return;
}
if (target == radio->partner_last_offered_to)
{
genderstr = GENDER_STR(target, "him", "her", "it");
gi.LocCenter_Print (ent, "Already awaiting confirmation from %s", genderstr);
return;
}
genderstr = GENDER_STR(ent, "him", "her", "it");
gi.LocCenter_Print (ent, "Awaiting confirmation from %s", target->client->pers.netname);
gi.LocCenter_Print (target,
"%s offers to be your partner\n"
"To accept:\nView %s and use the 'partner' command\n"
"To deny:\nUse the 'deny' command",
ent->client->pers.netname, genderstr);
radio->partner_last_offered_to = target;
tRadio->partner_last_offered_from = ent;
}
void Cmd_Unpartner_f (edict_t * ent)
{
edict_t *target;
radio_t *radio;
radio = &ent->client->resp.radio;
if (radio->partner && !radio->partner->inuse)
{ // just in case RadioThink hasn't caught it yet... avoid any problems
radio->partner = NULL;
}
target = radio->partner;
if (target == NULL) {
gi.Center_Print (ent, "You don't have a partner");
return;
}
if (target->client->resp.radio.partner == ent)
{
gi.LocCenter_Print (target, "%s broke your partnership", ent->client->pers.netname);
target->client->resp.radio.partner = NULL;
}
gi.LocCenter_Print (ent, "You broke your partnership with %s", target->client->pers.netname);
radio->partner = NULL;
}
void Cmd_Deny_f (edict_t * ent)
{
edict_t *target;
radio_t *radio;
if (!IS_ALIVE(ent))
return;
radio = &ent->client->resp.radio;
target = radio->partner_last_offered_from;
if (target && target->inuse)
{
gi.LocCenter_Print (ent, "You denied %s", target->client->pers.netname);
gi.LocCenter_Print (target, "%s has denied you", ent->client->pers.netname);
radio->partner_last_denied_from = target;
radio->partner_last_offered_from = NULL;
if (target->client->resp.radio.partner_last_offered_to == ent)
target->client->resp.radio.partner_last_offered_to = NULL;
}
else
{
gi.Center_Print (ent, "No one has offered to be your partner");
return;
}
}
void Cmd_Say_partner_f (edict_t * ent)
{
if (ent->client->resp.radio.partner == NULL)
{
gi.LocClient_Print (ent, PRINT_HIGH, "You don't have a partner.\n");
return;
}
Cmd_Say_f (ent, false, false, true);
}
//SLIC2 Redesigned and optimized these two functions
bool CheckForFlood( edict_t * ent )
{
radio_t *radio = &ent->client->resp.radio;
//If he's muted..
if (radio->rd_mute) {
if (radio->rd_mute > level.time.seconds()) // Still muted..
return false;
radio->rd_mute = 0; // No longer muted..
}
if (!radio->rd_Count) {
radio->rd_time = level.time.seconds();
radio->rd_Count++;
}
else {
if (level.time.seconds() - radio->rd_time < (int)(radio_time->value * 10)) {
if (++radio->rd_Count >= (int)radio_max->value) {
gi.LocClient_Print( ent, PRINT_HIGH,
"[RADIO FLOOD PROTECTION]: Flood Detected, you are silenced for %d secs\n", (int)radio_ban->value );
radio->rd_mute = level.time.seconds() + (int)(radio_ban->value * 10);
return false;
}
}
else {
radio->rd_Count = 0;
}
}
return true;
}
bool CheckForRepeat( edict_t * ent, int radioCode )
{
radio_t *radio = &ent->client->resp.radio;
//If he's muted..
if (radio->rd_mute) {
if (radio->rd_mute > level.time.seconds()) // Still muted..
return false;
radio->rd_mute = 0; // No longer muted..
}
if (radio->rd_lastRadio == radioCode) { //He's trying to repeat it..
if (level.time.seconds() - radio->rd_repTime < (int)(radio_repeat_time->value * 10)) {
if (++radio->rd_repCount == (int)radio_repeat->value) { //Busted
gi.LocClient_Print( ent, PRINT_HIGH, "[RADIO FLOOD PROTECTION]: Repeat Flood Detected, you are silenced for %d secs\n", (int)radio_ban->value );
radio->rd_mute = level.time.seconds() + (int)(radio_ban->value * 10);
return false;
}
}
else {
radio->rd_repCount = 0;
}
}
else {
radio->rd_lastRadio = radioCode;
radio->rd_repCount = 0;
}
radio->rd_repTime = level.time.seconds();
return true;
}

View file

@ -0,0 +1,79 @@
//-----------------------------------------------------------------------------
// a_radio.h
//
// Include file for use with radio stuff
// -Fireblade
//
// $Id: a_radio.h,v 1.4 2004/04/08 23:19:51 slicerdw Exp $
//
//-----------------------------------------------------------------------------
// $Log: a_radio.h,v $
// Revision 1.4 2004/04/08 23:19:51 slicerdw
// Optimized some code, added a couple of features and fixed minor bugs
//
// Revision 1.3 2001/09/28 13:48:34 ra
// I ran indent over the sources. All .c and .h files reindented.
//
// Revision 1.2 2001/08/15 14:50:48 slicerdw
// Added Flood protections to Radio & Voice, Fixed the sniper bug AGAIN
//
// Revision 1.1.1.1 2001/05/06 17:24:32 igor_rock
// This is the PG Bund Edition V1.25 with all stuff laying around here...
//
//-----------------------------------------------------------------------------
#define MAX_SOUNDFILE_PATH_LEN 32 // max length of a sound file path
#define MAX_RADIO_MSG_QUEUE_SIZE 4
#define MAX_RADIO_QUEUE_SIZE 6 // this must be at least 2 greater than the above
typedef struct radio_queue_entry_s
{
int sndIndex;
edict_t *from_player;
int from_gender; // true if female
int length;
bool click;
} radio_queue_entry_t;
typedef struct radio_s
{
int delay;
radio_queue_entry_t queue[MAX_RADIO_QUEUE_SIZE];
int queue_size;
bool gender; // radiogender
bool power_off; // radio_power
// Partners stuff
bool partner_mode; // 'radio' command using team or partner
edict_t *partner; // current partner
edict_t *partner_last_offered_to; // last person I offered a partnership to
edict_t *partner_last_offered_from; // last person I received a partnership offer from
edict_t *partner_last_denied_from; // last person I denied a partnership offer from
//Flood & Repeat
int rd_mute; //Time to be muted
int rd_Count; //Counter for the last msgs in "xx" secs allowed
int rd_time; //Frame for the first radio message of the ones to follow
int rd_lastRadio; //Code of the last radio used
int rd_repCount; //Counter for the number of repeated radio msgs
int rd_repTime; //Frame for the last repeated radio msg
} radio_t;
void RadioThink (edict_t *);
void Cmd_Radio_f (edict_t *);
void Cmd_Radiogender_f (edict_t *);
void Cmd_Radio_power_f (edict_t *);
void Cmd_Radiopartner_f (edict_t *);
void Cmd_Radioteam_f (edict_t *);
void Cmd_Channel_f (edict_t *);
void Cmd_Say_partner_f (edict_t *);
void Cmd_Partner_f (edict_t *);
void Cmd_Deny_f (edict_t *);
void Cmd_Unpartner_f (edict_t *);
void PrecacheRadioSounds ();
bool CheckForFlood (edict_t * ent);
bool CheckForRepeat (edict_t * ent, int radioCode);

2945
actionlite/action/a_team.cpp Normal file

File diff suppressed because it is too large Load diff

110
actionlite/action/a_team.h Normal file
View file

@ -0,0 +1,110 @@
#define NOTEAM 0
#define TEAM1 1
#define TEAM2 2
#define TEAM3 3
#define MAX_TEAMS 3
#define TEAM_TOP (MAX_TEAMS+1)
#define WINNER_NONE NOTEAM
#define WINNER_TIE TEAM_TOP
// Pre- and post-trace code for our teamplay anti-stick stuff. If there are
// still "transparent" (SOLID_TRIGGER) players, they need to be set to
// SOLID_BBOX before a trace is performed, then changed back again
// afterwards. PRETRACE() and POSTTRACE() should be called before and after
// traces in all places where combat is taking place (ie "transparent" players
// should be detected), ie shots being traced etc.
// FB 6/1/99: Now crouching players will have their bounding box adjusted here
// too, for better shot areas. (there has to be a better way to do this?)
#define PRETRACE() \
if (transparent_list && (((int)teamplay->value && !lights_camera_action))) \
TransparentListSet(SOLID_BBOX)
#define POSTTRACE() \
if (transparent_list && (((int)teamplay->value && !lights_camera_action))) \
TransparentListSet(SOLID_TRIGGER)
edict_t *SelectTeamplaySpawnPoint (edict_t *);
bool FallingDamageAmnesty (edict_t * targ);
char * TeamName (int team);
void UpdateJoinMenu( void );
void OpenJoinMenu (edict_t *);
void OpenWeaponMenu (edict_t *);
void OpenItemMenu (edict_t * ent);
void OpenItemKitMenu (edict_t * ent);
void JoinTeam (edict_t * ent, int desired_team, int skip_menuclose);
edict_t *FindOverlap (edict_t * ent, edict_t * last_overlap);
int CheckTeamRules (void);
void A_Scoreboard (edict_t * ent);
void Team_f (edict_t * ent);
void AssignSkin (edict_t * ent, const char *s, bool nickChanged);
void TallyEndOfLevelTeamScores (void);
void SetupTeamSpawnPoints ();
int CheckTeamSpawnPoints ();
void GetSpawnPoints ();
void CleanBodies (); // from p_client.c, removes all current dead bodies from map
void LeaveTeam (edict_t *);
int newrand (int top);
void InitTransparentList ();
void AddToTransparentList (edict_t *);
void RemoveFromTransparentList (edict_t *);
bool OnTransparentList( const edict_t *ent );
void PrintTransparentList ();
void CenterPrintAll (const char *msg);
int TeamHasPlayers( int team );
//TNG:Freud - new spawning system
void NS_GetSpawnPoints ();
bool NS_SelectFarTeamplaySpawnPoint (int team, bool teams_assigned[]);
void NS_SetupTeamSpawnPoints ();
int OtherTeam(int teamNum);
typedef struct spawn_distances_s
{
float distance;
edict_t *s;
}
spawn_distances_t;
typedef struct transparent_list_s
{
edict_t *ent;
struct transparent_list_s *next;
}
transparent_list_t;
extern bool team_game_going;
extern bool team_round_going;
extern int lights_camera_action;
extern int holding_on_tie_check;
extern int team_round_countdown;
extern int timewarning;
extern int fragwarning;
extern transparent_list_t *transparent_list;
extern trace_t trace_t_temp;
extern int current_round_length; // For RoundTimeLeft
extern int day_cycle_at;
extern int teamCount;
extern int in_warmup;
extern bool teams_changed;
typedef struct menu_list_weapon
{
int num;
char sound[40];
char name[40];
}
menu_list_weapon;
typedef struct menu_list_item
{
int num;
char sound[40];
char name[40];
}
menu_list_item;

View file

@ -0,0 +1,741 @@
/****************************************************************************/
/* */
/* project : CGF (c) 1999 William van der Sterren */
/* parts (c) 1998 id software */
/* */
/* file : cgf_sfx_glass.cpp "special effects for glass entities" */
/* author(s): William van der Sterren */
/* version : 0.5 */
/* */
/* date (last revision): Jun 12, 99 */
/* date (creation) : Jun 04, 99 */
/* */
/* */
/* revision history */
/* -- date ---- | -- revision ---------------------- | -- revisor -- */
/* Jun 12, 1999 | fixed knife slash breaks glass | William */
/* Jun 08, 1999 | improved fragment limit | William */
/* */
/******* http://www.botepidemic.com/aid/cgf for CGF for Action Quake2 *******/
//
// $Id: cgf_sfx_glass.c,v 1.2 2001/09/28 13:48:34 ra Exp $
//
//-----------------------------------------------------------------------------
// $Log: cgf_sfx_glass.c,v $
// Revision 1.2 2001/09/28 13:48:34 ra
// I ran indent over the sources. All .c and .h files reindented.
//
// Revision 1.1.1.1 2001/05/06 17:29:34 igor_rock
// This is the PG Bund Edition V1.25 with all stuff laying around here...
//
//-----------------------------------------------------------------------------
#ifdef __cplusplus
// VC++, for CGF
#include <cmath> // prevent problems between C and STL
extern "C"
{
#include "../g_local.h"
#include "cgf_sfx_glass.h"
}
#else
// C, for other AQ2 variants
#include "g_local.h"
#include "cgf_sfx_glass.h"
#endif
// cvar for breaking glass
static cvar_t *breakableglass = 0;
// cvar for max glass fragment count
static cvar_t *glassfragmentlimit = 0;
static int glassfragmentcount = 0;
// additional functions - Q2 expects C calling convention
#ifdef __cplusplus
extern "C"
{
#endif
void CGF_SFX_TouchGlass (edict_t * self, edict_t * other, cplane_t * plane,
csurface_t * surf);
// called whenever an entity hits the trigger spawned for the glass
void CGF_SFX_EmitGlass (edict_t * aGlassPane, edict_t * anInflictor,
vec3_t aPoint);
// emits glass fragments from aPoint, to show effects of firing thru window
void CGF_SFX_BreakGlass (edict_t * aGlassPane, edict_t * anOther,
edict_t * anAttacker, int aDamage, vec3_t aPoint,
vec_t aPaneDestructDelay);
// breaks glass
void CGF_SFX_InstallBreakableGlass (edict_t * aGlassPane);
// when working on a glass pane for the first time, just install trigger
// when working on a glass pane again (after a game ended), move
// glass back to original location
void CGF_SFX_HideBreakableGlass (edict_t * aGlassPane);
// after being broken, the pane cannot be removed as it is needed in
// subsequent missions/games, so hide it at about z = -1000
void CGF_SFX_ApplyGlassFragmentLimit (const char *aClassName);
// updates glassfragmentcount and removes oldest glass fragement if
// necessary to meet limit
void CGF_SFX_MiscGlassUse (edict_t * self, edict_t * other,
edict_t * activator);
// catches use from unforeseen objects (weapons, debris,
// etc. touching the window)
void CGF_SFX_MiscGlassDie (edict_t * self, edict_t * inflictor,
edict_t * attacker, int damage, vec3_t point);
// catches die calls caused by unforeseen objects (weapons, debris,
// etc. damaging the window)
void CGF_SFX_GlassThrowDebris (edict_t * self, char *modelname, float speed,
vec3_t origin);
// variant of id software's ThrowDebris, now numbering the entity (for later removal)
extern // from a_game.c
edict_t *FindEdictByClassnum (char *classname, int classnum);
// declaration from g_misc.c
extern // from g_misc.c
void debris_die (edict_t * self, edict_t * inflictor, edict_t * attacker,
int damage, vec3_t point);
#ifdef __cplusplus
}
#endif
void
CGF_SFX_InstallGlassSupport ()
{
breakableglass = gi.cvar ("breakableglass", "0", 0);
glassfragmentlimit = gi.cvar ("glassfragmentlimit", "30", 0);
}
int
CGF_SFX_IsBreakableGlassEnabled ()
{
// returns whether breakable glass is enabled (cvar) and allowed (dm mode)
return breakableglass->value;
}
void
CGF_SFX_TestBreakableGlassAndRemoveIfNot_Think (edict_t *
aPossibleGlassEntity)
{
// at level.time == 0.1 the entity has been introduced in the game,
// and we can use gi.pointcontents and gi.trace to check the entity
vec3_t origin;
int breakingglass;
trace_t trace;
// test for cvar
if (!CGF_SFX_IsBreakableGlassEnabled ())
{
G_FreeEdict (aPossibleGlassEntity);
return;
}
VectorAdd (aPossibleGlassEntity->absmax, aPossibleGlassEntity->absmin,
origin);
VectorScale (origin, 0.5, origin);
// detect glass (does not work for complex shapes,
// for example, the glass window near the satellite
// dish at Q2 base3
breakingglass = (gi.pointcontents (origin) & CONTENTS_TRANSLUCENT);
if (!breakingglass)
{
// test for complex brushes that happen to be
// hollow in their origin (for instance, the
// window at Q2 base3, near the satellite dish
trace =
gi.trace (origin, vec3_origin, vec3_origin,
aPossibleGlassEntity->absmax, 0, MASK_PLAYERSOLID);
breakingglass = ((trace.ent == aPossibleGlassEntity)
&& (trace.contents & CONTENTS_TRANSLUCENT));
trace =
gi.trace (origin, vec3_origin, vec3_origin,
aPossibleGlassEntity->absmin, 0, MASK_PLAYERSOLID);
breakingglass = ((breakingglass)
|| ((trace.ent == aPossibleGlassEntity)
&& (trace.contents & CONTENTS_TRANSLUCENT)));
}
if (!breakingglass)
{
// do remove other func_explosives
G_FreeEdict (aPossibleGlassEntity);
return;
}
// discovered some glass - now make store the origin
// we need that after hiding the glass
VectorCopy (aPossibleGlassEntity->s.origin, aPossibleGlassEntity->pos1); // IMPORTANT!
// make a backup of the health in light_level
aPossibleGlassEntity->light_level = aPossibleGlassEntity->health;
// install the glass
CGF_SFX_InstallBreakableGlass (aPossibleGlassEntity);
}
void
CGF_SFX_InstallBreakableGlass (edict_t * aGlassPane)
{
// when working on a glass pane for the first time, just install trigger
// when working on a glass pane again (after a game ended), move
// glass back to original location
edict_t *trigger;
vec3_t maxs;
vec3_t mins;
// reset origin based on aGlassPane->pos1
VectorCopy (aGlassPane->pos1, aGlassPane->s.origin);
// reset health based on aGlassPane->light_level
aGlassPane->health = aGlassPane->light_level;
// replace die and use functions by glass specific ones
aGlassPane->die = CGF_SFX_MiscGlassDie;
aGlassPane->use = CGF_SFX_MiscGlassUse;
// reset some pane attributes
aGlassPane->takedamage = DAMAGE_YES;
aGlassPane->solid = SOLID_BSP;
aGlassPane->movetype = MOVETYPE_FLYMISSILE;
// for other movetypes, cannot move pane to hidden location and back
// try to establish size
VectorCopy (aGlassPane->maxs, maxs);
VectorCopy (aGlassPane->mins, mins);
// set up trigger, similar to triggers for doors
// but with a smaller box
mins[0] -= 24;
mins[1] -= 24;
mins[2] -= 24;
maxs[0] += 24;
maxs[1] += 24;
maxs[2] += 24;
// adjust some settings
trigger = G_Spawn ();
trigger->classname = "breakableglass_trigger";
VectorCopy (mins, trigger->mins);
VectorCopy (maxs, trigger->maxs);
trigger->owner = aGlassPane;
trigger->solid = SOLID_TRIGGER;
trigger->movetype = MOVETYPE_NONE;
trigger->touch = CGF_SFX_TouchGlass;
gi.linkentity (trigger);
}
void
CGF_SFX_ShootBreakableGlass (edict_t * aGlassPane, edict_t * anAttacker,
/*trace_t* */ void *tr,
int mod)
{
// process gunshots thru glass
edict_t *trigger;
int destruct;
// depending on mod, destroy window or emit fragments
switch (mod)
{
// break for ap, shotgun, handcannon, and kick, destory window
case MOD_M3:
case MOD_HC:
case MOD_SNIPER:
case MOD_KICK:
case MOD_GRENADE:
case MOD_G_SPLASH:
case MOD_HANDGRENADE:
case MOD_HG_SPLASH:
case MOD_KNIFE: // slash damage
destruct = true;
break;
default:
destruct = (rand () % 3 == 0);
break;
};
if (destruct)
{
// break glass (and hurt if doing kick)
CGF_SFX_BreakGlass (aGlassPane, anAttacker, 0, aGlassPane->health,
vec3_origin, FRAMETIME);
if (mod.id == MOD_KICK)
{
vec3_t bloodorigin;
vec3_t dir;
vec3_t normal;
VectorAdd (aGlassPane->absmax, aGlassPane->absmin, bloodorigin);
VectorScale (bloodorigin, 0.5, bloodorigin);
VectorSubtract (bloodorigin, anAttacker->s.origin, dir);
VectorNormalize (dir);
VectorMA (anAttacker->s.origin, 32.0, dir, bloodorigin);
VectorSet (normal, 0, 0, -1);
T_Damage (anAttacker, aGlassPane, anAttacker, dir, bloodorigin,
normal, 15.0, 0, 0, MOD_BREAKINGGLASS);
}
// remove corresponding trigger
trigger = 0;
while ((trigger = G_Find (trigger, FOFS (classname), "breakableglass_trigger")) != NULL)
{
if (trigger->owner == aGlassPane)
{
// remove it
G_FreeEdict (trigger);
// only one to be found
break;
}
}
}
else
{
// add decal (if not grenade)
if ((mod != MOD_HANDGRENADE)
&& (mod != MOD_HG_SPLASH)
&& (mod != MOD_GRENADE) && (mod != MOD_G_SPLASH))
{
AddDecal (anAttacker, (trace_t *) tr);
}
// and emit glass
CGF_SFX_EmitGlass (aGlassPane, anAttacker, ((trace_t *) tr)->endpos);
}
}
void
CGF_SFX_TouchGlass (edict_t * self, edict_t * other, cplane_t * plane,
csurface_t * surf)
{
// called whenever an entity hits the trigger spawned for the glass
vec3_t origin;
vec3_t normal;
vec3_t spot;
trace_t trace;
edict_t *glass;
vec3_t velocity;
vec_t speed;
vec_t projected_speed;
int is_hgrenade;
int is_knife;
is_hgrenade = is_knife = false;
// ignore non-clients-non-grenade-non-knife
if (!other->client)
{
is_knife = (0 == Q_stricmp ("weapon_knife", other->classname));
if (!is_knife)
{
is_hgrenade = (0 == Q_stricmp ("hgrenade", other->classname));
}
if ((!is_knife) && (!is_hgrenade))
return;
if (is_knife)
goto knife_and_grenade_handling;
}
// test whether other really hits the glass - deal with
// the special case that other hits some boundary close to the border of the glass pane
//
//
// ....trigger.......
// +++++++++ +++++++++++
// .+---glass------+.
// wall .+--------------+.wall
// +++++++++ +++++++++++
// ----->..................
// wrong ^ ^
// | |
// wrong ok
//
glass = self->owner;
// hack - set glass' movetype to MOVETYPE_PUSH as it is not
// moving as long as the trigger is active
glass->movetype = MOVETYPE_PUSH;
VectorAdd (glass->absmax, glass->absmin, origin);
VectorScale (origin, 0.5, origin);
// other needs to be able to trace to glass origin
trace = gi.trace (other->s.origin, vec3_origin, vec3_origin, origin, other,
MASK_PLAYERSOLID);
if (trace.ent != glass)
return;
// we can reach the glass origin, so we have the normal of
// the glass plane
VectorCopy (trace.plane.normal, normal);
// we need to check if client is not running into wall next
// to the glass (the trigger stretches into the wall)
VectorScale (normal, -1000.0, spot);
VectorAdd (spot, other->s.origin, spot);
// line between other->s.origin and spot (perpendicular to glass
// surface should not hit wall but glass instead
trace = gi.trace (other->s.origin, vec3_origin, vec3_origin, spot, other,
MASK_PLAYERSOLID);
if (trace.ent != glass)
return;
// now, we check if the client's speed perpendicular to
// the glass plane, exceeds the required 175
// (speed should be < -200, as the plane's normal
// points towards the client
VectorCopy (other->velocity, velocity);
speed = VectorNormalize (velocity);
projected_speed = speed * DotProduct (velocity, normal);
// bump projected speed for grenades - they should break
// the window more easily
if (is_hgrenade)
projected_speed *= 1.5f;
// if hitting the glass with sufficient speed (project < -175),
// being jumpkicked (speed > 700, project < -5) break the window
if (!((projected_speed < -175.0) ||
((projected_speed < -5) && (speed > 700))))
goto knife_and_grenade_handling;
// break glass
CGF_SFX_BreakGlass (glass, other, other, glass->health, vec3_origin,
3.0f * FRAMETIME);
// glass can take care of itself, but the trigger isn't needed anymore
G_FreeEdict (self);
/* not needed
// reduce momentum of the client (he just broke the window
// so he should lose speed. in addition, it doesn't feel
// right if he overtakes the glass fragments
// VectorScale(normal, 200.0, velocity);
// VectorAdd(other->velocity, velocity, other->velocity);
*/
// make sure client takes damage
T_Damage (other, glass, other, normal, other->s.origin, normal, 15.0, 0, 0,
MOD_BREAKINGGLASS);
return;
// goto label
knife_and_grenade_handling:
// if knife or grenade, bounce them
if ((is_knife) || (is_hgrenade))
{
// change clipmask to bounce of glass
other->clipmask = MASK_SOLID;
}
}
void
CGF_SFX_BreakGlass (edict_t * aGlassPane, edict_t * anInflictor,
edict_t * anAttacker, int aDamage, vec3_t aPoint,
vec_t aPaneDestructDelay)
{
// based on func_explode, but with lotsa subtle differences
vec3_t origin;
vec3_t old_origin;
vec3_t chunkorigin;
vec3_t size;
int count;
int mass;
// bmodel origins are (0 0 0), we need to adjust that here
VectorCopy (aGlassPane->s.origin, old_origin);
VectorScale (aGlassPane->size, 0.5, size);
VectorAdd (aGlassPane->absmin, size, origin);
VectorCopy (origin, aGlassPane->s.origin);
aGlassPane->takedamage = DAMAGE_NO;
VectorSubtract (aGlassPane->s.origin, anInflictor->s.origin,
aGlassPane->velocity);
VectorNormalize (aGlassPane->velocity);
// use speed 250 instead of 150 for funkier glass spray
VectorScale (aGlassPane->velocity, 250.0, aGlassPane->velocity);
// start chunks towards the center
VectorScale (size, 0.75, size);
mass = aGlassPane->mass;
if (!mass)
mass = 75;
// big chunks
if (mass >= 100)
{
count = mass / 100;
if (count > 8)
count = 8;
while (count--)
{
CGF_SFX_ApplyGlassFragmentLimit ("debris");
chunkorigin[0] = origin[0] + crandom () * size[0];
chunkorigin[1] = origin[1] + crandom () * size[1];
chunkorigin[2] = origin[2] + crandom () * size[2];
CGF_SFX_GlassThrowDebris (aGlassPane,
"models/objects/debris1/tris.md2", 1,
chunkorigin);
}
}
// small chunks
count = mass / 25;
if (count > 16)
count = 16;
while (count--)
{
CGF_SFX_ApplyGlassFragmentLimit ("debris");
chunkorigin[0] = origin[0] + crandom () * size[0];
chunkorigin[1] = origin[1] + crandom () * size[1];
chunkorigin[2] = origin[2] + crandom () * size[2];
CGF_SFX_GlassThrowDebris (aGlassPane, "models/objects/debris2/tris.md2",
2, chunkorigin);
}
// clear velocity, reset origin (that has been abused in ThrowDebris)
VectorClear (aGlassPane->velocity);
VectorCopy (old_origin, aGlassPane->s.origin);
if (anAttacker)
{
// jumping thru
G_UseTargets (aGlassPane, anAttacker);
}
else
{
// firing thru - the pane has no direct attacker to hurt,
// but G_UseTargets expects one. So make it a DIY
G_UseTargets (aGlassPane, aGlassPane);
}
// have glass plane be visible for two more frames,
// and have it self-destruct then
// meanwhile, make sure the player can move thru
aGlassPane->solid = SOLID_NOT;
aGlassPane->think = CGF_SFX_HideBreakableGlass;
aGlassPane->nextthink = level.framenum + aPaneDestructDelay * HZ;
}
void
CGF_SFX_EmitGlass (edict_t * aGlassPane, edict_t * anInflictor, vec3_t aPoint)
{
// based on func_explode, but with lotsa subtle differences
vec3_t old_origin;
vec3_t chunkorigin;
vec3_t size;
int count;
// bmodel origins are (0 0 0), we need to adjust that here
VectorCopy (aGlassPane->s.origin, old_origin);
VectorCopy (aPoint, aGlassPane->s.origin);
VectorSubtract (aGlassPane->s.origin, anInflictor->s.origin,
aGlassPane->velocity);
VectorNormalize (aGlassPane->velocity);
// use speed 250 instead of 150 for funkier glass spray
VectorScale (aGlassPane->velocity, 250.0, aGlassPane->velocity);
// start chunks towards the center
VectorScale (aGlassPane->size, 0.25, size);
count = 4;
while (count--)
{
CGF_SFX_ApplyGlassFragmentLimit ("debris");
chunkorigin[0] = aPoint[0] + crandom () * size[0];
chunkorigin[1] = aPoint[1] + crandom () * size[1];
chunkorigin[2] = aPoint[2] + crandom () * size[2];
CGF_SFX_GlassThrowDebris (aGlassPane, "models/objects/debris2/tris.md2",
2, chunkorigin);
}
// clear velocity, reset origin (that has been abused in ThrowDebris)
VectorClear (aGlassPane->velocity);
VectorCopy (old_origin, aGlassPane->s.origin);
// firing thru - the pane has no direct attacker to hurt,
// but G_UseTargets expects one. So make it a DIY
G_UseTargets (aGlassPane, aGlassPane);
}
void
CGF_SFX_HideBreakableGlass (edict_t * aGlassPane)
{
// remove all attached decals
edict_t *decal;
decal = 0;
while ((decal = G_Find (decal, FOFS (classname), "decal")) != NULL)
{
if (decal->owner == aGlassPane)
{
// make it goaway in the next frame
decal->think = G_FreeEdict;
decal->nextthink = level.framenum + 1;
}
}
while ((decal = G_Find (decal, FOFS (classname), "splat")) != NULL)
{
if (decal->owner == aGlassPane)
{
// make it goaway in the next frame
decal->think = G_FreeEdict;
decal->nextthink = level.framenum + 1;
}
}
// after being broken, the pane cannot be freed as it is needed in
// subsequent missions/games, so hide it at about z = -1000 lower
aGlassPane->movetype = MOVETYPE_FLYMISSILE;
VectorCopy (aGlassPane->s.origin, aGlassPane->pos1);
aGlassPane->s.origin[2] -= 1000.0;
}
void
CGF_SFX_AttachDecalToGlass (edict_t * aGlassPane, edict_t * aDecal)
{
// just set aDecal's owner to be the glass pane
aDecal->owner = aGlassPane;
}
void
CGF_SFX_RebuildAllBrokenGlass ()
{
// iterate over all func_explosives
edict_t *glass;
glass = 0;
while ((glass = G_Find (glass, FOFS (classname), "func_explosive")) != NULL)
{
// glass is broken if solid != SOLID_BSP
if (glass->solid != SOLID_BSP)
{
CGF_SFX_InstallBreakableGlass (glass);
}
}
}
void
CGF_SFX_ApplyGlassFragmentLimit (const char *aClassName)
{
edict_t *oldfragment;
glassfragmentcount++;
if (glassfragmentcount > glassfragmentlimit->value)
glassfragmentcount = 1;
// remove fragment with corresponding number if any
oldfragment = FindEdictByClassnum ((char *) aClassName, glassfragmentcount);
if (oldfragment)
{
// oldfragment->nextthink = level.framenum + 1;
G_FreeEdict (oldfragment);
}
}
void
CGF_SFX_MiscGlassUse (edict_t * self, edict_t * other, edict_t * activator)
{
#ifdef _DEBUG
const char *classname;
classname = other->classname;
#endif
}
void
CGF_SFX_MiscGlassDie (edict_t * self, edict_t * inflictor, edict_t * attacker,
int damage, vec3_t point)
{
#ifdef _DEBUG
const char *classname;
classname = inflictor->classname;
#endif
}
static vec_t previous_throw_time = 0;
static int this_throw_count = 0;
void
CGF_SFX_GlassThrowDebris (edict_t * self, char *modelname, float speed,
vec3_t origin)
{
// based on ThrowDebris from id software - now returns debris created
edict_t *chunk;
vec3_t v;
if (level.time != previous_throw_time)
{
previous_throw_time = level.time;
this_throw_count = 0;
}
else
{
this_throw_count++;
if (this_throw_count > glassfragmentlimit->value)
return;
}
chunk = G_Spawn ();
VectorCopy (origin, chunk->s.origin);
gi.setmodel (chunk, modelname);
v[0] = 100 * crandom ();
v[1] = 100 * crandom ();
v[2] = 100 + 100 * crandom ();
VectorMA (self->velocity, speed, v, chunk->velocity);
chunk->movetype = MOVETYPE_BOUNCE;
chunk->solid = SOLID_NOT;
chunk->avelocity[0] = random () * 600;
chunk->avelocity[1] = random () * 600;
chunk->avelocity[2] = random () * 600;
chunk->think = G_FreeEdict;
chunk->nextthink = level.framenum + (5 + random() * 5) * HZ;
chunk->s.frame = 0;
chunk->flags = 0;
chunk->classname = "debris";
chunk->takedamage = DAMAGE_YES;
chunk->die = debris_die;
gi.linkentity (chunk);
// number chunk
chunk->classnum = glassfragmentcount;
}

View file

@ -0,0 +1,83 @@
/****************************************************************************/
/* */
/* project : CGF (c) 1999 William van der Sterren */
/* parts (c) 1998 id software */
/* */
/* file : cgf_sfx_glass.h "special effects for glass entities" */
/* author(s): William van der Sterren */
/* version : 0.5 */
/* */
/* date (last revision): Jun 12, 99 */
/* date (creation) : Jun 04, 99 */
/* */
/* */
/* revision history */
/* -- date ---- | -- revision ---------------------- | -- revisor -- */
/* Jun 12, 1999 | fixed knife slash breaks glass | William */
/* */
/******* http://www.botepidemic.com/aid/cgf for CGF for Action Quake2 *******/
//
// $Id: cgf_sfx_glass.h,v 1.2 2001/09/28 13:48:34 ra Exp $
//
//-----------------------------------------------------------------------------
// $Log: cgf_sfx_glass.h,v $
// Revision 1.2 2001/09/28 13:48:34 ra
// I ran indent over the sources. All .c and .h files reindented.
//
// Revision 1.1.1.1 2001/05/06 17:29:34 igor_rock
// This is the PG Bund Edition V1.25 with all stuff laying around here...
//
//-----------------------------------------------------------------------------
#ifndef __CGF_SFX_GLASS_H_
#define __CGF_SFX_GLASS_H_
// defines (should be consistent with other weapon defs in g_local.h)
#define MOD_BREAKINGGLASS 46
/*
// forward definitions
typedef struct edict_s edict_t;
*/
// export a number of functions to g_func.c:
//
//
void CGF_SFX_InstallGlassSupport ();
// registers cvar breakableglass (default 0)
// registers cvar glassfragmentlimit (default 30)
void CGF_SFX_RebuildAllBrokenGlass ();
// upon starting a new team play game, reconstruct any
// broken glass because we like to break it again
int CGF_SFX_IsBreakableGlassEnabled ();
// returns whether breakable glass is enabled (cvar breakableglass)
void CGF_SFX_TestBreakableGlassAndRemoveIfNot_Think (edict_t *
aPossibleGlassEntity);
// initial think function for all func_explosives
// because we cannot verify the contents of the entity unless the entity
// has been spawned, we need to first introduce the entity, and remove
// it later (level.time == 0.1) if required
void CGF_SFX_ShootBreakableGlass (edict_t * aGlassPane, edict_t * anAttacker,
/*trace_t */ void *tr,
int mod);
// shoot thru glass - depending on bullet types and
// random effects, glass just emits fragments, or breaks
void CGF_SFX_AttachDecalToGlass (edict_t * aGlassPane, edict_t * aDecal);
// a glass that breaks will remove all decals (blood splashes, bullet
// holes) if they are attached
#endif

View file

@ -10,12 +10,7 @@
#include "action/a_team.h"
#include "action/a_game.h"
#include "action/cgf_sfx_glass.h"
#include "action/a_match.h"
#include "action/a_radio.h"
#include "action/a_ini.h"
#include "action/a_stats.h"
#include "action/a_balancer.h"
// the "gameversion" client command will print this plus compile date
constexpr const char *GAMEVERSION = "action";
@ -1678,15 +1673,23 @@ extern cvar_t *medkit_max;
extern cvar_t *medkit_value;
extern cvar_t *stats_endmap; // If on (1), show the accuracy/etc stats at the end of a map
extern cvar_t *stats_afterround; // TNG Stats, collect stats between rounds
extern cvar_t *printrules;
extern cvar_t *auto_join; // Automaticly join clients to teams they were on in last map.
extern cvar_t *auto_equip; // Remember weapons and items for players between maps.
extern cvar_t *auto_menu; // Automatically show the join menu
extern cvar_t *use_newscore; // Use the new scoreboard
extern cvar_t *noscore; // Don't show the scoreboard
extern cvar_t *scoreboard; // Scoreboard style
extern cvar_t *ir;
extern cvar_t *knifelimit;
extern cvar_t *tgren;
extern cvar_t *dm_choose;
extern cvar_t *dm_shield;
extern cvar_t *uvtime;
extern cvar_t *warmup;
extern cvar_t *rrot;
extern cvar_t *vrot;
extern mod_id_t meansOfDeath;
@ -1694,12 +1697,16 @@ extern mod_id_t meansOfDeath;
extern int locOfDeath;
// stop an armor piercing round that hits a vest
extern int stopAP;
extern cvar_t *eventeams;
extern cvar_t *use_balancer;
bool CheckForUnevenTeams (edict_t *);
bool IsAllowedToJoin(edict_t *, int);
void TransparentListSet (solid_t solid_type);
char *GetPossesiveAdjectiveSingular(edict_t *ent);
char *GetPossesiveAdjective(edict_t *ent);
char *GetReflexivePronoun(edict_t *ent);
const char *GetPossesiveAdjectiveSingular(edict_t *ent);
const char *GetPossesiveAdjective(edict_t *ent);
const char *GetReflexivePronoun(edict_t *ent);
// Action Add end
@ -1933,9 +1940,13 @@ extern gitem_t itemlist[IT_TOTAL];
//======================================================================
// Action Add
//======================================================================
#define PARSE_BUFSIZE 256
#define IS_ALIVE(ent) ((ent)->solid != SOLID_NOT && (ent)->deadflag)
#define WEAPON_COUNT 9
#define ITEM_COUNT 6
#define AMMO_COUNT 5
#define GS_DEATHMATCH 1
#define GS_TEAMPLAY 2
#define GS_MATCHMODE 4
@ -1996,6 +2007,7 @@ extern cvar_t *weapon_respawn;
extern cvar_t *ammo_respawn;
extern cvar_t *hc_single;
extern cvar_t *use_punch;
extern cvar_t *radiolog;
extern cvar_t *radio_max;
extern cvar_t *radio_time;
extern cvar_t *radio_ban;
@ -2764,6 +2776,10 @@ void fire_doppleganger(edict_t *ent, const vec3_t &start, const vec3_t &aimdir)
void RemoveAttackingPainDaemons(edict_t *self);
bool G_ShouldPlayersCollide(bool weaponry);
bool P_UseCoopInstancedItems();
void Add_Frag(edict_t * ent, int mod);
void Subtract_Frag(edict_t * ent);
void Add_Death( edict_t *ent, bool end_streak );
void PrintDeathMessage(char *msg, edict_t * gibee);
// ACTION
void CL_FixUpGender(edict_t *ent, const char *userinfo);
void ClientFixLegs(edict_t *ent);
@ -3000,6 +3016,7 @@ struct client_respawn_t
int32_t totalidletime;
edict_t *kickvote;
int32_t *menu_shown;
char *mapvote; // pointer to map voted on (if any)
char *cvote; // pointer to config voted on (if any)
bool scramblevote; // want scramble
@ -3281,6 +3298,7 @@ struct gclient_t
edict_t *chase_target;
int32_t chase_mode;
bool team_force; // are we forcing a team change
// ammo capacities
int32_t ammo_index;
int32_t max_pistolmags;

View file

@ -172,6 +172,13 @@ cvar_t *use_killcounts;
cvar_t *use_rewards;
cvar_t *warmup;
cvar_t *round_begin;
cvar_t *eventeams;
cvar_t *use_balancer;
cvar_t *ir;
cvar_t *knifelimit;
cvar_t *tgren;
cvar_t *rrot;
cvar_t *vrot;
// UI / Menu / Messaging Settings
cvar_t *motd_time;
@ -181,7 +188,10 @@ cvar_t *stats_afterround; // TNG Stats, collect stats between rounds
cvar_t *auto_join; // Automaticly join clients to teams they were on in last map.
cvar_t *auto_equip; // Remember weapons and items for players between maps.
cvar_t *auto_menu; // Automatically show the join menu
cvar_t *printrules; // Print the rules at the start of the game
cvar_t *use_newscore; // Use the new scoreboard
cvar_t *noscore; // Don't show the scoreboard
cvar_t *scoreboard;
// Weapon and Item Settings
cvar_t *allitem;

View file

@ -270,4 +270,32 @@ int BoxOnPlaneSide (const vec3_t emins, const vec3_t emaxs, const struct cplane_
float anglemod (float a);
//float LerpAngle (float a1, float a2, float frac);
#define Q_isupper( c ) ( (c) >= 'A' && (c) <= 'Z' )
#define Q_islower( c ) ( (c) >= 'a' && (c) <= 'z' )
#define Q_isdigit( c ) ( (c) >= '0' && (c) <= '9' )
#define Q_isalpha( c ) ( Q_isupper( c ) || Q_islower( c ) )
#define Q_isalnum( c ) ( Q_isalpha( c ) || Q_isdigit( c ) )
int Q_tolower( int c );
int Q_toupper( int c );
char *Q_strlwr( char *s );
char *Q_strupr( char *s );
int Q_tolower( int c ) {
if (Q_isupper( c )) {
c += ('a' - 'A');
}
return c;
}
int Q_toupper( int c ) {
if (Q_islower( c )) {
c -= ('a' - 'A');
}
return c;
}
// EOF