📄 g_ctf.c
字号:
ctfgame.total2 += game.clients[i].resp.score;
}
}
void CTFID_f (edict_t *ent)
{
if (ent->client->resp.id_state) {
gi.cprintf(ent, PRINT_HIGH, "Disabling player identication display.\n");
ent->client->resp.id_state = false;
} else {
gi.cprintf(ent, PRINT_HIGH, "Activating player identication display.\n");
ent->client->resp.id_state = true;
}
}
static void CTFSetIDView(edict_t *ent)
{
vec3_t forward, dir;
trace_t tr;
edict_t *who, *best;
float bd = 0, d;
int i;
// only check every few frames
if (level.time - ent->client->resp.lastidtime < 0.25)
return;
ent->client->resp.lastidtime = level.time;
ent->client->ps.stats[STAT_CTF_ID_VIEW] = 0;
ent->client->ps.stats[STAT_CTF_ID_VIEW_COLOR] = 0;
AngleVectors(ent->client->v_angle, forward, NULL, NULL);
VectorScale(forward, 1024, forward);
VectorAdd(ent->s.origin, forward, forward);
tr = gi.trace(ent->s.origin, NULL, NULL, forward, ent, MASK_SOLID);
if (tr.fraction < 1 && tr.ent && tr.ent->client) {
ent->client->ps.stats[STAT_CTF_ID_VIEW] =
CS_GENERAL + (tr.ent - g_edicts - 1);
if (tr.ent->client->resp.ctf_team == CTF_TEAM1)
ent->client->ps.stats[STAT_CTF_ID_VIEW_COLOR] = imageindex_sbfctf1;
else if (tr.ent->client->resp.ctf_team == CTF_TEAM2)
ent->client->ps.stats[STAT_CTF_ID_VIEW_COLOR] = imageindex_sbfctf2;
return;
}
AngleVectors(ent->client->v_angle, forward, NULL, NULL);
best = NULL;
for (i = 1; i <= maxclients->value; i++) {
who = g_edicts + i;
if (!who->inuse || who->solid == SOLID_NOT)
continue;
VectorSubtract(who->s.origin, ent->s.origin, dir);
VectorNormalize(dir);
d = DotProduct(forward, dir);
if (d > bd && loc_CanSee(ent, who)) {
bd = d;
best = who;
}
}
if (bd > 0.90) {
ent->client->ps.stats[STAT_CTF_ID_VIEW] =
CS_GENERAL + (best - g_edicts - 1);
if (best->client->resp.ctf_team == CTF_TEAM1)
ent->client->ps.stats[STAT_CTF_ID_VIEW_COLOR] = imageindex_sbfctf1;
else if (best->client->resp.ctf_team == CTF_TEAM2)
ent->client->ps.stats[STAT_CTF_ID_VIEW_COLOR] = imageindex_sbfctf2;
}
}
void SetCTFStats(edict_t *ent)
{
gitem_t *tech;
int i;
int p1, p2;
edict_t *e;
if (ctfgame.match > MATCH_NONE)
ent->client->ps.stats[STAT_CTF_MATCH] = CONFIG_CTF_MATCH;
else
ent->client->ps.stats[STAT_CTF_MATCH] = 0;
if (ctfgame.warnactive)
ent->client->ps.stats[STAT_CTF_TEAMINFO] = CONFIG_CTF_TEAMINFO;
else
ent->client->ps.stats[STAT_CTF_TEAMINFO] = 0;
//ghosting
if (ent->client->resp.ghost) {
ent->client->resp.ghost->score = ent->client->resp.score;
strcpy(ent->client->resp.ghost->netname, ent->client->pers.netname);
ent->client->resp.ghost->number = ent->s.number;
}
// logo headers for the frag display
ent->client->ps.stats[STAT_CTF_TEAM1_HEADER] = imageindex_ctfsb1;
ent->client->ps.stats[STAT_CTF_TEAM2_HEADER] = imageindex_ctfsb2;
// if during intermission, we must blink the team header of the winning team
if (level.intermissiontime && (level.framenum & 8)) { // blink 1/8th second
// note that ctfgame.total[12] is set when we go to intermission
if (ctfgame.team1 > ctfgame.team2)
ent->client->ps.stats[STAT_CTF_TEAM1_HEADER] = 0;
else if (ctfgame.team2 > ctfgame.team1)
ent->client->ps.stats[STAT_CTF_TEAM2_HEADER] = 0;
else if (ctfgame.total1 > ctfgame.total2) // frag tie breaker
ent->client->ps.stats[STAT_CTF_TEAM1_HEADER] = 0;
else if (ctfgame.total2 > ctfgame.total1)
ent->client->ps.stats[STAT_CTF_TEAM2_HEADER] = 0;
else { // tie game!
ent->client->ps.stats[STAT_CTF_TEAM1_HEADER] = 0;
ent->client->ps.stats[STAT_CTF_TEAM2_HEADER] = 0;
}
}
// tech icon
i = 0;
ent->client->ps.stats[STAT_CTF_TECH] = 0;
while (tnames[i]) {
if ((tech = FindItemByClassname(tnames[i])) != NULL &&
ent->client->pers.inventory[ITEM_INDEX(tech)]) {
ent->client->ps.stats[STAT_CTF_TECH] = gi.imageindex(tech->icon);
break;
}
i++;
}
// figure out what icon to display for team logos
// three states:
// flag at base
// flag taken
// flag dropped
p1 = imageindex_i_ctf1;
e = G_Find(NULL, FOFS(classname), "item_flag_team1");
if (e != NULL) {
if (e->solid == SOLID_NOT) {
int i;
// not at base
// check if on player
p1 = imageindex_i_ctf1d; // default to dropped
for (i = 1; i <= maxclients->value; i++)
if (g_edicts[i].inuse &&
g_edicts[i].client->pers.inventory[ITEM_INDEX(flag1_item)]) {
// enemy has it
p1 = imageindex_i_ctf1t;
break;
}
} else if (e->spawnflags & DROPPED_ITEM)
p1 = imageindex_i_ctf1d; // must be dropped
}
p2 = imageindex_i_ctf2;
e = G_Find(NULL, FOFS(classname), "item_flag_team2");
if (e != NULL) {
if (e->solid == SOLID_NOT) {
int i;
// not at base
// check if on player
p2 = imageindex_i_ctf2d; // default to dropped
for (i = 1; i <= maxclients->value; i++)
if (g_edicts[i].inuse &&
g_edicts[i].client->pers.inventory[ITEM_INDEX(flag2_item)]) {
// enemy has it
p2 = imageindex_i_ctf2t;
break;
}
} else if (e->spawnflags & DROPPED_ITEM)
p2 = imageindex_i_ctf2d; // must be dropped
}
ent->client->ps.stats[STAT_CTF_TEAM1_PIC] = p1;
ent->client->ps.stats[STAT_CTF_TEAM2_PIC] = p2;
if (ctfgame.last_flag_capture && level.time - ctfgame.last_flag_capture < 5) {
if (ctfgame.last_capture_team == CTF_TEAM1)
if (level.framenum & 8)
ent->client->ps.stats[STAT_CTF_TEAM1_PIC] = p1;
else
ent->client->ps.stats[STAT_CTF_TEAM1_PIC] = 0;
else
if (level.framenum & 8)
ent->client->ps.stats[STAT_CTF_TEAM2_PIC] = p2;
else
ent->client->ps.stats[STAT_CTF_TEAM2_PIC] = 0;
}
ent->client->ps.stats[STAT_CTF_TEAM1_CAPS] = ctfgame.team1;
ent->client->ps.stats[STAT_CTF_TEAM2_CAPS] = ctfgame.team2;
ent->client->ps.stats[STAT_CTF_FLAG_PIC] = 0;
if (ent->client->resp.ctf_team == CTF_TEAM1 &&
ent->client->pers.inventory[ITEM_INDEX(flag2_item)] &&
(level.framenum & 8))
ent->client->ps.stats[STAT_CTF_FLAG_PIC] = imageindex_i_ctf2;
else if (ent->client->resp.ctf_team == CTF_TEAM2 &&
ent->client->pers.inventory[ITEM_INDEX(flag1_item)] &&
(level.framenum & 8))
ent->client->ps.stats[STAT_CTF_FLAG_PIC] = imageindex_i_ctf1;
ent->client->ps.stats[STAT_CTF_JOINED_TEAM1_PIC] = 0;
ent->client->ps.stats[STAT_CTF_JOINED_TEAM2_PIC] = 0;
if (ent->client->resp.ctf_team == CTF_TEAM1)
ent->client->ps.stats[STAT_CTF_JOINED_TEAM1_PIC] = imageindex_i_ctfj;
else if (ent->client->resp.ctf_team == CTF_TEAM2)
ent->client->ps.stats[STAT_CTF_JOINED_TEAM2_PIC] = imageindex_i_ctfj;
if (ent->client->resp.id_state)
CTFSetIDView(ent);
else {
ent->client->ps.stats[STAT_CTF_ID_VIEW] = 0;
ent->client->ps.stats[STAT_CTF_ID_VIEW_COLOR] = 0;
}
}
/*------------------------------------------------------------------------*/
/*QUAKED info_player_team1 (1 0 0) (-16 -16 -24) (16 16 32)
potential team1 spawning position for ctf games
*/
void SP_info_player_team1(edict_t *self)
{
}
/*QUAKED info_player_team2 (0 0 1) (-16 -16 -24) (16 16 32)
potential team2 spawning position for ctf games
*/
void SP_info_player_team2(edict_t *self)
{
}
/*------------------------------------------------------------------------*/
/* GRAPPLE */
/*------------------------------------------------------------------------*/
// ent is player
void CTFPlayerResetGrapple(edict_t *ent)
{
if (ent->client && ent->client->ctf_grapple)
CTFResetGrapple(ent->client->ctf_grapple);
}
// self is grapple, not player
void CTFResetGrapple(edict_t *self)
{
if (self->owner->client->ctf_grapple) {
float volume = 1.0;
gclient_t *cl;
if (self->owner->client->silencer_shots)
volume = 0.2;
gi.sound (self->owner, CHAN_RELIABLE+CHAN_WEAPON, gi.soundindex("weapons/grapple/grreset.wav"), volume, ATTN_NORM, 0);
cl = self->owner->client;
cl->ctf_grapple = NULL;
cl->ctf_grapplereleasetime = level.time;
cl->ctf_grapplestate = CTF_GRAPPLE_STATE_FLY; // we're firing, not on hook
cl->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;
G_FreeEdict(self);
}
}
void CTFGrappleTouch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
{
float volume = 1.0;
if (other == self->owner)
return;
if (self->owner->client->ctf_grapplestate != CTF_GRAPPLE_STATE_FLY)
return;
if (surf && (surf->flags & SURF_SKY))
{
CTFResetGrapple(self);
return;
}
VectorCopy(vec3_origin, self->velocity);
PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
if (other->takedamage) {
T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 1, 0, MOD_GRAPPLE);
CTFResetGrapple(self);
return;
}
self->owner->client->ctf_grapplestate = CTF_GRAPPLE_STATE_PULL; // we're on hook
self->enemy = other;
self->solid = SOLID_NOT;
if (self->owner->client->silencer_shots)
volume = 0.2;
gi.sound (self->owner, CHAN_RELIABLE+CHAN_WEAPON, gi.soundindex("weapons/grapple/grpull.wav"), volume, ATTN_NORM, 0);
gi.sound (self, CHAN_WEAPON, gi.soundindex("weapons/grapple/grhit.wav"), volume, ATTN_NORM, 0);
gi.WriteByte (svc_temp_entity);
gi.WriteByte (TE_SPARKS);
gi.WritePosition (self->s.origin);
if (!plane)
gi.WriteDir (vec3_origin);
else
gi.WriteDir (plane->normal);
gi.multicast (self->s.origin, MULTICAST_PVS);
}
// draw beam between grapple and self
void CTFGrappleDrawCable(edict_t *self)
{
vec3_t offset, start, end, f, r;
vec3_t dir;
float distance;
AngleVectors (self->owner->client->v_angle, f, r, NULL);
VectorSet(offset, 16, 16, self->owner->viewheight-8);
P_ProjectSource (self->owner->client, self->owner->s.origin, offset, f, r, start);
VectorSubtract(start, self->owner->s.origin, offset);
VectorSubtract (start, self->s.origin, dir);
distance = VectorLength(dir);
// don't draw cable if close
if (distance < 64)
return;
#if 0
if (distance > 256)
return;
// check for min/max pitch
vectoangles (dir, angles);
if (angles[0] < -180)
angles[0] += 360;
if (fabs(angles[0]) > 45)
return;
trace_t tr; //!!
tr = gi.trace (start, NULL, NULL, self->s.origin, self, MASK_SHOT);
if (tr.ent != self) {
CTFResetGrapple(self);
return;
}
#endif
// adjust start for beam origin being in middle of a segment
// VectorMA (start, 8, f, start);
VectorCopy (self->s.origin, end);
// adjust end z for end spot since the monster is currently dead
// end[2] = self->absmin[2] + self->size[2] / 2;
gi.WriteByte (svc_temp_entity);
#if 1 //def USE_GRAPPLE_CABLE
gi.WriteByte (TE_GRAPPLE_CABLE);
gi.WriteShort (self->owner - g_edicts);
gi.WritePosition (self->owner->s.origin);
gi.WritePosition (end);
gi.WritePosition (offset);
#else
gi.WriteByte (TE_MEDIC_CABLE_ATTACK);
gi.WriteShort (self - g_edicts);
gi.WritePosition (end);
gi.WritePosition (start);
#endif
gi.multicast (self->s.origin, MULTICAST_PVS);
}
void SV_AddGravity (edict_t *ent);
// pull the player toward the grapple
void CTFGrapplePull(edict_t *self)
{
vec3_t hookdir, v;
float vlen;
if (strcmp(self->owner->client->pers.weapon->classname, "weapon_grapple") == 0 &&
!self->owner->client->newweapon &&
self->owner->client->weaponstate != WEAPON_FIRING &&
self->owner->client->weaponstate != WEAPON_ACTIVATING) {
CTFResetGrapple(self);
return;
}
if (self->enemy) {
if (self->enemy->solid == SOLID_NOT) {
CTFResetGrapple(self);
return;
}
if (self->enemy->solid == SOLID_BBOX) {
VectorScale(self->enemy->size, 0.5, v);
VectorAdd(v, self->enemy->s.origin, v);
VectorAdd(v, self->enemy->mins, self->s.origin);
gi.linkentity (self);
} else
VectorCopy(self->enemy->velocity, self->velocity);
if (self->enemy->takedamage &&
!CheckTeamDamage (self->enemy, self->owner)) {
float volume = 1.0;
if (self->owner->client->silencer_shots)
volume = 0.2;
T_Damage (self->enemy, self, self->owner, self->velocity, self->s.origin, vec3_origin, 1, 1, 0, MOD_GRAPPLE);
gi.sound (self, CHAN_WEAPON, gi.soundindex("weapons/grapple/grhurt.wav"), volume, ATTN_NORM, 0);
}
if (self->enemy->deadflag) { // he died
CTFResetGrapple(self);
return;
}
}
CTFGrappleDrawCable(self);
if (self->owner->client->ctf_grapplestate > CTF_GRAPPLE_STATE_FLY) {
// pull player toward grapple
// this causes icky stuff with prediction, we need to extend
// the prediction layer to include two new fields in the player
// move stuff: a point and a velocity. The client should add
// that velociy in the direction of the point
vec3_t forward, up;
AngleVectors (self->owner->client->v_angle, forward, NULL, up);
VectorCopy(self->owner->s.origin, v);
v[2] += self->owner->viewheight;
VectorSubtract (self->s.origin, v, hookdir);
vlen = VectorLength(hookdir);
if (self->owner->client->ctf_grapplestate == CTF_GRAPPLE_STATE_PULL &&
vlen < 64) {
float volume = 1.0;
if (self->owner->client->silencer_shots)
volume = 0.2;
self->owner->client->ps.pmove.pm_flags |= PMF_NO_PREDICTION;
gi.sound (self->owner, CHAN_RELIABLE+CHAN_WEAPON, gi.soundindex("weapons/grapple/grhang.wav"), volume, ATTN_NORM, 0);
self->owner->client->ctf_grapplestate = CTF_GRAPPLE_STATE_HANG;
}
VectorNormalize (hookdir);
VectorScale(hookdir, CTF_GRAPPLE_PULL_SPEED, hookdir);
VectorCopy(hookdir, self->owner->velocity);
SV_AddGravity(self->owner);
}
}
void CTFFireGrapple (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect)
{
edict_t *grapple;
trace_t tr;
VectorNormalize (dir);
grapple = G_Spawn();
VectorCopy (start, grapple->s.origin);
VectorCopy (start, grapple->s.old_origin);
vectoangles (dir, grapple->s.angles);
VectorScale (dir, speed, grapple->velocity);
grapple->movetype = MOVETYPE_FLYMISSILE;
grapple->clipmask = MASK_SHOT;
grapple->solid = SOLID_BBOX;
grapple->s.effects |= effect;
VectorClear (grapple->mins);
VectorClear (grapple->maxs);
grapple->s.modelindex = gi.modelindex ("models/weapons/grapple/hook/tris.md2");
// grapple->s.sound = gi.soundindex ("misc/lasfly.wav");
grapple->owner = self;
grapple->touch = CTFGrappleTouch;
// grapple->nextthink = level.time + FRAMETIME;
// grapple->think = CTFGrappleThink;
grapple->dmg = damage;
self->client->ctf_grapple = grapple;
self->client->ctf_grapplestate = CTF_GRAPPLE_STATE_FLY; // we're firing, not on hook
gi.linkentity (grapple);
tr = gi.trace (self->s.origin, NULL, NULL, grapple->s.origin, grapple, MASK_SHOT);
if (tr.fraction < 1.0)
{
VectorMA (grapple->s.origin, -10, dir, grapple->s.origin);
grapple->touch (grapple, tr.ent, NULL, NULL);
}
}
void CTFGrappleFire (edict_t *ent, vec3_t g_offset, int damage, int effect)
{
vec3_t forward, right;
vec3_t start;
vec3_t offset;
float volume = 1.0;
if (ent->client->ctf_grapplestate > CTF_GRAPPLE_STATE_FLY)
return; // it's already out
AngleVectors (ent->client->v_angle, forward, right, NULL);
// VectorSet(offset, 24, 16, ent->viewheight-8+2);
VectorSet(offset, 24, 8, ent->viewheight-8+2);
VectorAdd (offset, g_offset, offset);
P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -