game: sync ctf g_{chase,func,items,misc,weapon}

This commit is contained in:
Denis Pauk 2023-11-03 00:36:57 +02:00
parent c41b72125a
commit 9947744114
14 changed files with 751 additions and 11851 deletions

View file

@ -1481,12 +1481,12 @@ CTF_OBJS_ = \
src/common/shared/rand.o \
src/common/shared/shared.o \
src/ctf/g_ai.o \
src/ctf/g_chase.o \
src/game/g_chase.o \
src/ctf/g_cmds.o \
src/ctf/g_combat.o \
src/game/g_ctf.o \
src/ctf/g_func.o \
src/ctf/g_items.o \
src/game/g_func.o \
src/game/g_items.o \
src/game/g_newai.o \
src/game/g_newdm.o \
src/game/g_newfnc.o \
@ -1494,8 +1494,8 @@ CTF_OBJS_ = \
src/game/g_newtrig.o \
src/game/g_newweap.o \
src/ctf/g_main.o \
src/ctf/g_misc.o \
src/ctf/g_monster.o \
src/game/g_misc.o \
src/game/g_monster.o \
src/ctf/g_phys.o \
src/ctf/g_save.o \
src/game/g_sphere.o \
@ -1543,7 +1543,7 @@ CTF_OBJS_ = \
src/ctf/player/hud.o \
src/ctf/player/trail.o \
src/ctf/player/view.o \
src/ctf/player/weapon.o
src/game/player/weapon.o
# ----------

View file

@ -1,210 +0,0 @@
/*
* Copyright (C) 1997-2001 Id Software, Inc.
*
* 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 the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* =======================================================================
*
* Chase cam.
*
* =======================================================================
*/
#include "header/local.h"
void
UpdateChaseCam(edict_t *ent)
{
vec3_t o, ownerv, goal;
edict_t *targ;
vec3_t forward, right;
trace_t trace;
int i;
vec3_t angles;
/* is our chase target gone? */
if (!ent->client->chase_target->inuse)
{
ent->client->chase_target = NULL;
return;
}
targ = ent->client->chase_target;
VectorCopy(targ->s.origin, ownerv);
ownerv[2] += targ->viewheight;
VectorCopy(targ->client->v_angle, angles);
if (angles[PITCH] > 56)
{
angles[PITCH] = 56;
}
AngleVectors(angles, forward, right, NULL);
VectorNormalize(forward);
VectorMA(ownerv, -30, forward, o);
if (o[2] < targ->s.origin[2] + 20)
{
o[2] = targ->s.origin[2] + 20;
}
/* jump animation lifts */
if (!targ->groundentity)
{
o[2] += 16;
}
trace = gi.trace(ownerv, vec3_origin, vec3_origin, o, targ, MASK_SOLID);
VectorCopy(trace.endpos, goal);
VectorMA(goal, 2, forward, goal);
/* pad for floors and ceilings */
VectorCopy(goal, o);
o[2] += 6;
trace = gi.trace(goal, vec3_origin, vec3_origin, o, targ, MASK_SOLID);
if (trace.fraction < 1)
{
VectorCopy(trace.endpos, goal);
goal[2] -= 6;
}
VectorCopy(goal, o);
o[2] -= 6;
trace = gi.trace(goal, vec3_origin, vec3_origin, o, targ, MASK_SOLID);
if (trace.fraction < 1)
{
VectorCopy(trace.endpos, goal);
goal[2] += 6;
}
ent->client->ps.pmove.pm_type = PM_FREEZE;
VectorCopy(goal, ent->s.origin);
for (i = 0; i < 3; i++)
{
ent->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(
targ->client->v_angle[i] - ent->client->resp.cmd_angles[i]);
}
VectorCopy(targ->client->v_angle, ent->client->ps.viewangles);
VectorCopy(targ->client->v_angle, ent->client->v_angle);
ent->viewheight = 0;
ent->client->ps.pmove.pm_flags |= PMF_NO_PREDICTION;
gi.linkentity(ent);
if ((!ent->client->showscores && !ent->client->menu &&
!ent->client->showinventory && !ent->client->showhelp &&
!(level.framenum & 31)) || ent->client->update_chase)
{
char s[1024];
ent->client->update_chase = false;
sprintf(s, "xv 0 yb -68 string2 \"Chasing %s\"",
targ->client->pers.netname);
gi.WriteByte(svc_layout);
gi.WriteString(s);
gi.unicast(ent, false);
}
}
void
ChaseNext(edict_t *ent)
{
int i;
edict_t *e;
if (!ent->client->chase_target)
{
return;
}
i = ent->client->chase_target - g_edicts;
do
{
i++;
if (i > maxclients->value)
{
i = 1;
}
e = g_edicts + i;
if (!e->inuse)
{
continue;
}
if (e->solid != SOLID_NOT)
{
break;
}
}
while (e != ent->client->chase_target);
ent->client->chase_target = e;
ent->client->update_chase = true;
}
void
ChasePrev(edict_t *ent)
{
int i;
edict_t *e;
if (!ent->client->chase_target)
{
return;
}
i = ent->client->chase_target - g_edicts;
do
{
i--;
if (i < 1)
{
i = maxclients->value;
}
e = g_edicts + i;
if (!e->inuse)
{
continue;
}
if (e->solid != SOLID_NOT)
{
break;
}
}
while (e != ent->client->chase_target);
ent->client->chase_target = e;
ent->client->update_chase = true;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -27,6 +27,24 @@
#include "header/local.h"
#define STOP_EPSILON 0.1
#define MAX_CLIP_PLANES 5
#define FRICTION 6
#define WATERFRICTION 1
void SV_Physics_NewToss(edict_t *ent);
typedef struct
{
edict_t *ent;
vec3_t origin;
vec3_t angles;
float deltayaw;
} pushed_t;
static pushed_t pushed[MAX_EDICTS], *pushed_p;
static edict_t *obstacle;
/*
* pushmove objects do not obey gravity, and do not interact with each other or
* trigger fields, but block normal movement and push normal objects when they move.
@ -164,11 +182,10 @@ SV_Impact(edict_t *e1, trace_t *trace)
/*
* Slide off of the impacting object
* returns the blocked flags (1 = floor,
* 2 = step / wall)
* returns the blocked flags:
* 1 = floor
* 2 = step / wall
*/
#define STOP_EPSILON 0.1
int
ClipVelocity(vec3_t in, vec3_t normal, vec3_t out, float overbounce)
{
@ -205,13 +222,15 @@ ClipVelocity(vec3_t in, vec3_t normal, vec3_t out, float overbounce)
}
/*
* The basic solid body movement clip that slides along multiple planes
* Returns the clipflags if the velocity was modified (hit something solid)
* The basic solid body movement clip
* that slides along multiple planes
* Returns the clipflags if the velocity
* was modified (hit something solid)
*
* 1 = floor
* 2 = wall / step
* 4 = dead stop
*/
#define MAX_CLIP_PLANES 5
int
SV_FlyMove(edict_t *ent, float time, int mask)
{
@ -228,6 +247,11 @@ SV_FlyMove(edict_t *ent, float time, int mask)
float time_left;
int blocked;
if (!ent)
{
return 0;
}
numbumps = 4;
blocked = 0;
@ -307,14 +331,15 @@ SV_FlyMove(edict_t *ent, float time, int mask)
VectorCopy(trace.plane.normal, planes[numplanes]);
numplanes++;
/* modify original_velocity so it parallels all of the clip planes */
/* modify original_velocity so it
parallels all of the clip planes */
for (i = 0; i < numplanes; i++)
{
ClipVelocity(original_velocity, planes[i], new_velocity, 1);
for (j = 0; j < numplanes; j++)
{
if (j != i)
if ((j != i) && !VectorCompare(planes[i], planes[j]))
{
if (DotProduct(new_velocity, planes[j]) < 0)
{
@ -336,7 +361,6 @@ SV_FlyMove(edict_t *ent, float time, int mask)
}
else
{
/* go along the crease */
if (numplanes != 2)
{
@ -349,8 +373,9 @@ SV_FlyMove(edict_t *ent, float time, int mask)
VectorScale(dir, d, ent->velocity);
}
/* if original velocity is against the original velocity,
stop dead to avoid tiny occilations in sloping corners */
/* if original velocity is against the original
velocity, stop dead to avoid tiny occilations
in sloping corners */
if (DotProduct(ent->velocity, primal_velocity) <= 0)
{
VectorCopy(vec3_origin, ent->velocity);
@ -364,7 +389,20 @@ SV_FlyMove(edict_t *ent, float time, int mask)
void
SV_AddGravity(edict_t *ent)
{
ent->velocity[2] -= ent->gravity * sv_gravity->value * FRAMETIME;
if (!ent)
{
return;
}
if (ent->gravityVector[2] > 0)
{
VectorMA(ent->velocity, ent->gravity * sv_gravity->value * FRAMETIME,
ent->gravityVector, ent->velocity);
}
else
{
ent->velocity[2] -= ent->gravity * sv_gravity->value * FRAMETIME;
}
}
/*
@ -384,6 +422,11 @@ RealBoundingBox(edict_t *ent, vec3_t mins, vec3_t maxs)
vec3_t p[8];
int i, j, k, j2, k4;
if (!ent)
{
return;
}
for (k = 0; k < 2; k++)
{
k4 = k * 4;
@ -479,13 +522,9 @@ RealBoundingBox(edict_t *ent, vec3_t mins, vec3_t maxs)
}
}
/*
* ===============================================================================
*
* PUSHMOVE
*
* ===============================================================================
*/
/* ================================================================== */
/* PUSHMOVE */
/*
* Does not change the entities velocity at all
@ -514,6 +553,17 @@ retry:
trace = gi.trace(start, ent->mins, ent->maxs, end, ent, mask);
/* startsolid treats different-content volumes
as continuous, like the bbox of a monster/player
and the floor of an elevator. So do another trace
that only collides with BSP so that we make a best
effort to keep these entities inside non-solid space
*/
if (trace.startsolid && (mask & ~MASK_SOLID))
{
trace = gi.trace (start, ent->mins, ent->maxs, end, ent, MASK_SOLID);
}
VectorCopy(trace.endpos, ent->s.origin);
gi.linkentity(ent);
@ -537,7 +587,8 @@ retry:
{
SV_Impact(ent, &trace);
/* if the pushed entity went away and the pusher is still there */
/* if the pushed entity went away
and the pusher is still there */
if (!trace.ent->inuse && ent->inuse)
{
/* move the pusher back and try again */
@ -547,6 +598,8 @@ retry:
}
}
ent->gravity = 1.0;
if (ent->inuse)
{
G_TouchTriggers(ent);
@ -555,17 +608,6 @@ retry:
return trace;
}
typedef struct
{
edict_t *ent;
vec3_t origin;
vec3_t angles;
float deltayaw;
} pushed_t;
pushed_t pushed[MAX_EDICTS], *pushed_p;
edict_t *obstacle;
/*
* Objects need to be moved back on a failed push,
* otherwise riders would continue to slide.
@ -579,6 +621,11 @@ SV_Push(edict_t *pusher, vec3_t move, vec3_t amove)
vec3_t org, org2, move2, forward, right, up;
vec3_t realmins, realmaxs;
if (!pusher)
{
return false;
}
/* clamp the move to 1/8 units, so the position will
be accurate for client side prediction */
for (i = 0; i < 3; i++)
@ -623,7 +670,8 @@ SV_Push(edict_t *pusher, vec3_t move, vec3_t amove)
rotating brush models. */
RealBoundingBox(pusher, realmins, realmaxs);
/* see if any solid entities are inside the final position */
/* see if any solid entities
are inside the final position */
check = g_edicts + 1;
for (e = 1; e < globals.num_edicts; e++, check++)
@ -646,7 +694,8 @@ SV_Push(edict_t *pusher, vec3_t move, vec3_t amove)
continue; /* not linked in anywhere */
}
/* if the entity is standing on the pusher, it will definitely be moved */
/* if the entity is standing on the pusher,
it will definitely be moved */
if (check->groundentity != pusher)
{
/* see if the ent needs to be tested */
@ -660,7 +709,8 @@ SV_Push(edict_t *pusher, vec3_t move, vec3_t amove)
continue;
}
/* see if the ent's bbox is inside the pusher's final position */
/* see if the ent's bbox is inside
the pusher's final position */
if (!SV_TestEntityPosition(check))
{
continue;
@ -702,13 +752,16 @@ SV_Push(edict_t *pusher, vec3_t move, vec3_t amove)
if (!block)
{
/* pushed ok */
/* pushed ok */
gi.linkentity(check);
/* impact? */
continue;
}
/* if it is ok to leave in the old position, do it
this is only relevent for riding entities, not pushed */
this is only relevent for riding entities, not
pushed */
VectorSubtract(check->s.origin, move, check->s.origin);
block = SV_TestEntityPosition(check);
@ -719,11 +772,12 @@ SV_Push(edict_t *pusher, vec3_t move, vec3_t amove)
}
}
/* save off the obstacle so we can call the block function */
/* save off the obstacle so we can
call the block function */
obstacle = check;
/* move back any entities we already moved
go backwards, so if the same entity was pushed/
go backwards, so if the same entity was pushed
twice, it goes back to the original position */
for (p = pushed_p - 1; p >= pushed; p--)
{
@ -760,15 +814,21 @@ SV_Physics_Pusher(edict_t *ent)
vec3_t move, amove;
edict_t *part, *mv;
/* if not a team captain, so movement will be handled elsewhere */
if (!ent)
{
return;
}
/* if not a team captain, so movement
will be handled elsewhere */
if (ent->flags & FL_TEAMSLAVE)
{
return;
}
/* make sure all team slaves can move before commiting any moves
or calling any think functions if the move is blocked, all
moved objects will be backed out */
/* make sure all team slaves can move before commiting
any moves or calling any think functions if the move
is blocked, all moved objects will be backed out */
pushed_p = pushed;
for (part = ent; part; part = part->teamchain)
@ -787,14 +847,15 @@ SV_Physics_Pusher(edict_t *ent)
}
}
if (pushed_p > &pushed[MAX_EDICTS-1])
if (pushed_p > &pushed[MAX_EDICTS - 1])
{
gi.error("pushed_p > &pushed[MAX_EDICTS-1], memory corrupted");
gi.error("pushed_p > &pushed[MAX_EDICTS - 1], memory corrupted");
}
if (part)
{
/* the move failed, bump all nextthink times and back out moves */
/* the move failed, bump all nextthink
times and back out moves */
for (mv = ent; mv; mv = mv->teamchain)
{
if (mv->nextthink > 0)
@ -816,7 +877,11 @@ SV_Physics_Pusher(edict_t *ent)
/* the move succeeded, so call all think functions */
for (part = ent; part; part = part->teamchain)
{
SV_RunThink(part);
/* prevent entities that are on trains that have gone away from thinking! */
if (part->inuse)
{
SV_RunThink(part);
}
}
}
}
@ -829,6 +894,11 @@ SV_Physics_Pusher(edict_t *ent)
void
SV_Physics_None(edict_t *ent)
{
if (!ent)
{
return;
}
/* regular thinking */
SV_RunThink(ent);
}
@ -839,6 +909,11 @@ SV_Physics_None(edict_t *ent)
void
SV_Physics_Noclip(edict_t *ent)
{
if (!ent)
{
return;
}
/* regular thinking */
if (!SV_RunThink(ent))
{
@ -851,16 +926,13 @@ SV_Physics_Noclip(edict_t *ent)
gi.linkentity(ent);
}
/*
* ==============================================================================
*
* TOSS / BOUNCE
*
* ==============================================================================
*/
/* ================================================================== */
/* TOSS / BOUNCE */
/*
* Toss, bounce, and fly movement. When onground, do nothing.
* Toss, bounce, and fly movement.
* When onground, do nothing.
*/
void
SV_Physics_Toss(edict_t *ent)
@ -873,10 +945,22 @@ SV_Physics_Toss(edict_t *ent)
qboolean isinwater;
vec3_t old_origin;
if (!ent)
{
return;
}
/* regular thinking */
SV_RunThink(ent);
/* if not a team captain, so movement will be handled elsewhere */
/* entities are very often freed during thinking */
if (!ent->inuse)
{
return;
}
/* if not a team captain, so movement
will be handled elsewhere */
if (ent->flags & FL_TEAMSLAVE)
{
return;
@ -897,7 +981,7 @@ SV_Physics_Toss(edict_t *ent)
}
/* if onground, return without moving */
if (ent->groundentity)
if (ent->groundentity && (ent->gravity > 0.0))
{
return;
}
@ -908,7 +992,8 @@ SV_Physics_Toss(edict_t *ent)
/* add gravity */
if ((ent->movetype != MOVETYPE_FLY) &&
(ent->movetype != MOVETYPE_FLYMISSILE))
(ent->movetype != MOVETYPE_FLYMISSILE)
&& (ent->movetype != MOVETYPE_WALLBOUNCE))
{
SV_AddGravity(ent);
}
@ -927,7 +1012,11 @@ SV_Physics_Toss(edict_t *ent)
if (trace.fraction < 1)
{
if (ent->movetype == MOVETYPE_BOUNCE)
if (ent->movetype == MOVETYPE_WALLBOUNCE)
{
backoff = 2.0;
}
else if (ent->movetype == MOVETYPE_BOUNCE)
{
backoff = 1.5;
}
@ -938,8 +1027,14 @@ SV_Physics_Toss(edict_t *ent)
ClipVelocity(ent->velocity, trace.plane.normal, ent->velocity, backoff);
if (ent->movetype == MOVETYPE_WALLBOUNCE)
{
vectoangles(ent->velocity, ent->s.angles);
}
/* stop if on ground */
if (trace.plane.normal[2] > 0.7)
if ((trace.plane.normal[2] > 0.7) &&
(ent->movetype != MOVETYPE_WALLBOUNCE))
{
if ((ent->velocity[2] < 60) || (ent->movetype != MOVETYPE_BOUNCE))
{
@ -967,8 +1062,12 @@ SV_Physics_Toss(edict_t *ent)
if (!wasinwater && isinwater)
{
gi.positioned_sound(old_origin, g_edicts, CHAN_AUTO,
gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0);
/* don't play splash sound for entities already in water on level start */
if (level.framenum > 3)
{
gi.positioned_sound(old_origin, g_edicts, CHAN_AUTO,
gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0);
}
}
else if (wasinwater && !isinwater)
{
@ -984,34 +1083,32 @@ SV_Physics_Toss(edict_t *ent)
}
}
/*
* ===============================================================================
*
* STEPPING MOVEMENT
*
* ===============================================================================
*/
/* ================================================================== */
/* STEPPING MOVEMENT */
/*
* Monsters freefall when they don't have a ground entity, otherwise
* all movement is done with discrete steps.
* Monsters freefall when they don't have a ground
* entity, otherwise all movement is done with
* discrete steps.
*
* This is also used for objects that have become still on the ground, but
* will fall if the floor is pulled out from under them.
* This is also used for objects that have become
* still on the ground, but will fall if the floor
* is pulled out from under them.
*/
#define sv_stopspeed 100
#define sv_friction 6
#define sv_waterfriction 1
void
SV_AddRotationalFriction(edict_t *ent)
{
int n;
float adjustment;
if (!ent)
{
return;
}
VectorMA(ent->s.angles, FRAMETIME, ent->avelocity, ent->s.angles);
adjustment = FRAMETIME * sv_stopspeed * sv_friction;
adjustment = FRAMETIME * sv_stopspeed->value * FRICTION;
for (n = 0; n < 3; n++)
{
@ -1046,6 +1143,13 @@ SV_Physics_Step(edict_t *ent)
float friction;
edict_t *groundentity;
int mask;
vec3_t oldorig;
trace_t tr;
if (!ent)
{
return;
}
/* airborn monsters should always check for ground */
if (!ent->groundentity)
@ -1072,8 +1176,8 @@ SV_Physics_Step(edict_t *ent)
}
/* add gravity except:
flying monsters
swimming monsters who are in the water */
- flying monsters
- swimming monsters who are in the water */
if (!wasonground)
{
if (!(ent->flags & FL_FLY))
@ -1097,8 +1201,8 @@ SV_Physics_Step(edict_t *ent)
if ((ent->flags & FL_FLY) && (ent->velocity[2] != 0))
{
speed = fabs(ent->velocity[2]);
control = speed < sv_stopspeed ? sv_stopspeed : speed;
friction = sv_friction / 3;
control = speed < sv_stopspeed->value ? sv_stopspeed->value : speed;
friction = FRICTION / 3;
newspeed = speed - (FRAMETIME * control * friction);
if (newspeed < 0)
@ -1114,9 +1218,8 @@ SV_Physics_Step(edict_t *ent)
if ((ent->flags & FL_SWIM) && (ent->velocity[2] != 0))
{
speed = fabs(ent->velocity[2]);
control = speed < sv_stopspeed ? sv_stopspeed : speed;
newspeed = speed -
(FRAMETIME * control * sv_waterfriction * ent->waterlevel);
control = speed < sv_stopspeed->value ? sv_stopspeed->value : speed;
newspeed = speed - (FRAMETIME * control * WATERFRICTION * ent->waterlevel);
if (newspeed < 0)
{
@ -1129,7 +1232,7 @@ SV_Physics_Step(edict_t *ent)
if (ent->velocity[2] || ent->velocity[1] || ent->velocity[0])
{
/* apply friction let dead monsters who
/* apply friction: let dead monsters who
aren't completely onground slide */
if ((wasonground) || (ent->flags & (FL_SWIM | FL_FLY)))
{
@ -1140,9 +1243,9 @@ SV_Physics_Step(edict_t *ent)
if (speed)
{
friction = sv_friction;
friction = FRICTION;
control = speed < sv_stopspeed ? sv_stopspeed : speed;
control = speed < sv_stopspeed->value ? sv_stopspeed->value : speed;
newspeed = speed - FRAMETIME * control * friction;
if (newspeed < 0)
@ -1167,11 +1270,33 @@ SV_Physics_Step(edict_t *ent)
mask = MASK_SOLID;
}
VectorCopy(ent->s.origin, oldorig);
SV_FlyMove(ent, FRAMETIME, mask);
/* Evil hack to work around dead parasites (and maybe other monster)
falling through the worldmodel into the void. We copy the current
origin (see above) and after the SV_FlyMove() was performend we
checl if we're stuck in the world model. If yes we're undoing the
move. */
if (!VectorCompare(ent->s.origin, oldorig))
{
tr = gi.trace(ent->s.origin, ent->mins, ent->maxs, ent->s.origin, ent, mask);
if (tr.startsolid)
{
VectorCopy(oldorig, ent->s.origin);
}
}
gi.linkentity(ent);
ent->gravity = 1.0;
G_TouchTriggers(ent);
if (!ent->inuse)
{
return;
}
if (ent->groundentity)
{
if (!wasonground)
@ -1184,15 +1309,39 @@ SV_Physics_Step(edict_t *ent)
}
}
if (!ent->inuse) /* g_touchtrigger free problem */
{
return;
}
/* regular thinking */
SV_RunThink(ent);
}
/* ============================================================================ */
/* ================================================================== */
void
G_RunEntity(edict_t *ent)
{
trace_t trace;
vec3_t previous_origin;
qboolean saved_origin;
if (!ent)
{
return;
}
if (ent->movetype == MOVETYPE_STEP)
{
VectorCopy(ent->s.origin, previous_origin);
saved_origin = true;
}
else
{
saved_origin = false;
}
if (ent->prethink)
{
ent->prethink(ent);
@ -1217,10 +1366,166 @@ G_RunEntity(edict_t *ent)
case MOVETYPE_BOUNCE:
case MOVETYPE_FLY:
case MOVETYPE_FLYMISSILE:
case MOVETYPE_WALLBOUNCE:
SV_Physics_Toss(ent);
break;
case MOVETYPE_NEWTOSS:
SV_Physics_NewToss(ent);
break;
default:
gi.error("SV_Physics: bad movetype %i", (int)ent->movetype);
}
/* if we moved, check and fix origin if needed */
/* also check inuse since entities are very often freed while thinking */
if (saved_origin && ent->inuse && !VectorCompare(ent->s.origin, previous_origin))
{
trace = gi.trace(ent->s.origin, ent->mins, ent->maxs,
previous_origin, ent, MASK_MONSTERSOLID);
if (trace.allsolid || trace.startsolid)
{
VectorCopy(previous_origin, ent->s.origin);
}
}
}
/*
* Toss, bounce, and fly movement. When on ground and
* no velocity, do nothing. With velocity, slide.
*/
void
SV_Physics_NewToss(edict_t *ent)
{
trace_t trace;
vec3_t move;
edict_t *slave;
qboolean wasinwater;
qboolean isinwater;
float speed, newspeed;
vec3_t old_origin;
if (!ent)
{
return;
}
/* regular thinking */
SV_RunThink(ent);
/* if not a team captain, so movement will be handled elsewhere */
if (ent->flags & FL_TEAMSLAVE)
{
return;
}
/* find out what we're sitting on. */
VectorCopy(ent->s.origin, move);
move[2] -= 0.25;
trace = gi.trace(ent->s.origin, ent->mins, ent->maxs,
move, ent, ent->clipmask);
if (ent->groundentity && ent->groundentity->inuse)
{
ent->groundentity = trace.ent;
}
else
{
ent->groundentity = NULL;
}
/* if we're sitting on something flat and have no velocity of our own, return. */
if (ent->groundentity && (trace.plane.normal[2] == 1.0) &&
!ent->velocity[0] && !ent->velocity[1] && !ent->velocity[2])
{
return;
}
/* store the old origin */
VectorCopy(ent->s.origin, old_origin);
SV_CheckVelocity(ent);
/* add gravity */
SV_AddGravity(ent);
if (ent->avelocity[0] || ent->avelocity[1] || ent->avelocity[2])
{
SV_AddRotationalFriction(ent);
}
/* add friction */
speed = VectorLength(ent->velocity);
if (ent->waterlevel) /* friction for water movement */
{
newspeed = speed - (WATERFRICTION * 6 * ent->waterlevel);
if (newspeed < 0)
{
newspeed = 0;
}
newspeed /= speed;
VectorScale(ent->velocity, newspeed, ent->velocity);
}
else if (!ent->groundentity) /* friction for air movement */
{
newspeed = speed - ((FRICTION));
if (newspeed < 0)
{
newspeed = 0;
}
newspeed /= speed;
VectorScale(ent->velocity, newspeed, ent->velocity);
}
else /* use ground friction */
{
newspeed = speed - (FRICTION * 6);
if (newspeed < 0)
{
newspeed = 0;
}
newspeed /= speed;
VectorScale(ent->velocity, newspeed, ent->velocity);
}
SV_FlyMove(ent, FRAMETIME, ent->clipmask);
gi.linkentity(ent);
G_TouchTriggers(ent);
/* check for water transition */
wasinwater = (ent->watertype & MASK_WATER);
ent->watertype = gi.pointcontents(ent->s.origin);
isinwater = ent->watertype & MASK_WATER;
if (isinwater)
{
ent->waterlevel = 1;
}
else
{
ent->waterlevel = 0;
}
if (!wasinwater && isinwater)
{
gi.positioned_sound(old_origin, g_edicts, CHAN_AUTO, gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0);
}
else if (wasinwater && !isinwater)
{
gi.positioned_sound(ent->s.origin, g_edicts, CHAN_AUTO, gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0);
}
/* move teamslaves */
for (slave = ent->teamchain; slave; slave = slave->teamchain)
{
VectorCopy(ent->s.origin, slave->s.origin);
gi.linkentity(slave);
}
}

File diff suppressed because it is too large Load diff

View file

@ -143,6 +143,20 @@ UpdateChaseCam(edict_t *ent)
ent->viewheight = 0;
ent->client->ps.pmove.pm_flags |= PMF_NO_PREDICTION;
gi.linkentity(ent);
if ((!ent->client->showscores && !ent->client->menu &&
!ent->client->showinventory && !ent->client->showhelp &&
!(level.framenum & 31)) || ent->client->update_chase)
{
char s[1024];
ent->client->update_chase = false;
sprintf(s, "xv 0 yb -68 string2 \"Chasing %s\"",
targ->client->pers.netname);
gi.WriteByte(svc_layout);
gi.WriteString(s);
gi.unicast(ent, false);
}
}
void
@ -179,6 +193,11 @@ ChaseNext(edict_t *ent)
continue;
}
if (e->solid != SOLID_NOT)
{
break;
}
if (!e->client->resp.spectator)
{
break;
@ -224,11 +243,17 @@ ChasePrev(edict_t *ent)
continue;
}
if (e->solid != SOLID_NOT)
{
break;
}
if (!e->client->resp.spectator)
{
break;
}
}
while (e != ent->client->chase_target);
ent->client->chase_target = e;

View file

@ -147,6 +147,11 @@ Killed(edict_t *targ, edict_t *inflictor, edict_t *attacker,
return;
}
if (targ->health < -999)
{
targ->health = -999;
}
/* 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. */
@ -660,6 +665,15 @@ M_ReactToDamage(edict_t *targ, edict_t *attacker, edict_t *inflictor)
qboolean
CheckTeamDamage(edict_t *targ, edict_t *attacker)
{
if (ctf->value && targ->client && attacker->client)
{
if ((targ->client->resp.ctf_team == attacker->client->resp.ctf_team) &&
(targ != attacker))
{
return true;
}
}
return false;
}

View file

@ -786,6 +786,7 @@ plat_spawn_inside_trigger(edict_t *ent)
tmin[0] = ent->mins[0] + 25;
tmin[1] = ent->mins[1] + 25;
tmin[2] = ent->mins[2];
tmax[0] = ent->maxs[0] - 25;
tmax[1] = ent->maxs[1] - 25;
@ -1701,7 +1702,11 @@ SP_func_rotating(edict_t *ent)
}
ent->use = rotating_use;
ent->blocked = rotating_blocked;
if (ent->dmg)
{
ent->blocked = rotating_blocked;
}
if (ent->spawnflags & 1)
{

View file

@ -163,14 +163,25 @@ DoRespawn(edict_t *ent)
master = ent->teammaster;
for (count = 0, ent = master; ent; ent = ent->chain, count++)
/* in ctf, when we are weapons stay, only the master
of a team of weapons is spawned */
if (ctf->value &&
((int)dmflags->value & DF_WEAPONS_STAY) &&
master->item && (master->item->flags & IT_WEAPON))
{
ent = master;
}
choice = count ? randk() % count : 0;
for (count = 0, ent = master; count < choice; ent = ent->chain, count++)
else
{
for (count = 0, ent = master; ent; ent = ent->chain, count++)
{
}
choice = count ? randk() % count : 0;
for (count = 0, ent = master; count < choice; ent = ent->chain, count++)
{
}
}
}
@ -246,6 +257,20 @@ Pickup_Powerup(edict_t *ent, edict_t *other)
{
SetRespawn(ent, ent->item->quantity);
}
if (((int)dmflags->value & DF_INSTANT_ITEMS) ||
((ent->item->use == Use_Quad) &&
(ent->spawnflags & DROPPED_PLAYER_ITEM)))
{
if ((ent->item->use == Use_Quad) &&
(ent->spawnflags & DROPPED_PLAYER_ITEM))
{
quad_drop_timeout_hack =
(ent->nextthink - level.time) / FRAMETIME;
}
ent->item->use(other, ent->item);
}
}
return true;
@ -1030,7 +1055,8 @@ Use_Invulnerability(edict_t *ent, gitem_t *item)
ent->client->invincible_framenum = level.framenum + 300;
}
gi.sound(ent, CHAN_ITEM, gi.soundindex("items/protect.wav"), 1, ATTN_NORM, 0);
gi.sound(ent, CHAN_ITEM, gi.soundindex(
"items/protect.wav"), 1, ATTN_NORM, 0);
}
/* ====================================================================== */
@ -1279,7 +1305,8 @@ MegaHealth_think(edict_t *self)
return;
}
if (self->owner->health > self->owner->max_health)
if ((self->owner->health > self->owner->max_health)
&& !CTFHasRegeneration(self->owner))
{
self->nextthink = level.time + 1;
self->owner->health -= 1;
@ -1312,8 +1339,18 @@ Pickup_Health(edict_t *ent, edict_t *other)
}
}
if ((other->health >= 250) && (ent->count > 25))
{
return false;
}
other->health += ent->count;
if ((other->health > 250) && (ent->count > 25))
{
other->health = 250;
}
if (!(ent->style & HEALTH_IGNORE_MAX))
{
if (other->health > other->max_health)
@ -1322,7 +1359,7 @@ Pickup_Health(edict_t *ent, edict_t *other)
}
}
if (ent->style & HEALTH_TIMED)
if ((ent->style & HEALTH_TIMED) && !CTFHasRegeneration(other))
{
ent->think = MegaHealth_think;
ent->nextthink = level.time + 5;
@ -1623,6 +1660,11 @@ Touch_Item(edict_t *ent, edict_t *other, cplane_t *plane /* unused */, csurface_
return; /* not a grabbable item? */
}
if (CTFMatchSetup())
{
return; /* can't pick stuff up right now */
}
taken = ent->item->pickup(ent, other);
if (taken)
@ -2251,6 +2293,14 @@ SpawnItem(edict_t *ent, gitem_t *item)
item->drop = NULL;
}
/* Don't spawn the flags unless enabled */
if (!ctf->value && ((strcmp(ent->classname, "item_flag_team1") == 0) ||
(strcmp(ent->classname, "item_flag_team2") == 0)))
{
G_FreeEdict(ent);
return;
}
ent->item = item;
ent->nextthink = level.time + 2 * FRAMETIME; /* items start after other solids */
ent->think = droptofloor;
@ -2262,6 +2312,13 @@ SpawnItem(edict_t *ent, gitem_t *item)
gi.modelindex(ent->model);
}
/* flags are server animated and have special handling */
if ((strcmp(ent->classname, "item_flag_team1") == 0) ||
(strcmp(ent->classname, "item_flag_team2") == 0))
{
ent->think = CTFFlagSetup;
}
if (ent->spawnflags & 1)
{
SetTriggeredSpawn(ent);
@ -2420,6 +2477,32 @@ static const gitem_t gameitemlist[] = {
"misc/power2.wav misc/power1.wav"
},
/*
* weapon_grapple (.3 .3 1) (-16 -16 -16) (16 16 16)
* always owned, never in the world
*/
{
"weapon_grapple",
NULL,
Use_Weapon,
NULL,
CTFWeapon_Grapple,
"misc/w_pkup.wav",
NULL, 0,
"models/weapons/grapple/tris.md2",
"w_grapple",
"Grapple",
0,
0,
NULL,
IT_WEAPON,
WEAP_GRAPPLE,
NULL,
0,
"weapons/grapple/grfire.wav weapons/grapple/grpull.wav weapons/grapple/grhang.wav weapons/grapple/grreset.wav weapons/grapple/grhit.wav"
},
/*
* weapon_blaster (.3 .3 1) (-16 -16 -16) (16 16 16)
* always owned, never in the world
@ -3903,6 +3986,142 @@ static const gitem_t gameitemlist[] = {
"items/s_health.wav items/n_health.wav items/l_health.wav items/m_health.wav"
},
/*
* QUAKED item_flag_team1 (1 0.2 0) (-16 -16 -24) (16 16 32)
*/
{
"item_flag_team1",
CTFPickup_Flag,
NULL,
CTFDrop_Flag,
NULL,
"ctf/flagtk.wav",
"players/male/flag1.md2", EF_FLAG1,
NULL,
"i_ctf1",
"Red Flag",
2,
0,
NULL,
0,
0,
NULL,
0,
"ctf/flagcap.wav"
},
/*
* QUAKED item_flag_team2 (1 0.2 0) (-16 -16 -24) (16 16 32)
*/
{
"item_flag_team2",
CTFPickup_Flag,
NULL,
CTFDrop_Flag,
NULL,
"ctf/flagtk.wav",
"players/male/flag2.md2", EF_FLAG2,
NULL,
"i_ctf2",
"Blue Flag",
2,
0,
NULL,
0,
0,
NULL,
0,
"ctf/flagcap.wav"
},
/* Resistance Tech */
{
"item_tech1",
CTFPickup_Tech,
NULL,
CTFDrop_Tech,
NULL,
"items/pkup.wav",
"models/ctf/resistance/tris.md2", EF_ROTATE,
NULL,
"tech1",
"Disruptor Shield",
2,
0,
NULL,
IT_TECH,
0,
NULL,
0,
"ctf/tech1.wav"
},
/* Strength Tech */
{
"item_tech2",
CTFPickup_Tech,
NULL,
CTFDrop_Tech,
NULL,
"items/pkup.wav",
"models/ctf/strength/tris.md2", EF_ROTATE,
NULL,
"tech2",
"Power Amplifier",
2,
0,
NULL,
IT_TECH,
0,
NULL,
0,
"ctf/tech2.wav ctf/tech2x.wav"
},
/* Haste Tech */
{
"item_tech3",
CTFPickup_Tech,
NULL,
CTFDrop_Tech,
NULL,
"items/pkup.wav",
"models/ctf/haste/tris.md2", EF_ROTATE,
NULL,
"tech3",
"Time Accel",
2,
0,
NULL,
IT_TECH,
0,
NULL,
0,
"ctf/tech3.wav"
},
/* Regeneration Tech */
{
"item_tech4",
CTFPickup_Tech,
NULL,
CTFDrop_Tech,
NULL,
"items/pkup.wav",
"models/ctf/regeneration/tris.md2", EF_ROTATE,
NULL,
"tech4",
"AutoDoc",
2,
0,
NULL,
IT_TECH,
0,
NULL,
0,
"ctf/tech4.wav"
},
/* end of list marker */
{NULL}
};

View file

@ -567,6 +567,30 @@ BecomeExplosion1(edict_t *self)
return;
}
/* flags are important */
if (strcmp(self->classname, "item_flag_team1") == 0)
{
CTFResetFlag(CTF_TEAM1); /* this will free self! */
gi.bprintf(PRINT_HIGH, "The %s flag has returned!\n",
CTFTeamName(CTF_TEAM1));
return;
}
if (strcmp(self->classname, "item_flag_team2") == 0)
{
CTFResetFlag(CTF_TEAM2); /* this will free self! */
gi.bprintf(PRINT_HIGH, "The %s flag has returned!\n",
CTFTeamName(CTF_TEAM2));
return;
}
/* techs are important too */
if (self->item && (self->item->flags & IT_TECH))
{
CTFRespawnTech(self); /* this frees self! */
return;
}
gi.WriteByte(svc_temp_entity);
gi.WriteByte(TE_EXPLOSION1);
gi.WritePosition(self->s.origin);
@ -819,6 +843,14 @@ TH_viewthing(edict_t *ent)
ent->s.frame = (ent->s.frame + 1) % 7;
ent->nextthink = level.time + FRAMETIME;
if (ent->spawnflags)
{
if (ent->s.frame == 0)
{
ent->spawnflags = (ent->spawnflags + 1) % 4 + 1;
}
}
}
void
@ -3029,6 +3061,8 @@ teleporter_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */,
return;
}
CTFPlayerResetGrapple(other);
/* unlink to make sure it can't possibly interfere with KillBox */
gi.unlinkentity(other);

View file

@ -824,8 +824,8 @@ Change_Weap_Animation(edict_t *ent)
* A generic function to handle
* the basics of weapon thinking
*/
void
Weapon_Generic(edict_t *ent, int FRAME_ACTIVATE_LAST, int FRAME_FIRE_LAST,
static void
Weapon_Generic2(edict_t *ent, int FRAME_ACTIVATE_LAST, int FRAME_FIRE_LAST,
int FRAME_IDLE_LAST, int FRAME_DEACTIVATE_LAST, int *pause_frames,
int *fire_frames, void (*fire)(edict_t *ent))
{
@ -965,17 +965,22 @@ Weapon_Generic(edict_t *ent, int FRAME_ACTIVATE_LAST, int FRAME_FIRE_LAST,
{
if (ent->client->ps.gunframe == fire_frames[n])
{
if (ent->client->quad_framenum > level.framenum)
if (!CTFApplyStrengthSound(ent))
{
gi.sound(ent, CHAN_ITEM, gi.soundindex(
"items/damage3.wav"), 1, ATTN_NORM, 0);
}
else if (ent->client->double_framenum > level.framenum)
{
gi.sound(ent, CHAN_ITEM, gi.soundindex(
"misc/ddamage3.wav"), 1, ATTN_NORM, 0);
if (ent->client->quad_framenum > level.framenum)
{
gi.sound(ent, CHAN_ITEM, gi.soundindex(
"items/damage3.wav"), 1, ATTN_NORM, 0);
}
else if (ent->client->double_framenum > level.framenum)
{
gi.sound(ent, CHAN_ITEM, gi.soundindex(
"misc/ddamage3.wav"), 1, ATTN_NORM, 0);
}
}
CTFApplyHasteSound(ent);
fire(ent);
break;
}
@ -993,6 +998,35 @@ Weapon_Generic(edict_t *ent, int FRAME_ACTIVATE_LAST, int FRAME_FIRE_LAST,
}
}
void
Weapon_Generic(edict_t *ent, int FRAME_ACTIVATE_LAST, int FRAME_FIRE_LAST,
int FRAME_IDLE_LAST, int FRAME_DEACTIVATE_LAST, int *pause_frames,
int *fire_frames, void (*fire)(edict_t *ent))
{
int oldstate = ent->client->weaponstate;
Weapon_Generic2(ent, FRAME_ACTIVATE_LAST, FRAME_FIRE_LAST,
FRAME_IDLE_LAST, FRAME_DEACTIVATE_LAST, pause_frames,
fire_frames, fire);
/* run the weapon frame again if hasted */
if ((Q_stricmp(ent->client->pers.weapon->pickup_name, "Grapple") == 0) &&
(ent->client->weaponstate == WEAPON_FIRING))
{
return;
}
if ((CTFApplyHaste(ent) ||
((Q_stricmp(ent->client->pers.weapon->pickup_name, "Grapple") == 0) &&
(ent->client->weaponstate != WEAPON_FIRING))) &&
(oldstate == ent->client->weaponstate))
{
Weapon_Generic2(ent, FRAME_ACTIVATE_LAST, FRAME_FIRE_LAST,
FRAME_IDLE_LAST, FRAME_DEACTIVATE_LAST, pause_frames,
fire_frames, fire);
}
}
/*
* ======================================================================
*