From 6b4f197ac00fc933e198cef56755d919b68fdb23 Mon Sep 17 00:00:00 2001 From: Denis Pauk Date: Sun, 24 Dec 2023 02:05:13 +0200 Subject: [PATCH] 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 --- Makefile | 1 + doc/050_commands.md | 2 + src/client/cl_main.c | 1 + src/game/g_cmds.c | 4 + src/game/g_phys.c | 22 +- src/game/header/local.h | 24 +- src/game/player/chase.c | 346 +++++++++++++++++++++++ src/game/player/client.c | 114 +++++++- src/game/player/hud.c | 6 +- src/game/player/view.c | 69 +++-- src/game/player/weapon.c | 320 ++++++++++++++++++--- src/game/savegame/tables/gamefunc_decs.h | 5 +- src/game/savegame/tables/gamefunc_list.h | 1 + 13 files changed, 842 insertions(+), 73 deletions(-) create mode 100644 src/game/player/chase.c diff --git a/Makefile b/Makefile index 35cda2aa..632f7e8b 100644 --- a/Makefile +++ b/Makefile @@ -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 \ diff --git a/doc/050_commands.md b/doc/050_commands.md index 259b5ef9..4b1caf97 100644 --- a/doc/050_commands.md +++ b/doc/050_commands.md @@ -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. diff --git a/src/client/cl_main.c b/src/client/cl_main.c index 8ee12e39..bccbb28b 100644 --- a/src/client/cl_main.c +++ b/src/client/cl_main.c @@ -628,6 +628,7 @@ CL_InitLocal(void) Cmd_AddCommand("spawnentity", NULL); Cmd_AddCommand("spawnonstart", NULL); Cmd_AddCommand("cycleweap", NULL); + Cmd_AddCommand("thirdperson", NULL); } /* diff --git a/src/game/g_cmds.c b/src/game/g_cmds.c index 54aac7e8..0b5b8dfd 100644 --- a/src/game/g_cmds.c +++ b/src/game/g_cmds.c @@ -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); diff --git a/src/game/g_phys.c b/src/game/g_phys.c index 17147e9f..c6918ba7 100644 --- a/src/game/g_phys.c +++ b/src/game/g_phys.c @@ -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) diff --git a/src/game/header/local.h b/src/game/header/local.h index 8f110ccb..0f7a5870 100644 --- a/src/game/header/local.h +++ b/src/game/header/local.h @@ -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); diff --git a/src/game/player/chase.c b/src/game/player/chase.c new file mode 100644 index 00000000..98d69cd0 --- /dev/null +++ b/src/game/player/chase.c @@ -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); + } +} + diff --git a/src/game/player/client.c b/src/game/player/client.c index 66e15788..b501af16 100644 --- a/src/game/player/client.c +++ b/src/game/player/client.c @@ -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)) diff --git a/src/game/player/hud.c b/src/game/player/hud.c index 8c98a294..5f2bf0d8 100644 --- a/src/game/player/hud.c +++ b/src/game/player/hud.c @@ -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; diff --git a/src/game/player/view.c b/src/game/player/view.c index dc23c07b..2c0f54d9 100644 --- a/src/game/player/view.c +++ b/src/game/player/view.c @@ -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); + } } diff --git a/src/game/player/weapon.c b/src/game/player/weapon.c index c37b9f48..da6be167 100644 --- a/src/game/player/weapon.c +++ b/src/game/player/weapon.c @@ -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); diff --git a/src/game/savegame/tables/gamefunc_decs.h b/src/game/savegame/tables/gamefunc_decs.h index 7fe38814..8448e5bd 100644 --- a/src/game/savegame/tables/gamefunc_decs.h +++ b/src/game/savegame/tables/gamefunc_decs.h @@ -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 ) ; diff --git a/src/game/savegame/tables/gamefunc_list.h b/src/game/savegame/tables/gamefunc_list.h index 39ef9ad2..1e25b929 100644 --- a/src/game/savegame/tables/gamefunc_list.h +++ b/src/game/savegame/tables/gamefunc_list.h @@ -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},