game: Add third person view

Based on codes:
 * Lazarus mod
 * KMQuake2 codes
 * Slight Mechanical Destruction mod

https://bitbucket.org/Knightmare66/kmquake2_stable/src/master/game/p_chase.c
This commit is contained in:
Denis Pauk 2023-12-24 02:05:13 +02:00
parent 45767961b0
commit 6b4f197ac0
13 changed files with 842 additions and 73 deletions

View file

@ -926,6 +926,7 @@ GAME_OBJS_ = \
src/game/monster/turret/turret.o \
src/game/monster/widow/widow2.o \
src/game/monster/widow/widow.o \
src/game/player/chase.o \
src/game/player/client.o \
src/game/player/hud.o \
src/game/player/trail.o \

View file

@ -46,3 +46,5 @@ original clients (Vanilla Quake II) commands are still in place.
* **set** / **seta** / **setu** / **sets**: set cvar valu with different flags.
* **listlights**: Show lights style and dlights list.
* **thirdperson**: Third person view.

View file

@ -628,6 +628,7 @@ CL_InitLocal(void)
Cmd_AddCommand("spawnentity", NULL);
Cmd_AddCommand("spawnonstart", NULL);
Cmd_AddCommand("cycleweap", NULL);
Cmd_AddCommand("thirdperson", NULL);
}
/*

View file

@ -2255,6 +2255,10 @@ ClientCommand(edict_t *ent)
{
CTFObserver(ent);
}
else if (Q_stricmp(cmd, "thirdperson") == 0)
{
Cmd_Chasecam_Toggle(ent);
}
else /* anything that doesn't match a command will be a chat */
{
Cmd_Say_f(ent, false, true);

View file

@ -1072,20 +1072,24 @@ SV_Physics_Toss(edict_t *ent)
ent->waterlevel = 0;
}
if (!wasinwater && isinwater)
/* Don't do the sounds for the camera */
if (Q_stricmp(ent->classname,"chasecam"))
{
/* don't play splash sound for entities already in water on level start */
if (level.framenum > 3)
if (!wasinwater && isinwater)
{
gi.positioned_sound(old_origin, g_edicts, CHAN_AUTO,
/* 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)
{
gi.positioned_sound(ent->s.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)

View file

@ -1143,6 +1143,7 @@ typedef struct
int helpchanged;
qboolean spectator; /* client is a spectator */
int chasetoggle; /* Chasetoggle */
int max_tesla;
int max_prox;
@ -1292,6 +1293,14 @@ struct gclient_s
float tracker_pain_framenum;
edict_t *owned_sphere; /* this points to the player's sphere */
/* Third person view */
int chasetoggle;
edict_t *chasecam;
edict_t *oldplayer;
int use;
int zoom;
int delayedstart;
};
struct edict_s
@ -1452,6 +1461,10 @@ struct edict_s
edict_t *target_hint_chain;
int hint_chain_id;
float lastMoveTime;
/* Third person view */
int chasedist1;
int chasedist2;
};
#define SPHERE_DEFENDER 0x0001
@ -1596,6 +1609,7 @@ typedef struct ghost_s
} ghost_t;
extern cvar_t *ctf;
extern char *ctf_statusbar;
#define CTF_TEAM1_SKIN "ctf_r"
#define CTF_TEAM2_SKIN "ctf_b"
@ -1702,11 +1716,11 @@ qboolean CTFCheckRules(void);
void SP_misc_ctf_banner(edict_t *ent);
void SP_misc_ctf_small_banner(edict_t *ent);
extern char *ctf_statusbar;
void UpdateChaseCam(edict_t *ent);
void ChaseNext(edict_t *ent);
void ChasePrev(edict_t *ent);
void Cmd_Chasecam_Toggle(edict_t *ent);
void ChasecamStart(edict_t *ent);
void ChasecamRemove(edict_t *ent);
void CheckChasecam_Viewent(edict_t *ent);
void ChasecamTrack(edict_t *ent);
void CTFObserver(edict_t *ent);

346
src/game/player/chase.c Normal file
View file

@ -0,0 +1,346 @@
/*
* Copyright (C) 1997-2001 Id Software, Inc.
* Copyright (c) ZeniMax Media 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.
*
* =======================================================================
*
* Third person view
*
* =======================================================================
*/
#include "../header/local.h"
/* The ent is the owner of the chasecam */
void
ChasecamStart(edict_t *ent)
{
/* This creates a tempory entity we can manipulate within this
* function */
edict_t *chasecam;
/* Don't work on a spectator! */
if (ent->client->resp.spectator)
{
return;
}
/* Don't turn back on during intermission! */
if (level.intermissiontime)
{
return;
}
/* Tell everything that looks at the toggle that our chasecam is on
* and working */
ent->client->chasetoggle = 1;
/* Make our gun model "non-existent" so it's more realistic to the
* player using the chasecam */
ent->client->ps.gunindex = 0;
chasecam = G_Spawn();
chasecam->owner = ent;
chasecam->solid = SOLID_NOT;
chasecam->movetype = MOVETYPE_FLYMISSILE;
/* this turns off Quake2's inclination to predict where the camera is going,
* making a much smoother ride */
ent->client->ps.pmove.pm_flags |= PMF_NO_PREDICTION;
/* this line tells Quake2 not to send the unnecessary info about the
* camera to other players */
ent->svflags |= SVF_NOCLIENT;
/* Now, make the angles of the player model, (!NOT THE HUMAN VIEW!) be
* copied to the same angle of the chasecam entity */
VectorCopy(ent->s.angles, chasecam->s.angles);
/* Clear the size of the entity, so it DOES technically have a size,
* but that of '0 0 0'-'0 0 0'. (xyz, xyz). mins = Minimum size,
* maxs = Maximum size */
VectorClear(chasecam->mins);
VectorClear(chasecam->maxs);
/* Make the chasecam's origin (position) be the same as the player
* entity's because as the camera starts, it will force itself out
* slowly backwards from the player model */
VectorCopy(ent->s.origin, chasecam->s.origin);
chasecam->classname = "chasecam";
chasecam->prethink = ChasecamTrack;
chasecam->think = ChasecamTrack;
ent->client->chasecam = chasecam;
ent->client->oldplayer = G_Spawn();
CheckChasecam_Viewent(ent);
}
/* ent = chasecam */
void
ChasecamRestart(edict_t *ent)
{
/* Keep thinking this function to check all the time whether the
* player is out of the water */
/* If the player is dead, the camera is not wanted... Kill me and stop
* the function. (return;) */
if (ent->owner->health <= 0)
{
G_FreeEdict(ent);
return;
}
// Put camera back
ChasecamStart(ent->owner);
// Remove this temporary ent
G_FreeEdict(ent);
}
/* Here, the "ent" is referring to the client, the player that owns the
* chasecam, and the "opt" integer is telling the function whether to
* totally get rid of the camera, or to put it into the background while
* it checks if the player is out of the water or not. */
void
ChasecamRemove(edict_t *ent)
{
/* Stop the chasecam from moving */
VectorClear (ent->client->chasecam->velocity);
/* Make the weapon model of the player appear on screen for 1st
* person reality and aiming */
//Don't turn back on during intermission!
if (!level.intermissiontime)
{
ent->client->ps.gunindex = gi.modelindex(ent->client->pers.weapon->view_model);
}
/* Make our invisible appearance the same model as the display entity
* that mimics us while in chasecam mode */
ent->s.modelindex = ent->client->oldplayer->s.modelindex;
ent->svflags &= ~SVF_NOCLIENT;
G_FreeEdict (ent->client->oldplayer);
ent->client->oldplayer = NULL;
ent->client->chasetoggle = 0;
G_FreeEdict (ent->client->chasecam);
ent->client->chasecam = NULL;
}
/* The "ent" is the chasecam */
void
ChasecamTrack(edict_t *ent)
{
/* Create tempory vectors and trace variables */
trace_t tr;
vec3_t spot1, spot2, dir;
vec3_t forward, right, up,angles;
int distance;
int tot;
ent->nextthink = level.time + 0.100;
/* get the CLIENT's angle, and break it down into direction vectors,
* of forward, right, and up. VERY useful */
VectorCopy(ent->owner->client->v_angle,angles);
if (angles[PITCH] > 56)
{
angles[PITCH] = 56;
}
AngleVectors(angles, forward, right, up);
VectorNormalize(forward);
/* go starting at the player's origin, forward, ent->chasedist1
* distance, and save the location in vector spot2 */
VectorMA (ent->owner->s.origin, -ent->chasedist1, forward, spot2);
/* make spot2 a bit higher, by adding viewheight to the Z coordinate */
spot2[2] += (ent->owner->viewheight + 16);
// jump animation lifts
if (!ent->owner->groundentity)
{
spot2[2] += 16;
}
/* make the tr traceline trace from the player model's position, to spot2,
* ignoring the player, with a mask. */
tr = gi.trace (ent->owner->s.origin, vec3_origin, vec3_origin, spot2, ent->owner, MASK_SOLID);
/* subtract the endpoint from the start point for length and
* direction manipulation */
VectorSubtract (tr.endpos, ent->owner->s.origin, spot1);
/* in this case, length */
ent->chasedist1 = VectorLength (spot1);
/* go, starting from the end of the trace, 2 points forward (client
* angles) and save the location in spot2 */
VectorMA (tr.endpos, 2, forward, spot2);
/* make spot1 the same for tempory vector modification and make spot1
* a bit higher than spot2 */
VectorCopy(spot2, spot1);
spot1[2] += 32;
/* another trace from spot2 to spot1, ignoring player, no masks */
tr = gi.trace (spot2, vec3_origin, vec3_origin, spot1, ent->owner, MASK_SOLID);
/* if we hit something, copy the trace end to spot2 and lower spot2 */
if (tr.fraction < 1.000)
{
VectorCopy(tr.endpos, spot2);
spot2[2] -= 32;
}
/* subtract endpos spot2 from startpos the camera origin, saving it to
* the dir vector, and normalize dir for a direction from the camera
* origin, to the spot2 */
VectorSubtract (spot2, ent->s.origin, dir);
distance = VectorLength (dir);
VectorNormalize (dir);
/* another traceline */
tr = gi.trace (ent->s.origin, vec3_origin, vec3_origin, spot2, ent->owner, MASK_SOLID);
/* if we DON'T hit anyting, do some freaky stuff */
if (tr.fraction == 1.000)
{
/* subtract the endpos camera position, from the startpos, the
* player, and save in spot1. Normalize spot1 for a direction, and
* make that direction the angles of the chasecam for copying to the
* clients view angle which is displayed to the client. (human) */
VectorSubtract (ent->s.origin, ent->owner->s.origin, spot1);
VectorNormalize (spot1);
VectorCopy(spot1, ent->s.angles);
/* calculate the percentages of the distances, and make sure we're
* not going too far, or too short, in relation to our panning
* speed of the chasecam entity */
tot = (distance * 0.400);
/* if we're going too fast, make us top speed */
if (tot > 5.200)
{
ent->velocity[0] = ((dir[0] * distance) * 5.2);
ent->velocity[1] = ((dir[1] * distance) * 5.2);
ent->velocity[2] = ((dir[2] * distance) * 5.2);
}
else
{
/* if we're NOT going top speed, but we're going faster than
* 1, relative to the total, make us as fast as we're going */
if (tot > 1.000)
{
ent->velocity[0] = ((dir[0] * distance) * tot);
ent->velocity[1] = ((dir[1] * distance) * tot);
ent->velocity[2] = ((dir[2] * distance) * tot);
}
else
{
/* if we're not going faster than one, don't accelerate our
* speed at all, make us go slow to our destination */
ent->velocity[0] = (dir[0] * distance);
ent->velocity[1] = (dir[1] * distance);
ent->velocity[2] = (dir[2] * distance);
}
}
/* subtract endpos,player position, from chasecam position to get
* a length to determine whether we should accelerate faster from
* the player or not */
VectorSubtract (ent->owner->s.origin, ent->s.origin, spot1);
if (VectorLength(spot1) < 20)
{
ent->velocity[0] *= 2;
ent->velocity[1] *= 2;
ent->velocity[2] *= 2;
}
}
/* if we DID hit something in the tr.fraction call ages back, then
* make the spot2 we created, the position for the chasecamera. */
else
{
VectorCopy(spot2, ent->s.origin);
}
/* add to the distance between the player and the camera */
ent->chasedist1 += 2;
/* if we're too far away, give us a maximum distance */
if (ent->chasedist1 > (60.00 + ent->owner->client->zoom))
{
ent->chasedist1 = (60.00 + ent->owner->client->zoom);
}
/* if we haven't gone anywhere since the last think routine, and we
* are greater than 20 points in the distance calculated, add one to
* the second chasedistance variable
* The "ent->movedir" is a vector which is not used in this entity, so
* we can use this a tempory vector belonging to the chasecam, which
* can be carried through think routines. */
if (VectorCompare(ent->movedir, ent->s.origin))
{
if (distance > 20)
{
ent->chasedist2++;
}
}
/* if we've buggered up more than 3 times, there must be some mistake,
* so restart the camera so we re-create a chasecam, destroy the old one,
* slowly go outwards from the player, and keep thinking this routing in
* the new camera entity */
if (ent->chasedist2 > 3)
{
G_FreeEdict(ent->owner->client->oldplayer);
ChasecamStart(ent->owner);
G_FreeEdict(ent);
return;
}
/* Copy the position of the chasecam now, and stick it to the movedir
* variable, for position checking when we rethink this function */
VectorCopy(ent->s.origin, ent->movedir);
/* MUST LINK SINCE WE CHANGED THE ORIGIN! */
gi.linkentity(ent);
}
void
Cmd_Chasecam_Toggle(edict_t *ent)
{
if (!ent->deadflag)
{
if (ent->client->chasetoggle)
{
ChasecamRemove(ent);
}
else
{
ChasecamStart(ent);
}
}
}
void
CheckChasecam_Viewent(edict_t *ent)
{
vec3_t angles;
/*
Oldplayer is the fake player that everyone else sees.
Assign the same client as the ent we're following so the game
can read any vars it wants from there
*/
if (!ent->client->oldplayer->client)
{
ent->client->oldplayer->client = ent->client;
}
/* Copy the angle and model from ourselves to the old player.
* Even though people can't see us we still have all this stuff */
if ((ent->client->chasetoggle == 1) && (ent->client->oldplayer))
{
if (ent->client->use)
{
VectorCopy(ent->client->oldplayer->s.angles, angles);
}
/* Copy player related info */
ent->client->oldplayer->s = ent->s;
/* Lazarus: s.numbers shouldn't be the same */
ent->client->oldplayer->s.number = ent->client->oldplayer - g_edicts;
if (ent->client->use)
{
VectorCopy(angles, ent->client->oldplayer->s.angles);
}
ent->client->oldplayer->flags = ent->flags;
/* end Lazarus */
gi.linkentity (ent->client->oldplayer);
}
}

View file

@ -1044,6 +1044,16 @@ player_die(edict_t *self, edict_t *inflictor, edict_t *attacker,
return;
}
if (self->client->chasetoggle)
{
ChasecamRemove(self);
self->client->pers.chasetoggle = 1;
}
else
{
self->client->pers.chasetoggle = 0;
}
VectorClear(self->avelocity);
self->takedamage = DAMAGE_YES;
@ -1278,6 +1288,9 @@ InitClientPersistant(gclient_t *client)
client->pers.max_rounds = 100;
client->pers.connected = true;
/* Default chasecam to off */
client->pers.chasetoggle = 0;
}
void
@ -1326,6 +1339,7 @@ SaveClientData(void)
continue;
}
game.clients[i].pers.chasetoggle = ent->client->chasetoggle;
game.clients[i].pers.health = ent->health;
game.clients[i].pers.max_health = ent->max_health;
game.clients[i].pers.savedFlags =
@ -1910,6 +1924,18 @@ respawn(edict_t *self)
return;
}
if (self->client->oldplayer)
{
G_FreeEdict (self->client->oldplayer);
}
self->client->oldplayer = NULL;
if (self->client->chasecam)
{
G_FreeEdict (self->client->chasecam);
}
self->client->chasecam = NULL;
if (deathmatch->value || coop->value)
{
/* spectator's don't leave bodies */
@ -1990,6 +2016,17 @@ spectator_respawn(edict_t *ent)
gi.unicast(ent, true);
return;
}
/* Third person view */
if (ent->client->chasetoggle)
{
ChasecamRemove(ent);
ent->client->pers.chasetoggle = 1;
}
else
{
ent->client->pers.chasetoggle = 0;
}
}
else
{
@ -2057,7 +2094,7 @@ PutClientInServer(edict_t *ent)
int index;
vec3_t spawn_origin, spawn_angles;
gclient_t *client;
int i;
int i, chasetoggle;
client_persistant_t saved;
client_respawn_t resp;
@ -2080,6 +2117,7 @@ PutClientInServer(edict_t *ent)
index = ent - g_edicts - 1;
client = ent->client;
chasetoggle = client->pers.chasetoggle;
/* deathmatch wipes most client data every spawn */
if (deathmatch->value)
@ -2120,7 +2158,11 @@ PutClientInServer(edict_t *ent)
}
else
{
char userinfo[MAX_INFO_STRING];
memset(&resp, 0, sizeof(resp));
memcpy(userinfo, client->pers.userinfo, sizeof(userinfo));
ClientUserinfoChanged (ent, userinfo);
}
/* clear everything but the persistant data */
@ -2134,6 +2176,7 @@ PutClientInServer(edict_t *ent)
}
client->resp = resp;
client->pers.chasetoggle = chasetoggle;
/* copy some data from the client to the entity */
FetchClientEntData(ent);
@ -2158,6 +2201,10 @@ PutClientInServer(edict_t *ent)
ent->watertype = 0;
ent->flags &= ~FL_NO_KNOCKBACK;
ent->svflags &= ~SVF_DEADMONSTER;
/* Third person view */
ent->svflags &= ~SVF_NOCLIENT;
/* Turn off prediction */
ent->client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;
VectorCopy(mins, ent->mins);
VectorCopy(maxs, ent->maxs);
@ -2258,6 +2305,13 @@ PutClientInServer(edict_t *ent)
gi.linkentity(ent);
ent->client->chasetoggle = 0;
/* If chasetoggle set then turn on (delayed start of 5 frames - 0.5s) */
if (ent->client->pers.chasetoggle && !ent->client->chasetoggle)
{
ent->client->delayedstart = 5;
}
/* my tribute to cash's level-specific hacks. I hope
* live up to his trailblazing cheese. */
if (Q_stricmp(level.mapname, "rboss") == 0)
@ -2603,6 +2657,11 @@ ClientDisconnect(edict_t *ent)
return;
}
if(ent->client->chasetoggle)
{
ChasecamRemove(ent);
}
gi.bprintf(PRINT_HIGH, "%s disconnected\n", ent->client->pers.netname);
if (ctf->value)
@ -2728,6 +2787,11 @@ ClientThink(edict_t *ent, usercmd_t *ucmd)
if (level.intermissiontime)
{
if (client->chasetoggle)
{
ChasecamRemove(ent);
}
client->ps.pmove.pm_type = PM_FREEZE;
/* can exit intermission after five seconds */
@ -2740,6 +2804,44 @@ ClientThink(edict_t *ent, usercmd_t *ucmd)
return;
}
if (client->chasetoggle)
{
ent->client->ps.pmove.pm_flags |= PMF_NO_PREDICTION;
}
else
{
ent->client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;
}
/* +use now does the cool look around stuff but only in SP games */
if ((ucmd->buttons & BUTTON_USE) && (!deathmatch->value))
{
client->use = 1;
if ((ucmd->forwardmove < 0) && (client->zoom < 60))
{
client->zoom++;
}
else if ((ucmd->forwardmove > 0) && (client->zoom > -40))
{
client->zoom--;
}
ucmd->forwardmove = 0;
ucmd->sidemove = 0;
}
else if (client->use)
{
if (client->oldplayer)
{
// set angles
for (i=0 ; i<3 ; i++)
{
ent->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(
ent->client->oldplayer->s.angles[i] - ent->client->resp.cmd_angles[i]);
}
}
client->use = 0;
}
pm_passent = ent;
if (ent->client->chase_target)
@ -2997,6 +3099,16 @@ ClientBeginServerFrame(edict_t *ent)
client = ent->client;
if (client->delayedstart > 0)
{
client->delayedstart--;
}
if (client->delayedstart == 1)
{
ChasecamStart(ent);
}
if (deathmatch->value &&
(client->pers.spectator != client->resp.spectator) &&
((level.time - client->respawn_time) >= 5))

View file

@ -133,6 +133,9 @@ BeginIntermission(edict_t *targ)
{
respawn(client);
}
/* Save third person view */
client->client->pers.chasetoggle = client->client->chasetoggle;
}
level.intermissiontime = level.time;
@ -664,7 +667,8 @@ G_SetStats(edict_t *ent)
ent->client->ps.stats[STAT_HELPICON] = gi.imageindex("i_help");
}
else if (((ent->client->pers.hand == CENTER_HANDED) ||
(ent->client->ps.fov > 91)) &&
(ent->client->ps.fov > 91) ||
(ent->client->chasetoggle)) &&
ent->client->pers.weapon)
{
cvar_t *gun;

View file

@ -401,31 +401,44 @@ SV_CalcViewOffset(edict_t *ent)
/* absolutely bound offsets
so the view can never be
outside the player box */
if (v[0] < -14)
if (!ent->client->chasetoggle)
{
v[0] = -14;
}
else if (v[0] > 14)
{
v[0] = 14;
}
if (v[0] < -14)
{
v[0] = -14;
}
else if (v[0] > 14)
{
v[0] = 14;
}
if (v[1] < -14)
{
v[1] = -14;
}
else if (v[1] > 14)
{
v[1] = 14;
}
if (v[1] < -14)
{
v[1] = -14;
}
else if (v[1] > 14)
{
v[1] = 14;
}
if (v[2] < -22)
{
v[2] = -22;
if (v[2] < -22)
{
v[2] = -22;
}
else if (v[2] > 30)
{
v[2] = 30;
}
}
else if (v[2] > 30)
else
{
v[2] = 30;
VectorSet (v, 0, 0, 0);
if (ent->client->chasecam)
{
ent->client->ps.pmove.origin[0] = ent->client->chasecam->s.origin[0] * 8;
ent->client->ps.pmove.origin[1] = ent->client->chasecam->s.origin[1] * 8;
ent->client->ps.pmove.origin[2] = ent->client->chasecam->s.origin[2] * 8;
}
}
VectorCopy(v, ent->client->ps.viewoffset);
@ -557,7 +570,16 @@ SV_CalcBlend(edict_t *ent)
ent->client->ps.blend[2] = ent->client->ps.blend[3] = 0;
/* add for contents */
VectorAdd(ent->s.origin, ent->client->ps.viewoffset, vieworg);
if (ent->client->chasetoggle)
{
/* if always on then do shading to camera not player */
VectorCopy(ent->client->chasecam->s.origin, vieworg);
}
else
{
VectorAdd(ent->s.origin, ent->client->ps.viewoffset, vieworg);
}
contents = gi.pointcontents(vieworg);
if (contents & (CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER))
@ -1616,4 +1638,9 @@ ClientEndServerFrame(edict_t *ent)
InventoryMessage(ent);
gi.unicast(ent, false);
}
if (ent->client->chasetoggle == 1)
{
CheckChasecam_Viewent(ent);
}
}

View file

@ -510,8 +510,16 @@ ChangeWeapon(edict_t *ent)
ent->client->weaponstate = WEAPON_ACTIVATING;
ent->client->ps.gunframe = 0;
ent->client->ps.gunindex = gi.modelindex(
ent->client->pers.weapon->view_model);
/* Don't display weapon if chasetoggle is on */
if (ent->client->chasetoggle)
{
ent->client->ps.gunindex = 0;
}
else
{
ent->client->ps.gunindex = gi.modelindex(
ent->client->pers.weapon->view_model);
}
ent->client->anim_priority = ANIM_PAIN;
@ -1069,7 +1077,14 @@ weapon_grenade_fire(edict_t *ent, qboolean held)
}
}
AngleVectors(ent->client->v_angle, forward, right, up);
if ((ent->client->use) && (ent->client->oldplayer))
{
AngleVectors(ent->client->oldplayer->s.angles, forward, right, up);
}
else
{
AngleVectors(ent->client->v_angle, forward, right, up);
}
if (ent->client->pers.weapon->tag == AMMO_TESLA)
{
@ -1360,7 +1375,16 @@ weapon_grenadelauncher_fire(edict_t *ent)
}
VectorSet(offset, 8, 8, ent->viewheight - 8);
AngleVectors(ent->client->v_angle, forward, right, NULL);
if ((ent->client->use) && (ent->client->oldplayer))
{
AngleVectors(ent->client->oldplayer->s.angles, forward, right, NULL);
}
else
{
AngleVectors(ent->client->v_angle, forward, right, NULL);
}
P_ProjectSource(ent, offset, forward, right, start);
VectorScale(forward, -2, ent->client->kick_origin);
@ -1377,9 +1401,26 @@ weapon_grenadelauncher_fire(edict_t *ent)
}
gi.WriteByte(svc_muzzleflash);
gi.WriteShort(ent - g_edicts);
if (ent->client->oldplayer)
{
gi.WriteShort(ent->client->oldplayer - g_edicts);
}
else
{
gi.WriteShort(ent - g_edicts);
}
gi.WriteByte(MZ_GRENADE | is_silenced);
gi.multicast(ent->s.origin, MULTICAST_PVS);
if (ent->client->oldplayer)
{
gi.multicast (ent->client->oldplayer->s.origin, MULTICAST_PVS);
}
else
{
gi.multicast (ent->s.origin, MULTICAST_PVS);
}
ent->client->ps.gunframe++;
@ -1459,7 +1500,14 @@ Weapon_RocketLauncher_Fire(edict_t *ent)
radius_damage *= damage_multiplier;
}
AngleVectors(ent->client->v_angle, forward, right, NULL);
if ((ent->client->use) && (ent->client->oldplayer))
{
AngleVectors(ent->client->oldplayer->s.angles, forward, right, NULL);
}
else
{
AngleVectors(ent->client->v_angle, forward, right, NULL);
}
VectorScale(forward, -2, ent->client->kick_origin);
ent->client->kick_angles[0] = -1;
@ -1470,9 +1518,26 @@ Weapon_RocketLauncher_Fire(edict_t *ent)
/* send muzzle flash */
gi.WriteByte(svc_muzzleflash);
gi.WriteShort(ent - g_edicts);
if (ent->client->oldplayer)
{
gi.WriteShort(ent->client->oldplayer - g_edicts);
}
else
{
gi.WriteShort(ent - g_edicts);
}
gi.WriteByte(MZ_ROCKET | is_silenced);
gi.multicast(ent->s.origin, MULTICAST_PVS);
if (ent->client->oldplayer)
{
gi.multicast(ent->client->oldplayer->s.origin, MULTICAST_PVS);
}
else
{
gi.multicast(ent->s.origin, MULTICAST_PVS);
}
ent->client->ps.gunframe++;
@ -1531,7 +1596,15 @@ Blaster_Fire(edict_t *ent, vec3_t g_offset, int damage,
damage *= damage_multiplier;
}
AngleVectors(ent->client->v_angle, forward, right, NULL);
if ((ent->client->use) && (ent->client->oldplayer))
{
AngleVectors(ent->client->oldplayer->s.angles, forward, right, NULL);
}
else
{
AngleVectors(ent->client->v_angle, forward, right, NULL);
}
VectorSet(offset, 24, 8, ent->viewheight - 8);
VectorAdd(offset, g_offset, offset);
P_ProjectSource(ent, offset, forward, right, start);
@ -1543,7 +1616,15 @@ Blaster_Fire(edict_t *ent, vec3_t g_offset, int damage,
/* send muzzle flash */
gi.WriteByte(svc_muzzleflash);
gi.WriteShort(ent - g_edicts);
if (ent->client->oldplayer)
{
gi.WriteShort(ent->client->oldplayer - g_edicts);
}
else
{
gi.WriteShort(ent - g_edicts);
}
if (hyper)
{
@ -1554,7 +1635,14 @@ Blaster_Fire(edict_t *ent, vec3_t g_offset, int damage,
gi.WriteByte(MZ_BLASTER | is_silenced);
}
gi.multicast(ent->s.origin, MULTICAST_PVS);
if (ent->client->oldplayer)
{
gi.multicast (ent->client->oldplayer->s.origin, MULTICAST_PVS);
}
else
{
gi.multicast(ent->s.origin, MULTICAST_PVS);
}
PlayerNoise(ent, start, PNOISE_WEAPON);
}
@ -1802,7 +1890,15 @@ Machinegun_Fire(edict_t *ent)
}
/* get start / end positions */
VectorAdd(ent->client->v_angle, ent->client->kick_angles, angles);
if ((ent->client->use) && (ent->client->oldplayer))
{
VectorAdd (ent->client->oldplayer->s.angles, ent->client->kick_angles, angles);
}
else
{
VectorAdd(ent->client->v_angle, ent->client->kick_angles, angles);
}
AngleVectors(angles, forward, right, NULL);
VectorSet(offset, 0, 8, ent->viewheight - 8);
P_ProjectSource(ent, offset, forward, right, start);
@ -1810,9 +1906,26 @@ Machinegun_Fire(edict_t *ent)
DEFAULT_BULLET_VSPREAD, MOD_MACHINEGUN);
gi.WriteByte(svc_muzzleflash);
gi.WriteShort(ent - g_edicts);
if (ent->client->oldplayer)
{
gi.WriteShort(ent->client->oldplayer - g_edicts);
}
else
{
gi.WriteShort(ent - g_edicts);
}
gi.WriteByte(MZ_MACHINEGUN | is_silenced);
gi.multicast(ent->s.origin, MULTICAST_PVS);
if (ent->client->oldplayer)
{
gi.multicast (ent->client->oldplayer->s.origin, MULTICAST_PVS);
}
else
{
gi.multicast(ent->s.origin, MULTICAST_PVS);
}
PlayerNoise(ent, start, PNOISE_WEAPON);
@ -1983,7 +2096,15 @@ Chaingun_Fire(edict_t *ent)
for (i = 0; i < shots; i++)
{
/* get start / end positions */
AngleVectors(ent->client->v_angle, forward, right, up);
if ((ent->client->use) && (ent->client->oldplayer))
{
AngleVectors(ent->client->oldplayer->s.angles, forward, right, NULL);
}
else
{
AngleVectors(ent->client->v_angle, forward, right, up);
}
r = 7 + crandom() * 4;
u = crandom() * 4;
VectorSet(offset, 0, r, u + ent->viewheight - 8);
@ -1997,9 +2118,26 @@ Chaingun_Fire(edict_t *ent)
/* send muzzle flash */
gi.WriteByte(svc_muzzleflash);
gi.WriteShort(ent - g_edicts);
if (ent->client->oldplayer)
{
gi.WriteShort(ent->client->oldplayer - g_edicts);
}
else
{
gi.WriteShort(ent - g_edicts);
}
gi.WriteByte((MZ_CHAINGUN1 + shots - 1) | is_silenced);
gi.multicast(ent->s.origin, MULTICAST_PVS);
if (ent->client->oldplayer)
{
gi.multicast(ent->client->oldplayer->s.origin, MULTICAST_PVS);
}
else
{
gi.multicast(ent->s.origin, MULTICAST_PVS);
}
PlayerNoise(ent, start, PNOISE_WEAPON);
@ -2057,7 +2195,14 @@ weapon_shotgun_fire(edict_t *ent)
return;
}
AngleVectors(ent->client->v_angle, forward, right, NULL);
if ((ent->client->use) && (ent->client->oldplayer))
{
AngleVectors(ent->client->oldplayer->s.angles, forward, right, NULL);
}
else
{
AngleVectors(ent->client->v_angle, forward, right, NULL);
}
VectorScale(forward, -2, ent->client->kick_origin);
ent->client->kick_angles[0] = -2;
@ -2086,9 +2231,26 @@ weapon_shotgun_fire(edict_t *ent)
/* send muzzle flash */
gi.WriteByte(svc_muzzleflash);
gi.WriteShort(ent - g_edicts);
if (ent->client->oldplayer)
{
gi.WriteShort(ent->client->oldplayer - g_edicts);
}
else
{
gi.WriteShort(ent - g_edicts);
}
gi.WriteByte(MZ_SHOTGUN | is_silenced);
gi.multicast(ent->s.origin, MULTICAST_PVS);
if (ent->client->oldplayer)
{
gi.multicast(ent->client->oldplayer->s.origin, MULTICAST_PVS);
}
else
{
gi.multicast(ent->s.origin, MULTICAST_PVS);
}
ent->client->ps.gunframe++;
PlayerNoise(ent, start, PNOISE_WEAPON);
@ -2135,7 +2297,14 @@ weapon_supershotgun_fire(edict_t *ent)
return;
}
AngleVectors(ent->client->v_angle, forward, right, NULL);
if ((ent->client->use) && (ent->client->oldplayer))
{
AngleVectors(ent->client->oldplayer->s.angles, forward, right, NULL);
}
else
{
AngleVectors(ent->client->v_angle, forward, right, NULL);
}
VectorScale(forward, -2, ent->client->kick_origin);
ent->client->kick_angles[0] = -2;
@ -2149,9 +2318,19 @@ weapon_supershotgun_fire(edict_t *ent)
kick *= damage_multiplier;
}
v[PITCH] = ent->client->v_angle[PITCH];
v[YAW] = ent->client->v_angle[YAW] - 5;
v[ROLL] = ent->client->v_angle[ROLL];
if ((ent->client->use) && (ent->client->oldplayer))
{
v[PITCH] = ent->client->oldplayer->s.angles[PITCH];
v[YAW] = ent->client->oldplayer->s.angles[YAW] - 5;
v[ROLL] = ent->client->oldplayer->s.angles[ROLL];
}
else
{
v[PITCH] = ent->client->v_angle[PITCH];
v[YAW] = ent->client->v_angle[YAW] - 5;
v[ROLL] = ent->client->v_angle[ROLL];
}
AngleVectors(v, forward, NULL, NULL);
if (aimfix->value)
@ -2169,7 +2348,15 @@ weapon_supershotgun_fire(edict_t *ent)
DEFAULT_SHOTGUN_HSPREAD, DEFAULT_SHOTGUN_VSPREAD,
DEFAULT_SSHOTGUN_COUNT / 2, MOD_SSHOTGUN);
v[YAW] = ent->client->v_angle[YAW] + 5;
if ((ent->client->use) && (ent->client->oldplayer))
{
v[YAW] = ent->client->oldplayer->s.angles[YAW] + 5;
}
else
{
v[YAW] = ent->client->v_angle[YAW] + 5;
}
AngleVectors(v, forward, NULL, NULL);
if (aimfix->value)
@ -2189,9 +2376,26 @@ weapon_supershotgun_fire(edict_t *ent)
/* send muzzle flash */
gi.WriteByte(svc_muzzleflash);
gi.WriteShort(ent - g_edicts);
if (ent->client->oldplayer)
{
gi.WriteShort(ent->client->oldplayer - g_edicts);
}
else
{
gi.WriteShort(ent - g_edicts);
}
gi.WriteByte(MZ_SSHOTGUN | is_silenced);
gi.multicast(ent->s.origin, MULTICAST_PVS);
if (ent->client->oldplayer)
{
gi.multicast (ent->client->oldplayer->s.origin, MULTICAST_PVS);
}
else
{
gi.multicast(ent->s.origin, MULTICAST_PVS);
}
ent->client->ps.gunframe++;
PlayerNoise(ent, start, PNOISE_WEAPON);
@ -2263,7 +2467,14 @@ weapon_railgun_fire(edict_t *ent)
kick *= damage_multiplier;
}
AngleVectors(ent->client->v_angle, forward, right, NULL);
if ((ent->client->use) && (ent->client->oldplayer))
{
AngleVectors(ent->client->oldplayer->s.angles, forward, right, NULL);
}
else
{
AngleVectors(ent->client->v_angle, forward, right, NULL);
}
VectorScale(forward, -3, ent->client->kick_origin);
ent->client->kick_angles[0] = -3;
@ -2274,9 +2485,26 @@ weapon_railgun_fire(edict_t *ent)
/* send muzzle flash */
gi.WriteByte(svc_muzzleflash);
gi.WriteShort(ent - g_edicts);
if (ent->client->oldplayer)
{
gi.WriteShort(ent->client->oldplayer - g_edicts);
}
else
{
gi.WriteShort(ent - g_edicts);
}
gi.WriteByte(MZ_RAILGUN | is_silenced);
gi.multicast(ent->s.origin, MULTICAST_PVS);
if (ent->client->oldplayer)
{
gi.multicast(ent->client->oldplayer->s.origin, MULTICAST_PVS);
}
else
{
gi.multicast(ent->s.origin, MULTICAST_PVS);
}
ent->client->ps.gunframe++;
PlayerNoise(ent, start, PNOISE_WEAPON);
@ -2342,9 +2570,26 @@ weapon_bfg_fire(edict_t *ent)
{
/* send muzzle flash */
gi.WriteByte(svc_muzzleflash);
gi.WriteShort(ent - g_edicts);
if (ent->client->oldplayer)
{
gi.WriteShort(ent->client->oldplayer - g_edicts);
}
else
{
gi.WriteShort(ent - g_edicts);
}
gi.WriteByte(MZ_BFG | is_silenced);
gi.multicast(ent->s.origin, MULTICAST_PVS);
if (ent->client->oldplayer)
{
gi.multicast(ent->client->oldplayer->s.origin, MULTICAST_PVS);
}
else
{
gi.multicast(ent->s.origin, MULTICAST_PVS);
}
ent->client->ps.gunframe++;
@ -2365,7 +2610,14 @@ weapon_bfg_fire(edict_t *ent)
damage *= damage_multiplier;
}
AngleVectors(ent->client->v_angle, forward, right, NULL);
if ((ent->client->use) && (ent->client->oldplayer))
{
AngleVectors(ent->client->oldplayer->s.angles, forward, right, NULL);
}
else
{
AngleVectors(ent->client->v_angle, forward, right, NULL);
}
VectorScale(forward, -2, ent->client->kick_origin);

View file

@ -76,7 +76,7 @@ extern void VectorScale ( const vec3_t in , vec_t scale , vec3_t out ) ;
extern void VectorInverse ( vec3_t v ) ;
extern vec_t VectorLength ( const vec3_t v ) ;
extern void CrossProduct ( const vec3_t v1 , const vec3_t v2 , vec3_t cross ) ;
extern void _VectorCopy ( const vec3_t in , vec3_t out ) ;
extern void _VectorCopy( const vec3_t in , vec3_t out ) ;
extern void _VectorAdd ( const vec3_t veca , const vec3_t vecb , vec3_t out ) ;
extern void _VectorSubtract ( const vec3_t veca , const vec3_t vecb , vec3_t out ) ;
extern vec_t _DotProduct ( const vec3_t v1 , const vec3_t v2 ) ;
@ -93,7 +93,7 @@ extern void R_ConcatRotations ( const float in1 [ 3 ] [ 3 ] , const float in2 [
extern void PerpendicularVector ( vec3_t dst , const vec3_t src ) ;
extern void ProjectPointOnPlane ( vec3_t dst , const vec3_t p , const vec3_t normal ) ;
extern void AngleVectors2 ( const vec3_t value1 , vec3_t angles ) ;
extern void AngleVectors ( const vec3_t angles , vec3_t forward , vec3_t right , vec3_t up ) ;
extern void AngleVectors( const vec3_t angles , vec3_t forward , vec3_t right , vec3_t up ) ;
extern void RotatePointAroundVector ( vec3_t dst , const vec3_t dir , const vec3_t point , float degrees ) ;
extern void Weapon_Trap ( edict_t * ent ) ;
extern void weapon_trap_fire ( edict_t * ent , qboolean held ) ;
@ -1769,6 +1769,7 @@ extern void GetChaseTarget ( edict_t * ent ) ;
extern void ChasePrev ( edict_t * ent ) ;
extern void ChaseNext ( edict_t * ent ) ;
extern void UpdateChaseCam ( edict_t * ent ) ;
extern void ChasecamTrack(edict_t *ent);
extern void ai_run ( edict_t * self , float dist ) ;
extern qboolean ai_checkattack ( edict_t * self ) ;
extern void ai_run_slide ( edict_t * self , float distance ) ;

View file

@ -1768,6 +1768,7 @@
{"ChasePrev", (byte *)ChasePrev},
{"ChaseNext", (byte *)ChaseNext},
{"UpdateChaseCam", (byte *)UpdateChaseCam},
{"ChasecamTrack", (byte *)ChasecamTrack},
{"ai_run", (byte *)ai_run},
{"ai_checkattack", (byte *)ai_checkattack},
{"ai_run_slide", (byte *)ai_run_slide},