xash3d-fwgs/engine/client/cl_events.c
Gleb Mazovetskiy 5e0a0765ce Trim all trailing whitespace
The `.editorconfig` file in this repo is configured to trim all trailing
whitespace regardless of whether the line is modified.

Trims all trailing whitespace in the repository to make the codebase easier
to work with in editors that respect `.editorconfig`.

`git blame` becomes less useful on these lines but it already isn't very useful.

Commands:

```
find . -type f -name '*.h' -exec sed --in-place 's/[[:space:]]\+$//' {} \+
find . -type f -name '*.c' -exec sed --in-place 's/[[:space:]]\+$//' {} \+
```
2021-01-04 20:55:10 +03:00

496 lines
9.4 KiB
C

/*
cl_events.c - client-side event system implementation
Copyright (C) 2011 Uncle Mike
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 3 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.
*/
#include "common.h"
#include "client.h"
#include "event_flags.h"
#include "net_encode.h"
#include "con_nprint.h"
/*
===============
CL_ResetEvent
===============
*/
void CL_ResetEvent( event_info_t *ei )
{
ei->index = 0;
memset( &ei->args, 0, sizeof( ei->args ));
ei->fire_time = 0.0;
ei->flags = 0;
}
/*
=============
CL_CalcPlayerVelocity
compute velocity for a given client
=============
*/
void CL_CalcPlayerVelocity( int idx, vec3_t velocity )
{
clientdata_t *pcd;
vec3_t delta;
double dt;
VectorClear( velocity );
if( idx <= 0 || idx > cl.maxclients )
return;
if( idx == cl.playernum + 1 )
{
pcd = &cl.frames[cl.parsecountmod].clientdata;
VectorCopy( pcd->velocity, velocity );
}
else
{
dt = clgame.entities[idx].curstate.animtime - clgame.entities[idx].prevstate.animtime;
if( dt != 0.0 )
{
VectorSubtract( clgame.entities[idx].curstate.velocity, clgame.entities[idx].prevstate.velocity, delta );
VectorScale( delta, 1.0f / dt, velocity );
}
else
{
VectorCopy( clgame.entities[idx].curstate.velocity, velocity );
}
}
}
/*
=============
CL_DescribeEvent
=============
*/
void CL_DescribeEvent( int slot, int flags, const char *eventname )
{
int idx = (slot & 31);
con_nprint_t info;
if( !eventname || !cl_showevents->value )
return;
// mark reliable as green and unreliable as red
if( FBitSet( flags, FEV_RELIABLE ))
VectorSet( info.color, 0.0f, 1.0f, 0.0f );
else VectorSet( info.color, 1.0f, 0.0f, 0.0f );
info.time_to_live = 0.5f;
info.index = idx;
Con_NXPrintf( &info, "%i %f %s", slot, cl.time, eventname );
}
/*
=============
CL_SetEventIndex
=============
*/
void CL_SetEventIndex( const char *szEvName, int ev_index )
{
cl_user_event_t *ev;
int i;
if( !szEvName || !*szEvName )
return; // ignore blank names
// search event by name to link with
for( i = 0; i < MAX_EVENTS; i++ )
{
ev = clgame.events[i];
if( !ev ) break;
if( !Q_stricmp( ev->name, szEvName ))
{
ev->index = ev_index;
return;
}
}
}
/*
=============
CL_EventIndex
=============
*/
word CL_EventIndex( const char *name )
{
int i;
if( !COM_CheckString( name ))
return 0;
for( i = 1; i < MAX_EVENTS && cl.event_precache[i][0]; i++ )
{
if( !Q_stricmp( cl.event_precache[i], name ))
return i;
}
return 0;
}
/*
=============
CL_RegisterEvent
=============
*/
void CL_RegisterEvent( int lastnum, const char *szEvName, pfnEventHook func )
{
cl_user_event_t *ev;
if( lastnum == MAX_EVENTS )
return;
// clear existing or allocate new one
if( !clgame.events[lastnum] )
clgame.events[lastnum] = Mem_Calloc( cls.mempool, sizeof( cl_user_event_t ));
else memset( clgame.events[lastnum], 0, sizeof( cl_user_event_t ));
ev = clgame.events[lastnum];
// NOTE: ev->index will be set later
Q_strncpy( ev->name, szEvName, MAX_QPATH );
ev->func = func;
}
/*
=============
CL_FireEvent
=============
*/
qboolean CL_FireEvent( event_info_t *ei, int slot )
{
cl_user_event_t *ev;
const char *name;
int i, idx;
if( !ei || !ei->index )
return false;
// get the func pointer
for( i = 0; i < MAX_EVENTS; i++ )
{
ev = clgame.events[i];
if( !ev )
{
idx = bound( 1, ei->index, ( MAX_EVENTS - 1 ));
Con_Reportf( S_ERROR "CL_FireEvent: %s not precached\n", cl.event_precache[idx] );
break;
}
if( ev->index == ei->index )
{
if( ev->func )
{
CL_DescribeEvent( slot, ei->flags, cl.event_precache[ei->index] );
ev->func( &ei->args );
return true;
}
name = cl.event_precache[ei->index];
Con_Reportf( S_ERROR "CL_FireEvent: %s not hooked\n", name );
break;
}
}
return false;
}
/*
=============
CL_FireEvents
called right before draw frame
=============
*/
void CL_FireEvents( void )
{
event_state_t *es;
event_info_t *ei;
int i;
es = &cl.events;
for( i = 0; i < MAX_EVENT_QUEUE; i++ )
{
ei = &es->ei[i];
if( ei->index == 0 )
continue;
// delayed event!
if( ei->fire_time && ( ei->fire_time > cl.time ))
continue;
CL_FireEvent( ei, i );
// zero out the remaining fields
CL_ResetEvent( ei );
}
}
/*
=============
CL_FindEvent
find first empty event
=============
*/
event_info_t *CL_FindEmptyEvent( void )
{
int i;
event_state_t *es;
event_info_t *ei;
es = &cl.events;
// look for first slot where index is != 0
for( i = 0; i < MAX_EVENT_QUEUE; i++ )
{
ei = &es->ei[i];
if( ei->index != 0 )
continue;
return ei;
}
// no slots available
return NULL;
}
/*
=============
CL_FindEvent
replace only unreliable events
=============
*/
event_info_t *CL_FindUnreliableEvent( void )
{
event_state_t *es;
event_info_t *ei;
int i;
es = &cl.events;
for ( i = 0; i < MAX_EVENT_QUEUE; i++ )
{
ei = &es->ei[i];
if( ei->index != 0 )
{
// it's reliable, so skip it
if( FBitSet( ei->flags, FEV_RELIABLE ))
continue;
}
return ei;
}
// this should never happen
return NULL;
}
/*
=============
CL_QueueEvent
=============
*/
void CL_QueueEvent( int flags, int index, float delay, event_args_t *args )
{
event_info_t *ei;
// find a normal slot
ei = CL_FindEmptyEvent();
if( !ei )
{
if( FBitSet( flags, FEV_RELIABLE ))
{
ei = CL_FindUnreliableEvent();
}
if( !ei ) return;
}
ei->index = index;
ei->packet_index = 0;
ei->fire_time = delay ? (cl.time + delay) : 0.0f;
ei->flags = flags;
ei->args = *args;
}
/*
=============
CL_ParseReliableEvent
=============
*/
void CL_ParseReliableEvent( sizebuf_t *msg )
{
int event_index;
event_args_t nullargs, args;
float delay = 0.0f;
memset( &nullargs, 0, sizeof( nullargs ));
event_index = MSG_ReadUBitLong( msg, MAX_EVENT_BITS );
if( MSG_ReadOneBit( msg ))
delay = (float)MSG_ReadWord( msg ) * (1.0f / 100.0f);
// reliable events not use delta-compression just null-compression
MSG_ReadDeltaEvent( msg, &nullargs, &args );
if( args.entindex > 0 && args.entindex <= cl.maxclients )
args.angles[PITCH] *= -3.0f;
CL_QueueEvent( FEV_RELIABLE|FEV_SERVER, event_index, delay, &args );
}
/*
=============
CL_ParseEvent
=============
*/
void CL_ParseEvent( sizebuf_t *msg )
{
int event_index;
int i, num_events;
int packet_index;
event_args_t nullargs, args;
entity_state_t *state;
float delay;
memset( &nullargs, 0, sizeof( nullargs ));
memset( &args, 0, sizeof( args ));
num_events = MSG_ReadUBitLong( msg, 5 );
// parse events queue
for( i = 0 ; i < num_events; i++ )
{
event_index = MSG_ReadUBitLong( msg, MAX_EVENT_BITS );
if( MSG_ReadOneBit( msg ))
packet_index = MSG_ReadUBitLong( msg, cls.legacymode ? MAX_LEGACY_ENTITY_BITS : MAX_ENTITY_BITS );
else packet_index = -1;
if( MSG_ReadOneBit( msg ))
{
MSG_ReadDeltaEvent( msg, &nullargs, &args );
}
if( MSG_ReadOneBit( msg ))
delay = (float)MSG_ReadWord( msg ) * (1.0f / 100.0f);
else delay = 0.0f;
if( packet_index != -1 )
{
frame_t *frame = &cl.frames[cl.parsecountmod];
if( packet_index < frame->num_entities )
{
state = &cls.packet_entities[(frame->first_entity+packet_index)%cls.num_client_entities];
args.entindex = state->number;
if( VectorIsNull( args.origin ))
VectorCopy( state->origin, args.origin );
if( VectorIsNull( args.angles ))
VectorCopy( state->angles, args.angles );
COM_NormalizeAngles( args.angles );
if( state->number > 0 && state->number <= cl.maxclients )
{
args.angles[PITCH] *= -3.0f;
CL_CalcPlayerVelocity( state->number, args.velocity );
args.ducking = ( state->usehull == 1 );
}
}
else
{
if( args.entindex != 0 )
{
if( args.entindex > 0 && args.entindex <= cl.maxclients )
args.angles[PITCH] /= -3.0f;
}
}
// Place event on queue
CL_QueueEvent( FEV_SERVER, event_index, delay, &args );
}
}
}
/*
=============
CL_PlaybackEvent
=============
*/
void GAME_EXPORT CL_PlaybackEvent( int flags, const edict_t *pInvoker, word eventindex, float delay, float *origin,
float *angles, float fparam1, float fparam2, int iparam1, int iparam2, int bparam1, int bparam2 )
{
event_args_t args;
if( FBitSet( flags, FEV_SERVER ))
return;
// first check event for out of bounds
if( eventindex < 1 || eventindex > MAX_EVENTS )
{
Con_DPrintf( S_ERROR "CL_PlaybackEvent: invalid eventindex %i\n", eventindex );
return;
}
// check event for precached
if( !CL_EventIndex( cl.event_precache[eventindex] ))
{
Con_DPrintf( S_ERROR "CL_PlaybackEvent: event %i was not precached\n", eventindex );
return;
}
SetBits( flags, FEV_CLIENT ); // it's a client event
ClearBits( flags, FEV_NOTHOST|FEV_HOSTONLY|FEV_GLOBAL );
if( delay < 0.0f ) delay = 0.0f; // fixup negative delays
memset( &args, 0, sizeof( args ));
VectorCopy( origin, args.origin );
VectorCopy( angles, args.angles );
VectorCopy( cl.simvel, args.velocity );
args.entindex = cl.playernum + 1;
args.ducking = ( cl.local.usehull == 1 );
args.fparam1 = fparam1;
args.fparam2 = fparam2;
args.iparam1 = iparam1;
args.iparam2 = iparam2;
args.bparam1 = bparam1;
args.bparam2 = bparam2;
CL_QueueEvent( flags, eventindex, delay, &args );
}