mirror of
https://git.code.sf.net/p/quake/game-source
synced 2024-11-25 13:21:30 +00:00
1499 lines
36 KiB
C++
1499 lines
36 KiB
C++
/*
|
||
teamplay.qc
|
||
|
||
This program is free software; you can redistribute it and/or
|
||
modify it under the terms of the GNU General Public License
|
||
as published by the Free Software Foundation; either version 2
|
||
of the License, or (at your option) any later version.
|
||
|
||
This program is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||
|
||
See the GNU General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with this program; if not, write to:
|
||
|
||
Free Software Foundation, Inc.
|
||
59 Temple Place - Suite 330
|
||
Boston, MA 02111-1307, USA
|
||
|
||
$Id$
|
||
*/
|
||
|
||
/****************************************************************************
|
||
* ThreeWave Capture The Flag
|
||
****************************************************************************
|
||
* Based on John Spikles Complete Enhanced Teamplay
|
||
****************************************************************************
|
||
* Version 4.0 rewrite Mar 21, 1997
|
||
****************************************************************************/
|
||
|
||
/** Defs **/
|
||
|
||
/** MODIFIABLE CONSTANTS **/
|
||
|
||
float TEAM_DEFAULT_PENALTY = 1; // Default frag penalty
|
||
float TEAM_STRICT_COOP = 0; // Strict Coop
|
||
|
||
// Team colors
|
||
float TEAM_COLOR1 = 4;
|
||
float TEAM_COLOR2 = 13;
|
||
|
||
/** End of MODIFIABLE CONSTANTS **/
|
||
|
||
// Globals
|
||
|
||
entity team1_lastspawn;
|
||
entity team2_lastspawn;
|
||
float nextteamupdtime; // time until next team update
|
||
float last_flag_capture; // time of last capture
|
||
float last_capture_team; // last team that captured
|
||
|
||
float teamscr1; // team 1's teamscr score
|
||
float teamscr2; // team 2's teamscr score
|
||
float lastteamscrtime; // last time we calculated it
|
||
float TEAMSCRTIME = 1;
|
||
|
||
// Teamplay bitfield entries
|
||
float TEAM_HEALTH_PROTECT = 1; // No health damage from friendly fire
|
||
float TEAM_ARMOR_PROTECT = 2; // No armor damage from friendly fire
|
||
float TEAM_ATTACKER_DAMAGE = 4; // Attacker takes damage from hitting teammates
|
||
float TEAM_FRAG_PENALTY = 8; // One frag penalty for killing teammate
|
||
float TEAM_DEATH_PENALTY = 16; // Die when you kill a teammate.
|
||
float TEAM_LOCK_COLORS = 32; // Allow only team colors
|
||
float TEAM_STATIC_TEAMS = 64; // Don't allow players to switch teams
|
||
float TEAM_DROP_ITEMS = 128; // Allow players to drop packs and
|
||
float TEAM_CAPTURE_FLAG = 256; // Play capture the flag
|
||
float TEAM_DISABLE_GRAPPLE = 2048; // team selection
|
||
|
||
float TEAM_CAPTURE_CAPTURE_BONUS = 15; // what you get for capture
|
||
float TEAM_CAPTURE_TEAM_BONUS = 10; // what your team gets for capture
|
||
float TEAM_CAPTURE_RECOVERY_BONUS = 1; // what you get for recovery
|
||
float TEAM_CAPTURE_FLAG_BONUS = 0; // what you get for picking up enemy flag
|
||
float TEAM_CAPTURE_FRAG_CARRIER_BONUS = 2; // what you get for fragging
|
||
//enemy flag carrier
|
||
float TEAM_CAPTURE_FLAG_RETURN_TIME = 40; // seconds until auto return
|
||
|
||
// XXX EXPERT CTF Additional scoring system
|
||
|
||
// bonuses
|
||
float TEAM_CAPTURE_CARRIER_DANGER_PROTECT_BONUS = 2; // bonus for fraggin someone
|
||
// who has recently hurt your flag carrier
|
||
float TEAM_CAPTURE_CARRIER_PROTECT_BONUS = 1; // bonus for fraggin someone while
|
||
// either you or your target are near your flag carrier
|
||
float TEAM_CAPTURE_FLAG_DEFENSE_BONUS = 1; // bonus for fraggin someone while
|
||
// either you or your target are near your flag
|
||
float TEAM_CAPTURE_RETURN_FLAG_ASSIST_BONUS = 1; // awarded for returning a flag that causes a
|
||
// capture to happen almost immediately
|
||
float TEAM_CAPTURE_FRAG_CARRIER_ASSIST_BONUS = 2; // award for fragging a flag carrier if a
|
||
// capture happens almost immediately
|
||
|
||
// radii
|
||
float TEAM_CAPTURE_TARGET_PROTECT_RADIUS = 400; // the radius around an object being
|
||
// defended where a target will be worth extra frags
|
||
float TEAM_CAPTURE_ATTACKER_PROTECT_RADIUS = 400; // the radius around an object being
|
||
// defended where an attacker will get extra frags when making kills
|
||
|
||
// timeouts
|
||
float TEAM_CAPTURE_CARRIER_DANGER_PROTECT_TIMEOUT = 4;
|
||
float TEAM_CAPTURE_CARRIER_FLAG_SINCE_TIMEOUT = 2;
|
||
float TEAM_CAPTURE_FRAG_CARRIER_ASSIST_TIMEOUT = 6;
|
||
float TEAM_CAPTURE_RETURN_FLAG_ASSIST_TIMEOUT = 4;
|
||
|
||
float TEAM_CAPTURE_UPDATE_TIME = 120;
|
||
|
||
// END EXPERT CTF
|
||
|
||
// flag status used in cnt field of flag
|
||
float FLAG_AT_BASE = 0;
|
||
float FLAG_CARRIED = 1;
|
||
float FLAG_DROPPED = 2;
|
||
|
||
// Prototypes
|
||
float() W_BestWeapon;
|
||
void() W_SetCurrentAmmo;
|
||
void() bound_other_ammo;
|
||
void(float o, float n) Deathmatch_Weapon;
|
||
void() BackpackTouch;
|
||
void(entity who, string s) TeamPlayerUpdate;
|
||
void() TeamCaptureResetUpdate;
|
||
|
||
// Return a name for the color of a team
|
||
string (float Team)
|
||
GetTeamColor =
|
||
{
|
||
switch (Team) {
|
||
case 0:
|
||
return "Grey";
|
||
case 1:
|
||
return "Brown";
|
||
case 2:
|
||
return "Steel blue";
|
||
case 3:
|
||
return "Green";
|
||
case 4:
|
||
return "Red";
|
||
case 5:
|
||
return "Olive";
|
||
case 6:
|
||
return "Orange";
|
||
case 7:
|
||
return "Peach";
|
||
case 8:
|
||
return "Purple";
|
||
case 9:
|
||
return "Majenta";
|
||
case 10:
|
||
return "Tan";
|
||
case 11:
|
||
return "Aqua";
|
||
case 12:
|
||
return "Yellow";
|
||
case 13:
|
||
return "Blue";
|
||
case 14:
|
||
return "Bright Orange";
|
||
case 15:
|
||
return "Bright Red";
|
||
default:
|
||
return "Unknown";
|
||
}
|
||
};
|
||
|
||
// Return a name for the color of a team
|
||
string (float Team)
|
||
GetShortTeamColor =
|
||
{
|
||
Team = Team & 15; // color loops
|
||
|
||
switch (Team) {
|
||
case 0:
|
||
return "grey";
|
||
case 1:
|
||
return "brwn";
|
||
case 2:
|
||
return "sblu";
|
||
case 3:
|
||
return "grn";
|
||
case 4:
|
||
return "red";
|
||
case 5:
|
||
return "olve";
|
||
case 6:
|
||
return "orng";
|
||
case 7:
|
||
return "pch";
|
||
case 8:
|
||
return "purp";
|
||
case 9:
|
||
return "majn";
|
||
case 10:
|
||
return "tan";
|
||
case 11:
|
||
return "aqua";
|
||
case 12:
|
||
return "ylw";
|
||
case 13:
|
||
return "blue";
|
||
case 14:
|
||
return "bojn";
|
||
case 15:
|
||
return "bred";
|
||
default:
|
||
return "Unknown";
|
||
}
|
||
};
|
||
|
||
// *XXX* EXPERT CTF
|
||
// Just a quickie to return the ASCII-ized team names for CTF
|
||
string (float Team)
|
||
GetCTFTeam =
|
||
{
|
||
switch (Team) {
|
||
case TEAM_COLOR1:
|
||
return "<EFBFBD><EFBFBD><EFBFBD>";
|
||
case TEAM_COLOR2:
|
||
return "<EFBFBD><EFBFBD><EFBFBD><EFBFBD>";
|
||
default:
|
||
return "";
|
||
}
|
||
};
|
||
|
||
/*
|
||
================
|
||
TeamPrintSettings
|
||
|
||
Print out current teamplay options
|
||
================
|
||
*/
|
||
void ()
|
||
TeamPrintSettings =
|
||
{
|
||
local string s;
|
||
|
||
sprint (self, PRINT_HIGH, "The following Teamplay options are set:\n");
|
||
|
||
if (teamplay < 0) {
|
||
sprint (self, PRINT_HIGH, "Frag penalty manually set to ");
|
||
s = ftos (teamplay);
|
||
sprint (self, PRINT_HIGH, s);
|
||
sprint (self, PRINT_HIGH, "\n");
|
||
return;
|
||
}
|
||
|
||
if (!teamplay) {
|
||
sprint (self, PRINT_HIGH, "None\n");
|
||
return;
|
||
}
|
||
|
||
if (1 == teamplay) {
|
||
sprint (self, PRINT_HIGH, "ID's original teamplay 1\n");
|
||
return;
|
||
}
|
||
|
||
if (teamplay & TEAM_HEALTH_PROTECT)
|
||
sprint (self, PRINT_HIGH, "Health-Protect ");
|
||
|
||
if (teamplay & TEAM_ARMOR_PROTECT)
|
||
sprint (self, PRINT_HIGH, "Armor-Protect ");
|
||
|
||
if (teamplay & TEAM_ATTACKER_DAMAGE)
|
||
sprint (self, PRINT_HIGH, "Mirror-Damage ");
|
||
|
||
if (teamplay & TEAM_FRAG_PENALTY)
|
||
sprint (self, PRINT_HIGH, "Frag-Penalty ");
|
||
|
||
if (teamplay & TEAM_DEATH_PENALTY)
|
||
sprint (self, PRINT_HIGH, "Death-Penalty ");
|
||
|
||
if (teamplay & TEAM_LOCK_COLORS)
|
||
sprint (self, PRINT_HIGH, "Lock-Colors ");
|
||
|
||
if (teamplay & TEAM_STATIC_TEAMS)
|
||
sprint (self, PRINT_HIGH, "Static-Teams ");
|
||
|
||
if (teamplay & TEAM_DROP_ITEMS)
|
||
sprint (self, PRINT_HIGH, "Drop-Items (Backpack Impulse 20, Weapon "
|
||
"Impulse 21) ");
|
||
|
||
if (teamplay & TEAM_CAPTURE_FLAG)
|
||
sprint (self, PRINT_HIGH, "Capture-The-Flag ");
|
||
|
||
sprint(self, PRINT_HIGH, "\n");
|
||
};
|
||
|
||
/*
|
||
================
|
||
TeamArmorDam
|
||
|
||
Return TRUE if the target's armor can take damage from this attacker.
|
||
================
|
||
*/
|
||
float (entity targ, entity inflictor, entity attacker, float damage)
|
||
TeamArmorDam =
|
||
{
|
||
if (teamplay < 0 || gamestart)
|
||
return TRUE;
|
||
if ((teamplay & TEAM_ARMOR_PROTECT) && attacker.steam == targ.steam
|
||
&& attacker != targ) {
|
||
// Armor is protected
|
||
return FALSE;
|
||
}
|
||
return TRUE;
|
||
};
|
||
|
||
/*
|
||
================
|
||
TeamHealthDam
|
||
|
||
Return TRUE if the target can take health damage from this attacker.
|
||
================
|
||
*/
|
||
float (entity targ, entity inflictor, entity attacker, float damage)
|
||
TeamHealthDam =
|
||
{
|
||
if (teamplay < 0 || gamestart)
|
||
return TRUE;
|
||
|
||
if (attacker.steam == targ.steam && attacker != targ) {
|
||
// Attacker and target are on the same team.
|
||
if (teamplay & TEAM_ATTACKER_DAMAGE) {
|
||
// Damage applied to teammate.
|
||
T_Damage (attacker, inflictor, attacker, damage);
|
||
}
|
||
if (teamplay & TEAM_HEALTH_PROTECT) {
|
||
// Health is protected
|
||
return FALSE;
|
||
}
|
||
}
|
||
return TRUE;
|
||
};
|
||
|
||
/*
|
||
================
|
||
TeamPFrags
|
||
|
||
Return the number of frags we should penalize attacker for killing targ.
|
||
================
|
||
*/
|
||
float (entity targ, entity attacker)
|
||
TeamPFrags =
|
||
{
|
||
if (teamplay < 0)
|
||
return (-teamplay);
|
||
|
||
if (targ != attacker && targ.steam == attacker.steam) {
|
||
// targ and attacker are on the same team
|
||
if (teamplay < 0) {
|
||
// teamplay indicates frag penalty
|
||
return (-teamplay);
|
||
}
|
||
if (teamplay & TEAM_FRAG_PENALTY) {
|
||
// default penalty
|
||
return TEAM_DEFAULT_PENALTY;
|
||
}
|
||
}
|
||
// No frag penalty
|
||
return 0;
|
||
};
|
||
|
||
/*
|
||
================
|
||
TeamFragPenalty
|
||
|
||
If attacker should be penalized for killing targ, penalize attacker
|
||
and return TRUE.
|
||
================
|
||
*/
|
||
float (entity targ, entity attacker)
|
||
TeamFragPenalty =
|
||
{
|
||
local float f;
|
||
|
||
f = TeamPFrags(targ, attacker);
|
||
|
||
if (f) {
|
||
// We should penalize some frags.
|
||
attacker.frags = attacker.frags - f;
|
||
return TRUE;
|
||
}
|
||
// No penalty
|
||
return FALSE;
|
||
};
|
||
|
||
/*
|
||
=================
|
||
TeamDeathPenalty
|
||
|
||
If attacker should be killed for killing targ, kill attacker and
|
||
add a frag to offset the one attacker will lose for killing himself.
|
||
*/
|
||
void (entity targ, entity attacker)
|
||
TeamDeathPenalty =
|
||
{
|
||
//Don't kill anyone if teamplay is negative.
|
||
if (teamplay < 0)
|
||
return;
|
||
|
||
if ((teamplay & TEAM_DEATH_PENALTY)
|
||
&& attacker != targ && attacker.steam == targ.steam) {
|
||
//We should kill the attacker.
|
||
T_Damage (attacker, attacker, attacker, 1000);
|
||
//Add a frag to offset the self-kill penalty.
|
||
attacker.frags++;
|
||
}
|
||
};
|
||
|
||
/*
|
||
==================
|
||
TeamColorIsLegal
|
||
|
||
Return TRUE if the indicated color is legal
|
||
==================
|
||
*/
|
||
float (float color)
|
||
TeamColorIsLegal =
|
||
{
|
||
// All colors are legal if teamplay is negative.
|
||
if (teamplay < 0)
|
||
return TRUE;
|
||
// All colors are legal if TEAM_LOCK_COLORS is off.
|
||
if (!(teamplay & TEAM_LOCK_COLORS))
|
||
return TRUE;
|
||
if (color == TEAM_COLOR1)
|
||
return TRUE;
|
||
if (color == TEAM_COLOR2)
|
||
return TRUE;
|
||
return FALSE;
|
||
};
|
||
|
||
void ()
|
||
TeamSkinSet =
|
||
{
|
||
switch (self.ctfskinno) {
|
||
case 1:
|
||
self.ctfskin = "ctfr1";
|
||
return;
|
||
case 2:
|
||
self.ctfskin = "ctfr2";
|
||
return;
|
||
case 3:
|
||
self.ctfskin = "ctfb1";
|
||
return;
|
||
case 4:
|
||
self.ctfskin = "ctfb2";
|
||
return;
|
||
default:
|
||
switch (self.steam) {
|
||
case TEAM_COLOR1:
|
||
self.ctfskin = "ctfr1";
|
||
return;
|
||
case TEAM_COLOR2:
|
||
self.ctfskin = "ctfb1";
|
||
return;
|
||
default:
|
||
self.ctfskin = "base";
|
||
return;
|
||
}
|
||
}
|
||
};
|
||
|
||
void ()
|
||
TeamSkinAssign =
|
||
{
|
||
if (!(teamplay & TEAM_CAPTURE_FLAG))
|
||
return;
|
||
|
||
if (self.steam == TEAM_COLOR1)
|
||
self.ctfskinno = 1;
|
||
else
|
||
self.ctfskinno = 3;
|
||
if (random () < 0.5)
|
||
self.ctfskinno++;
|
||
self.player_flag &= ~65280;
|
||
self.player_flag |= self.ctfskinno * 256;
|
||
|
||
TeamSkinSet ();
|
||
};
|
||
|
||
float (float tcolor, float bcolor)
|
||
CrossDressCheck =
|
||
{
|
||
// All colors are legal if teamplay is negative.
|
||
if (teamplay < 0)
|
||
return FALSE;
|
||
// All colors are legal is TEAM_LOCK_COLORS is off.
|
||
if (!(teamplay & TEAM_LOCK_COLORS))
|
||
return FALSE;
|
||
if (tcolor == TEAM_COLOR1 && bcolor == TEAM_COLOR2)
|
||
return TRUE;
|
||
if (tcolor == TEAM_COLOR2 && bcolor == TEAM_COLOR1)
|
||
return TRUE;
|
||
return FALSE;
|
||
};
|
||
|
||
/*
|
||
==================
|
||
TeamAssign
|
||
|
||
Check if the team self is on is legal, and put self in a legal team if not.
|
||
==================
|
||
*/
|
||
void ()
|
||
TeamAssign =
|
||
{
|
||
local entity p;
|
||
local float TEAM1, TEAM2, newcolor;
|
||
local string n;
|
||
|
||
if (self.steam >= 0) {
|
||
if (TeamColorIsLegal (self.steam)) {
|
||
TeamSkinAssign ();
|
||
return;
|
||
}
|
||
}
|
||
|
||
// Assign the player to a team.
|
||
|
||
// Sum the players on all the teams.
|
||
TEAM1 = 0;
|
||
TEAM2 = 0;
|
||
|
||
p = find (world, classname, "player");
|
||
|
||
while (p) {
|
||
if (p != self && !(p.player_flag & PF_GHOST)) {
|
||
if (p.steam == TEAM_COLOR1)
|
||
TEAM1 = TEAM1 + 1;
|
||
else if (p.steam == TEAM_COLOR2)
|
||
TEAM2 = TEAM2 + 1;
|
||
}
|
||
p = find (p, classname, "player");
|
||
}
|
||
|
||
// Find the team with the least players.
|
||
if (TEAM1 < TEAM2 || ((TEAM1 == TEAM2) && (random () < 0.5)))
|
||
newcolor = TEAM_COLOR1;
|
||
else
|
||
newcolor = TEAM_COLOR2;
|
||
|
||
// Put the player on a the new team.
|
||
sprint (self, PRINT_HIGH, "You have been assigned to ");
|
||
n = GetTeamColor (newcolor);
|
||
sprint (self, PRINT_HIGH, n);
|
||
sprint (self, PRINT_HIGH, " team.\n");
|
||
|
||
self.steam = newcolor;
|
||
TeamSkinAssign ();
|
||
|
||
self.player_flag |= TEAM_STUFF_COLOR;
|
||
};
|
||
|
||
/*
|
||
===============
|
||
TeamCheckLock
|
||
|
||
Check for team changing and perform whatever actions are neccessary.
|
||
===============
|
||
*/
|
||
void ()
|
||
TeamCheckLock =
|
||
{
|
||
local float pteam, n;
|
||
local string s;
|
||
|
||
// Don't do anything if teamplay is negative
|
||
if (teamplay < 0)
|
||
return;
|
||
|
||
if (gamestart) {
|
||
s = infokey (self, "bottomcolor");
|
||
pteam = stof (s);
|
||
if (pteam != 0)
|
||
stuffcmd (self, "setinfo bottomcolor 0\n");
|
||
self.steam = -1;
|
||
return;
|
||
}
|
||
|
||
if (self.player_flag & TEAM_STUFF_COLOR) {
|
||
self.player_flag = self.player_flag - TEAM_STUFF_COLOR;
|
||
s = ftos (self.steam);
|
||
stuffcmd (self, "setinfo bottomcolor ");
|
||
stuffcmd (self, s);
|
||
stuffcmd (self, "\n");
|
||
|
||
s = infokey (self, "topcolor");
|
||
n = stof (s);
|
||
if (CrossDressCheck(n, self.steam)) {
|
||
s = ftos (self.steam);
|
||
stuffcmd (self, "setinfo topcolor ");
|
||
stuffcmd (self, s);
|
||
stuffcmd (self, "\n");
|
||
}
|
||
|
||
s = GetShortTeamColor (self.steam);
|
||
stuffcmd (self, "setinfo team ");
|
||
stuffcmd (self, s);
|
||
stuffcmd (self, "\n");
|
||
|
||
if (teamplay & TEAM_CAPTURE_FLAG) {
|
||
stuffcmd (self, "setinfo skin ");
|
||
stuffcmd (self, self.ctfskin);
|
||
stuffcmd (self, "\n");
|
||
}
|
||
|
||
self.lastteamset = time;
|
||
return;
|
||
}
|
||
|
||
s = infokey (self, "bottomcolor");
|
||
pteam = stof (s);
|
||
|
||
// Check to see if the player has changed skins
|
||
if (self.steam > 0 && time - self.lastteamset > 2) {
|
||
if (teamplay & TEAM_CAPTURE_FLAG)
|
||
if (infokey (self, "skin") != self.ctfskin) {
|
||
stuffcmd (self, "setinfo skin ");
|
||
stuffcmd (self, self.ctfskin);
|
||
stuffcmd (self, "\n");
|
||
self.lastteamset = time;
|
||
}
|
||
|
||
s = GetShortTeamColor (self.steam);
|
||
if (infokey (self, "team") != s) {
|
||
stuffcmd (self, "setinfo team ");
|
||
stuffcmd (self, s);
|
||
stuffcmd (self, "\n");
|
||
self.lastteamset = time;
|
||
}
|
||
}
|
||
|
||
// check for crossdressing
|
||
s = infokey (self, "topcolor");
|
||
n = stof (s);
|
||
if (CrossDressCheck (n, self.steam)) {
|
||
s = ftos (self.steam);
|
||
stuffcmd (self, "setinfo topcolor ");
|
||
stuffcmd (self, s);
|
||
stuffcmd (self, "\n");
|
||
return;
|
||
}
|
||
|
||
// Check to see if the player has changed colors
|
||
if (time - self.lastteamset > 2 && pteam != self.steam) {
|
||
// Player has changed colors
|
||
|
||
// If teams are static and we've been on some team already,
|
||
// put us back on the team we were on.
|
||
if ((teamplay & TEAM_STATIC_TEAMS) && (self.steam >= 0)) {
|
||
if (TeamColorIsLegal (self.steam)) {
|
||
// changing teams sucks, kill him
|
||
|
||
// if he has tried to change teams several times,
|
||
// kick him off the server.
|
||
if (self.suicide_count > 3) {
|
||
sprint(self, PRINT_HIGH, "You were told you can't change "
|
||
"teams.\nGo play color games somewhere else.\n");
|
||
stuffcmd (self, "disconnect\n");
|
||
bprint (PRINT_MEDIUM, self.netname);
|
||
bprint (PRINT_MEDIUM, " has bad color sense\n");
|
||
}
|
||
// case base respawn
|
||
self.killed = 99;
|
||
T_Damage (self, self, self, 1000); // Kill the player
|
||
self.killed = 2;
|
||
// trying to change teams counts as a suicide
|
||
self.suicide_count++;
|
||
logfrag (self, self); // he pays for it
|
||
|
||
sprint (self, PRINT_HIGH, "You cannot change teams.\n");
|
||
stuffcmd (self, "setinfo bottomcolor ");
|
||
s = ftos (self.steam);
|
||
stuffcmd (self, s);
|
||
stuffcmd (self, "\n");
|
||
self.lastteamset = time;
|
||
return;
|
||
} else {
|
||
// If we're on an illegal team, force a change.
|
||
self.steam = -1;
|
||
}
|
||
} else
|
||
// If teamlock is turned off, don't do anything more.
|
||
if (!(teamplay & TEAM_LOCK_COLORS))
|
||
return;
|
||
|
||
if (self.steam >= 0) {
|
||
// case base respawn
|
||
self.killed = 99;
|
||
T_Damage (self, self, self, 1000); // Kill the player
|
||
self.killed = 0;
|
||
}
|
||
|
||
self.steam = pteam;
|
||
self.frags = 0; // Zero out frags
|
||
TeamAssign ();
|
||
}
|
||
};
|
||
|
||
/*
|
||
=======================
|
||
TossBackPack
|
||
|
||
Original idea by Vhold
|
||
Rewritten by John Spickes
|
||
|
||
Toss out a backpack containing some ammo from your current weapon,
|
||
and any weapons you don't have.
|
||
=======================
|
||
*/
|
||
void ()
|
||
TossBackpack =
|
||
{
|
||
local entity item;
|
||
|
||
// If we don't have any ammo, return (except AXE/GRAPPLE)
|
||
if (self.currentammo <= 0) {
|
||
if ((self.weapon != IT_AXE) && (self.weapon != IT_GRAPPLE))
|
||
return;
|
||
}
|
||
|
||
item = spawn ();
|
||
|
||
// See if you have the Shotgun or Super Shotgun on
|
||
if ((self.weapon == IT_SHOTGUN) || (self.weapon == IT_SUPER_SHOTGUN)) {
|
||
if (self.ammo_shells >= 20) {
|
||
item.ammo_shells = 20;
|
||
self.ammo_shells -= 20;
|
||
} else {
|
||
item.ammo_shells = self.ammo_shells;
|
||
self.ammo_shells = 0;
|
||
}
|
||
}
|
||
|
||
// See if you have neither the Shotgun or Super Shotgun
|
||
if (!(self.items & IT_SHOTGUN) && !(self.items & IT_SUPER_SHOTGUN)) {
|
||
if (self.ammo_shells >= 20) {
|
||
item.ammo_shells = 20;
|
||
self.ammo_shells -= 20;
|
||
} else {
|
||
item.ammo_shells = self.ammo_shells;
|
||
self.ammo_shells = 0;
|
||
}
|
||
}
|
||
|
||
// See if we are using a nailgun
|
||
if ((self.weapon == IT_NAILGUN) || (self.weapon == IT_SUPER_NAILGUN)) {
|
||
if (self.ammo_nails >= 20) {
|
||
item.ammo_nails = 20;
|
||
self.ammo_nails -= 20;
|
||
} else {
|
||
item.ammo_nails = self.ammo_nails;
|
||
self.ammo_nails = 0;
|
||
}
|
||
}
|
||
// Check to see if we have neither nailgun
|
||
if (!(self.items & IT_NAILGUN) && !(self.items & IT_SUPER_NAILGUN)) {
|
||
if (self.ammo_nails >= 20) {
|
||
item.ammo_nails = 20;
|
||
self.ammo_nails -= 20;
|
||
} else {
|
||
item.ammo_nails = self.ammo_nails;
|
||
self.ammo_nails = 0;
|
||
}
|
||
}
|
||
|
||
// See if we are using a grenade or rocket launcher
|
||
if ((self.weapon == IT_GRENADE_LAUNCHER)
|
||
|| (self.weapon == IT_ROCKET_LAUNCHER)) {
|
||
if (self.ammo_rockets >= 10) {
|
||
item.ammo_rockets = 10;
|
||
self.ammo_rockets -= 10;
|
||
} else {
|
||
item.ammo_rockets = self.ammo_rockets;
|
||
self.ammo_rockets = 0;
|
||
}
|
||
}
|
||
// See if we have neither the Grenade or rocket launcher
|
||
if (!(self.items & IT_GRENADE_LAUNCHER)
|
||
&& !(self.items & IT_ROCKET_LAUNCHER)) {
|
||
if (self.ammo_rockets >= 10) {
|
||
item.ammo_rockets = 10;
|
||
self.ammo_rockets -= 10;
|
||
} else {
|
||
item.ammo_rockets = self.ammo_rockets;
|
||
self.ammo_rockets = 0;
|
||
}
|
||
}
|
||
|
||
// See if we're using the lightning gun
|
||
if (self.weapon == IT_LIGHTNING) {
|
||
if (self.ammo_cells >= 20) {
|
||
item.ammo_cells = 20;
|
||
self.ammo_cells -= 20;
|
||
} else {
|
||
item.ammo_cells = self.ammo_cells;
|
||
self.ammo_cells = 0;
|
||
}
|
||
}
|
||
// see if we don't have the lightning gun
|
||
if (!(self.items & IT_LIGHTNING)) {
|
||
if (self.ammo_cells >= 20) {
|
||
item.ammo_cells = 20;
|
||
self.ammo_cells -= 20;
|
||
} else {
|
||
item.ammo_cells = self.ammo_cells;
|
||
self.ammo_cells = 0;
|
||
}
|
||
}
|
||
|
||
if (!item.ammo_shells && !item.ammo_nails && !item.ammo_rockets
|
||
&& !item.ammo_cells) {
|
||
// we didn't put anything in
|
||
remove (item);
|
||
return;
|
||
}
|
||
|
||
item.owner = self;
|
||
makevectors (self.v_angle);
|
||
|
||
setorigin (item, self.origin + '0 0 16');
|
||
item.velocity = aim (self, 1000);
|
||
item.velocity *= 500;
|
||
item.flags = FL_ITEM;
|
||
item.solid = SOLID_TRIGGER;
|
||
item.movetype = MOVETYPE_BOUNCE;
|
||
|
||
setmodel (item, "progs/backpack.mdl");
|
||
setsize (item, '-16 -16 0', '16 16 56');
|
||
item.touch = BackpackTouch;
|
||
item.nextthink = time + 120; // remove after 2 minutes
|
||
item.think = SUB_Remove;
|
||
|
||
W_SetCurrentAmmo();
|
||
};
|
||
|
||
void ()
|
||
Team_weapon_touch =
|
||
{
|
||
local entity stemp;
|
||
local float hadammo, best, new = 0, old;
|
||
|
||
if (!(other.flags & FL_CLIENT))
|
||
return;
|
||
// Don't let the owner pick up his own weapon for a second.
|
||
if ((other == self.owner) && ((self.nextthink - time) > 119))
|
||
return;
|
||
|
||
// if the player was using his best weapon, change to the new one if better
|
||
stemp = self;
|
||
self = other;
|
||
best = W_BestWeapon ();
|
||
self = stemp;
|
||
|
||
switch (self.classname) {
|
||
case "weapon_nailgun":
|
||
hadammo = other.ammo_nails;
|
||
new = IT_NAILGUN;
|
||
break;
|
||
case "weapon_supernailgun":
|
||
hadammo = other.ammo_rockets;
|
||
new = IT_SUPER_NAILGUN;
|
||
break;
|
||
case "weapon_supershotgun":
|
||
hadammo = other.ammo_rockets;
|
||
new = IT_SUPER_SHOTGUN;
|
||
break;
|
||
case "weapon_rocketlauncher":
|
||
hadammo = other.ammo_rockets;
|
||
new = IT_ROCKET_LAUNCHER;
|
||
break;
|
||
case "weapon_grenadelauncher":
|
||
hadammo = other.ammo_rockets;
|
||
new = IT_GRENADE_LAUNCHER;
|
||
break;
|
||
case "weapon_lightning":
|
||
hadammo = other.ammo_rockets;
|
||
new = IT_LIGHTNING;
|
||
break;
|
||
default:
|
||
objerror ("Team_weapon_touch: unknown classname");
|
||
break;
|
||
}
|
||
|
||
sprint (other, PRINT_LOW, "You got the ");
|
||
sprint (other, PRINT_LOW, self.netname);
|
||
sprint (other, PRINT_LOW, "\n");
|
||
|
||
// weapon touch sound
|
||
sound (other, CHAN_ITEM, "weapons/pkup.wav", 1, ATTN_NORM);
|
||
stuffcmd (other, "bf\n");
|
||
|
||
bound_other_ammo ();
|
||
|
||
// change to the weapon
|
||
old = other.items;
|
||
other.items |= new;
|
||
|
||
remove (self);
|
||
self = other;
|
||
|
||
if (!deathmatch)
|
||
self.weapon = new;
|
||
else
|
||
Deathmatch_Weapon (old, new);
|
||
|
||
W_SetCurrentAmmo();
|
||
|
||
activator = other;
|
||
SUB_UseTargets (); // fire all targets / killtargets
|
||
};
|
||
|
||
void ()
|
||
TossWeapon =
|
||
{
|
||
local entity item;
|
||
|
||
if (deathmatch != 1)
|
||
return; // only in deathmatch 1
|
||
if ((self.weapon == IT_AXE) || (self.weapon == IT_SHOTGUN)
|
||
|| (self.weapon == IT_GRAPPLE))
|
||
return;
|
||
|
||
item = spawn ();
|
||
item.owner = self;
|
||
makevectors (self.v_angle);
|
||
|
||
setorigin (item, self.origin + '0 0 16');
|
||
item.velocity = aim (self, 1000);
|
||
item.velocity = item.velocity * 500;
|
||
item.flags = FL_ITEM;
|
||
item.solid = SOLID_TRIGGER;
|
||
item.movetype = MOVETYPE_BOUNCE;
|
||
|
||
switch (self.weapon) {
|
||
case IT_SUPER_SHOTGUN:
|
||
setmodel (item, "progs/g_shot.mdl");
|
||
item.weapon = IT_SUPER_SHOTGUN;
|
||
item.netname = "Double-barrelled Shotgun";
|
||
item.classname = "weapon_supershotgun";
|
||
self.items &= ~IT_SUPER_SHOTGUN;
|
||
break;
|
||
case IT_NAILGUN:
|
||
setmodel (item, "progs/g_nail.mdl");
|
||
item.weapon = IT_NAILGUN;
|
||
item.netname = "nailgun";
|
||
item.classname = "weapon_nailgun";
|
||
self.items &= ~IT_NAILGUN;
|
||
break;
|
||
case IT_SUPER_NAILGUN:
|
||
setmodel (item, "progs/g_nail2.mdl");
|
||
item.weapon = IT_SUPER_NAILGUN;
|
||
item.netname = "Super Nailgun";
|
||
item.classname = "weapon_supernailgun";
|
||
self.items &= ~IT_SUPER_NAILGUN;
|
||
break;
|
||
case IT_GRENADE_LAUNCHER:
|
||
setmodel (item, "progs/g_rock.mdl");
|
||
item.weapon = 3;
|
||
item.netname = "Grenade Launcher";
|
||
item.classname = "weapon_grenadelauncher";
|
||
self.items &= ~IT_GRENADE_LAUNCHER;
|
||
break;
|
||
case IT_ROCKET_LAUNCHER:
|
||
setmodel (item, "progs/g_rock2.mdl");
|
||
item.weapon = 3;
|
||
item.netname = "Rocket Launcher";
|
||
item.classname = "weapon_rocketlauncher";
|
||
self.items &= ~IT_ROCKET_LAUNCHER;
|
||
break;
|
||
case IT_LIGHTNING:
|
||
setmodel (item, "progs/g_light.mdl");
|
||
item.weapon = 3;
|
||
item.netname = "Thunderbolt";
|
||
item.classname = "weapon_lightning";
|
||
self.items &= ~IT_LIGHTNING;
|
||
default:
|
||
break;
|
||
}
|
||
|
||
setsize (item, '-16 -16 0', '16 16 56');
|
||
item.touch = Team_weapon_touch;
|
||
item.think = SUB_Remove;
|
||
item.nextthink = time + 120;
|
||
|
||
self.weapon = W_BestWeapon ();
|
||
W_SetCurrentAmmo ();
|
||
};
|
||
|
||
void (entity flg)
|
||
RegenFlag =
|
||
{
|
||
flg.movetype = MOVETYPE_TOSS;
|
||
flg.solid = SOLID_TRIGGER;
|
||
setmodel (flg, flg.mdl);
|
||
setorigin (flg, flg.oldorigin);
|
||
flg.angles = flg.mangle;
|
||
flg.cnt = FLAG_AT_BASE; // it's at home base
|
||
flg.owner = world;
|
||
flg.velocity = '0 0 0';
|
||
|
||
// play respawn sound
|
||
sound (flg, CHAN_VOICE, "items/itembk2.wav", 1, ATTN_NORM);
|
||
};
|
||
|
||
void (entity flg)
|
||
TeamCaptureReturnFlag =
|
||
{
|
||
local entity p;
|
||
|
||
RegenFlag (flg);
|
||
|
||
p = find (world, classname, "player");
|
||
while (p != world) {
|
||
if (p.steam != flg.steam)
|
||
TeamPlayerUpdate (p, "Enemy flag has been returned to base!");
|
||
else if (p.steam == flg.steam)
|
||
TeamPlayerUpdate (p, "Your flag has been returned to base!");
|
||
p = find (p, classname, "player");
|
||
}
|
||
};
|
||
|
||
void ()
|
||
TeamCaptureRegenFlags =
|
||
{
|
||
local entity f;
|
||
|
||
f = find (world, classname, "item_flag_team1");
|
||
if (f != world)
|
||
RegenFlag (f);
|
||
f = find (world, classname, "item_flag_team2");
|
||
if (f != world)
|
||
RegenFlag (f);
|
||
};
|
||
|
||
void (entity flg)
|
||
TeamDropFlag =
|
||
{
|
||
local entity p;
|
||
|
||
p = flg.owner;
|
||
|
||
bprint (PRINT_HIGH, p.netname);
|
||
if (p.steam == TEAM_COLOR1)
|
||
bprint (PRINT_HIGH, " <20><><EFBFBD><EFBFBD> the <20><><EFBFBD><EFBFBD> flag!\n"); // blue
|
||
else
|
||
bprint (PRINT_HIGH, " <20><><EFBFBD><EFBFBD> the <20><><EFBFBD> flag!\n"); // red
|
||
p.effects &= ~(EF_FLAG1 | EF_FLAG2);
|
||
|
||
flg.origin = p.origin - '0 0 24';
|
||
flg.cnt = FLAG_DROPPED;
|
||
flg.velocity = '0 0 300';
|
||
flg.flags = FL_ITEM;
|
||
flg.solid = SOLID_TRIGGER;
|
||
flg.movetype = MOVETYPE_TOSS;
|
||
setmodel (flg, flg.mdl);
|
||
setsize (self, '-16 -16 0', '16 16 74');
|
||
// return it after so long
|
||
flg.super_time = time + TEAM_CAPTURE_FLAG_RETURN_TIME;
|
||
};
|
||
|
||
void (entity player)
|
||
TeamCaptureDropFlagOfPlayer =
|
||
{
|
||
local entity e;
|
||
local string kn;
|
||
|
||
if (!(player.player_flag & ITEM_ENEMY_FLAG))
|
||
return;
|
||
if (player.steam == TEAM_COLOR1)
|
||
kn = "item_flag_team2";
|
||
else
|
||
kn = "item_flag_team1";
|
||
|
||
player.player_flag &= ~ITEM_ENEMY_FLAG;
|
||
e = find (world, classname, kn);
|
||
if (e != world)
|
||
TeamDropFlag (e);
|
||
};
|
||
|
||
// possum: 3 loops in one. Hopefully this will help prevent an
|
||
// occasional server lockup when a player picks up and then captures the
|
||
// flag in rapid succession
|
||
void ()
|
||
LoopThroughPlayersAfterCapture =
|
||
{
|
||
local entity p;
|
||
|
||
// count up teamscr
|
||
lastteamscrtime = time + TEAMSCRTIME;
|
||
teamscr1 = teamscr2 = 0;
|
||
|
||
// Ok, let's do the player loop, hand out the bonuses, add up
|
||
// the scores, and inform the players about the capture
|
||
|
||
p = find (world, classname, "player");
|
||
while (p != world) {
|
||
// Ok, let's do the player loop, hand out the bonuses
|
||
self = p;
|
||
self.killed = 0;
|
||
if (self.steam == other.steam && self != other)
|
||
self.frags += TEAM_CAPTURE_TEAM_BONUS;
|
||
if (self.steam != other.steam) {
|
||
// *XXX* EXPERT CTF
|
||
// reset the last_hurt_carrier variable in all enemy
|
||
// players, so that you don't get bonuses for defending
|
||
// the flag carrier if the flag carrier has already
|
||
// completed a capture
|
||
self.last_hurt_carrier = -5;
|
||
} else if (self.steam == other.steam) {
|
||
// done to all players on the capturing team
|
||
// *XXX* EXPERT CTF
|
||
// award extra points for capture assists
|
||
if (self.last_returned_flag
|
||
+ TEAM_CAPTURE_RETURN_FLAG_ASSIST_TIMEOUT > time) {
|
||
bprint (PRINT_HIGH, self.netname);
|
||
bprint (PRINT_HIGH, " gets an assist for returning his "
|
||
"flag!\n");
|
||
self.frags += TEAM_CAPTURE_RETURN_FLAG_ASSIST_BONUS;
|
||
}
|
||
if (self.last_fragged_carrier
|
||
+ TEAM_CAPTURE_FRAG_CARRIER_ASSIST_TIMEOUT > time) {
|
||
bprint (PRINT_HIGH, self.netname);
|
||
bprint (PRINT_HIGH, " gets an assist for fragging the flag "
|
||
"carrier!\n");
|
||
self.frags += TEAM_CAPTURE_FRAG_CARRIER_ASSIST_BONUS;
|
||
}
|
||
}
|
||
self.player_flag &= ~ITEM_ENEMY_FLAG;
|
||
|
||
// count up teamscr
|
||
if (p.steam == TEAM_COLOR1)
|
||
teamscr1 = teamscr1 + p.frags;
|
||
else if (p.steam == TEAM_COLOR2)
|
||
teamscr2 = teamscr2 + p.frags;
|
||
|
||
// inform players about capture
|
||
if (p.steam == other.steam)
|
||
TeamPlayerUpdate (p, "Your team captured the flag!");
|
||
else if ((p.steam == TEAM_COLOR1 && other.steam == TEAM_COLOR2) ||
|
||
(p.steam == TEAM_COLOR2 && other.steam == TEAM_COLOR1))
|
||
TeamPlayerUpdate (p, "Your flag was captured!");
|
||
|
||
// remove any flags
|
||
p.effects &= ~(EF_FLAG1 | EF_FLAG2);
|
||
|
||
p = find (p, classname, "player");
|
||
}
|
||
};
|
||
|
||
void ()
|
||
TeamCaptureFlagTouch =
|
||
{
|
||
local entity p;
|
||
|
||
if (other.classname != "player")
|
||
return;
|
||
if (other.health <= 0)
|
||
return;
|
||
|
||
if (self.steam == other.steam) {
|
||
// same team, if the flag is *not* at the base, return it to base.
|
||
// we overload the 'cnt' field for this
|
||
if (self.cnt == FLAG_AT_BASE) {
|
||
// the flag is at home base. if the player has the enemy flag,
|
||
// he's just won!
|
||
|
||
if (other.player_flag & ITEM_ENEMY_FLAG) {
|
||
bprint (PRINT_HIGH, other.netname);
|
||
if (other.steam == TEAM_COLOR1)
|
||
bprint (PRINT_HIGH, " <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> the <20><><EFBFBD><EFBFBD> flag!\n"); // blue
|
||
else
|
||
bprint (PRINT_HIGH, " <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> the <20><><EFBFBD> flag!\n"); // red
|
||
other.items &= ~(IT_KEY1 | IT_KEY2);
|
||
|
||
last_flag_capture = time;
|
||
last_capture_team = other.steam;
|
||
|
||
sound (other, CHAN_VOICE, "misc/flagcap.wav", 1, ATTN_NONE);
|
||
|
||
// other gets another 10 frag bonus
|
||
other.frags += TEAM_CAPTURE_CAPTURE_BONUS;
|
||
|
||
// possum: 3 loops in one
|
||
LoopThroughPlayersAfterCapture ();
|
||
|
||
// respawn flags
|
||
TeamCaptureRegenFlags ();
|
||
return;
|
||
}
|
||
return; // its at home base already
|
||
}
|
||
// hey, its not home. return it by teleporting it back
|
||
bprint (PRINT_HIGH, other.netname);
|
||
if (other.steam == TEAM_COLOR1)
|
||
bprint(PRINT_HIGH, " <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> the <20><><EFBFBD> flag!\n"); // red
|
||
else
|
||
bprint(PRINT_HIGH, " <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> the <20><><EFBFBD><EFBFBD> flag!\n"); // blue
|
||
other.frags += TEAM_CAPTURE_RECOVERY_BONUS;
|
||
// *XXX* EXPERT CTF set time when player last returned his flag
|
||
other.last_returned_flag = time;
|
||
sound (other, CHAN_ITEM, self.noise1, 1, ATTN_NORM);
|
||
TeamCaptureReturnFlag (self);
|
||
return;
|
||
}
|
||
|
||
// hey, its not our flag, pick it up
|
||
bprint (PRINT_HIGH, other.netname);
|
||
if (other.steam == TEAM_COLOR1)
|
||
bprint (PRINT_HIGH, " <20><><EFBFBD> the <20><><EFBFBD><EFBFBD> flag!\n"); // blue
|
||
else
|
||
bprint (PRINT_HIGH, " <20><><EFBFBD> the <20><><EFBFBD> flag!\n"); // red
|
||
if (TEAM_CAPTURE_FLAG_BONUS)
|
||
other.frags += TEAM_CAPTURE_FLAG_BONUS;
|
||
// TeamPlayerUpdate (other, "YOU GOT THE ENEMY FLAG!\n\nRETURN TO BASE!");
|
||
TeamPlayerUpdate (other, "<EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>\n\n<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD>");
|
||
sound (other, CHAN_ITEM, self.noise, 1, ATTN_NORM);
|
||
|
||
other.player_flag += ITEM_ENEMY_FLAG;
|
||
other.items |= self.items;
|
||
|
||
// *XXX* EXPERT CTF set the time at which the carrier picked up the flag
|
||
other.flag_since = time;
|
||
|
||
// pick up the flag
|
||
self.cnt = FLAG_CARRIED;
|
||
self.solid = SOLID_NOT;
|
||
self.owner = other;
|
||
if (self.steam == TEAM_COLOR1)
|
||
self.owner.effects |= EF_FLAG1;
|
||
else // must be other team
|
||
self.owner.effects |= EF_FLAG2;
|
||
setmodel (self, "");
|
||
|
||
p = find (world, classname, "player");
|
||
while (p != world) {
|
||
if (p != other) {
|
||
if (p.steam != other.steam)
|
||
TeamPlayerUpdate (p, "Your flag has been taken!");
|
||
else if (p.steam == other.steam)
|
||
TeamPlayerUpdate (p, "Your team has the enemy flag!");
|
||
}
|
||
p = find (p, classname, "player");
|
||
}
|
||
};
|
||
|
||
void ()
|
||
TeamCaptureFlagThink =
|
||
{
|
||
self.nextthink = time + 0.1;
|
||
|
||
switch (self.cnt) {
|
||
case FLAG_AT_BASE:
|
||
return; // just sitting around waiting to be picked up
|
||
case FLAG_DROPPED:
|
||
if (time - self.super_time > TEAM_CAPTURE_FLAG_RETURN_TIME)
|
||
TeamCaptureReturnFlag (self);
|
||
return;
|
||
case FLAG_CARRIED:
|
||
break;
|
||
default:
|
||
objerror ("Flag in invalid state\n");
|
||
break;
|
||
}
|
||
};
|
||
|
||
// self is player
|
||
entity ()
|
||
TeamCaptureSpawn =
|
||
{
|
||
if (!(teamplay & TEAM_CAPTURE_FLAG))
|
||
return world;
|
||
|
||
if (self.steam == TEAM_COLOR1) {
|
||
team1_lastspawn = find (team1_lastspawn, classname,
|
||
"info_player_team1");
|
||
if (team1_lastspawn == world)
|
||
team1_lastspawn = find (team1_lastspawn, classname,
|
||
"info_player_team1");
|
||
return team1_lastspawn;
|
||
} else if (self.steam == TEAM_COLOR2) {
|
||
team2_lastspawn = find (team2_lastspawn, classname,
|
||
"info_player_team2");
|
||
if (team2_lastspawn == world)
|
||
team2_lastspawn = find (team2_lastspawn, classname,
|
||
"info_player_team2");
|
||
return team2_lastspawn;
|
||
}
|
||
return world;
|
||
};
|
||
|
||
/*
|
||
From byron@caseware.com Wed Oct 16 18:57:44 1996
|
||
Date: Wed, 16 Oct 1996 21:22:37 -0400
|
||
From: Byron Long <byron@caseware.com>
|
||
To: zoid@mindlink.net
|
||
Subject: Team Status Command (source code included) :-)
|
||
|
||
A co-worker of mine wondered if it was possible to add a function to
|
||
your capture the flag code that would give a status report on an
|
||
impulse. I think he may have mailed you, but I wrote a quick version
|
||
myself, which your welcome to use if you like the feature (it offsets
|
||
some of the problems with the chat capabilities in Quake so it seems
|
||
like a worthwhile feature). Feel free to change it as necessary.
|
||
*/
|
||
|
||
// *Capture The Flag - Status report by Wonko
|
||
void ()
|
||
TeamFlagStatusReport =
|
||
{
|
||
local entity flag1, flag2, p;
|
||
|
||
if (!(teamplay & TEAM_CAPTURE_FLAG)) {
|
||
sprint (self, PRINT_HIGH, "Capture the Flag is not enabled.\n");
|
||
return;
|
||
}
|
||
|
||
// Find the flags at home base
|
||
flag1 = find (world,classname, "item_flag_team1");
|
||
flag2 = find (world,classname, "item_flag_team2");
|
||
|
||
if (self.classname == "spectator") {
|
||
if (flag1 != world && flag1.cnt == FLAG_CARRIED) {
|
||
sprint (self, PRINT_HIGH, flag1.owner.netname);
|
||
sprint (self, PRINT_HIGH, " has the <20><><EFBFBD> flag.\n");
|
||
} else {
|
||
sprint (self, PRINT_HIGH, "The <20><><EFBFBD> flag is ");
|
||
if (flag1 == world)
|
||
sprint(self, PRINT_HIGH, "missing!\n");
|
||
if (flag1.cnt == FLAG_AT_BASE)
|
||
sprint (self, PRINT_HIGH, "at base.\n");
|
||
else if (flag1.cnt == FLAG_DROPPED)
|
||
sprint (self, PRINT_HIGH, "lying about.\n");
|
||
else
|
||
sprint (self, PRINT_HIGH, " corrupt.\n");
|
||
}
|
||
if (flag2 != world && flag2.cnt == FLAG_CARRIED) {
|
||
sprint (self, PRINT_HIGH, flag2.owner.netname);
|
||
sprint (self, PRINT_HIGH, " has the <20><><EFBFBD><EFBFBD> flag. ");
|
||
} else {
|
||
sprint (self, PRINT_HIGH, "The <20><><EFBFBD><EFBFBD> flag is ");
|
||
if (flag2 == world)
|
||
sprint (self, PRINT_HIGH, "missing!\n");
|
||
if (flag2.cnt == FLAG_AT_BASE)
|
||
sprint (self, PRINT_HIGH, "at base.\n");
|
||
else if (flag2.cnt == FLAG_DROPPED)
|
||
sprint (self, PRINT_HIGH, "lying about.\n");
|
||
else
|
||
sprint (self, PRINT_HIGH, " corrupt.\n");
|
||
}
|
||
return;
|
||
}
|
||
|
||
// If on team 2 switch meanings of flags
|
||
if (self.steam != TEAM_COLOR1) {
|
||
p = flag1;
|
||
flag1 = flag2;
|
||
flag2 = p;
|
||
}
|
||
|
||
if (flag1 != world && flag1.cnt == FLAG_CARRIED) {
|
||
sprint (self, PRINT_HIGH, flag1.owner.netname);
|
||
sprint (self, PRINT_HIGH, " has your flag. ");
|
||
} else {
|
||
sprint (self, PRINT_HIGH, "Your flag is ");
|
||
if (flag1 == world)
|
||
sprint (self, PRINT_HIGH, "missing! ");
|
||
if (flag1.cnt == FLAG_AT_BASE)
|
||
sprint (self, PRINT_HIGH, "in your base. ");
|
||
else if (flag1.cnt == FLAG_DROPPED)
|
||
sprint (self, PRINT_HIGH, "lying about. ");
|
||
else
|
||
sprint (self, PRINT_HIGH, " corrupt. ");
|
||
}
|
||
|
||
if (flag2 != world && flag2.cnt == FLAG_CARRIED) {
|
||
if (self == flag2.owner)
|
||
sprint (self, PRINT_HIGH, "You have the enemy flag.\n");
|
||
else {
|
||
sprint (self, PRINT_HIGH, flag2.owner.netname);
|
||
sprint (self, PRINT_HIGH, " has the enemy flag.\n");
|
||
}
|
||
} else {
|
||
sprint (self, PRINT_HIGH, "The enemy flag is ");
|
||
if (flag2 == world)
|
||
sprint (self, PRINT_HIGH, "missing!\n");
|
||
if (flag2.cnt == FLAG_AT_BASE)
|
||
sprint (self, PRINT_HIGH, "in their base.\n");
|
||
else if (flag2.cnt == FLAG_DROPPED)
|
||
sprint (self, PRINT_HIGH, "lying about.\n");
|
||
else
|
||
sprint (self, PRINT_HIGH, " corrupt.\n");
|
||
}
|
||
};
|
||
|
||
/////////////////////////////////////////////////////////////////////////
|
||
|
||
$cd id1/models/flag
|
||
$base base
|
||
$skin skin
|
||
|
||
void ()
|
||
place_flag = {
|
||
self.mdl = self.model; // so it can be restored on respawn
|
||
self.flags = FL_ITEM; // make extra wide
|
||
self.solid = SOLID_TRIGGER;
|
||
self.movetype = MOVETYPE_TOSS;
|
||
self.velocity = '0 0 0';
|
||
self.origin_z += 6;
|
||
self.think = TeamCaptureFlagThink;
|
||
self.touch = TeamCaptureFlagTouch;
|
||
self.nextthink = time + 0.1;
|
||
self.cnt = FLAG_AT_BASE;
|
||
self.mangle = self.angles;
|
||
self.effects |= EF_DIMLIGHT;
|
||
if (!droptofloor()) {
|
||
dprint ("Flag fell out of level at ");
|
||
dprint (vtos (self.origin));
|
||
dprint ("\n");
|
||
remove (self);
|
||
return;
|
||
}
|
||
self.oldorigin = self.origin; // save for flag return
|
||
};
|
||
|
||
// ZOID Capture the flag
|
||
void ()
|
||
item_flag_team1 =
|
||
{
|
||
if (!deathmatch || !(cvar ("teamplay") & TEAM_CAPTURE_FLAG)) {
|
||
remove (self);
|
||
return;
|
||
}
|
||
|
||
self.steam = TEAM_COLOR1;
|
||
self.items = IT_KEY2;
|
||
precache_model ("progs/flag.mdl");
|
||
setmodel (self, "progs/flag.mdl");
|
||
self.skin = 0;
|
||
precache_sound ("misc/flagtk.wav"); // flag taken
|
||
precache_sound ("misc/flagcap.wav"); // flag capture
|
||
precache_sound ("doors/runetry.wav");
|
||
self.noise = "misc/flagtk.wav";
|
||
self.noise1 = "doors/runetry.wav";
|
||
setsize (self, '-16 -16 0', '16 16 74');
|
||
self.nextthink = time + 0.2; // items start after other solids
|
||
self.think = place_flag;
|
||
};
|
||
|
||
void ()
|
||
item_flag_team2 =
|
||
{
|
||
if (!deathmatch || !(cvar("teamplay") & TEAM_CAPTURE_FLAG)) {
|
||
remove(self);
|
||
return;
|
||
}
|
||
|
||
self.steam = TEAM_COLOR2;
|
||
self.items = IT_KEY1;
|
||
precache_model ("progs/flag.mdl");
|
||
setmodel (self, "progs/flag.mdl");
|
||
self.skin = 1;
|
||
precache_sound ("misc/flagtk.wav"); // flag taken
|
||
precache_sound ("misc/flagcap.wav"); // flag capture
|
||
precache_sound ("doors/runetry.wav");
|
||
self.noise = "misc/flagtk.wav";
|
||
self.noise1 = "doors/runetry.wav";
|
||
setsize(self, '-16 -16 0', '16 16 74');
|
||
// make it glow
|
||
self.nextthink = time + 0.2; // items start after other solids
|
||
self.think = place_flag;
|
||
};
|
||
|
||
// Team base starting locations
|
||
void ()
|
||
info_player_team1 =
|
||
{
|
||
};
|
||
|
||
void ()
|
||
info_player_team2 =
|
||
{
|
||
};
|
||
|
||
/*QUAKED func_ctf_wall (0 .5 .8) ?
|
||
This is just a solid wall if not inhibitted
|
||
Only appears in CTF teamplay
|
||
*/
|
||
void ()
|
||
func_ctf_wall =
|
||
{
|
||
if (cvar ("teamplay") & TEAM_CAPTURE_FLAG) {
|
||
self.angles = '0 0 0';
|
||
self.movetype = MOVETYPE_PUSH; // so it doesn't get pushed by anything
|
||
self.solid = SOLID_BSP;
|
||
setmodel (self, self.model);
|
||
} else
|
||
remove (self);
|
||
};
|