1650 lines
42 KiB
C++
1650 lines
42 KiB
C++
/***
|
|
*
|
|
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
|
*
|
|
* This product contains software technology licensed from Id
|
|
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
|
* All Rights Reserved.
|
|
*
|
|
* Use, distribution, and modification of this source code and/or resulting
|
|
* object code is restricted to non-commercial enhancements to products from
|
|
* Valve LLC. All other use, distribution, or modification is prohibited
|
|
* without written permission from Valve LLC.
|
|
*
|
|
****/
|
|
#include "hud.h"
|
|
#include "cl_util.h"
|
|
|
|
#include "extdll.h"
|
|
#include "util.h"
|
|
#include "cbase.h"
|
|
#include "weapons.h"
|
|
|
|
#include "com_weapons.h"
|
|
#include "const.h"
|
|
#include "entity_state.h"
|
|
#include "cl_entity.h"
|
|
#include "entity_types.h"
|
|
#include "usercmd.h"
|
|
#include "pm_defs.h"
|
|
#include "pm_materials.h"
|
|
|
|
#include "eventscripts.h"
|
|
#include "ev_hldm.h"
|
|
|
|
#include "r_efx.h"
|
|
#include "event_api.h"
|
|
#include "event_args.h"
|
|
#include "in_defs.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include "r_studioint.h"
|
|
#include "com_model.h"
|
|
|
|
extern engine_studio_api_t IEngineStudio;
|
|
|
|
static int tracerCount[MAX_PLAYERS];
|
|
|
|
#include "pm_shared.h"
|
|
|
|
void V_PunchAxis(int axis, float punch);
|
|
void VectorAngles(const float* forward, float* angles);
|
|
|
|
extern cvar_t* cl_lw;
|
|
extern cvar_t* r_decals;
|
|
|
|
static inline bool EV_HLDM_IsBSPModel(physent_t* pe)
|
|
{
|
|
return pe != nullptr && (pe->solid == SOLID_BSP || pe->movetype == MOVETYPE_PUSHSTEP);
|
|
}
|
|
|
|
// play a strike sound based on the texture that was hit by the attack traceline. VecSrc/VecEnd are the
|
|
// original traceline endpoints used by the attacker, iBulletType is the type of bullet that hit the texture.
|
|
// returns volume of strike instrument (crowbar) to play
|
|
float EV_HLDM_PlayTextureSound(int idx, pmtrace_t* ptr, float* vecSrc, float* vecEnd, int iBulletType)
|
|
{
|
|
// hit the world, try to play sound based on texture material type
|
|
char chTextureType = CHAR_TEX_CONCRETE;
|
|
cl_entity_t* cl_entity = NULL;
|
|
float fvol;
|
|
float fvolbar;
|
|
const char* rgsz[4];
|
|
int cnt;
|
|
float fattn = ATTN_NORM;
|
|
int entity;
|
|
char* pTextureName;
|
|
char texname[64];
|
|
char szbuffer[64];
|
|
|
|
entity = gEngfuncs.pEventAPI->EV_IndexFromTrace(ptr);
|
|
|
|
// FIXME check if playtexture sounds movevar is set
|
|
//
|
|
|
|
chTextureType = 0;
|
|
|
|
// Player
|
|
if (entity == 0)
|
|
{
|
|
// get texture from entity or world (world is ent(0))
|
|
pTextureName = (char*)gEngfuncs.pEventAPI->EV_TraceTexture(ptr->ent, vecSrc, vecEnd);
|
|
|
|
if (pTextureName)
|
|
{
|
|
strcpy(texname, pTextureName);
|
|
pTextureName = texname;
|
|
|
|
// strip leading '-0' or '+0~' or '{' or '!'
|
|
if (*pTextureName == '-' || *pTextureName == '+')
|
|
{
|
|
pTextureName += 2;
|
|
}
|
|
|
|
if (*pTextureName == '{' || *pTextureName == '!' || *pTextureName == '~' || *pTextureName == ' ')
|
|
{
|
|
pTextureName++;
|
|
}
|
|
|
|
// '}}'
|
|
strcpy(szbuffer, pTextureName);
|
|
szbuffer[CBTEXTURENAMEMAX - 1] = 0;
|
|
|
|
// get texture type
|
|
chTextureType = PM_FindTextureType(szbuffer);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// JoshA: Look up the entity and find the EFLAG_FLESH_SOUND flag.
|
|
// This broke at some point then TF:C added prediction.
|
|
//
|
|
// It used to use Classify of pEntity->Classify() != CLASS_NONE && pEntity->Classify() != CLASS_MACHINE
|
|
// to determine what sound to play, but that's server side and isn't available on the client
|
|
// and got lost in the translation to that.
|
|
// Now the server will replicate that state via an eflag.
|
|
cl_entity = gEngfuncs.GetEntityByIndex(entity);
|
|
|
|
if (cl_entity && !!(cl_entity->curstate.eflags & EFLAG_FLESH_SOUND))
|
|
chTextureType = CHAR_TEX_FLESH;
|
|
}
|
|
|
|
switch (chTextureType)
|
|
{
|
|
default:
|
|
case CHAR_TEX_CONCRETE:
|
|
fvol = 0.9;
|
|
fvolbar = 0.6;
|
|
rgsz[0] = "player/pl_step1.wav";
|
|
rgsz[1] = "player/pl_step2.wav";
|
|
cnt = 2;
|
|
break;
|
|
case CHAR_TEX_METAL:
|
|
fvol = 0.9;
|
|
fvolbar = 0.3;
|
|
rgsz[0] = "player/pl_metal1.wav";
|
|
rgsz[1] = "player/pl_metal2.wav";
|
|
cnt = 2;
|
|
break;
|
|
case CHAR_TEX_DIRT:
|
|
fvol = 0.9;
|
|
fvolbar = 0.1;
|
|
rgsz[0] = "player/pl_dirt1.wav";
|
|
rgsz[1] = "player/pl_dirt2.wav";
|
|
rgsz[2] = "player/pl_dirt3.wav";
|
|
cnt = 3;
|
|
break;
|
|
case CHAR_TEX_VENT:
|
|
fvol = 0.5;
|
|
fvolbar = 0.3;
|
|
rgsz[0] = "player/pl_duct1.wav";
|
|
rgsz[1] = "player/pl_duct1.wav";
|
|
cnt = 2;
|
|
break;
|
|
case CHAR_TEX_GRATE:
|
|
fvol = 0.9;
|
|
fvolbar = 0.5;
|
|
rgsz[0] = "player/pl_grate1.wav";
|
|
rgsz[1] = "player/pl_grate4.wav";
|
|
cnt = 2;
|
|
break;
|
|
case CHAR_TEX_TILE:
|
|
fvol = 0.8;
|
|
fvolbar = 0.2;
|
|
rgsz[0] = "player/pl_tile1.wav";
|
|
rgsz[1] = "player/pl_tile3.wav";
|
|
rgsz[2] = "player/pl_tile2.wav";
|
|
rgsz[3] = "player/pl_tile4.wav";
|
|
cnt = 4;
|
|
break;
|
|
case CHAR_TEX_SLOSH:
|
|
fvol = 0.9;
|
|
fvolbar = 0.0;
|
|
rgsz[0] = "player/pl_slosh1.wav";
|
|
rgsz[1] = "player/pl_slosh3.wav";
|
|
rgsz[2] = "player/pl_slosh2.wav";
|
|
rgsz[3] = "player/pl_slosh4.wav";
|
|
cnt = 4;
|
|
break;
|
|
case CHAR_TEX_WOOD:
|
|
fvol = 0.9;
|
|
fvolbar = 0.2;
|
|
rgsz[0] = "debris/wood1.wav";
|
|
rgsz[1] = "debris/wood2.wav";
|
|
rgsz[2] = "debris/wood3.wav";
|
|
cnt = 3;
|
|
break;
|
|
case CHAR_TEX_GLASS:
|
|
case CHAR_TEX_COMPUTER:
|
|
fvol = 0.8;
|
|
fvolbar = 0.2;
|
|
rgsz[0] = "debris/glass1.wav";
|
|
rgsz[1] = "debris/glass2.wav";
|
|
rgsz[2] = "debris/glass3.wav";
|
|
cnt = 3;
|
|
break;
|
|
case CHAR_TEX_FLESH:
|
|
if (iBulletType == BULLET_PLAYER_CROWBAR)
|
|
return 0.0; // crowbar already makes this sound
|
|
fvol = 1.0;
|
|
fvolbar = 0.2;
|
|
rgsz[0] = "weapons/bullet_hit1.wav";
|
|
rgsz[1] = "weapons/bullet_hit2.wav";
|
|
fattn = 1.0;
|
|
cnt = 2;
|
|
break;
|
|
}
|
|
|
|
// play material hit sound
|
|
gEngfuncs.pEventAPI->EV_PlaySound(0, ptr->endpos, CHAN_STATIC, rgsz[gEngfuncs.pfnRandomLong(0, cnt - 1)], fvol, fattn, 0, 96 + gEngfuncs.pfnRandomLong(0, 0xf));
|
|
return fvolbar;
|
|
}
|
|
|
|
char* EV_HLDM_DamageDecal(physent_t* pe)
|
|
{
|
|
static char decalname[32];
|
|
int idx;
|
|
|
|
if (pe->rendermode == kRenderTransAlpha)
|
|
return nullptr;
|
|
|
|
if (pe->classnumber == 1)
|
|
{
|
|
idx = gEngfuncs.pfnRandomLong(0, 2);
|
|
sprintf(decalname, "{break%i", idx + 1);
|
|
}
|
|
else if (pe->rendermode != kRenderNormal)
|
|
{
|
|
sprintf(decalname, "{bproof1");
|
|
}
|
|
else
|
|
{
|
|
idx = gEngfuncs.pfnRandomLong(0, 4);
|
|
sprintf(decalname, "{shot%i", idx + 1);
|
|
}
|
|
return decalname;
|
|
}
|
|
|
|
void EV_HLDM_GunshotDecalTrace(pmtrace_t* pTrace, char* decalName)
|
|
{
|
|
int iRand;
|
|
|
|
gEngfuncs.pEfxAPI->R_BulletImpactParticles(pTrace->endpos);
|
|
|
|
iRand = gEngfuncs.pfnRandomLong(0, 0x7FFF);
|
|
if (iRand < (0x7fff / 2)) // not every bullet makes a sound.
|
|
{
|
|
switch (iRand % 5)
|
|
{
|
|
case 0:
|
|
gEngfuncs.pEventAPI->EV_PlaySound(-1, pTrace->endpos, 0, "weapons/ric1.wav", 1.0, ATTN_NORM, 0, PITCH_NORM);
|
|
break;
|
|
case 1:
|
|
gEngfuncs.pEventAPI->EV_PlaySound(-1, pTrace->endpos, 0, "weapons/ric2.wav", 1.0, ATTN_NORM, 0, PITCH_NORM);
|
|
break;
|
|
case 2:
|
|
gEngfuncs.pEventAPI->EV_PlaySound(-1, pTrace->endpos, 0, "weapons/ric3.wav", 1.0, ATTN_NORM, 0, PITCH_NORM);
|
|
break;
|
|
case 3:
|
|
gEngfuncs.pEventAPI->EV_PlaySound(-1, pTrace->endpos, 0, "weapons/ric4.wav", 1.0, ATTN_NORM, 0, PITCH_NORM);
|
|
break;
|
|
case 4:
|
|
gEngfuncs.pEventAPI->EV_PlaySound(-1, pTrace->endpos, 0, "weapons/ric5.wav", 1.0, ATTN_NORM, 0, PITCH_NORM);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Only decal brush models such as the world etc.
|
|
if (decalName && '\0' != decalName[0] && r_decals->value > 0)
|
|
{
|
|
gEngfuncs.pEfxAPI->R_DecalShoot(
|
|
gEngfuncs.pEfxAPI->Draw_DecalIndex(gEngfuncs.pEfxAPI->Draw_DecalIndexFromName(decalName)),
|
|
gEngfuncs.pEventAPI->EV_IndexFromTrace(pTrace), 0, pTrace->endpos, 0);
|
|
}
|
|
}
|
|
|
|
void EV_HLDM_DecalGunshot(pmtrace_t* pTrace, int iBulletType)
|
|
{
|
|
physent_t* pe;
|
|
|
|
pe = gEngfuncs.pEventAPI->EV_GetPhysent(pTrace->ent);
|
|
|
|
if (EV_HLDM_IsBSPModel(pe))
|
|
{
|
|
switch (iBulletType)
|
|
{
|
|
case BULLET_PLAYER_9MM:
|
|
case BULLET_MONSTER_9MM:
|
|
case BULLET_PLAYER_MP5:
|
|
case BULLET_MONSTER_MP5:
|
|
case BULLET_PLAYER_BUCKSHOT:
|
|
case BULLET_PLAYER_357:
|
|
default:
|
|
// smoke and decal
|
|
EV_HLDM_GunshotDecalTrace(pTrace, EV_HLDM_DamageDecal(pe));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void EV_HLDM_CheckTracer(int idx, float* vecSrc, float* end, float* forward, float* right, int iBulletType, int iTracerFreq, int* tracerCount)
|
|
{
|
|
int i;
|
|
bool player = idx >= 1 && idx <= gEngfuncs.GetMaxClients();
|
|
|
|
if (iTracerFreq != 0 && ((*tracerCount)++ % iTracerFreq) == 0)
|
|
{
|
|
Vector vecTracerSrc;
|
|
|
|
if (player)
|
|
{
|
|
Vector offset(0, 0, -4);
|
|
|
|
// adjust tracer position for player
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
vecTracerSrc[i] = vecSrc[i] + offset[i] + right[i] * 2 + forward[i] * 16;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
VectorCopy(vecSrc, vecTracerSrc);
|
|
}
|
|
|
|
switch (iBulletType)
|
|
{
|
|
case BULLET_PLAYER_MP5:
|
|
case BULLET_MONSTER_MP5:
|
|
case BULLET_MONSTER_9MM:
|
|
case BULLET_MONSTER_12MM:
|
|
default:
|
|
EV_CreateTracer(vecTracerSrc, end);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
FireBullets
|
|
|
|
Go to the trouble of combining multiple pellets into a single damage call.
|
|
================
|
|
*/
|
|
void EV_HLDM_FireBullets(int idx, float* forward, float* right, float* up, int cShots, float* vecSrc, float* vecDirShooting, float flDistance, int iBulletType, int iTracerFreq, int* tracerCount, float flSpreadX, float flSpreadY)
|
|
{
|
|
int i;
|
|
pmtrace_t tr;
|
|
int iShot;
|
|
|
|
for (iShot = 1; iShot <= cShots; iShot++)
|
|
{
|
|
Vector vecDir, vecEnd;
|
|
|
|
float x, y, z;
|
|
//We randomize for the Shotgun.
|
|
if (iBulletType == BULLET_PLAYER_BUCKSHOT)
|
|
{
|
|
do
|
|
{
|
|
x = gEngfuncs.pfnRandomFloat(-0.5, 0.5) + gEngfuncs.pfnRandomFloat(-0.5, 0.5);
|
|
y = gEngfuncs.pfnRandomFloat(-0.5, 0.5) + gEngfuncs.pfnRandomFloat(-0.5, 0.5);
|
|
z = x * x + y * y;
|
|
} while (z > 1);
|
|
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
vecDir[i] = vecDirShooting[i] + x * flSpreadX * right[i] + y * flSpreadY * up[i];
|
|
vecEnd[i] = vecSrc[i] + flDistance * vecDir[i];
|
|
}
|
|
} //But other guns already have their spread randomized in the synched spread.
|
|
else
|
|
{
|
|
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
vecDir[i] = vecDirShooting[i] + flSpreadX * right[i] + flSpreadY * up[i];
|
|
vecEnd[i] = vecSrc[i] + flDistance * vecDir[i];
|
|
}
|
|
}
|
|
|
|
gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction(0, 1);
|
|
|
|
// Store off the old count
|
|
gEngfuncs.pEventAPI->EV_PushPMStates();
|
|
|
|
// Now add in all of the players.
|
|
gEngfuncs.pEventAPI->EV_SetSolidPlayers(idx - 1);
|
|
|
|
gEngfuncs.pEventAPI->EV_SetTraceHull(2);
|
|
gEngfuncs.pEventAPI->EV_PlayerTrace(vecSrc, vecEnd, PM_STUDIO_BOX, -1, &tr);
|
|
|
|
EV_HLDM_CheckTracer(idx, vecSrc, tr.endpos, forward, right, iBulletType, iTracerFreq, tracerCount);
|
|
|
|
// do damage, paint decals
|
|
if (tr.fraction != 1.0)
|
|
{
|
|
switch (iBulletType)
|
|
{
|
|
default:
|
|
case BULLET_PLAYER_9MM:
|
|
|
|
EV_HLDM_PlayTextureSound(idx, &tr, vecSrc, vecEnd, iBulletType);
|
|
EV_HLDM_DecalGunshot(&tr, iBulletType);
|
|
|
|
break;
|
|
case BULLET_PLAYER_MP5:
|
|
|
|
EV_HLDM_PlayTextureSound(idx, &tr, vecSrc, vecEnd, iBulletType);
|
|
EV_HLDM_DecalGunshot(&tr, iBulletType);
|
|
break;
|
|
case BULLET_PLAYER_BUCKSHOT:
|
|
|
|
EV_HLDM_DecalGunshot(&tr, iBulletType);
|
|
|
|
break;
|
|
case BULLET_PLAYER_357:
|
|
|
|
EV_HLDM_PlayTextureSound(idx, &tr, vecSrc, vecEnd, iBulletType);
|
|
EV_HLDM_DecalGunshot(&tr, iBulletType);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
gEngfuncs.pEventAPI->EV_PopPMStates();
|
|
}
|
|
}
|
|
|
|
//======================
|
|
// GLOCK START
|
|
//======================
|
|
void EV_FireGlock1(event_args_t* args)
|
|
{
|
|
int idx;
|
|
Vector origin;
|
|
Vector angles;
|
|
Vector velocity;
|
|
bool empty;
|
|
|
|
Vector ShellVelocity;
|
|
Vector ShellOrigin;
|
|
int shell;
|
|
Vector vecSrc, vecAiming;
|
|
Vector up, right, forward;
|
|
|
|
idx = args->entindex;
|
|
VectorCopy(args->origin, origin);
|
|
VectorCopy(args->angles, angles);
|
|
VectorCopy(args->velocity, velocity);
|
|
|
|
empty = 0 != args->bparam1;
|
|
AngleVectors(angles, forward, right, up);
|
|
|
|
shell = gEngfuncs.pEventAPI->EV_FindModelIndex("models/shell.mdl"); // brass shell
|
|
|
|
if (EV_IsLocal(idx))
|
|
{
|
|
EV_MuzzleFlash();
|
|
gEngfuncs.pEventAPI->EV_WeaponAnimation(empty ? GLOCK_SHOOT_EMPTY : GLOCK_SHOOT, 0);
|
|
|
|
V_PunchAxis(0, -2.0);
|
|
}
|
|
|
|
EV_GetDefaultShellInfo(args, origin, velocity, ShellVelocity, ShellOrigin, forward, right, up, 20, -12, 4);
|
|
|
|
EV_EjectBrass(ShellOrigin, ShellVelocity, angles[YAW], shell, TE_BOUNCE_SHELL);
|
|
|
|
gEngfuncs.pEventAPI->EV_PlaySound(idx, origin, CHAN_WEAPON, "weapons/pl_gun3.wav", gEngfuncs.pfnRandomFloat(0.92, 1.0), ATTN_NORM, 0, 98 + gEngfuncs.pfnRandomLong(0, 3));
|
|
|
|
EV_GetGunPosition(args, vecSrc, origin);
|
|
|
|
VectorCopy(forward, vecAiming);
|
|
|
|
EV_HLDM_FireBullets(idx, forward, right, up, 1, vecSrc, vecAiming, 8192, BULLET_PLAYER_9MM, 0, &tracerCount[idx - 1], args->fparam1, args->fparam2);
|
|
}
|
|
|
|
void EV_FireGlock2(event_args_t* args)
|
|
{
|
|
int idx;
|
|
Vector origin;
|
|
Vector angles;
|
|
Vector velocity;
|
|
bool empty;
|
|
|
|
Vector ShellVelocity;
|
|
Vector ShellOrigin;
|
|
int shell;
|
|
Vector vecSrc, vecAiming;
|
|
Vector up, right, forward;
|
|
|
|
idx = args->entindex;
|
|
VectorCopy(args->origin, origin);
|
|
VectorCopy(args->angles, angles);
|
|
VectorCopy(args->velocity, velocity);
|
|
|
|
empty = 0 != args->bparam1;
|
|
AngleVectors(angles, forward, right, up);
|
|
|
|
shell = gEngfuncs.pEventAPI->EV_FindModelIndex("models/shell.mdl"); // brass shell
|
|
|
|
if (EV_IsLocal(idx))
|
|
{
|
|
// Add muzzle flash to current weapon model
|
|
EV_MuzzleFlash();
|
|
gEngfuncs.pEventAPI->EV_WeaponAnimation(empty ? GLOCK_SHOOT_EMPTY : GLOCK_SHOOT, 0);
|
|
|
|
V_PunchAxis(0, -2.0);
|
|
}
|
|
|
|
EV_GetDefaultShellInfo(args, origin, velocity, ShellVelocity, ShellOrigin, forward, right, up, 20, -12, 4);
|
|
|
|
EV_EjectBrass(ShellOrigin, ShellVelocity, angles[YAW], shell, TE_BOUNCE_SHELL);
|
|
|
|
gEngfuncs.pEventAPI->EV_PlaySound(idx, origin, CHAN_WEAPON, "weapons/pl_gun3.wav", gEngfuncs.pfnRandomFloat(0.92, 1.0), ATTN_NORM, 0, 98 + gEngfuncs.pfnRandomLong(0, 3));
|
|
|
|
EV_GetGunPosition(args, vecSrc, origin);
|
|
|
|
VectorCopy(forward, vecAiming);
|
|
|
|
EV_HLDM_FireBullets(idx, forward, right, up, 1, vecSrc, vecAiming, 8192, BULLET_PLAYER_9MM, 0, &tracerCount[idx - 1], args->fparam1, args->fparam2);
|
|
}
|
|
//======================
|
|
// GLOCK END
|
|
//======================
|
|
|
|
//======================
|
|
// SHOTGUN START
|
|
//======================
|
|
void EV_FireShotGunDouble(event_args_t* args)
|
|
{
|
|
int idx;
|
|
Vector origin;
|
|
Vector angles;
|
|
Vector velocity;
|
|
|
|
int j;
|
|
Vector ShellVelocity;
|
|
Vector ShellOrigin;
|
|
int shell;
|
|
Vector vecSrc, vecAiming;
|
|
Vector up, right, forward;
|
|
|
|
idx = args->entindex;
|
|
VectorCopy(args->origin, origin);
|
|
VectorCopy(args->angles, angles);
|
|
VectorCopy(args->velocity, velocity);
|
|
|
|
AngleVectors(angles, forward, right, up);
|
|
|
|
shell = gEngfuncs.pEventAPI->EV_FindModelIndex("models/shotgunshell.mdl"); // brass shell
|
|
|
|
if (EV_IsLocal(idx))
|
|
{
|
|
// Add muzzle flash to current weapon model
|
|
EV_MuzzleFlash();
|
|
gEngfuncs.pEventAPI->EV_WeaponAnimation(SHOTGUN_FIRE2, 0);
|
|
V_PunchAxis(0, -10.0);
|
|
}
|
|
|
|
for (j = 0; j < 2; j++)
|
|
{
|
|
EV_GetDefaultShellInfo(args, origin, velocity, ShellVelocity, ShellOrigin, forward, right, up, 32, -12, 6);
|
|
|
|
EV_EjectBrass(ShellOrigin, ShellVelocity, angles[YAW], shell, TE_BOUNCE_SHOTSHELL);
|
|
}
|
|
|
|
gEngfuncs.pEventAPI->EV_PlaySound(idx, origin, CHAN_WEAPON, "weapons/dbarrel1.wav", gEngfuncs.pfnRandomFloat(0.98, 1.0), ATTN_NORM, 0, 85 + gEngfuncs.pfnRandomLong(0, 0x1f));
|
|
|
|
EV_GetGunPosition(args, vecSrc, origin);
|
|
VectorCopy(forward, vecAiming);
|
|
|
|
if (gEngfuncs.GetMaxClients() > 1)
|
|
{
|
|
EV_HLDM_FireBullets(idx, forward, right, up, 8, vecSrc, vecAiming, 2048, BULLET_PLAYER_BUCKSHOT, 0, &tracerCount[idx - 1], 0.17365, 0.04362);
|
|
}
|
|
else
|
|
{
|
|
EV_HLDM_FireBullets(idx, forward, right, up, 12, vecSrc, vecAiming, 2048, BULLET_PLAYER_BUCKSHOT, 0, &tracerCount[idx - 1], 0.08716, 0.08716);
|
|
}
|
|
}
|
|
|
|
void EV_FireShotGunSingle(event_args_t* args)
|
|
{
|
|
int idx;
|
|
Vector origin;
|
|
Vector angles;
|
|
Vector velocity;
|
|
|
|
Vector ShellVelocity;
|
|
Vector ShellOrigin;
|
|
int shell;
|
|
Vector vecSrc, vecAiming;
|
|
Vector up, right, forward;
|
|
|
|
idx = args->entindex;
|
|
VectorCopy(args->origin, origin);
|
|
VectorCopy(args->angles, angles);
|
|
VectorCopy(args->velocity, velocity);
|
|
|
|
AngleVectors(angles, forward, right, up);
|
|
|
|
shell = gEngfuncs.pEventAPI->EV_FindModelIndex("models/shotgunshell.mdl"); // brass shell
|
|
|
|
if (EV_IsLocal(idx))
|
|
{
|
|
// Add muzzle flash to current weapon model
|
|
EV_MuzzleFlash();
|
|
gEngfuncs.pEventAPI->EV_WeaponAnimation(SHOTGUN_FIRE, 0);
|
|
|
|
V_PunchAxis(0, -5.0);
|
|
}
|
|
|
|
EV_GetDefaultShellInfo(args, origin, velocity, ShellVelocity, ShellOrigin, forward, right, up, 32, -12, 6);
|
|
|
|
EV_EjectBrass(ShellOrigin, ShellVelocity, angles[YAW], shell, TE_BOUNCE_SHOTSHELL);
|
|
|
|
gEngfuncs.pEventAPI->EV_PlaySound(idx, origin, CHAN_WEAPON, "weapons/sbarrel1.wav", gEngfuncs.pfnRandomFloat(0.95, 1.0), ATTN_NORM, 0, 93 + gEngfuncs.pfnRandomLong(0, 0x1f));
|
|
|
|
EV_GetGunPosition(args, vecSrc, origin);
|
|
VectorCopy(forward, vecAiming);
|
|
|
|
if (gEngfuncs.GetMaxClients() > 1)
|
|
{
|
|
EV_HLDM_FireBullets(idx, forward, right, up, 4, vecSrc, vecAiming, 2048, BULLET_PLAYER_BUCKSHOT, 0, &tracerCount[idx - 1], 0.08716, 0.04362);
|
|
}
|
|
else
|
|
{
|
|
EV_HLDM_FireBullets(idx, forward, right, up, 6, vecSrc, vecAiming, 2048, BULLET_PLAYER_BUCKSHOT, 0, &tracerCount[idx - 1], 0.08716, 0.08716);
|
|
}
|
|
}
|
|
//======================
|
|
// SHOTGUN END
|
|
//======================
|
|
|
|
//======================
|
|
// MP5 START
|
|
//======================
|
|
void EV_FireMP5(event_args_t* args)
|
|
{
|
|
int idx;
|
|
Vector origin;
|
|
Vector angles;
|
|
Vector velocity;
|
|
|
|
Vector ShellVelocity;
|
|
Vector ShellOrigin;
|
|
int shell;
|
|
Vector vecSrc, vecAiming;
|
|
Vector up, right, forward;
|
|
|
|
idx = args->entindex;
|
|
VectorCopy(args->origin, origin);
|
|
VectorCopy(args->angles, angles);
|
|
VectorCopy(args->velocity, velocity);
|
|
|
|
AngleVectors(angles, forward, right, up);
|
|
|
|
shell = gEngfuncs.pEventAPI->EV_FindModelIndex("models/shell.mdl"); // brass shell
|
|
|
|
if (EV_IsLocal(idx))
|
|
{
|
|
// Add muzzle flash to current weapon model
|
|
EV_MuzzleFlash();
|
|
gEngfuncs.pEventAPI->EV_WeaponAnimation(MP5_FIRE1 + gEngfuncs.pfnRandomLong(0, 2), 0);
|
|
|
|
V_PunchAxis(0, gEngfuncs.pfnRandomFloat(-2, 2));
|
|
}
|
|
|
|
EV_GetDefaultShellInfo(args, origin, velocity, ShellVelocity, ShellOrigin, forward, right, up, 20, -12, 4);
|
|
|
|
EV_EjectBrass(ShellOrigin, ShellVelocity, angles[YAW], shell, TE_BOUNCE_SHELL);
|
|
|
|
switch (gEngfuncs.pfnRandomLong(0, 1))
|
|
{
|
|
case 0:
|
|
gEngfuncs.pEventAPI->EV_PlaySound(idx, origin, CHAN_WEAPON, "weapons/hks1.wav", 1, ATTN_NORM, 0, 94 + gEngfuncs.pfnRandomLong(0, 0xf));
|
|
break;
|
|
case 1:
|
|
gEngfuncs.pEventAPI->EV_PlaySound(idx, origin, CHAN_WEAPON, "weapons/hks2.wav", 1, ATTN_NORM, 0, 94 + gEngfuncs.pfnRandomLong(0, 0xf));
|
|
break;
|
|
}
|
|
|
|
EV_GetGunPosition(args, vecSrc, origin);
|
|
VectorCopy(forward, vecAiming);
|
|
|
|
EV_HLDM_FireBullets(idx, forward, right, up, 1, vecSrc, vecAiming, 8192, BULLET_PLAYER_MP5, 2, &tracerCount[idx - 1], args->fparam1, args->fparam2);
|
|
}
|
|
|
|
// We only predict the animation and sound
|
|
// The grenade is still launched from the server.
|
|
void EV_FireMP52(event_args_t* args)
|
|
{
|
|
int idx;
|
|
Vector origin;
|
|
|
|
idx = args->entindex;
|
|
VectorCopy(args->origin, origin);
|
|
|
|
if (EV_IsLocal(idx))
|
|
{
|
|
gEngfuncs.pEventAPI->EV_WeaponAnimation(MP5_LAUNCH, 0);
|
|
V_PunchAxis(0, -10);
|
|
}
|
|
|
|
switch (gEngfuncs.pfnRandomLong(0, 1))
|
|
{
|
|
case 0:
|
|
gEngfuncs.pEventAPI->EV_PlaySound(idx, origin, CHAN_WEAPON, "weapons/glauncher.wav", 1, ATTN_NORM, 0, 94 + gEngfuncs.pfnRandomLong(0, 0xf));
|
|
break;
|
|
case 1:
|
|
gEngfuncs.pEventAPI->EV_PlaySound(idx, origin, CHAN_WEAPON, "weapons/glauncher2.wav", 1, ATTN_NORM, 0, 94 + gEngfuncs.pfnRandomLong(0, 0xf));
|
|
break;
|
|
}
|
|
}
|
|
//======================
|
|
// MP5 END
|
|
//======================
|
|
|
|
//======================
|
|
// PHYTON START
|
|
// ( .357 )
|
|
//======================
|
|
void EV_FirePython(event_args_t* args)
|
|
{
|
|
int idx;
|
|
Vector origin;
|
|
Vector angles;
|
|
Vector velocity;
|
|
|
|
Vector vecSrc, vecAiming;
|
|
Vector up, right, forward;
|
|
|
|
idx = args->entindex;
|
|
VectorCopy(args->origin, origin);
|
|
VectorCopy(args->angles, angles);
|
|
VectorCopy(args->velocity, velocity);
|
|
|
|
AngleVectors(angles, forward, right, up);
|
|
|
|
if (EV_IsLocal(idx))
|
|
{
|
|
// Python uses different body in multiplayer versus single player
|
|
bool multiplayer = gEngfuncs.GetMaxClients() != 1;
|
|
|
|
// Add muzzle flash to current weapon model
|
|
EV_MuzzleFlash();
|
|
gEngfuncs.pEventAPI->EV_WeaponAnimation(PYTHON_FIRE1, multiplayer ? 1 : 0);
|
|
|
|
V_PunchAxis(0, -10.0);
|
|
}
|
|
|
|
switch (gEngfuncs.pfnRandomLong(0, 1))
|
|
{
|
|
case 0:
|
|
gEngfuncs.pEventAPI->EV_PlaySound(idx, origin, CHAN_WEAPON, "weapons/357_shot1.wav", gEngfuncs.pfnRandomFloat(0.8, 0.9), ATTN_NORM, 0, PITCH_NORM);
|
|
break;
|
|
case 1:
|
|
gEngfuncs.pEventAPI->EV_PlaySound(idx, origin, CHAN_WEAPON, "weapons/357_shot2.wav", gEngfuncs.pfnRandomFloat(0.8, 0.9), ATTN_NORM, 0, PITCH_NORM);
|
|
break;
|
|
}
|
|
|
|
EV_GetGunPosition(args, vecSrc, origin);
|
|
|
|
VectorCopy(forward, vecAiming);
|
|
|
|
EV_HLDM_FireBullets(idx, forward, right, up, 1, vecSrc, vecAiming, 8192, BULLET_PLAYER_357, 0, &tracerCount[idx - 1], args->fparam1, args->fparam2);
|
|
}
|
|
//======================
|
|
// PHYTON END
|
|
// ( .357 )
|
|
//======================
|
|
|
|
//======================
|
|
// GAUSS START
|
|
//======================
|
|
void EV_SpinGauss(event_args_t* args)
|
|
{
|
|
int idx;
|
|
Vector origin;
|
|
Vector angles;
|
|
Vector velocity;
|
|
int iSoundState = 0;
|
|
|
|
int pitch;
|
|
|
|
idx = args->entindex;
|
|
VectorCopy(args->origin, origin);
|
|
VectorCopy(args->angles, angles);
|
|
VectorCopy(args->velocity, velocity);
|
|
|
|
pitch = args->iparam1;
|
|
|
|
iSoundState = 0 != args->bparam1 ? SND_CHANGE_PITCH : 0;
|
|
|
|
gEngfuncs.pEventAPI->EV_PlaySound(idx, origin, CHAN_WEAPON, "ambience/pulsemachine.wav", 1.0, ATTN_NORM, iSoundState, pitch);
|
|
}
|
|
|
|
/*
|
|
==============================
|
|
EV_StopPreviousGauss
|
|
|
|
==============================
|
|
*/
|
|
void EV_StopPreviousGauss(int idx)
|
|
{
|
|
// Make sure we don't have a gauss spin event in the queue for this guy
|
|
gEngfuncs.pEventAPI->EV_KillEvents(idx, "events/gaussspin.sc");
|
|
gEngfuncs.pEventAPI->EV_StopSound(idx, CHAN_WEAPON, "ambience/pulsemachine.wav");
|
|
}
|
|
|
|
extern float g_flApplyVel;
|
|
|
|
void EV_FireGauss(event_args_t* args)
|
|
{
|
|
int idx;
|
|
Vector origin;
|
|
Vector angles;
|
|
Vector velocity;
|
|
float flDamage = args->fparam1;
|
|
|
|
bool m_fPrimaryFire = 0 != args->bparam1;
|
|
Vector vecSrc;
|
|
Vector vecDest;
|
|
edict_t* pentIgnore;
|
|
pmtrace_t tr, beam_tr;
|
|
float flMaxFrac = 1.0;
|
|
bool fHasPunched = false;
|
|
bool fFirstBeam = true;
|
|
int nMaxHits = 10;
|
|
physent_t* pEntity;
|
|
int m_iBeam, m_iGlow, m_iBalls;
|
|
Vector up, right, forward;
|
|
|
|
idx = args->entindex;
|
|
VectorCopy(args->origin, origin);
|
|
VectorCopy(args->angles, angles);
|
|
VectorCopy(args->velocity, velocity);
|
|
|
|
if (0 != args->bparam2)
|
|
{
|
|
EV_StopPreviousGauss(idx);
|
|
return;
|
|
}
|
|
|
|
// Con_Printf( "Firing gauss with %f\n", flDamage );
|
|
EV_GetGunPosition(args, vecSrc, origin);
|
|
|
|
m_iBeam = gEngfuncs.pEventAPI->EV_FindModelIndex("sprites/smoke.spr");
|
|
m_iBalls = m_iGlow = gEngfuncs.pEventAPI->EV_FindModelIndex("sprites/hotglow.spr");
|
|
|
|
AngleVectors(angles, forward, right, up);
|
|
|
|
VectorMA(vecSrc, 8192, forward, vecDest);
|
|
|
|
if (EV_IsLocal(idx))
|
|
{
|
|
V_PunchAxis(0, -2.0);
|
|
gEngfuncs.pEventAPI->EV_WeaponAnimation(GAUSS_FIRE2, 0);
|
|
|
|
if (!m_fPrimaryFire)
|
|
g_flApplyVel = flDamage;
|
|
}
|
|
|
|
gEngfuncs.pEventAPI->EV_PlaySound(idx, origin, CHAN_WEAPON, "weapons/gauss2.wav", 0.5 + flDamage * (1.0 / 400.0), ATTN_NORM, 0, 85 + gEngfuncs.pfnRandomLong(0, 0x1f));
|
|
|
|
while (flDamage > 10 && nMaxHits > 0)
|
|
{
|
|
nMaxHits--;
|
|
|
|
gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction(0, 1);
|
|
|
|
// Store off the old count
|
|
gEngfuncs.pEventAPI->EV_PushPMStates();
|
|
|
|
// Now add in all of the players.
|
|
gEngfuncs.pEventAPI->EV_SetSolidPlayers(idx - 1);
|
|
|
|
gEngfuncs.pEventAPI->EV_SetTraceHull(2);
|
|
gEngfuncs.pEventAPI->EV_PlayerTrace(vecSrc, vecDest, PM_STUDIO_BOX, -1, &tr);
|
|
|
|
gEngfuncs.pEventAPI->EV_PopPMStates();
|
|
|
|
if (0 != tr.allsolid)
|
|
break;
|
|
|
|
if (fFirstBeam)
|
|
{
|
|
if (EV_IsLocal(idx))
|
|
{
|
|
// Add muzzle flash to current weapon model
|
|
EV_MuzzleFlash();
|
|
}
|
|
fFirstBeam = false;
|
|
|
|
gEngfuncs.pEfxAPI->R_BeamEntPoint(
|
|
idx | 0x1000,
|
|
tr.endpos,
|
|
m_iBeam,
|
|
0.1,
|
|
m_fPrimaryFire ? 1.0 : 2.5,
|
|
0.0,
|
|
(m_fPrimaryFire ? 128.0 : flDamage) / 255.0,
|
|
0,
|
|
0,
|
|
0,
|
|
(m_fPrimaryFire ? 255 : 255) / 255.0,
|
|
(m_fPrimaryFire ? 128 : 255) / 255.0,
|
|
(m_fPrimaryFire ? 0 : 255) / 255.0);
|
|
}
|
|
else
|
|
{
|
|
gEngfuncs.pEfxAPI->R_BeamPoints(vecSrc,
|
|
tr.endpos,
|
|
m_iBeam,
|
|
0.1,
|
|
m_fPrimaryFire ? 1.0 : 2.5,
|
|
0.0,
|
|
(m_fPrimaryFire ? 128.0 : flDamage) / 255.0,
|
|
0,
|
|
0,
|
|
0,
|
|
(m_fPrimaryFire ? 255 : 255) / 255.0,
|
|
(m_fPrimaryFire ? 128 : 255) / 255.0,
|
|
(m_fPrimaryFire ? 0 : 255) / 255.0);
|
|
}
|
|
|
|
pEntity = gEngfuncs.pEventAPI->EV_GetPhysent(tr.ent);
|
|
if (pEntity == NULL)
|
|
break;
|
|
|
|
if (EV_HLDM_IsBSPModel(pEntity))
|
|
{
|
|
float n;
|
|
|
|
pentIgnore = NULL;
|
|
|
|
n = -DotProduct(tr.plane.normal, forward);
|
|
|
|
if (n < 0.5) // 60 degrees
|
|
{
|
|
// ALERT( at_console, "reflect %f\n", n );
|
|
// reflect
|
|
Vector r;
|
|
|
|
VectorMA(forward, 2.0 * n, tr.plane.normal, r);
|
|
|
|
flMaxFrac = flMaxFrac - tr.fraction;
|
|
|
|
VectorCopy(r, forward);
|
|
|
|
VectorMA(tr.endpos, 8.0, forward, vecSrc);
|
|
VectorMA(vecSrc, 8192.0, forward, vecDest);
|
|
|
|
gEngfuncs.pEfxAPI->R_TempSprite(tr.endpos, vec3_origin, 0.2, m_iGlow, kRenderGlow, kRenderFxNoDissipation, flDamage * n / 255.0, flDamage * n * 0.5 * 0.1, FTENT_FADEOUT);
|
|
|
|
Vector fwd;
|
|
VectorAdd(tr.endpos, tr.plane.normal, fwd);
|
|
|
|
gEngfuncs.pEfxAPI->R_Sprite_Trail(TE_SPRITETRAIL, tr.endpos, fwd, m_iBalls, 3, 0.1, gEngfuncs.pfnRandomFloat(10, 20) / 100.0, 100,
|
|
255, 100);
|
|
|
|
// lose energy
|
|
if (n == 0)
|
|
{
|
|
n = 0.1;
|
|
}
|
|
|
|
flDamage = flDamage * (1 - n);
|
|
}
|
|
else
|
|
{
|
|
// tunnel
|
|
EV_HLDM_DecalGunshot(&tr, BULLET_MONSTER_12MM);
|
|
|
|
gEngfuncs.pEfxAPI->R_TempSprite(tr.endpos, vec3_origin, 1.0, m_iGlow, kRenderGlow, kRenderFxNoDissipation, flDamage / 255.0, 6.0, FTENT_FADEOUT);
|
|
|
|
// limit it to one hole punch
|
|
if (fHasPunched)
|
|
{
|
|
break;
|
|
}
|
|
fHasPunched = true;
|
|
|
|
// try punching through wall if secondary attack (primary is incapable of breaking through)
|
|
if (!m_fPrimaryFire)
|
|
{
|
|
Vector start;
|
|
|
|
VectorMA(tr.endpos, 8.0, forward, start);
|
|
|
|
// Store off the old count
|
|
gEngfuncs.pEventAPI->EV_PushPMStates();
|
|
|
|
// Now add in all of the players.
|
|
gEngfuncs.pEventAPI->EV_SetSolidPlayers(idx - 1);
|
|
|
|
gEngfuncs.pEventAPI->EV_SetTraceHull(2);
|
|
gEngfuncs.pEventAPI->EV_PlayerTrace(start, vecDest, PM_STUDIO_BOX, -1, &beam_tr);
|
|
|
|
if (0 == beam_tr.allsolid)
|
|
{
|
|
Vector delta;
|
|
float n;
|
|
|
|
// trace backwards to find exit point
|
|
|
|
gEngfuncs.pEventAPI->EV_PlayerTrace(beam_tr.endpos, tr.endpos, PM_STUDIO_BOX, -1, &beam_tr);
|
|
|
|
VectorSubtract(beam_tr.endpos, tr.endpos, delta);
|
|
|
|
n = Length(delta);
|
|
|
|
if (n < flDamage)
|
|
{
|
|
if (n == 0)
|
|
n = 1;
|
|
flDamage -= n;
|
|
|
|
// absorption balls
|
|
{
|
|
Vector fwd;
|
|
VectorSubtract(tr.endpos, forward, fwd);
|
|
gEngfuncs.pEfxAPI->R_Sprite_Trail(TE_SPRITETRAIL, tr.endpos, fwd, m_iBalls, 3, 0.1, gEngfuncs.pfnRandomFloat(10, 20) / 100.0, 100,
|
|
255, 100);
|
|
}
|
|
|
|
//////////////////////////////////// WHAT TO DO HERE
|
|
// CSoundEnt::InsertSound ( bits_SOUND_COMBAT, pev->origin, NORMAL_EXPLOSION_VOLUME, 3.0 );
|
|
|
|
EV_HLDM_DecalGunshot(&beam_tr, BULLET_MONSTER_12MM);
|
|
|
|
gEngfuncs.pEfxAPI->R_TempSprite(beam_tr.endpos, vec3_origin, 0.1, m_iGlow, kRenderGlow, kRenderFxNoDissipation, flDamage / 255.0, 6.0, FTENT_FADEOUT);
|
|
|
|
// balls
|
|
{
|
|
Vector fwd;
|
|
VectorSubtract(beam_tr.endpos, forward, fwd);
|
|
gEngfuncs.pEfxAPI->R_Sprite_Trail(TE_SPRITETRAIL, beam_tr.endpos, fwd, m_iBalls, (int)(flDamage * 0.3), 0.1, gEngfuncs.pfnRandomFloat(10, 20) / 100.0, 200,
|
|
255, 40);
|
|
}
|
|
|
|
VectorAdd(beam_tr.endpos, forward, vecSrc);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
flDamage = 0;
|
|
}
|
|
|
|
gEngfuncs.pEventAPI->EV_PopPMStates();
|
|
}
|
|
else
|
|
{
|
|
if (m_fPrimaryFire)
|
|
{
|
|
// slug doesn't punch through ever with primary
|
|
// fire, so leave a little glowy bit and make some balls
|
|
gEngfuncs.pEfxAPI->R_TempSprite(tr.endpos, vec3_origin, 0.2, m_iGlow, kRenderGlow, kRenderFxNoDissipation, 200.0 / 255.0, 0.3, FTENT_FADEOUT);
|
|
|
|
{
|
|
Vector fwd;
|
|
VectorAdd(tr.endpos, tr.plane.normal, fwd);
|
|
gEngfuncs.pEfxAPI->R_Sprite_Trail(TE_SPRITETRAIL, tr.endpos, fwd, m_iBalls, 8, 0.6, gEngfuncs.pfnRandomFloat(10, 20) / 100.0, 100,
|
|
255, 200);
|
|
}
|
|
}
|
|
|
|
flDamage = 0;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
VectorAdd(tr.endpos, forward, vecSrc);
|
|
}
|
|
}
|
|
}
|
|
//======================
|
|
// GAUSS END
|
|
//======================
|
|
|
|
//======================
|
|
// CROWBAR START
|
|
//======================
|
|
int g_iSwing;
|
|
|
|
//Only predict the miss sounds, hit sounds are still played
|
|
//server side, so players don't get the wrong idea.
|
|
void EV_Crowbar(event_args_t* args)
|
|
{
|
|
int idx;
|
|
Vector origin;
|
|
|
|
idx = args->entindex;
|
|
VectorCopy(args->origin, origin);
|
|
|
|
//Play Swing sound
|
|
gEngfuncs.pEventAPI->EV_PlaySound(idx, origin, CHAN_WEAPON, "weapons/cbar_miss1.wav", 1, ATTN_NORM, 0, PITCH_NORM);
|
|
|
|
if (EV_IsLocal(idx))
|
|
{
|
|
switch ((g_iSwing++) % 3)
|
|
{
|
|
case 0:
|
|
gEngfuncs.pEventAPI->EV_WeaponAnimation(CROWBAR_ATTACK1MISS, 0);
|
|
break;
|
|
case 1:
|
|
gEngfuncs.pEventAPI->EV_WeaponAnimation(CROWBAR_ATTACK2MISS, 0);
|
|
break;
|
|
case 2:
|
|
gEngfuncs.pEventAPI->EV_WeaponAnimation(CROWBAR_ATTACK3MISS, 0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
//======================
|
|
// CROWBAR END
|
|
//======================
|
|
|
|
//======================
|
|
// CROSSBOW START
|
|
//======================
|
|
//=====================
|
|
// EV_BoltCallback
|
|
// This function is used to correct the origin and angles
|
|
// of the bolt, so it looks like it's stuck on the wall.
|
|
//=====================
|
|
void EV_BoltCallback(struct tempent_s* ent, float frametime, float currenttime)
|
|
{
|
|
ent->entity.origin = ent->entity.baseline.vuser1;
|
|
ent->entity.angles = ent->entity.baseline.vuser2;
|
|
}
|
|
|
|
void EV_FireCrossbow2(event_args_t* args)
|
|
{
|
|
Vector vecSrc, vecEnd;
|
|
Vector up, right, forward;
|
|
pmtrace_t tr;
|
|
|
|
int idx;
|
|
Vector origin;
|
|
Vector angles;
|
|
Vector velocity;
|
|
|
|
idx = args->entindex;
|
|
VectorCopy(args->origin, origin);
|
|
VectorCopy(args->angles, angles);
|
|
|
|
VectorCopy(args->velocity, velocity);
|
|
|
|
AngleVectors(angles, forward, right, up);
|
|
|
|
EV_GetGunPosition(args, vecSrc, origin);
|
|
|
|
VectorMA(vecSrc, 8192, forward, vecEnd);
|
|
|
|
gEngfuncs.pEventAPI->EV_PlaySound(idx, origin, CHAN_WEAPON, "weapons/xbow_fire1.wav", 1, ATTN_NORM, 0, 93 + gEngfuncs.pfnRandomLong(0, 0xF));
|
|
gEngfuncs.pEventAPI->EV_PlaySound(idx, origin, CHAN_ITEM, "weapons/xbow_reload1.wav", gEngfuncs.pfnRandomFloat(0.95, 1.0), ATTN_NORM, 0, 93 + gEngfuncs.pfnRandomLong(0, 0xF));
|
|
|
|
if (EV_IsLocal(idx))
|
|
{
|
|
if (0 != args->iparam1)
|
|
gEngfuncs.pEventAPI->EV_WeaponAnimation(CROSSBOW_FIRE1, 0);
|
|
else
|
|
gEngfuncs.pEventAPI->EV_WeaponAnimation(CROSSBOW_FIRE3, 0);
|
|
}
|
|
|
|
// Store off the old count
|
|
gEngfuncs.pEventAPI->EV_PushPMStates();
|
|
|
|
// Now add in all of the players.
|
|
gEngfuncs.pEventAPI->EV_SetSolidPlayers(idx - 1);
|
|
gEngfuncs.pEventAPI->EV_SetTraceHull(2);
|
|
gEngfuncs.pEventAPI->EV_PlayerTrace(vecSrc, vecEnd, PM_STUDIO_BOX, -1, &tr);
|
|
|
|
//We hit something
|
|
if (tr.fraction < 1.0)
|
|
{
|
|
physent_t* pe = gEngfuncs.pEventAPI->EV_GetPhysent(tr.ent);
|
|
|
|
//Not the world, let's assume we hit something organic ( dog, cat, uncle joe, etc ).
|
|
if (!EV_HLDM_IsBSPModel(pe))
|
|
{
|
|
switch (gEngfuncs.pfnRandomLong(0, 1))
|
|
{
|
|
case 0:
|
|
gEngfuncs.pEventAPI->EV_PlaySound(idx, tr.endpos, CHAN_BODY, "weapons/xbow_hitbod1.wav", 1, ATTN_NORM, 0, PITCH_NORM);
|
|
break;
|
|
case 1:
|
|
gEngfuncs.pEventAPI->EV_PlaySound(idx, tr.endpos, CHAN_BODY, "weapons/xbow_hitbod2.wav", 1, ATTN_NORM, 0, PITCH_NORM);
|
|
break;
|
|
}
|
|
}
|
|
//Stick to world but don't stick to glass, it might break and leave the bolt floating. It can still stick to other non-transparent breakables though.
|
|
else if (pe->rendermode == kRenderNormal)
|
|
{
|
|
gEngfuncs.pEventAPI->EV_PlaySound(0, tr.endpos, CHAN_BODY, "weapons/xbow_hit1.wav", gEngfuncs.pfnRandomFloat(0.95, 1.0), ATTN_NORM, 0, PITCH_NORM);
|
|
|
|
//Not underwater, do some sparks...
|
|
if (gEngfuncs.PM_PointContents(tr.endpos, NULL) != CONTENTS_WATER)
|
|
gEngfuncs.pEfxAPI->R_SparkShower(tr.endpos);
|
|
|
|
Vector vBoltAngles;
|
|
int iModelIndex = gEngfuncs.pEventAPI->EV_FindModelIndex("models/crossbow_bolt.mdl");
|
|
|
|
VectorAngles(forward, vBoltAngles);
|
|
|
|
TEMPENTITY* bolt = gEngfuncs.pEfxAPI->R_TempModel(tr.endpos - forward * 10, Vector(0, 0, 0), vBoltAngles, 5, iModelIndex, TE_BOUNCE_NULL);
|
|
|
|
if (bolt)
|
|
{
|
|
bolt->flags |= (FTENT_CLIENTCUSTOM); //So it calls the callback function.
|
|
bolt->entity.baseline.vuser1 = tr.endpos - forward * 10; // Pull out a little bit
|
|
bolt->entity.baseline.vuser2 = vBoltAngles; //Look forward!
|
|
bolt->callback = EV_BoltCallback; //So we can set the angles and origin back. (Stick the bolt to the wall)
|
|
}
|
|
}
|
|
}
|
|
|
|
gEngfuncs.pEventAPI->EV_PopPMStates();
|
|
}
|
|
|
|
//TODO: Fully predict the fliying bolt.
|
|
void EV_FireCrossbow(event_args_t* args)
|
|
{
|
|
int idx;
|
|
Vector origin;
|
|
|
|
idx = args->entindex;
|
|
VectorCopy(args->origin, origin);
|
|
|
|
gEngfuncs.pEventAPI->EV_PlaySound(idx, origin, CHAN_WEAPON, "weapons/xbow_fire1.wav", 1, ATTN_NORM, 0, 93 + gEngfuncs.pfnRandomLong(0, 0xF));
|
|
gEngfuncs.pEventAPI->EV_PlaySound(idx, origin, CHAN_ITEM, "weapons/xbow_reload1.wav", gEngfuncs.pfnRandomFloat(0.95, 1.0), ATTN_NORM, 0, 93 + gEngfuncs.pfnRandomLong(0, 0xF));
|
|
|
|
//Only play the weapon anims if I shot it.
|
|
if (EV_IsLocal(idx))
|
|
{
|
|
if (0 != args->iparam1)
|
|
gEngfuncs.pEventAPI->EV_WeaponAnimation(CROSSBOW_FIRE1, 0);
|
|
else
|
|
gEngfuncs.pEventAPI->EV_WeaponAnimation(CROSSBOW_FIRE3, 0);
|
|
|
|
V_PunchAxis(0, -2.0);
|
|
}
|
|
}
|
|
//======================
|
|
// CROSSBOW END
|
|
//======================
|
|
|
|
//======================
|
|
// RPG START
|
|
//======================
|
|
void EV_FireRpg(event_args_t* args)
|
|
{
|
|
int idx;
|
|
Vector origin;
|
|
|
|
idx = args->entindex;
|
|
VectorCopy(args->origin, origin);
|
|
|
|
gEngfuncs.pEventAPI->EV_PlaySound(idx, origin, CHAN_WEAPON, "weapons/rocketfire1.wav", 0.9, ATTN_NORM, 0, PITCH_NORM);
|
|
gEngfuncs.pEventAPI->EV_PlaySound(idx, origin, CHAN_ITEM, "weapons/glauncher.wav", 0.7, ATTN_NORM, 0, PITCH_NORM);
|
|
|
|
//Only play the weapon anims if I shot it.
|
|
if (EV_IsLocal(idx))
|
|
{
|
|
gEngfuncs.pEventAPI->EV_WeaponAnimation(RPG_FIRE2, 0);
|
|
|
|
V_PunchAxis(0, -5.0);
|
|
}
|
|
}
|
|
//======================
|
|
// RPG END
|
|
//======================
|
|
|
|
//======================
|
|
// EGON END
|
|
//======================
|
|
int g_fireAnims1[] = {EGON_FIRE1, EGON_FIRE2, EGON_FIRE3, EGON_FIRE4};
|
|
int g_fireAnims2[] = {EGON_ALTFIRECYCLE};
|
|
|
|
BEAM* pBeam;
|
|
BEAM* pBeam2;
|
|
TEMPENTITY* pFlare; // Vit_amiN: egon's beam flare
|
|
|
|
void EV_EgonFlareCallback(struct tempent_s* ent, float frametime, float currenttime)
|
|
{
|
|
float delta = currenttime - ent->tentOffset.z; // time past since the last scale
|
|
if (delta >= ent->tentOffset.y)
|
|
{
|
|
ent->entity.curstate.scale += ent->tentOffset.x * delta;
|
|
ent->tentOffset.z = currenttime;
|
|
}
|
|
}
|
|
|
|
void EV_EgonFire(event_args_t* args)
|
|
{
|
|
int idx, iFireMode;
|
|
Vector origin;
|
|
|
|
idx = args->entindex;
|
|
VectorCopy(args->origin, origin);
|
|
iFireMode = args->iparam2;
|
|
bool iStartup = 0 != args->bparam1;
|
|
|
|
|
|
if (iStartup)
|
|
{
|
|
if (iFireMode == FIRE_WIDE)
|
|
gEngfuncs.pEventAPI->EV_PlaySound(idx, origin, CHAN_WEAPON, EGON_SOUND_STARTUP, 0.98, ATTN_NORM, 0, 125);
|
|
else
|
|
gEngfuncs.pEventAPI->EV_PlaySound(idx, origin, CHAN_WEAPON, EGON_SOUND_STARTUP, 0.9, ATTN_NORM, 0, 100);
|
|
}
|
|
else
|
|
{
|
|
//If there is any sound playing already, kill it.
|
|
//This is necessary because multiple sounds can play on the same channel at the same time.
|
|
//In some cases, more than 1 run sound plays when the egon stops firing, in which case only the earliest entry in the list is stopped.
|
|
//This ensures no more than 1 of those is ever active at the same time.
|
|
gEngfuncs.pEventAPI->EV_StopSound(idx, CHAN_STATIC, EGON_SOUND_RUN);
|
|
|
|
if (iFireMode == FIRE_WIDE)
|
|
gEngfuncs.pEventAPI->EV_PlaySound(idx, origin, CHAN_STATIC, EGON_SOUND_RUN, 0.98, ATTN_NORM, 0, 125);
|
|
else
|
|
gEngfuncs.pEventAPI->EV_PlaySound(idx, origin, CHAN_STATIC, EGON_SOUND_RUN, 0.9, ATTN_NORM, 0, 100);
|
|
}
|
|
|
|
//Only play the weapon anims if I shot it.
|
|
if (EV_IsLocal(idx))
|
|
gEngfuncs.pEventAPI->EV_WeaponAnimation(g_fireAnims1[gEngfuncs.pfnRandomLong(0, 3)], 0);
|
|
|
|
if (iStartup && EV_IsLocal(idx) && !pBeam && !pBeam2 && !pFlare && 0 != cl_lw->value) //Adrian: Added the cl_lw check for those lital people that hate weapon prediction.
|
|
{
|
|
Vector vecSrc, vecEnd, angles, forward, right, up;
|
|
pmtrace_t tr;
|
|
|
|
cl_entity_t* pl = gEngfuncs.GetEntityByIndex(idx);
|
|
|
|
if (pl)
|
|
{
|
|
VectorCopy(gHUD.m_vecAngles, angles);
|
|
|
|
AngleVectors(angles, forward, right, up);
|
|
|
|
EV_GetGunPosition(args, vecSrc, pl->origin);
|
|
|
|
VectorMA(vecSrc, 2048, forward, vecEnd);
|
|
|
|
gEngfuncs.pEventAPI->EV_SetUpPlayerPrediction(0, 1);
|
|
|
|
// Store off the old count
|
|
gEngfuncs.pEventAPI->EV_PushPMStates();
|
|
|
|
// Now add in all of the players.
|
|
gEngfuncs.pEventAPI->EV_SetSolidPlayers(idx - 1);
|
|
|
|
gEngfuncs.pEventAPI->EV_SetTraceHull(2);
|
|
gEngfuncs.pEventAPI->EV_PlayerTrace(vecSrc, vecEnd, PM_STUDIO_BOX, -1, &tr);
|
|
|
|
gEngfuncs.pEventAPI->EV_PopPMStates();
|
|
|
|
int iBeamModelIndex = gEngfuncs.pEventAPI->EV_FindModelIndex(EGON_BEAM_SPRITE);
|
|
|
|
float r = 50.0f;
|
|
float g = 50.0f;
|
|
float b = 125.0f;
|
|
|
|
//if ( IEngineStudio.IsHardware() )
|
|
{
|
|
r /= 255.0f;
|
|
g /= 255.0f;
|
|
b /= 255.0f;
|
|
}
|
|
|
|
|
|
pBeam = gEngfuncs.pEfxAPI->R_BeamEntPoint(idx | 0x1000, tr.endpos, iBeamModelIndex, 99999, 3.5, 0.2, 0.7, 55, 0, 0, r, g, b);
|
|
|
|
if (pBeam)
|
|
pBeam->flags |= (FBEAM_SINENOISE);
|
|
|
|
pBeam2 = gEngfuncs.pEfxAPI->R_BeamEntPoint(idx | 0x1000, tr.endpos, iBeamModelIndex, 99999, 5.0, 0.08, 0.7, 25, 0, 0, r, g, b);
|
|
|
|
// Vit_amiN: egon beam flare
|
|
pFlare = gEngfuncs.pEfxAPI->R_TempSprite(tr.endpos, vec3_origin, 1.0,
|
|
gEngfuncs.pEventAPI->EV_FindModelIndex(EGON_FLARE_SPRITE),
|
|
kRenderGlow, kRenderFxNoDissipation, 1.0, 99999, FTENT_SPRCYCLE | FTENT_PERSIST);
|
|
}
|
|
}
|
|
|
|
if (pFlare) // Vit_amiN: store the last mode for EV_EgonStop()
|
|
{
|
|
pFlare->tentOffset.x = (iFireMode == FIRE_WIDE) ? 1.0f : 0.0f;
|
|
}
|
|
}
|
|
|
|
void EV_EgonStop(event_args_t* args)
|
|
{
|
|
int idx;
|
|
Vector origin;
|
|
|
|
idx = args->entindex;
|
|
VectorCopy(args->origin, origin);
|
|
|
|
gEngfuncs.pEventAPI->EV_StopSound(idx, CHAN_STATIC, EGON_SOUND_RUN);
|
|
|
|
//Only stop the sound if the event was sent by the same source as the owner of the egon.
|
|
//If the local player owns the egon then only the local event should play this sound.
|
|
//If another player owns it, only the server event should play it.
|
|
if (0 != args->iparam1)
|
|
gEngfuncs.pEventAPI->EV_PlaySound(idx, origin, CHAN_WEAPON, EGON_SOUND_OFF, 0.98, ATTN_NORM, 0, 100);
|
|
|
|
if (EV_IsLocal(idx))
|
|
{
|
|
if (pBeam)
|
|
{
|
|
pBeam->die = 0.0;
|
|
pBeam = NULL;
|
|
}
|
|
|
|
|
|
if (pBeam2)
|
|
{
|
|
pBeam2->die = 0.0;
|
|
pBeam2 = NULL;
|
|
}
|
|
|
|
if (pFlare) // Vit_amiN: egon beam flare
|
|
{
|
|
pFlare->die = gEngfuncs.GetClientTime();
|
|
|
|
if (gEngfuncs.GetMaxClients() == 1 || (pFlare->flags & FTENT_NOMODEL) == 0)
|
|
{
|
|
if (pFlare->tentOffset.x != 0.0f) // true for iFireMode == FIRE_WIDE
|
|
{
|
|
pFlare->callback = &EV_EgonFlareCallback;
|
|
pFlare->fadeSpeed = 2.0; // fade out will take 0.5 sec
|
|
pFlare->tentOffset.x = 10.0; // scaling speed per second
|
|
pFlare->tentOffset.y = 0.1; // min time between two scales
|
|
pFlare->tentOffset.z = pFlare->die; // the last callback run time
|
|
pFlare->flags = FTENT_FADEOUT | FTENT_CLIENTCUSTOM;
|
|
}
|
|
}
|
|
|
|
pFlare = NULL;
|
|
}
|
|
|
|
// HACK: only reset animation if the Egon is still equipped.
|
|
if (g_CurrentWeaponId == WEAPON_EGON)
|
|
{
|
|
gEngfuncs.pEventAPI->EV_WeaponAnimation(EGON_IDLE1, 0);
|
|
}
|
|
}
|
|
}
|
|
//======================
|
|
// EGON END
|
|
//======================
|
|
|
|
//======================
|
|
// HORNET START
|
|
//======================
|
|
void EV_HornetGunFire(event_args_t* args)
|
|
{
|
|
int idx;
|
|
Vector origin, angles;
|
|
|
|
idx = args->entindex;
|
|
VectorCopy(args->origin, origin);
|
|
VectorCopy(args->angles, angles);
|
|
|
|
//Only play the weapon anims if I shot it.
|
|
if (EV_IsLocal(idx))
|
|
{
|
|
V_PunchAxis(0, gEngfuncs.pfnRandomLong(0, 2));
|
|
gEngfuncs.pEventAPI->EV_WeaponAnimation(HGUN_SHOOT, 0);
|
|
}
|
|
|
|
switch (gEngfuncs.pfnRandomLong(0, 2))
|
|
{
|
|
case 0:
|
|
gEngfuncs.pEventAPI->EV_PlaySound(idx, origin, CHAN_WEAPON, "agrunt/ag_fire1.wav", 1, ATTN_NORM, 0, 100);
|
|
break;
|
|
case 1:
|
|
gEngfuncs.pEventAPI->EV_PlaySound(idx, origin, CHAN_WEAPON, "agrunt/ag_fire2.wav", 1, ATTN_NORM, 0, 100);
|
|
break;
|
|
case 2:
|
|
gEngfuncs.pEventAPI->EV_PlaySound(idx, origin, CHAN_WEAPON, "agrunt/ag_fire3.wav", 1, ATTN_NORM, 0, 100);
|
|
break;
|
|
}
|
|
}
|
|
//======================
|
|
// HORNET END
|
|
//======================
|
|
|
|
//======================
|
|
// TRIPMINE START
|
|
//======================
|
|
//We only check if it's possible to put a trip mine
|
|
//and if it is, then we play the animation. Server still places it.
|
|
void EV_TripmineFire(event_args_t* args)
|
|
{
|
|
int idx;
|
|
Vector vecSrc, angles, view_ofs, forward;
|
|
pmtrace_t tr;
|
|
|
|
idx = args->entindex;
|
|
VectorCopy(args->origin, vecSrc);
|
|
VectorCopy(args->angles, angles);
|
|
|
|
AngleVectors(angles, forward, NULL, NULL);
|
|
|
|
if (!EV_IsLocal(idx))
|
|
return;
|
|
|
|
// Grab predicted result for local player
|
|
gEngfuncs.pEventAPI->EV_LocalPlayerViewheight(view_ofs);
|
|
|
|
vecSrc = vecSrc + view_ofs;
|
|
|
|
// Store off the old count
|
|
gEngfuncs.pEventAPI->EV_PushPMStates();
|
|
|
|
// Now add in all of the players.
|
|
gEngfuncs.pEventAPI->EV_SetSolidPlayers(idx - 1);
|
|
gEngfuncs.pEventAPI->EV_SetTraceHull(2);
|
|
gEngfuncs.pEventAPI->EV_PlayerTrace(vecSrc, vecSrc + forward * 128, PM_NORMAL, -1, &tr);
|
|
|
|
//Hit something solid
|
|
if (tr.fraction < 1.0)
|
|
gEngfuncs.pEventAPI->EV_WeaponAnimation(TRIPMINE_DRAW, 0);
|
|
|
|
gEngfuncs.pEventAPI->EV_PopPMStates();
|
|
}
|
|
//======================
|
|
// TRIPMINE END
|
|
//======================
|
|
|
|
//======================
|
|
// SQUEAK START
|
|
//======================
|
|
void EV_SnarkFire(event_args_t* args)
|
|
{
|
|
int idx;
|
|
Vector vecSrc, angles, forward;
|
|
pmtrace_t tr;
|
|
|
|
idx = args->entindex;
|
|
VectorCopy(args->origin, vecSrc);
|
|
VectorCopy(args->angles, angles);
|
|
|
|
AngleVectors(angles, forward, NULL, NULL);
|
|
|
|
if (!EV_IsLocal(idx))
|
|
return;
|
|
|
|
if (0 != args->ducking)
|
|
vecSrc = vecSrc - (VEC_HULL_MIN - VEC_DUCK_HULL_MIN);
|
|
|
|
// Store off the old count
|
|
gEngfuncs.pEventAPI->EV_PushPMStates();
|
|
|
|
// Now add in all of the players.
|
|
gEngfuncs.pEventAPI->EV_SetSolidPlayers(idx - 1);
|
|
gEngfuncs.pEventAPI->EV_SetTraceHull(2);
|
|
gEngfuncs.pEventAPI->EV_PlayerTrace(vecSrc + forward * 20, vecSrc + forward * 64, PM_NORMAL, -1, &tr);
|
|
|
|
//Find space to drop the thing.
|
|
if (tr.allsolid == 0 && tr.startsolid == 0 && tr.fraction > 0.25)
|
|
gEngfuncs.pEventAPI->EV_WeaponAnimation(SQUEAK_THROW, 0);
|
|
|
|
gEngfuncs.pEventAPI->EV_PopPMStates();
|
|
}
|
|
//======================
|
|
// SQUEAK END
|
|
//======================
|
|
|
|
void EV_TrainPitchAdjust(event_args_t* args)
|
|
{
|
|
int idx;
|
|
Vector origin;
|
|
|
|
unsigned short us_params;
|
|
int noise;
|
|
float m_flVolume;
|
|
int pitch;
|
|
bool stop;
|
|
|
|
char sz[256];
|
|
|
|
idx = args->entindex;
|
|
|
|
VectorCopy(args->origin, origin);
|
|
|
|
us_params = (unsigned short)args->iparam1;
|
|
stop = 0 != args->bparam1;
|
|
|
|
m_flVolume = (float)(us_params & 0x003f) / 40.0;
|
|
noise = (int)(((us_params) >> 12) & 0x0007);
|
|
pitch = (int)(10.0 * (float)((us_params >> 6) & 0x003f));
|
|
|
|
switch (noise)
|
|
{
|
|
case 1:
|
|
strcpy(sz, "plats/ttrain1.wav");
|
|
break;
|
|
case 2:
|
|
strcpy(sz, "plats/ttrain2.wav");
|
|
break;
|
|
case 3:
|
|
strcpy(sz, "plats/ttrain3.wav");
|
|
break;
|
|
case 4:
|
|
strcpy(sz, "plats/ttrain4.wav");
|
|
break;
|
|
case 5:
|
|
strcpy(sz, "plats/ttrain6.wav");
|
|
break;
|
|
case 6:
|
|
strcpy(sz, "plats/ttrain7.wav");
|
|
break;
|
|
default:
|
|
// no sound
|
|
strcpy(sz, "");
|
|
return;
|
|
}
|
|
|
|
if (stop)
|
|
{
|
|
gEngfuncs.pEventAPI->EV_StopSound(idx, CHAN_STATIC, sz);
|
|
}
|
|
else
|
|
{
|
|
gEngfuncs.pEventAPI->EV_PlaySound(idx, origin, CHAN_STATIC, sz, m_flVolume, ATTN_NORM, SND_CHANGE_PITCH, pitch);
|
|
}
|
|
}
|
|
|
|
bool EV_TFC_IsAllyTeam(int iTeam1, int iTeam2)
|
|
{
|
|
return false;
|
|
}
|