game: sync ctf g_cmds

This commit is contained in:
Denis Pauk 2023-11-04 17:32:56 +02:00
parent 2b69f3774f
commit 802a874f64
6 changed files with 520 additions and 1518 deletions

View file

@ -1482,7 +1482,7 @@ CTF_OBJS_ = \
src/common/shared/shared.o \ src/common/shared/shared.o \
src/ctf/g_ai.o \ src/ctf/g_ai.o \
src/game/g_chase.o \ src/game/g_chase.o \
src/ctf/g_cmds.o \ src/game/g_cmds.o \
src/ctf/g_combat.o \ src/ctf/g_combat.o \
src/game/g_ctf.o \ src/game/g_ctf.o \
src/game/g_func.o \ src/game/g_func.o \

File diff suppressed because it is too large Load diff

View file

@ -142,16 +142,82 @@ void
Killed(edict_t *targ, edict_t *inflictor, edict_t *attacker, Killed(edict_t *targ, edict_t *inflictor, edict_t *attacker,
int damage, vec3_t point) int damage, vec3_t point)
{ {
if (!targ || !inflictor || !attacker)
{
return;
}
if (targ->health < -999) if (targ->health < -999)
{ {
targ->health = -999; targ->health = -999;
} }
targ->enemy = attacker; /* Reset AI flag for being ducked. This fixes a corner case
were the monster is ressurected by a medic and get's stuck
in the next frame for mmove_t not matching the AI state. */
if (targ->monsterinfo.aiflags & AI_DUCKED)
{
targ->monsterinfo.aiflags &= ~AI_DUCKED;
}
if (targ->monsterinfo.aiflags & AI_MEDIC)
{
if (targ->enemy)
{
cleanupHealTarget(targ->enemy);
}
/* clean up self */
targ->monsterinfo.aiflags &= ~AI_MEDIC;
targ->enemy = attacker;
}
else
{
targ->enemy = attacker;
}
if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD)) if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD))
{ {
if (!(targ->monsterinfo.aiflags & AI_GOOD_GUY)) /* free up slot for spawned monster if it's spawned */
if (targ->monsterinfo.aiflags & AI_SPAWNED_CARRIER)
{
if (targ->monsterinfo.commander &&
targ->monsterinfo.commander->inuse &&
!strcmp(targ->monsterinfo.commander->classname, "monster_carrier"))
{
targ->monsterinfo.commander->monsterinfo.monster_slots++;
}
}
if (targ->monsterinfo.aiflags & AI_SPAWNED_MEDIC_C)
{
if (targ->monsterinfo.commander)
{
if (targ->monsterinfo.commander->inuse &&
!strcmp(targ->monsterinfo.commander->classname, "monster_medic_commander"))
{
targ->monsterinfo.commander->monsterinfo.monster_slots++;
}
}
}
if (targ->monsterinfo.aiflags & AI_SPAWNED_WIDOW)
{
/* need to check this because we can
have variable numbers of coop players */
if (targ->monsterinfo.commander &&
targ->monsterinfo.commander->inuse &&
!strncmp(targ->monsterinfo.commander->classname, "monster_widow", 13))
{
if (targ->monsterinfo.commander->monsterinfo.monster_used > 0)
{
targ->monsterinfo.commander->monsterinfo.monster_used--;
}
}
}
if ((!(targ->monsterinfo.aiflags & AI_GOOD_GUY)) &&
(!(targ->monsterinfo.aiflags & AI_DO_NOT_COUNT)))
{ {
level.killed_monsters++; level.killed_monsters++;
@ -161,7 +227,7 @@ Killed(edict_t *targ, edict_t *inflictor, edict_t *attacker,
} }
/* medics won't heal monsters that they kill themselves */ /* medics won't heal monsters that they kill themselves */
if (strcmp(attacker->classname, "monster_medic") == 0) if (attacker->classname && strcmp(attacker->classname, "monster_medic") == 0)
{ {
targ->owner = attacker; targ->owner = attacker;
} }
@ -169,7 +235,8 @@ Killed(edict_t *targ, edict_t *inflictor, edict_t *attacker,
} }
if ((targ->movetype == MOVETYPE_PUSH) || if ((targ->movetype == MOVETYPE_PUSH) ||
(targ->movetype == MOVETYPE_STOP) || (targ->movetype == MOVETYPE_NONE)) (targ->movetype == MOVETYPE_STOP) ||
(targ->movetype == MOVETYPE_NONE))
{ {
/* doors, triggers, etc */ /* doors, triggers, etc */
targ->die(targ, inflictor, attacker, damage, point); targ->die(targ, inflictor, attacker, damage, point);
@ -301,7 +368,15 @@ CheckPowerArmor(edict_t *ent, vec3_t point, vec3_t normal,
} }
else else
{ {
damagePerCell = 1; /* power armor is weaker in CTF */ if (ctf->value)
{
/* power armor is weaker in CTF */
damagePerCell = 1;
}
else
{
damagePerCell = 2;
}
pa_te_type = TE_SHIELD_SPARKS; pa_te_type = TE_SHIELD_SPARKS;
damage = (2 * damage) / 3; damage = (2 * damage) / 3;
} }
@ -418,19 +493,47 @@ CheckArmor(edict_t *ent, vec3_t point, vec3_t normal, int damage,
void void
M_ReactToDamage(edict_t *targ, edict_t *attacker, edict_t *inflictor) M_ReactToDamage(edict_t *targ, edict_t *attacker, edict_t *inflictor)
{ {
qboolean new_tesla;
if (!targ || !attacker || !inflictor)
{
return;
}
if (targ->health <= 0)
{
return;
}
if (!(attacker->client) && !(attacker->svflags & SVF_MONSTER)) if (!(attacker->client) && !(attacker->svflags & SVF_MONSTER))
{ {
return; return;
} }
/* logic for tesla - if you are hit by a tesla,
and can't see who you should be mad at (attacker)
attack the tesla also, target the tesla if it's
a "new" tesla */
if (!strcmp(inflictor->classname, "tesla"))
{
new_tesla = MarkTeslaArea(targ, inflictor);
if (new_tesla || !targ->enemy)
{
TargetTesla(targ, inflictor);
}
return;
}
if ((attacker == targ) || (attacker == targ->enemy)) if ((attacker == targ) || (attacker == targ->enemy))
{ {
return; return;
} }
/* if we are a good guy monster and our /* if we are a good guy monster and
attacker is a player or another good our attacker is a player or another
guy, do not get mad at them */ good guy, do not get mad at them */
if (targ->monsterinfo.aiflags & AI_GOOD_GUY) if (targ->monsterinfo.aiflags & AI_GOOD_GUY)
{ {
if (attacker->client || (attacker->monsterinfo.aiflags & AI_GOOD_GUY)) if (attacker->client || (attacker->monsterinfo.aiflags & AI_GOOD_GUY))
@ -439,14 +542,55 @@ M_ReactToDamage(edict_t *targ, edict_t *attacker, edict_t *inflictor)
} }
} }
/* if we're currently mad at something
a target_anger made us mad at, ignore
damage */
if (targ->enemy && targ->monsterinfo.aiflags & AI_TARGET_ANGER)
{
float percentHealth;
/* make sure whatever we were pissed at is still around. */
if (targ->enemy->inuse)
{
percentHealth = (float)(targ->health) / (float)(targ->max_health);
if (percentHealth > 0.33)
{
return;
}
}
/* remove the target anger flag */
targ->monsterinfo.aiflags &= ~AI_TARGET_ANGER;
}
/* if we're healing someone, do like above and try to stay with them */
if ((targ->enemy) && (targ->monsterinfo.aiflags & AI_MEDIC))
{
float percentHealth;
percentHealth = (float)(targ->health) / (float)(targ->max_health);
/* ignore it some of the time */
if (targ->enemy->inuse && (percentHealth > 0.25))
{
return;
}
/* remove the medic flag */
targ->monsterinfo.aiflags &= ~AI_MEDIC;
cleanupHealTarget(targ->enemy);
}
/* if attacker is a client, get mad at /* if attacker is a client, get mad at
them because he's good and we're not */ them because he's good and we're not */
if (attacker->client) if (attacker->client)
{ {
/* this can only happen in coop (both targ->monsterinfo.aiflags &= ~AI_SOUND_TARGET;
new and old enemies are clients)
only switch if can't see the /* this can only happen in coop (both new and
current enemy */ old enemies are clients) only switch if can't
see the current enemy */
if (targ->enemy && targ->enemy->client) if (targ->enemy && targ->enemy->client)
{ {
if (visible(targ, targ->enemy)) if (visible(targ, targ->enemy))
@ -468,8 +612,8 @@ M_ReactToDamage(edict_t *targ, edict_t *attacker, edict_t *inflictor)
return; return;
} }
/* it's the same base (walk/swim/fly) type /* it's the same base (walk/swim/fly) type and a
and a different classname and it's not a tank different classname and it's not a tank
(they spray too much), get mad at them */ (they spray too much), get mad at them */
if (((targ->flags & (FL_FLY | FL_SWIM)) == if (((targ->flags & (FL_FLY | FL_SWIM)) ==
(attacker->flags & (FL_FLY | FL_SWIM))) && (attacker->flags & (FL_FLY | FL_SWIM))) &&
@ -477,14 +621,13 @@ M_ReactToDamage(edict_t *targ, edict_t *attacker, edict_t *inflictor)
(strcmp(attacker->classname, "monster_tank") != 0) && (strcmp(attacker->classname, "monster_tank") != 0) &&
(strcmp(attacker->classname, "monster_supertank") != 0) && (strcmp(attacker->classname, "monster_supertank") != 0) &&
(strcmp(attacker->classname, "monster_makron") != 0) && (strcmp(attacker->classname, "monster_makron") != 0) &&
(strcmp(attacker->classname, "monster_jorg") != 0)) (strcmp(attacker->classname, "monster_jorg") != 0) &&
!(attacker->monsterinfo.aiflags & AI_IGNORE_SHOTS) &&
!(targ->monsterinfo.aiflags & AI_IGNORE_SHOTS))
{ {
if (targ->enemy) if (targ->enemy && targ->enemy->client)
{ {
if (targ->enemy->client) targ->oldenemy = targ->enemy;
{
targ->oldenemy = targ->enemy;
}
} }
targ->enemy = attacker; targ->enemy = attacker;
@ -494,18 +637,36 @@ M_ReactToDamage(edict_t *targ, edict_t *attacker, edict_t *inflictor)
FoundTarget(targ); FoundTarget(targ);
} }
} }
else /* otherwise get mad at whoever they are mad at (help our buddy) */ /* if they *meant* to shoot us, then shoot back */
else if (attacker->enemy == targ)
{ {
if (targ->enemy) if (targ->enemy && targ->enemy->client)
{ {
if (targ->enemy->client) targ->oldenemy = targ->enemy;
{ }
targ->oldenemy = targ->enemy;
} targ->enemy = attacker;
if (!(targ->monsterinfo.aiflags & AI_DUCKED))
{
FoundTarget(targ);
}
}
/* otherwise get mad at whoever they are mad
at (help our buddy) unless it is us! */
else if (attacker->enemy)
{
if (targ->enemy && targ->enemy->client)
{
targ->oldenemy = targ->enemy;
} }
targ->enemy = attacker->enemy; targ->enemy = attacker->enemy;
FoundTarget(targ);
if (!(targ->monsterinfo.aiflags & AI_DUCKED))
{
FoundTarget(targ);
}
} }
} }
@ -524,10 +685,28 @@ CheckTeamDamage(edict_t *targ, edict_t *attacker)
return false; return false;
} }
static void
apply_knockback(edict_t *targ, vec3_t dir, float knockback, float scale)
{
vec3_t kvel;
float mass;
if (!knockback)
{
return;
}
mass = (targ->mass < 50) ? 50.0f : (float)targ->mass;
VectorNormalize2(dir, kvel);
VectorScale(kvel, scale * (knockback / mass), kvel);
VectorAdd(targ->velocity, kvel, targ->velocity);
}
void void
T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir,
vec3_t dir, vec3_t point, vec3_t normal, int damage, vec3_t point, vec3_t normal, int damage, int knockback, int dflags,
int knockback, int dflags, int mod) int mod)
{ {
gclient_t *client; gclient_t *client;
int take; int take;
@ -535,24 +714,32 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker,
int asave; int asave;
int psave; int psave;
int te_sparks; int te_sparks;
int sphere_notified;
if (!targ || !inflictor || !attacker)
{
return;
}
if (!targ->takedamage) if (!targ->takedamage)
{ {
return; return;
} }
/* friendly fire avoidance sphere_notified = false;
if enabled you can't hurt
teammates (but you can hurt /* friendly fire avoidance. If enabled you can't
yourself) knockback still occurs */ hurt teammates (but you can hurt yourself)
if ((targ != attacker) && knockback still occurs */
((deathmatch->value && if ((targ != attacker) && ((deathmatch->value &&
((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) || ((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) ||
coop->value)) coop->value))
{ {
if (OnSameTeam(targ, attacker)) if (OnSameTeam(targ, attacker))
{ {
if ((int)(dmflags->value) & DF_NO_FRIENDLY_FIRE) /* nukes kill everyone */
if (((int)(dmflags->value) & DF_NO_FRIENDLY_FIRE) &&
(mod != MOD_NUKE))
{ {
damage = 0; damage = 0;
} }
@ -565,6 +752,25 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker,
meansOfDeath = mod; meansOfDeath = mod;
/* allow the deathmatch game to change values */
if (deathmatch->value && gamerules && gamerules->value)
{
if (DMGame.ChangeDamage)
{
damage = DMGame.ChangeDamage(targ, attacker, damage, mod);
}
if (DMGame.ChangeKnockback)
{
knockback = DMGame.ChangeKnockback(targ, attacker, knockback, mod);
}
if (!damage)
{
return;
}
}
/* easy mode takes half damage */ /* easy mode takes half damage */
if ((skill->value == SKILL_EASY) && (deathmatch->value == 0) && targ->client) if ((skill->value == SKILL_EASY) && (deathmatch->value == 0) && targ->client)
{ {
@ -578,6 +784,18 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker,
client = targ->client; client = targ->client;
/* defender sphere takes half damage */
if ((client) && (client->owned_sphere) &&
(client->owned_sphere->spawnflags == 1))
{
damage *= 0.5;
if (!damage)
{
damage = 1;
}
}
if (dflags & DAMAGE_BULLET) if (dflags & DAMAGE_BULLET)
{ {
te_sparks = TE_BULLET_SPARKS; te_sparks = TE_BULLET_SPARKS;
@ -651,11 +869,28 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker,
/* check for invincibility */ /* check for invincibility */
if ((client && if ((client &&
(client->invincible_framenum > level.framenum)) && (client->invincible_framenum > level.framenum)) &&
!(dflags & DAMAGE_NO_PROTECTION) && (mod != MOD_TRAP))
{
if (targ->pain_debounce_time < level.time)
{
gi.sound(targ, CHAN_ITEM, gi.soundindex(
"items/protect4.wav"), 1, ATTN_NORM, 0);
targ->pain_debounce_time = level.time + 2;
}
take = 0;
save = damage;
}
/* check for monster invincibility */
if (((targ->svflags & SVF_MONSTER) &&
(targ->monsterinfo.invincible_framenum > level.framenum)) &&
!(dflags & DAMAGE_NO_PROTECTION)) !(dflags & DAMAGE_NO_PROTECTION))
{ {
if (targ->pain_debounce_time < level.time) if (targ->pain_debounce_time < level.time)
{ {
gi.sound(targ, CHAN_ITEM, gi.soundindex("items/protect4.wav"), 1, ATTN_NORM, 0); gi.sound(targ, CHAN_ITEM, gi.soundindex(
"items/protect4.wav"), 1, ATTN_NORM, 0);
targ->pain_debounce_time = level.time + 2; targ->pain_debounce_time = level.time + 2;
} }
@ -696,9 +931,25 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker,
/* do the damage */ /* do the damage */
if (take) if (take)
{ {
if ((targ->svflags & SVF_MONSTER) || (client)) /* need more blood for chainfist. */
if (targ->flags & FL_MECHANICAL)
{ {
SpawnDamage(TE_BLOOD, point, normal); SpawnDamage(TE_ELECTRIC_SPARKS, point, normal);
}
else if ((targ->svflags & SVF_MONSTER) || (client))
{
if (strcmp(targ->classname, "monster_gekk") == 0)
{
SpawnDamage(TE_GREENBLOOD, point, normal);
}
else if (mod == MOD_CHAINFIST)
{
SpawnDamage(TE_MOREBLOOD, point, normal);
}
else
{
SpawnDamage(TE_BLOOD, point, normal);
}
} }
else else
{ {
@ -710,6 +961,17 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker,
targ->health = targ->health - take; targ->health = targ->health - take;
} }
/* spheres need to know who to shoot at */
if (client && client->owned_sphere)
{
sphere_notified = true;
if (client->owned_sphere->pain)
{
client->owned_sphere->pain(client->owned_sphere, attacker, 0, 0);
}
}
if (targ->health <= 0) if (targ->health <= 0)
{ {
if ((targ->svflags & SVF_MONSTER) || (client)) if ((targ->svflags & SVF_MONSTER) || (client))
@ -722,6 +984,19 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker,
} }
} }
/* spheres need to know who to shoot at */
if (!sphere_notified)
{
if (client && client->owned_sphere)
{
if (client->owned_sphere->pain)
{
client->owned_sphere->pain(client->owned_sphere, attacker, 0,
0);
}
}
}
if (targ->svflags & SVF_MONSTER) if (targ->svflags & SVF_MONSTER)
{ {
M_ReactToDamage(targ, attacker, inflictor); M_ReactToDamage(targ, attacker, inflictor);
@ -776,6 +1051,11 @@ T_RadiusDamage(edict_t *inflictor, edict_t *attacker, float damage,
vec3_t v; vec3_t v;
vec3_t dir; vec3_t dir;
if (!inflictor || !attacker)
{
return;
}
while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL) while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL)
{ {
if (ent == ignore) if (ent == ignore)

View file

@ -28,6 +28,9 @@
#include "../header/local.h" #include "../header/local.h"
#include "../monster/misc/player.h" #include "../monster/misc/player.h"
edict_t *pm_passent;
void ClientUserinfoChanged(edict_t *ent, char *userinfo);
void SP_misc_teleporter_dest(edict_t *ent); void SP_misc_teleporter_dest(edict_t *ent);
void Touch_Item(edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf); void Touch_Item(edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf);
@ -2370,3 +2373,34 @@ ClientBeginServerFrame(edict_t *ent)
client->latched_buttons = 0; client->latched_buttons = 0;
} }
/*
* This is called to clean up the pain daemons that
* the disruptor attaches to clients to damage them.
*/
void
RemoveAttackingPainDaemons(edict_t *self)
{
edict_t *tracker;
if (!self)
{
return;
}
tracker = G_Find(NULL, FOFS(classname), "pain daemon");
while (tracker)
{
if (tracker->enemy == self)
{
G_FreeEdict(tracker);
}
tracker = G_Find(tracker, FOFS(classname), "pain daemon");
}
if (self->client)
{
self->client->tracker_pain_framenum = 0;
}
}

View file

@ -28,6 +28,8 @@
#include "header/local.h" #include "header/local.h"
#include "monster/misc/player.h" #include "monster/misc/player.h"
gitem_t *CTFWhat_Tech(edict_t *ent);
static char * static char *
ClientTeam(edict_t *ent, char* value) ClientTeam(edict_t *ent, char* value)
{ {
@ -103,7 +105,12 @@ SelectNextItem(edict_t *ent, int itflags)
cl = ent->client; cl = ent->client;
if (cl->chase_target) if (cl->menu)
{
PMenu_Next(ent);
return;
}
else if (cl->chase_target)
{ {
ChaseNext(ent); ChaseNext(ent);
return; return;
@ -152,7 +159,12 @@ SelectPrevItem(edict_t *ent, int itflags)
cl = ent->client; cl = ent->client;
if (cl->chase_target) if (cl->menu)
{
PMenu_Prev(ent);
return;
}
else if (cl->chase_target)
{ {
ChasePrev(ent); ChasePrev(ent);
return; return;
@ -622,6 +634,12 @@ Cmd_Drop_f(edict_t *ent)
return; return;
} }
if ((Q_stricmp(gi.args(), "tech") == 0) && ((it = CTFWhat_Tech(ent)) != NULL))
{
it->drop(ent, it);
return;
}
s = gi.args(); s = gi.args();
it = FindItem(s); it = FindItem(s);
@ -700,6 +718,7 @@ Cmd_Score_f(edict_t *ent)
if (ent->client->showscores) if (ent->client->showscores)
{ {
ent->client->showscores = false; ent->client->showscores = false;
ent->client->update_chase = true;
return; return;
} }
@ -708,6 +727,9 @@ Cmd_Score_f(edict_t *ent)
gi.unicast(ent, true); gi.unicast(ent, true);
} }
/*
* Display the current help message
*/
void void
Cmd_Help_f(edict_t *ent) Cmd_Help_f(edict_t *ent)
{ {
@ -726,7 +748,8 @@ Cmd_Help_f(edict_t *ent)
ent->client->showinventory = false; ent->client->showinventory = false;
ent->client->showscores = false; ent->client->showscores = false;
if (ent->client->showhelp) if (ent->client->showhelp &&
(ent->client->resp.game_helpchanged == game.helpchanged))
{ {
ent->client->showhelp = false; ent->client->showhelp = false;
return; return;
@ -753,12 +776,25 @@ Cmd_Inven_f(edict_t *ent)
cl->showscores = false; cl->showscores = false;
cl->showhelp = false; cl->showhelp = false;
if (ent->client->menu)
{
PMenu_Close(ent);
ent->client->update_chase = true;
return;
}
if (cl->showinventory) if (cl->showinventory)
{ {
cl->showinventory = false; cl->showinventory = false;
return; return;
} }
if (ctf->value && (cl->resp.ctf_team == CTF_NOTEAM))
{
CTFOpenJoinMenu(ent);
return;
}
cl->showinventory = true; cl->showinventory = true;
InventoryMessage(ent); InventoryMessage(ent);
@ -775,6 +811,12 @@ Cmd_InvUse_f(edict_t *ent)
return; return;
} }
if (ent->client->menu)
{
PMenu_Select(ent);
return;
}
ValidateSelectedItem(ent); ValidateSelectedItem(ent);
if (ent->client->pers.selected_item == -1) if (ent->client->pers.selected_item == -1)
@ -794,6 +836,21 @@ Cmd_InvUse_f(edict_t *ent)
it->use(ent, it); it->use(ent, it);
} }
void
Cmd_LastWeap_f(edict_t *ent)
{
gclient_t *cl;
cl = ent->client;
if (!cl->pers.weapon || !cl->pers.lastweapon)
{
return;
}
cl->pers.lastweapon->use(ent, cl->pers.lastweapon);
}
void void
Cmd_WeapPrev_f(edict_t *ent) Cmd_WeapPrev_f(edict_t *ent)
{ {
@ -982,6 +1039,11 @@ Cmd_Kill_f(edict_t *ent)
return; return;
} }
if (ent->solid == SOLID_NOT)
{
return;
}
if (((level.time - ent->client->respawn_time) < 5) || if (((level.time - ent->client->respawn_time) < 5) ||
(ent->client->resp.spectator)) (ent->client->resp.spectator))
{ {
@ -1018,6 +1080,13 @@ Cmd_PutAway_f(edict_t *ent)
ent->client->showscores = false; ent->client->showscores = false;
ent->client->showhelp = false; ent->client->showhelp = false;
ent->client->showinventory = false; ent->client->showinventory = false;
if (ent->client->menu)
{
PMenu_Close(ent);
}
ent->client->update_chase = true;
} }
int int
@ -1973,9 +2042,9 @@ ClientCommand(edict_t *ent)
return; return;
} }
if (Q_stricmp(cmd, "say_team") == 0) if ((Q_stricmp(cmd, "say_team") == 0) || (Q_stricmp(cmd, "steam") == 0))
{ {
Cmd_Say_f(ent, true, false); CTFSay_Team(ent, gi.args());
return; return;
} }
@ -2080,9 +2149,61 @@ ClientCommand(edict_t *ent)
{ {
Cmd_Wave_f(ent); Cmd_Wave_f(ent);
} }
/* ZOID */
else if (Q_stricmp(cmd, "team") == 0)
{
CTFTeam_f(ent);
}
else if (Q_stricmp(cmd, "id") == 0)
{
CTFID_f(ent);
}
else if (Q_stricmp(cmd, "yes") == 0)
{
CTFVoteYes(ent);
}
else if (Q_stricmp(cmd, "no") == 0)
{
CTFVoteNo(ent);
}
else if (Q_stricmp(cmd, "ready") == 0)
{
CTFReady(ent);
}
else if (Q_stricmp(cmd, "notready") == 0)
{
CTFNotReady(ent);
}
else if (Q_stricmp(cmd, "ghost") == 0)
{
CTFGhost(ent);
}
else if (Q_stricmp(cmd, "admin") == 0)
{
CTFAdmin(ent);
}
else if (Q_stricmp(cmd, "stats") == 0)
{
CTFStats(ent);
}
else if (Q_stricmp(cmd, "warp") == 0)
{
CTFWarp(ent);
}
else if (Q_stricmp(cmd, "boot") == 0)
{
CTFBoot(ent);
}
else if (Q_stricmp(cmd, "playerlist") == 0) else if (Q_stricmp(cmd, "playerlist") == 0)
{ {
Cmd_PlayerList_f(ent); if (ctf->value)
{
CTFPlayerList(ent);
}
else
{
Cmd_PlayerList_f(ent);
}
} }
else if (Q_stricmp(cmd, "entcount") == 0) else if (Q_stricmp(cmd, "entcount") == 0)
{ {
@ -2116,6 +2237,10 @@ ClientCommand(edict_t *ent)
{ {
Cmd_PrefWeap_f(ent); Cmd_PrefWeap_f(ent);
} }
else if (Q_stricmp(cmd, "observer") == 0)
{
CTFObserver(ent);
}
else /* anything that doesn't match a command will be a chat */ else /* anything that doesn't match a command will be a chat */
{ {
Cmd_Say_f(ent, false, true); Cmd_Say_f(ent, false, true);

View file

@ -368,7 +368,15 @@ CheckPowerArmor(edict_t *ent, vec3_t point, vec3_t normal,
} }
else else
{ {
damagePerCell = 2; if (ctf->value)
{
/* power armor is weaker in CTF */
damagePerCell = 1;
}
else
{
damagePerCell = 2;
}
pa_te_type = TE_SHIELD_SPARKS; pa_te_type = TE_SHIELD_SPARKS;
damage = (2 * damage) / 3; damage = (2 * damage) / 3;
} }
@ -863,11 +871,21 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir,
save = damage; save = damage;
} }
psave = CheckPowerArmor(targ, point, normal, take, dflags); /* team armor protect */
take -= psave; if (ctf->value && targ->client && attacker->client &&
(targ->client->resp.ctf_team == attacker->client->resp.ctf_team) &&
(targ != attacker) && ((int)dmflags->value & DF_ARMOR_PROTECT))
{
psave = asave = 0;
}
else
{
psave = CheckPowerArmor(targ, point, normal, take, dflags);
take -= psave;
asave = CheckArmor(targ, point, normal, take, te_sparks, dflags); asave = CheckArmor(targ, point, normal, take, te_sparks, dflags);
take -= asave; take -= asave;
}
/* treat cheat/powerup savings the same as armor */ /* treat cheat/powerup savings the same as armor */
asave += save; asave += save;
@ -911,7 +929,10 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir,
SpawnDamage(te_sparks, point, normal); SpawnDamage(te_sparks, point, normal);
} }
targ->health = targ->health - take; if (!CTFMatchSetup())
{
targ->health = targ->health - take;
}
/* spheres need to know who to shoot at */ /* spheres need to know who to shoot at */
if (client && client->owned_sphere) if (client && client->owned_sphere)
@ -966,7 +987,7 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir,
} }
else if (client) else if (client)
{ {
if (!(targ->flags & FL_GODMODE) && (take)) if (!(targ->flags & FL_GODMODE) && (take) && !CTFMatchSetup())
{ {
targ->pain(targ, attacker, knockback, take); targ->pain(targ, attacker, knockback, take);
} }
@ -979,9 +1000,11 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir,
} }
} }
/* add to the damage inflicted on a player this frame /* add to the damage inflicted on a
the total will be turned into screen blends and view player this frame the total will
angle kicks at the end of the frame */ be turned into screen blends and
view angle kicks at the end of
the frame */
if (client) if (client)
{ {
client->damage_parmor += psave; client->damage_parmor += psave;