26 Dec 2008

This commit is contained in:
g-cont 2008-12-26 00:00:00 +03:00 committed by Alibek Omarov
parent 0f5ce4a782
commit 093d8f5cb6
200 changed files with 86086 additions and 1090 deletions

View File

@ -13,10 +13,16 @@ launchers.bat
todo.log
baserc\
cl_dll\
client\
client\hud\
client\global\
public\
physic\
sv_dll\
server\
server\ents\
server\game\
server\global\
server\monsters\
render\
vprogs\
vsound\

5
client/client.def Normal file
View File

@ -0,0 +1,5 @@
LIBRARY client
EXPORTS
CreateAPI @1
SECTIONS
.data READ WRITE

260
client/client.dsp Normal file
View File

@ -0,0 +1,260 @@
# Microsoft Developer Studio Project File - Name="client" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
CFG=client - Win32 Release
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
!MESSAGE NMAKE /f "client.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "client.mak" CFG="client - Win32 Release"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "client - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
!MESSAGE "client - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""$/GoldSrc/client", HGEBAAAA"
# PROP Scc_LocalPath "."
CPP=cl.exe
MTL=midl.exe
RSC=rc.exe
!IF "$(CFG)" == "client - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir ".\!Release"
# PROP BASE Intermediate_Dir ".\!Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "..\temp\client\!release"
# PROP Intermediate_Dir "..\temp\client\!release"
# PROP Ignore_Export_Lib 1
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
# ADD CPP /nologo /MD /W3 /GX /O2 /I "./" /I "../public" /I "./global" /I "./hud" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
# SUBTRACT CPP /Fr /YX
# ADD BASE MTL /nologo /D "NDEBUG" /win32
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
# ADD LINK32 msvcrt.lib /nologo /subsystem:windows /dll /machine:I386 /nodefaultlib:"libc" /def:".\client.def" /libpath:"..\common\libs"
# SUBTRACT LINK32 /map
# Begin Custom Build
TargetDir=\Xash3D\src_main\temp\client\!release
InputPath=\Xash3D\src_main\temp\client\!release\client.dll
SOURCE="$(InputPath)"
"D:\Xash3D\tmpQuArK\bin\client.dll" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
copy $(TargetDir)\client.dll "D:\Xash3D\tmpQuArK\bin\client.dll"
# End Custom Build
!ELSEIF "$(CFG)" == "client - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "client___Win32_Debug"
# PROP BASE Intermediate_Dir "client___Win32_Debug"
# PROP BASE Ignore_Export_Lib 0
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "..\temp\client\!debug"
# PROP Intermediate_Dir "..\temp\client\!debug"
# PROP Ignore_Export_Lib 1
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MT /W3 /GX /Zi /O2 /I "..\common\vgui" /I "..\client" /I "..\client\render" /I ".\hud" /I "..\common\engine" /I "..\common" /I "..\server" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "CLIENT_DLL" /YX /FD /c
# SUBTRACT BASE CPP /Fr
# ADD CPP /nologo /MDd /W3 /Gm /Gi /GX /ZI /Od /I "./" /I "../public" /I "./global" /I "./hud" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR /FD /c
# SUBTRACT CPP /YX
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib opengl32.lib glu32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib winmm.lib vgui.lib wsock32.lib cvaLib.lib /nologo /subsystem:windows /dll /machine:I386 /nodefaultlib:"libc" /def:".\client.def" /libpath:"..\common\libs"
# SUBTRACT BASE LINK32 /map
# ADD LINK32 msvcrtd.lib /nologo /subsystem:windows /dll /incremental:yes /debug /machine:I386 /nodefaultlib:"libc.lib" /def:".\client.def" /pdbtype:sept /libpath:"..\common\libs"
# SUBTRACT LINK32 /map
# Begin Custom Build
TargetDir=\Xash3D\src_main\temp\client\!debug
InputPath=\Xash3D\src_main\temp\client\!debug\client.dll
SOURCE="$(InputPath)"
"D:\Xash3D\tmpQuArK\bin\client.dll" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
copy $(TargetDir)\client.dll "D:\Xash3D\tmpQuArK\bin\client.dll"
# End Custom Build
!ENDIF
# Begin Target
# Name "client - Win32 Release"
# Name "client - Win32 Debug"
# Begin Group "Source Files"
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat;for;f90"
# Begin Source File
SOURCE=.\global\dll_int.cpp
# End Source File
# Begin Source File
SOURCE=.\hud\hud.cpp
# End Source File
# Begin Source File
SOURCE=.\hud\hud_ammo.cpp
# End Source File
# Begin Source File
SOURCE=.\hud\hud_ammohistory.cpp
# End Source File
# Begin Source File
SOURCE=.\hud\hud_battery.cpp
# End Source File
# Begin Source File
SOURCE=.\hud\hud_death.cpp
# End Source File
# Begin Source File
SOURCE=.\hud\hud_flashlight.cpp
# End Source File
# Begin Source File
SOURCE=.\hud\hud_geiger.cpp
# End Source File
# Begin Source File
SOURCE=.\hud\hud_health.cpp
# End Source File
# Begin Source File
SOURCE=.\hud\hud_icons.cpp
# End Source File
# Begin Source File
SOURCE=.\hud\hud_menu.cpp
# End Source File
# Begin Source File
SOURCE=.\hud\hud_message.cpp
# End Source File
# Begin Source File
SOURCE=.\hud\hud_motd.cpp
# End Source File
# Begin Source File
SOURCE=.\hud\hud_msg.cpp
# End Source File
# Begin Source File
SOURCE=.\hud\hud_saytext.cpp
# End Source File
# Begin Source File
SOURCE=.\hud\hud_scoreboard.cpp
# End Source File
# Begin Source File
SOURCE=.\hud\hud_sound.cpp
# End Source File
# Begin Source File
SOURCE=.\hud\hud_statusbar.cpp
# End Source File
# Begin Source File
SOURCE=.\hud\hud_text.cpp
# End Source File
# Begin Source File
SOURCE=.\hud\hud_train.cpp
# End Source File
# Begin Source File
SOURCE=.\hud\hud_utils.cpp
# End Source File
# Begin Source File
SOURCE=.\hud\hud_warhead.cpp
# End Source File
# Begin Source File
SOURCE=.\hud\hud_zoom.cpp
# End Source File
# Begin Source File
SOURCE=.\global\tempents.cpp
# End Source File
# Begin Source File
SOURCE=.\global\triapi.cpp
# End Source File
# Begin Source File
SOURCE=.\global\view.cpp
# End Source File
# End Group
# Begin Group "Header Files"
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# Begin Source File
SOURCE=.\global\enginecallback.h
# End Source File
# Begin Source File
SOURCE=.\global\extdll.h
# End Source File
# Begin Source File
SOURCE=.\hud\hud.h
# End Source File
# Begin Source File
SOURCE=.\hud\hud_ammo.h
# End Source File
# Begin Source File
SOURCE=.\hud\hud_ammohistory.h
# End Source File
# Begin Source File
SOURCE=.\hud\hud_health.h
# End Source File
# Begin Source File
SOURCE=.\hud\hud_iface.h
# End Source File
# Begin Source File
SOURCE=.\global\vector.h
# End Source File
# End Group
# End Target
# End Project

View File

@ -3,14 +3,14 @@
<pre>
<h1>Build Log</h1>
<h3>
--------------------Configuration: server - Win32 Debug--------------------
--------------------Configuration: client - Win32 Debug--------------------
</h3>
<h3>Command Lines</h3>
<h3>Results</h3>
server.dll - 0 error(s), 0 warning(s)
client.dll - 0 error(s), 0 warning(s)
</pre>
</body>
</html>

91
client/global/dll_int.cpp Normal file
View File

@ -0,0 +1,91 @@
//=======================================================================
// Copyright XashXT Group 2008 ©
// dll_int.cpp - dll entry points
//=======================================================================
#include "extdll.h"
#include "hud_iface.h"
#include "hud.h"
cl_enginefuncs_t g_engfuncs;
CHud gHUD;
// main DLL entry point
BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )
{
return TRUE;
}
static HUD_FUNCTIONS gFunctionTable =
{
sizeof( HUD_FUNCTIONS ),
HUD_VidInit,
HUD_Init,
HUD_Redraw,
HUD_UpdateClientData,
HUD_Reset,
HUD_Frame,
HUD_Shutdown,
HUD_DrawNormalTriangles,
HUD_DrawTransparentTriangles,
HUD_CreateEntities,
HUD_StudioEvent,
V_CalcRefdef,
};
//=======================================================================
// GetApi
//=======================================================================
int CreateAPI( HUD_FUNCTIONS *pFunctionTable, cl_enginefuncs_t* pEngfuncsFromEngine, int interfaceVersion )
{
if( !pFunctionTable || !pEngfuncsFromEngine || interfaceVersion != INTERFACE_VERSION )
{
return FALSE;
}
// copy HUD_FUNCTIONS table to engine, copy engfuncs table from engine
memcpy( pFunctionTable, &gFunctionTable, sizeof( HUD_FUNCTIONS ));
memcpy( &g_engfuncs, pEngfuncsFromEngine, sizeof( cl_enginefuncs_t ));
return TRUE;
}
int HUD_VidInit( void )
{
gHUD.VidInit();
return 1;
}
void HUD_Init( void )
{
gHUD.Init();
}
int HUD_Redraw( float flTime, int intermission )
{
gHUD.Redraw( flTime, intermission );
DrawCrosshair();
return 1;
}
int HUD_UpdateClientData( ref_params_t *parms, float flTime )
{
return gHUD.UpdateClientData( parms, flTime );
}
void HUD_Reset( void )
{
gHUD.VidInit();
}
void HUD_Frame( double time )
{
// place to call vgui_frame
}
void HUD_Shutdown( void )
{
// no shutdown operations
}

View File

@ -0,0 +1,92 @@
//=======================================================================
// Copyright XashXT Group 2008 ©
// enginecallback.h - actual engine callbacks
//=======================================================================
#ifndef ENGINECALLBACKS_H
#define ENGINECALLBACKS_H
// built-in memory manager
#define MALLOC( x ) (*g_engfuncs.pfnMemAlloc)( x, __FILE__, __LINE__ )
#define CALLOC( x, y ) (*g_engfuncs.pfnMemAlloc)((x) * (y), __FILE__, __LINE__ )
#define FREE( x ) (*g_engfuncs.pfnMemFree)( x, __FILE__, __LINE__ )
// screen handlers
#define LOAD_SHADER (*g_engfuncs.pfnLoadShader)
#define DrawImage (*g_engfuncs.pfnDrawImage)
#define SetColor (*g_engfuncs.pfnSetColor)
#define CVAR_REGISTER (*g_engfuncs.pfnRegisterVariable)
#define CVAR_SET_FLOAT (*g_engfuncs.pfnCvarSetValue)
#define CVAR_GET_FLOAT (*g_engfuncs.pfnGetCvarFloat)
#define CVAR_GET_STRING (*g_engfuncs.pfnGetCvarString)
#define SERVER_COMMAND (*g_engfuncs.pfnServerCmd)
#define CLIENT_COMMAND (*g_engfuncs.pfnClientCmd)
#define GET_PLAYER_INFO (*g_engfuncs.pfnGetPlayerInfo)
#define GET_GAME_MESSAGE (*g_engfuncs.pfnTextMessageGet)
#define CMD_ARGC (*g_engfuncs.pfnCmdArgc)
#define CMD_ARGV (*g_engfuncs.pfnCmdArgv)
#define ALERT (*g_engfuncs.pfnAlertMessage)
inline void CL_PlaySound( const char *szSound, float flVolume )
{
g_engfuncs.pfnPlaySoundByName( szSound, flVolume, NULL );
}
inline void CL_PlaySound( int iSound, float flVolume )
{
g_engfuncs.pfnPlaySoundByIndex( iSound, flVolume, NULL );
}
inline void CL_PlaySound( const char *szSound, float flVolume, Vector &pos )
{
g_engfuncs.pfnPlaySoundByName( szSound, flVolume, pos );
}
inline void CL_PlaySound( int iSound, float flVolume, Vector &pos )
{
g_engfuncs.pfnPlaySoundByIndex( iSound, flVolume, pos );
}
#define AngleVectors (*g_engfuncs.pfnAngleVectors)
#define DrawCenterPrint (*g_engfuncs.pfnDrawCenterPrint)
#define CenterPrint (*g_engfuncs.pfnCenterPrint)
#define DrawChar (*g_engfuncs.pfnDrawCharacter)
#define DrawString (*g_engfuncs.pfnDrawString)
#define GetImageSize (*g_engfuncs.pfnGetImageSize)
#define GetViewAngles (*g_engfuncs.pfnGetViewAngles)
#define GetEntityByIndex (*g_engfuncs.pfnGetEntityByIndex)
#define GetLocalPlayer (*g_engfuncs.pfnGetLocalPlayer)
#define IsSpectateOnly (*g_engfuncs.pfnIsSpectateOnly)
#define GetClientTime (*g_engfuncs.pfnGetClientTime)
#define GetMaxClients (*g_engfuncs.pfnGetMaxClients)
#define GetViewModel (*g_engfuncs.pfnGetViewModel)
#define POINT_CONTENTS (*g_engfuncs.pfnPointContents)
#define TRACE_LINE (*g_engfuncs.pfnTraceLine)
#define RANDOM_LONG (*g_engfuncs.pfnRandomLong)
#define RANDOM_FLOAT (*g_engfuncs.pfnRandomFloat)
#define LOAD_FILE (*g_engfuncs.pfnLoadFile)
#define FREE_FILE (*g_engfuncs.pfnFreeFile)
#define GET_GAME_DIR (*g_engfuncs.pfnGetGameDir)
#define HOST_ERROR (*g_engfuncs.pfnHostError)
// heavy legacy of Valve...
// tune char size by taste
inline void TextMessageDrawChar( int xpos, int ypos, int number, int r, int g, int b )
{
SetColor((r / 255.0f), (g / 255.0f), (b / 255.0f), 1.0f );
DrawChar( xpos, ypos, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, number );
}
inline void FillRGBA( int x, int y, int width, int height, int r, int g, int b, int a )
{
Vector RGB;
RGB.x = (float)(r / 255.0f);
RGB.y = (float)(g / 255.0f);
RGB.z = (float)(b / 255.0f);
g_engfuncs.pfnFillRGBA( x, y, width, height, RGB, (float)(a / 255.0f));
}
#endif//ENGINECALLBACKS_H

41
client/global/extdll.h Normal file
View File

@ -0,0 +1,41 @@
//=======================================================================
// Copyright XashXT Group 2008 ©
// extdll.h - must be included into all client files
//=======================================================================
#ifndef EXTDLL_H
#define EXTDLL_H
// shut-up compiler warnings
#pragma warning(disable : 4305) // int or float data truncation
#pragma warning(disable : 4201) // nameless struct/union
#pragma warning(disable : 4514) // unreferenced inline function removed
#pragma warning(disable : 4100) // unreferenced formal parameter
#include "windows.h"
#ifndef BIT
#define BIT( n ) (1<<( n ))
#endif
#define DLLEXPORT _declspec( dllexport )
// Misc C-runtime library headers
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
// shared engine/DLL constants
#include "const.h"
// Vector class
#include "vector.h"
// Shared header describing protocol between engine and DLLs
#include "entity_def.h"
#include "qfiles_ref.h"
#include "clgame_api.h"
#endif//EXTDLL_H

103
client/global/tempents.cpp Normal file
View File

@ -0,0 +1,103 @@
//=======================================================================
// Copyright XashXT Group 2008 ©
// tempents.cpp - client side entity management functions
//=======================================================================
#include "extdll.h"
#include "hud_iface.h"
void HUD_CreateEntities( void )
{
}
//======================
// DRAW BEAM EVENT
//======================
void EV_DrawBeam( void )
{
// special effect for displacer
cl_entity_t *view = gEngfuncs.GetViewModel();
if( view != NULL )
{
float life = 1.05;
int m_iBeam = gEngfuncs.pEventAPI->EV_FindModelIndex( "sprites/plasma.spr" );
gEngfuncs.pEfxAPI->R_BeamEnts(view->index | 0x1000, view->index | 0x2000, m_iBeam, life, 0.8, 0.5, 0.5, 0.6, 0, 10, 2, 10, 0);
gEngfuncs.pEfxAPI->R_BeamEnts(view->index | 0x1000, view->index | 0x3000, m_iBeam, life, 0.8, 0.5, 0.5, 0.6, 0, 10, 2, 10, 0);
gEngfuncs.pEfxAPI->R_BeamEnts(view->index | 0x1000, view->index | 0x4000, m_iBeam, life, 0.8, 0.5, 0.5, 0.6, 0, 10, 2, 10, 0);
}
}
//======================
// Eject Shell
//======================
void EV_EjectShell( const dstudioevent_t *event, edict_t *entity )
{
vec3_t view_ofs, ShellOrigin, ShellVelocity, forward, right, up;
vec3_t origin = entity->origin;
vec3_t angles = entity->angles;
float fR, fU;
int shell = g_engfuncs.pEventAPI->EV_FindModelIndex (event->options);
g_engfuncs.pEventAPI->EV_LocalPlayerViewheight( view_ofs );
origin.z = origin.z - view_ofs[2];
for(int j = 0; j < 3; j++ )
{
if(angles[j] < -180) angles[j] += 360;
else if(angles[j] > 180) angles[j] -= 360;
}
angles.x = -angles.x;
AngleVectors( angles, forward, right, up );
fR = gEngfuncs.pfnRandomFloat( 50, 70 );
fU = gEngfuncs.pfnRandomFloat( 100, 150 );
for (int i = 0; i < 3; i++ )
{
ShellVelocity[i] = p_velocity[i] + right[i] * fR + up[i] * fU + forward[i] * 25;
ShellOrigin[i] = origin[i] + view_ofs[i] + up[i] * -12 + forward[i] * 20 + right[i] * 4;
}
EV_EjectBrass ( ShellOrigin, ShellVelocity, angles[ YAW ], shell, TE_BOUNCE_SHELL );
}
void HUD_StudioEvent( const dstudioevent_t *event, edict_t *entity )
{
switch( event->event )
{
case 5001:
gEngfuncs.pEfxAPI->R_MuzzleFlash( (float *)&entity->attachment[0], atoi( event->options) );
break;
case 5011:
gEngfuncs.pEfxAPI->R_MuzzleFlash( (float *)&entity->attachment[1], atoi( event->options) );
break;
case 5021:
gEngfuncs.pEfxAPI->R_MuzzleFlash( (float *)&entity->attachment[2], atoi( event->options) );
break;
case 5031:
gEngfuncs.pEfxAPI->R_MuzzleFlash( (float *)&entity->attachment[3], atoi( event->options) );
break;
case 5002:
gEngfuncs.pEfxAPI->R_SparkEffect( (float *)&entity->attachment[0], atoi( event->options), -100, 100 );
break;
// Client side sound
case 5004:
gEngfuncs.pfnPlaySoundByNameAtLocation( (char *)event->options, 1.0, (float *)&entity->attachment[0] );
break;
// Client side sound with random pitch
case 5005:
gEngfuncs.pEventAPI->EV_PlaySound( entity->index, (float *)&entity->attachment[0], CHAN_WEAPON, (char *)event->options, gEngfuncs.pfnRandomFloat(0.7, 0.9), ATTN_NORM, 0, 85 + gEngfuncs.pfnRandomLong(0,0x1f) );
break;
// Special event for displacer
case 5050:
EV_DrawBeam();
break;
case 5060:
EV_EjectShell( event, entity );
break;
default:
break;
}
}

15
client/global/triapi.cpp Normal file
View File

@ -0,0 +1,15 @@
//=======================================================================
// Copyright XashXT Group 2008 ©
// triapi.cpp - triangle rendering, if any
//=======================================================================
#include "extdll.h"
#include "hud_iface.h"
void HUD_DrawNormalTriangles( void )
{
}
void HUD_DrawTransparentTriangles( void )
{
}

243
client/global/vector.h Normal file
View File

@ -0,0 +1,243 @@
//=======================================================================
// Copyright (C) Shambler Team 2005
// vector.h - shared vector operations
//=======================================================================
#ifndef VECTOR_H
#define VECTOR_H
#include <math.h>
#include <float.h>
#include <stdlib.h>
#include <stdio.h>
#pragma warning(disable : 4244) // int or float down-conversion
// Header file containing definition of globalvars_t and entvars_t
typedef int func_t; //
typedef int string_t; // from engine's pr_comp.h;
typedef float vec_t; // needed before including progdefs.h
//=========================================================
// 2DVector - used for many pathfinding and many other
// operations that are treated as planar rather than 3d.
//=========================================================
class Vector2D
{
public:
inline Vector2D(void) { }
inline Vector2D(float X, float Y) { x = X; y = Y; }
inline Vector2D operator+(const Vector2D& v) const { return Vector2D(x+v.x, y+v.y); }
inline Vector2D operator-(const Vector2D& v) const { return Vector2D(x-v.x, y-v.y); }
inline Vector2D operator*(float fl) const { return Vector2D(x*fl, y*fl); }
inline Vector2D operator/(float fl) const { return Vector2D(x/fl, y/fl); }
inline float Length(void) const { return (float)sqrt(x*x + y*y ); }
inline Vector2D Normalize ( void ) const
{
Vector2D vec2;
float flLen = Length();
if ( flLen == 0 )
{
return Vector2D( (float)0, (float)0 );
}
else
{
flLen = 1 / flLen;
return Vector2D( x * flLen, y * flLen );
}
}
vec_t x, y;
};
#define nanmask (255<<23)
#define IS_NAN(x) (((*(int *)&x)&nanmask)==nanmask)
inline float DotProduct(const Vector2D& a, const Vector2D& b) { return( a.x*b.x + a.y*b.y ); }
inline Vector2D operator*(float fl, const Vector2D& v) { return v * fl; }
//=========================================================
// 3D Vector
//=========================================================
class Vector // same data-layout as engine's vec3_t,
{ // which is a vec_t[3]
public:
// Construction/destruction
inline Vector(void) { }
inline Vector(float X, float Y, float Z) { x = X; y = Y; z = Z; }
inline Vector(const Vector& v) { x = v.x; y = v.y; z = v.z; }
inline Vector(float rgfl[3]) { x = rgfl[0]; y = rgfl[1]; z = rgfl[2]; }
// Initialization
void Init(vec_t ix=0.0f, vec_t iy=0.0f, vec_t iz=0.0f){ x = ix; y = iy; z = iz; }
// Operators
inline Vector operator-(void) const { return Vector(-x,-y,-z); }
inline int operator==(const Vector& v) const { return x==v.x && y==v.y && z==v.z; }
inline int operator!=(const Vector& v) const { return !(*this==v); }
inline Vector operator+(const Vector& v) const { return Vector(x+v.x, y+v.y, z+v.z); }
inline Vector operator-(const Vector& v) const { return Vector(x-v.x, y-v.y, z-v.z); }
inline Vector operator*(float fl) const { return Vector(x*fl, y*fl, z*fl); }
inline Vector operator/(float fl) const { return Vector(x/fl, y/fl, z/fl); }
_forceinline Vector& operator+=(const Vector &v)
{
x+=v.x; y+=v.y; z += v.z;
return *this;
}
_forceinline Vector& operator-=(const Vector &v)
{
x-=v.x; y-=v.y; z -= v.z;
return *this;
}
_forceinline Vector& operator*=(const Vector &v)
{
x *= v.x; y *= v.y; z *= v.z;
return *this;
}
_forceinline Vector& operator*=(float s)
{
x *= s; y *= s; z *= s;
return *this;
}
_forceinline Vector& operator/=(const Vector &v)
{
x /= v.x; y /= v.y; z /= v.z;
return *this;
}
_forceinline Vector& operator/=(float s)
{
float oofl = 1.0f / s;
x *= oofl; y *= oofl; z *= oofl;
return *this;
}
_forceinline Vector MA( const Vector &start, float scale, const Vector &direction ) const
{
return Vector(start.x + scale * direction.x, start.y + scale * direction.y, start.z + scale * direction.z) ;
}
// Methods
inline void CopyToArray(float* rgfl) const { rgfl[0] = x, rgfl[1] = y, rgfl[2] = z; }
inline float Length(void) const { return (float)sqrt(x*x + y*y + z*z); }
operator float *() { return &x; } // Vectors will now automatically convert to float * when needed
operator const float *() const { return &x; } // Vectors will now automatically convert to float * when needed
// array access...
vec_t operator[](int i) const { return ((vec_t*)this)[i];}
vec_t& operator[](int i) { return ((vec_t*)this)[i];}
inline Vector Normalize(void) const
{
float flLen = Length();
if (flLen == 0) return Vector(0,0,1); // ????
flLen = 1 / flLen;
return Vector(x * flLen, y * flLen, z * flLen);
}
vec_t Dot(Vector const& vOther) const
{
return(x*vOther.x+y*vOther.y+z*vOther.z);
}
Vector Cross(const Vector &vOther) const
{
return Vector(y*vOther.z - z*vOther.y, z*vOther.x - x*vOther.z, x*vOther.y - y*vOther.x);
}
inline Vector2D Make2D ( void ) const
{
Vector2D Vec2;
Vec2.x = x;
Vec2.y = y;
return Vec2;
}
inline float Length2D(void) const { return (float)sqrt(x*x + y*y); }
// Members
vec_t x, y, z;
};
inline Vector operator*(float fl, const Vector& v) { return v * fl; }
inline float DotProduct(const Vector& a, const Vector& b) { return(a.x*b.x+a.y*b.y+a.z*b.z); }
inline Vector CrossProduct(const Vector& a, const Vector& b) { return Vector( a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x ); }
#define vec3_t Vector
//=========================================================
// 4D Vector - for matrix operations
//=========================================================
class Vector4D
{
public:
// Members
vec_t x, y, z, w;
// Construction/destruction
Vector4D(void){}
Vector4D(vec_t X, vec_t Y, vec_t Z, vec_t W) { x = X; y = Y; z = Z; w = W;}
Vector4D(double X, double Y, double Z, double W) { x = (double)X; y = (double)Y; z = (double)Z; w = (double)W;}
Vector4D(const float *pFloat) { x = pFloat[0]; y = pFloat[1]; z = pFloat[2]; w = pFloat[3];}
// array access...
vec_t operator[](int i) const { return ((vec_t*)this)[i];}
vec_t& operator[](int i) { return ((vec_t*)this)[i];}
// equality
bool operator==(const Vector4D& src) const{ return(src.x == x) && (src.y == y) && (src.z == z) && (src.w == w);}
bool operator!=(const Vector4D& src) const{ return(src.x != x) || (src.y != y) || (src.z != z) || (src.w != w);}
// arithmetic operations
Vector4D& operator+=(const Vector4D &v){ x+=v.x; y+=v.y; z += v.z; return *this;}
Vector4D& operator-=(const Vector4D &v){ x-=v.x; y-=v.y; z -= v.z; return *this;}
Vector4D operator+ (const Vector4D &v)const {Vector4D res; res.x = x + v.x; res.y = y + v.y; res.z = z + v.z; res.w = w; return res;}
Vector4D operator- (const Vector4D &v)const {Vector4D res; res.x = x - v.x; res.y = y - v.y; res.z = z - v.z; res.w = w; return res;}
Vector4D operator* (const Vector4D &v)const {Vector4D res; res.x = y * v.z - z * v.y; res.y = z * v.x - x * v.z; res.z = x * v.y - y * v.x; res.w = w; return res;}
float operator% (const Vector4D &v)const { return (x * v.x + y * v.y + z * v.z); }
Vector4D Scale( float scale)const {Vector4D res; res.x = x * scale; res.y = y * scale; res.z = z * scale; res.w = w;return res; }
Vector4D CompProduct (const Vector4D &v)const {Vector4D res; res.x = x * v.x; res.y = y * v.y; res.z = z * v.z; res.w = w; return res;}
};
//=========================================================
// RandomRange - for random values
//=========================================================
class RandomRange
{
public:
float m_flMax, m_flMin;//class members
RandomRange() { m_flMin = m_flMax = 0; }
RandomRange(float fValue) { m_flMin = m_flMax = fValue; }
RandomRange(float fMin, float fMax) { m_flMin = fMin; m_flMax = fMax; }
RandomRange( char *szToken )
{
char *cOneDot = NULL;
m_flMin = m_flMax = 0;
for (char *c = szToken; *c; c++)
{
if (*c == '.')
{
if (cOneDot != NULL)
{
// found two dots in a row - it's a range
*cOneDot = 0; // null terminate the first number
m_flMin = atof(szToken); // parse the first number
*cOneDot = '.'; // change it back, just in case
c++;
m_flMax = atof(c); // parse the second number
return;
}
else cOneDot = c;
}
else cOneDot = NULL;
}
// no range, just record the number
m_flMax = m_flMin = atof(szToken);
}
// FIXME: float Random() { return RANDOM_FLOAT(m_flMin, m_flMax); }
float Random() { return m_flMin - m_flMax; }
// array access...
float operator[](int i) const { return ((float*)this)[i];}
float& operator[](int i) { return ((float*)this)[i];}
};
#endif

79
client/global/view.cpp Normal file
View File

@ -0,0 +1,79 @@
//=======================================================================
// Copyright XashXT Group 2008 ©
// view.cpp - view/refresh setup functions
//=======================================================================
#include "extdll.h"
#include "hud_iface.h"
#include "hud.h"
void V_CalcShake( void )
{
float frametime;
int i;
float fraction, freq;
if( gHUD.m_Shake.time == 0 )
return;
if(( gHUD.m_flTime > gHUD.m_Shake.time ) || gHUD.m_Shake.duration <= 0
|| gHUD.m_Shake.amplitude <= 0 || gHUD.m_Shake.frequency <= 0 )
{
memset( &gHUD.m_Shake, 0, sizeof( gHUD.m_Shake ));
return;
}
frametime = gHUD.m_flTimeDelta;
if( gHUD.m_flTime > gHUD.m_Shake.nextShake )
{
// higher frequency means we recalc the extents more often and perturb the display again
gHUD.m_Shake.nextShake = gHUD.m_flTime + (1.0f / gHUD.m_Shake.frequency);
// Compute random shake extents (the shake will settle down from this)
for( i = 0; i < 3; i++ )
{
gHUD.m_Shake.offset[i] = RANDOM_FLOAT( -gHUD.m_Shake.amplitude, gHUD.m_Shake.amplitude );
}
gHUD.m_Shake.angle = RANDOM_FLOAT( -gHUD.m_Shake.amplitude * 0.25, gHUD.m_Shake.amplitude * 0.25 );
}
// ramp down amplitude over duration (fraction goes from 1 to 0 linearly with slope 1/duration)
fraction = ( gHUD.m_Shake.time - gHUD.m_flTime ) / gHUD.m_Shake.duration;
// ramp up frequency over duration
if( fraction )
{
freq = (gHUD.m_Shake.frequency / fraction);
}
else
{
freq = 0;
}
// square fraction to approach zero more quickly
fraction *= fraction;
// Sine wave that slowly settles to zero
fraction = fraction * sin( gHUD.m_flTime * freq );
// add to view origin
gHUD.m_Shake.appliedOffset = gHUD.m_Shake.offset * fraction;
// add to roll
gHUD.m_Shake.appliedAngle = gHUD.m_Shake.angle * fraction;
// drop amplitude a bit, less for higher frequency shakes
float localAmp = gHUD.m_Shake.amplitude * ( frametime / ( gHUD.m_Shake.duration * gHUD.m_Shake.frequency ));
gHUD.m_Shake.amplitude -= localAmp;
}
void V_ApplyShake( Vector& origin, Vector& angles, float factor )
{
origin.MA( origin, factor, gHUD.m_Shake.appliedOffset );
angles.z += gHUD.m_Shake.appliedAngle * factor;
}
void V_CalcRefdef( ref_params_t *parms )
{
}

341
client/hud/hud.cpp Normal file
View File

@ -0,0 +1,341 @@
//=======================================================================
// Copyright (C) XashXT Group 2007
//=======================================================================
#include "extdll.h"
#include "hud_iface.h"
#include "hud.h"
extern float HUD_GetFOV( void );
extern float v_idlescale;
float in_fov;
void CHud :: Init( void )
{
InitMessages();
m_Ammo.Init();
m_Health.Init();
m_SayText.Init();
m_Geiger.Init();
m_Train.Init();
m_Battery.Init();
m_Flash.Init();
m_Redeemer.Init();
m_Zoom.Init();
m_Message.Init();
m_Scoreboard.Init();
m_StatusBar.Init();
m_DeathNotice.Init();
m_AmmoSecondary.Init();
m_TextMessage.Init();
m_StatusIcons.Init();
m_Menu.Init();
m_Sound.Init();
m_MOTD.Init();
MsgFunc_ResetHUD(0, 0, NULL );
}
CHud :: ~CHud( void )
{
m_Sound.Close();
if( m_pHudList )
{
HUDLIST *pList;
while( m_pHudList )
{
pList = m_pHudList;
m_pHudList = m_pHudList->pNext;
FREE( pList );
}
m_pHudList = NULL;
}
}
int CHud :: GetSpriteIndex( const char *SpriteName )
{
// use built-in Shader Manager
return LOAD_SHADER( SpriteName );
}
void CHud :: VidInit( void )
{
// ----------
// Load Sprites
// ---------
m_hsprCursor = 0;
m_hHudError = 0;
// TODO: build real table of fonts widthInChars
for( int i = 0; i < 256; i++ )
charWidths[i] = SMALLCHAR_WIDTH;
iCharHeight = SMALLCHAR_HEIGHT;
// assumption: number_1, number_2, etc, are all listed and loaded sequentially
m_HUD_number_0 = GetSpriteIndex( "number_0" );
GetImageSize( NULL, &m_iFontHeight, m_HUD_number_0 );
// loading error sprite
m_HUD_error = GetSpriteIndex( "error" );
m_hHudError = GetSprite( m_HUD_error );
m_Sound.VidInit();
m_Ammo.VidInit();
m_Health.VidInit();
m_Geiger.VidInit();
m_Train.VidInit();
m_Battery.VidInit();
m_Flash.VidInit();
m_Redeemer.VidInit();
m_Zoom.VidInit();
m_MOTD.VidInit();
m_Message.VidInit();
m_Scoreboard.VidInit();
m_StatusBar.VidInit();
m_DeathNotice.VidInit();
m_SayText.VidInit();
m_Menu.VidInit();
m_AmmoSecondary.VidInit();
m_TextMessage.VidInit();
m_StatusIcons.VidInit();
}
void CHud :: Think( void )
{
HUDLIST *pList = m_pHudList;
while( pList )
{
if (pList->p->m_iFlags & HUD_ACTIVE)
pList->p->Think();
pList = pList->pNext;
}
// think about default fov
if( m_iFOV == 0 )
{
// only let players adjust up in fov, and only if they are not overriden by something else
m_iFOV = max( CVAR_GET_FLOAT( "default_fov" ), 90 );
}
}
int CHud :: UpdateClientData( ref_params_t *cdata, float time )
{
memcpy( m_vecOrigin, cdata->origin, sizeof( vec3_t ));
memcpy( m_vecAngles, cdata->angles, sizeof( vec3_t ));
m_iKeyBits = cdata->iKeyBits;
m_iWeaponBits = cdata->iWeaponBits;
Think();
cdata->fov = m_iFOV;
cdata->iKeyBits = m_iKeyBits;
cdata->fov = m_iFOV;
cdata->v_idlescale = m_iConcussionEffect;
if( m_flMouseSensitivity )
cdata->mouse_sensitivity = m_flMouseSensitivity;
return 1;
}
int CHud :: Redraw( float flTime, int intermission )
{
m_fOldTime = m_flTime; // save time of previous redraw
m_flTime = flTime;
m_flTimeDelta = (double)m_flTime - m_fOldTime;
// clock was reset, reset delta
if( m_flTimeDelta < 0 ) m_flTimeDelta = 0;
m_iIntermission = intermission;
// draw screen fade before hud
DrawScreenFade();
// redeemer hud stuff
if( m_Redeemer.m_iHudMode > 0 )
{
m_Redeemer.Draw( flTime );
return 1;
}
// zoom hud stuff
if( m_Zoom.m_iHudMode > 0 )
{
m_Zoom.Draw( flTime );
return 1;
}
// custom view active, and flag "draw hud" isn't set
if(( viewFlags & 1 ) && !( viewFlags & 2 ))
return 1;
if( CVAR_GET_FLOAT( "hud_draw" ))
{
HUDLIST *pList = m_pHudList;
while( pList )
{
if( !intermission )
{
if(( pList->p->m_iFlags & HUD_ACTIVE ) && !(m_iHideHUDDisplay & HIDEHUD_ALL ))
pList->p->Draw(flTime);
}
else
{
// it's an intermission, so only draw hud elements
// that are set to draw during intermissions
if( pList->p->m_iFlags & HUD_INTERMISSION )
pList->p->Draw( flTime );
}
pList = pList->pNext;
}
}
return 1;
}
int CHud :: DrawHudString( int xpos, int ypos, int iMaxX, char *szIt, int r, int g, int b )
{
// draw the string until we hit the null character or a newline character
for( ; *szIt != 0 && *szIt != '\n'; szIt++ )
{
int next = xpos + gHUD.charWidths[*szIt]; // variable-width fonts look cool
if ( next > iMaxX )
return xpos;
TextMessageDrawChar( xpos, ypos, *szIt, r, g, b );
xpos = next;
}
return xpos;
}
int CHud :: DrawHudNumberString( int xpos, int ypos, int iMinX, int iNumber, int r, int g, int b )
{
char szString[32];
sprintf( szString, "%d", iNumber );
return DrawHudStringReverse( xpos, ypos, iMinX, szString, r, g, b );
}
int CHud :: DrawHudStringReverse( int xpos, int ypos, int iMinX, char *szString, int r, int g, int b )
{
// find the end of the string
for ( char *szIt = szString; *szIt != 0; szIt++ )
{ // we should count the length?
}
// iterate throug the string in reverse
for( szIt--; szIt != (szString-1); szIt-- )
{
int next = xpos - gHUD.charWidths[ *szIt ]; // variable-width fonts look cool
if( next < iMinX )
return xpos;
xpos = next;
TextMessageDrawChar( xpos, ypos, *szIt, r, g, b );
}
return xpos;
}
int CHud :: DrawHudNumber( int x, int y, int iFlags, int iNumber, int r, int g, int b )
{
int iWidth = GetSpriteRect( m_HUD_number_0 ).right - GetSpriteRect( m_HUD_number_0 ).left;
int k;
if( iNumber > 0 )
{
// SPR_Draw 100's
if( iNumber >= 100 )
{
k = iNumber / 100;
SPR_Set(GetSprite(m_HUD_number_0 + k), r, g, b );
SPR_DrawAdditive( 0, x, y, &GetSpriteRect(m_HUD_number_0 + k));
x += iWidth;
}
else if( iFlags & DHN_3DIGITS )
{
x += iWidth;
}
// SPR_Draw 10's
if( iNumber >= 10 )
{
k = (iNumber % 100)/10;
SPR_Set(GetSprite(m_HUD_number_0 + k), r, g, b );
SPR_DrawAdditive( 0, x, y, &GetSpriteRect(m_HUD_number_0 + k));
x += iWidth;
}
else if( iFlags & (DHN_3DIGITS|DHN_2DIGITS))
{
x += iWidth;
}
// SPR_Draw ones
k = iNumber % 10;
SPR_Set(GetSprite(m_HUD_number_0 + k), r, g, b );
SPR_DrawAdditive(0, x, y, &GetSpriteRect(m_HUD_number_0 + k));
x += iWidth;
}
else if( iFlags & DHN_DRAWZERO )
{
SPR_Set(GetSprite(m_HUD_number_0), r, g, b );
// SPR_Draw 100's
if( iFlags & DHN_3DIGITS )
{
x += iWidth;
}
if( iFlags & (DHN_3DIGITS|DHN_2DIGITS)) x += iWidth;
SPR_DrawAdditive( 0, x, y, &GetSpriteRect( m_HUD_number_0 ));
x += iWidth;
}
return x;
}
int CHud::GetNumWidth( int iNumber, int iFlags )
{
if( iFlags & DHN_3DIGITS ) return 3;
if( iFlags & DHN_2DIGITS ) return 2;
if( iNumber <= 0 )
{
if( iFlags & DHN_DRAWZERO )
return 1;
else return 0;
}
if( iNumber < 10 ) return 1;
if( iNumber < 100 ) return 2;
return 3;
}
void CHud::AddHudElem( CHudBase *phudelem )
{
HUDLIST *pdl, *ptemp;
if( !phudelem ) return;
pdl = (HUDLIST *)CALLOC( sizeof( HUDLIST ), 1 );
pdl->p = phudelem;
if( !m_pHudList )
{
m_pHudList = pdl;
return;
}
ptemp = m_pHudList;
while( ptemp->pNext )
ptemp = ptemp->pNext;
ptemp->pNext = pdl;
}

680
client/hud/hud.h Normal file
View File

@ -0,0 +1,680 @@
//=======================================================================
// Copyright XashXT Group 2008 ©
// hud.h - hud primary header
//=======================================================================
#define RGB_YELLOWISH 0x00FFA000 // 255, 160, 0
#define RGB_REDISH 0x00FF1010 // 255, 160, 0
#define RGB_GREENISH 0x0000A000 // 0, 160, 0
#include "hud_ammo.h"
#define DHN_DRAWZERO 1
#define DHN_2DIGITS 2
#define DHN_3DIGITS 4
#define MIN_ALPHA 100
#define HUDELEM_ACTIVE 1
typedef struct
{
int x, y;
} POSITION;
// This structure is sent over the net to describe a screen shake event
typedef struct
{
float time;
float duration;
float amplitude;
float frequency;
float nextShake;
Vector offset;
float angle;
Vector appliedOffset;
float appliedAngle;
} ScreenShake;
typedef struct
{
int dripsPerSecond;
float distFromPlayer;
float windX, windY;
float randX, randY;
int weatherMode; // 0 - snow, 1 - rain
float globalHeight;
} RainData;
typedef struct
{
byte r, g, b, a;
} RGBA;
#define HUD_ACTIVE 1
#define HUD_INTERMISSION 2
#define MAX_SEC_AMMO_VALUES 4
#define MAX_PLAYER_NAME_LENGTH 32
#define MAX_MOTD_LENGTH 1536
#define FADE_TIME 100
#define maxHUDMessages 16
#define MAX_SPRITE_NAME_LENGTH 24
//
//-----------------------------------------------------
//
class CHudBase
{
public:
POSITION m_pos;
int m_type;
int m_iFlags; // active, moving,
virtual ~CHudBase(){ }
virtual int Init( void ){ return 0; }
virtual int VidInit( void ){ return 0; }
virtual int Draw( float flTime ){ return 0; }
virtual void Think( void ){ return; }
virtual void Reset( void ){ return; }
virtual void InitHUDData( void ){ } // called every time a server is connected to
};
struct HUDLIST
{
CHudBase *p;
HUDLIST *pNext;
};
//
//-----------------------------------------------------
//
class CHudAmmo: public CHudBase
{
public:
int Init( void );
int VidInit( void );
int Draw(float flTime);
void Think(void);
void Reset(void);
int DrawWList(float flTime);
int MsgFunc_CurWeapon( const char *pszName, int iSize, void *pbuf );
int MsgFunc_WeaponList( const char *pszName, int iSize, void *pbuf );
int MsgFunc_AmmoX(const char *pszName, int iSize, void *pbuf );
int MsgFunc_AmmoPickup( const char *pszName, int iSize, void *pbuf );
int MsgFunc_WeapPickup( const char *pszName, int iSize, void *pbuf );
int MsgFunc_ItemPickup( const char *pszName, int iSize, void *pbuf );
int MsgFunc_HideWeapon( const char *pszName, int iSize, void *pbuf );
void SlotInput( int iSlot );
void _cdecl UserCmd_Slot1( void );
void _cdecl UserCmd_Slot2( void );
void _cdecl UserCmd_Slot3( void );
void _cdecl UserCmd_Slot4( void );
void _cdecl UserCmd_Slot5( void );
void _cdecl UserCmd_Slot6( void );
void _cdecl UserCmd_Slot7( void );
void _cdecl UserCmd_Slot8( void );
void _cdecl UserCmd_Slot9( void );
void _cdecl UserCmd_Slot10( void );
void _cdecl UserCmd_Close( void );
void _cdecl UserCmd_NextWeapon( void );
void _cdecl UserCmd_PrevWeapon( void );
private:
float m_fFade;
RGBA m_rgba;
WEAPON *m_pWeapon;
int m_HUD_bucket0;
int m_HUD_selection;
};
//
//-----------------------------------------------------
//
class CHudAmmoSecondary: public CHudBase
{
public:
int Init( void );
int VidInit( void );
void Reset( void );
int Draw(float flTime);
int MsgFunc_SecAmmoVal( const char *pszName, int iSize, void *pbuf );
int MsgFunc_SecAmmoIcon( const char *pszName, int iSize, void *pbuf );
private:
int m_HUD_ammoicon; // sprite indices
int m_iAmmoAmounts[MAX_SEC_AMMO_VALUES];
float m_fFade;
};
#include "hud_health.h"
//
//-----------------------------------------------------
//
class CHudGeiger: public CHudBase
{
public:
int Init( void );
int VidInit( void );
int Draw( float flTime );
int MsgFunc_Geiger( const char *pszName, int iSize, void *pbuf );
private:
int m_iGeigerRange;
};
//
//-----------------------------------------------------
//
class CHudTrain: public CHudBase
{
public:
int Init( void );
int VidInit( void );
int Draw( float flTime );
int MsgFunc_Train( const char *pszName, int iSize, void *pbuf );
private:
HSPRITE m_hSprite;
int m_iPos;
};
class CHudMOTD : public CHudBase
{
public:
int Init( void );
int VidInit( void );
int Draw( float flTime );
void Reset( void );
int MsgFunc_MOTD( const char *pszName, int iSize, void *pbuf );
protected:
static int MOTD_DISPLAY_TIME;
char m_szMOTD[MAX_MOTD_LENGTH];
float m_flActiveTill;
int m_iLines;
};
//
//-----------------------------------------------------
//
class CHudSound: public CHudBase
{
public:
int Init( void );
int VidInit( void );
int MsgFunc_Fsound( const char *pszName, int iSize, void *pbuf );
int PlayStream( const char* name );
int Draw( float flTime ); // used for get pause
int Close( void );
private:
int m_iStatus;
int m_iTime;
};
//
//-----------------------------------------------------
//
class CHudScoreboard: public CHudBase
{
public:
int Init( void );
void InitHUDData( void );
int VidInit( void );
int Draw( float flTime );
int DrawPlayers( int xoffset, float listslot, int nameoffset = 0, char *team = NULL ); // returns the ypos where it finishes drawing
void UserCmd_ShowScores( void );
void UserCmd_HideScores( void );
int MsgFunc_ScoreInfo( const char *pszName, int iSize, void *pbuf );
int MsgFunc_TeamInfo( const char *pszName, int iSize, void *pbuf );
int MsgFunc_TeamScore( const char *pszName, int iSize, void *pbuf );
void DeathMsg( int killer, int victim );
enum
{
MAX_PLAYERS = 64,
MAX_TEAMS = 64,
MAX_TEAM_NAME = 16,
};
struct extra_player_info_t
{
short frags;
short deaths;
char teamname[MAX_TEAM_NAME];
};
struct team_info_t
{
char name[MAX_TEAM_NAME];
short frags;
short deaths;
short ping;
short packetloss;
short ownteam;
short players;
int already_drawn;
int scores_overriden;
};
hud_player_info_t m_PlayerInfoList[MAX_PLAYERS+1]; // player info from the engine
extra_player_info_t m_PlayerExtraInfo[MAX_PLAYERS+1]; // additional player info sent directly to the client.dll
team_info_t m_TeamInfo[MAX_TEAMS+1];
int m_iNumTeams;
int m_iLastKilledBy;
int m_fLastKillTime;
int m_iPlayerNum;
int m_iShowscoresHeld;
void GetAllPlayersInfo( void );
};
//
//-----------------------------------------------------
//
class CHudStatusBar : public CHudBase
{
public:
int Init( void );
int VidInit( void );
int Draw( float flTime );
void Reset( void );
void ParseStatusString( int line_num );
int MsgFunc_StatusText( const char *pszName, int iSize, void *pbuf );
int MsgFunc_StatusValue( const char *pszName, int iSize, void *pbuf );
protected:
enum
{
MAX_STATUSTEXT_LENGTH = 128,
MAX_STATUSBAR_VALUES = 8,
MAX_STATUSBAR_LINES = 2,
};
char m_szStatusText[MAX_STATUSBAR_LINES][MAX_STATUSTEXT_LENGTH]; // a text string describing how the status bar is to be drawn
char m_szStatusBar[MAX_STATUSBAR_LINES][MAX_STATUSTEXT_LENGTH]; // the constructed bar that is drawn
int m_iStatusValues[MAX_STATUSBAR_VALUES]; // an array of values for use in the status bar
int m_bReparseString; // set to TRUE whenever the m_szStatusBar needs to be recalculated
// an array of colors...one color for each line
float *m_pflNameColors[MAX_STATUSBAR_LINES];
};
//
//-----------------------------------------------------
//
class CHudDeathNotice : public CHudBase
{
public:
int Init( void );
void InitHUDData( void );
int VidInit( void );
int Draw( float flTime );
int MsgFunc_DeathMsg( const char *pszName, int iSize, void *pbuf );
private:
int m_HUD_d_skull; // sprite index of skull icon
};
//
//-----------------------------------------------------
//
class CHudMenu : public CHudBase
{
public:
int Init( void );
void InitHUDData( void );
int VidInit( void );
void Reset( void );
int Draw( float flTime );
int MsgFunc_ShowMenu( const char *pszName, int iSize, void *pbuf );
void SelectMenuItem( int menu_item );
int m_fMenuDisplayed;
int m_bitsValidSlots;
float m_flShutoffTime;
int m_fWaitingForMore;
};
//
//-----------------------------------------------------
//
class CHudSayText : public CHudBase
{
public:
int Init( void );
void InitHUDData( void );
int VidInit( void );
int Draw( float flTime );
int MsgFunc_SayText( const char *pszName, int iSize, void *pbuf );
void SayTextPrint( const char *pszBuf, int iBufSize, int clientIndex = -1 );
void EnsureTextFitsInOneLineAndWrapIfHaveTo( int line );
private:
float m_HUD_saytext;
float m_HUD_saytext_time;
};
//
//-----------------------------------------------------
//
class CHudBattery: public CHudBase
{
public:
int Init( void );
int VidInit( void );
int Draw( float flTime );
int MsgFunc_Battery(const char *pszName, int iSize, void *pbuf );
private:
HSPRITE m_hSprite1;
HSPRITE m_hSprite2;
wrect_t *m_prc1;
wrect_t *m_prc2;
int m_iBat;
float m_fFade;
int m_iHeight; // width of the battery innards
};
//
//-----------------------------------------------------
//
class CHudFlashlight: public CHudBase
{
public:
int Init( void );
int VidInit( void );
int Draw(float flTime);
void Reset( void );
int MsgFunc_Flashlight( const char *pszName, int iSize, void *pbuf );
int MsgFunc_FlashBat( const char *pszName, int iSize, void *pbuf );
private:
HSPRITE m_hSprite1;
HSPRITE m_hSprite2;
HSPRITE m_hBeam;
wrect_t *m_prc1;
wrect_t *m_prc2;
wrect_t *m_prcBeam;
float m_flBat;
int m_iBat;
int m_fOn;
float m_fFade;
int m_iWidth; // width of the battery innards
};
class CHudRedeemer: public CHudBase
{
public:
int Init( void );
int VidInit( void );
int Draw( float flTime );
int MsgFunc_WarHUD( const char *pszName, int iSize, void *pbuf );
int m_iHudMode;
private:
HSPRITE m_hSprite;
HSPRITE m_hCrosshair;
HSPRITE m_hStatic;
HSPRITE m_hCamera;
HSPRITE m_hCamRec;
};
class CHudZoom: public CHudBase
{
public:
int Init( void );
int VidInit( void );
int Draw( float flTime );
int MsgFunc_ZoomHUD( const char *pszName, int iSize, void *pbuf );
int m_iHudMode;
private:
HSPRITE m_hCrosshair;
HSPRITE m_hLines;
};
//
//-----------------------------------------------------
//
struct message_parms_t
{
client_textmessage_t *pMessage;
float time;
int x, y;
int totalWidth, totalHeight;
int width;
int lines;
int lineLength;
int length;
int r, g, b;
int text;
int fadeBlend;
float charTime;
float fadeTime;
};
//
//-----------------------------------------------------
//
class CHudTextMessage: public CHudBase
{
public:
int Init( void );
static char *LocaliseTextString( const char *msg, char *dst_buffer, int buffer_size );
static char *BufferedLocaliseTextString( const char *msg );
char *LookupString( const char *msg_name, int *msg_dest = NULL );
int MsgFunc_TextMsg( const char *pszName, int iSize, void *pbuf );
};
//
//-----------------------------------------------------
//
class CHudMessage: public CHudBase
{
public:
int Init( void );
int VidInit( void );
int Draw( float flTime );
int MsgFunc_HudText( const char *pszName, int iSize, void *pbuf );
int MsgFunc_GameTitle( const char *pszName, int iSize, void *pbuf );
float FadeBlend( float fadein, float fadeout, float hold, float localTime );
int XPosition( float x, int width, int lineWidth );
int YPosition( float y, int height );
void MessageAdd( const char *pName, float time );
void MessageAdd( client_textmessage_t * newMessage );
void MessageDrawScan( client_textmessage_t *pMessage, float time );
void MessageScanStart( void );
void MessageScanNextChar( void );
void Reset( void );
private:
client_textmessage_t *m_pMessages[maxHUDMessages];
float m_startTime[maxHUDMessages];
message_parms_t m_parms;
float m_gameTitleTime;
client_textmessage_t *m_pGameTitle;
int HUD_Logo; // display logo
};
//
//-----------------------------------------------------
//
class CHudStatusIcons: public CHudBase
{
public:
int Init( void );
int VidInit( void );
void Reset( void );
int Draw( float flTime );
int MsgFunc_StatusIcon( const char *pszName, int iSize, void *pbuf );
enum
{
MAX_ICONSPRITENAME_LENGTH = MAX_SPRITE_NAME_LENGTH,
MAX_ICONSPRITES = 4,
};
// had to make these public so CHud could access them (to enable concussion icon)
// could use a friend declaration instead...
void EnableIcon( char *pszIconName, byte red, byte green, byte blue );
void DisableIcon( char *pszIconName );
private:
typedef struct
{
char szSpriteName[MAX_ICONSPRITENAME_LENGTH];
HSPRITE spr;
wrect_t rc;
byte r, g, b;
} icon_sprite_t;
icon_sprite_t m_IconList[MAX_ICONSPRITES];
};
//
//-----------------------------------------------------
//
#define SKY_OFF 0
#define SKY_ON 1
class CHud
{
private:
HUDLIST *m_pHudList;
float m_flMouseSensitivity;
int m_iConcussionEffect;
public:
HSPRITE m_hsprCursor;
float m_flTime; // the current client time
float m_fOldTime; // the time at which the HUD was last redrawn
double m_flTimeDelta;// the difference between flTime and fOldTime
float m_vecOrigin[3];
float m_vecAngles[3];
int m_iKeyBits;
int m_iHideHUDDisplay;
int m_iFOV;
int m_Teamplay;
int m_iRes;
Vector m_vecSkyPos;
int m_iSkyMode;
int m_iCameraMode;
int m_iLastCameraMode;
int m_iFontHeight;
int DrawHudNumber( int x, int y, int iFlags, int iNumber, int r, int g, int b );
int DrawHudString( int x, int y, int iMaxX, char *szString, int r, int g, int b );
int DrawHudStringReverse( int xpos, int ypos, int iMinX, char *szString, int r, int g, int b );
int DrawHudNumberString( int xpos, int ypos, int iMinX, int iNumber, int r, int g, int b );
int GetNumWidth( int iNumber, int iFlags );
int viewEntityIndex;
int m_iHUDColor;
int viewFlags;
private:
wrect_t m_Rect;
public:
HSPRITE GetSprite( int index ) { return (HSPRITE)index; }
wrect_t& GetSpriteRect( HSPRITE index )
{
m_Rect.left = m_Rect.top = 0;
GetImageSize( &m_Rect.right, &m_Rect.bottom, index );
return m_Rect;
}
int InitMessages( void ); // init hud messages
int GetSpriteIndex( const char *SpriteName );
CHudAmmo m_Ammo;
CHudHealth m_Health;
CHudGeiger m_Geiger;
CHudBattery m_Battery;
CHudTrain m_Train;
CHudFlashlight m_Flash;
CHudRedeemer m_Redeemer;
CHudZoom m_Zoom;
CHudMessage m_Message;
CHudScoreboard m_Scoreboard;
CHudStatusBar m_StatusBar;
CHudDeathNotice m_DeathNotice;
CHudSayText m_SayText;
CHudMenu m_Menu;
CHudAmmoSecondary m_AmmoSecondary;
CHudTextMessage m_TextMessage;
CHudStatusIcons m_StatusIcons;
CHudSound m_Sound;
CHudMOTD m_MOTD;
void Init( void );
void VidInit( void );
void Think( void );
int Redraw( float flTime, int intermission );
int UpdateClientData( ref_params_t *pparams, float time );
CHud() : m_pHudList(NULL) { }
~CHud(); // destructor, frees allocated memory
// user messages
int _cdecl MsgFunc_Damage( const char *pszName, int iSize, void *pbuf );
int _cdecl MsgFunc_GameMode( const char *pszName, int iSize, void *pbuf );
int _cdecl MsgFunc_ServerName( const char *pszName, int iSize, void *pbuf );
int _cdecl MsgFunc_ResetHUD( const char *pszName, int iSize, void *pbuf);
int _cdecl MsgFunc_InitHUD( const char *pszName, int iSize, void *pbuf );
int _cdecl MsgFunc_ViewMode( const char *pszName, int iSize, void *pbuf );
int _cdecl MsgFunc_SetFOV( const char *pszName, int iSize, void *pbuf );
int _cdecl MsgFunc_Concuss( const char *pszName, int iSize, void *pbuf );
int _cdecl MsgFunc_ScreenShake( const char *pszName, int iSize, void *pbuf );
int _cdecl MsgFunc_RainData( const char *pszName, int iSize, void *pbuf );
int _cdecl MsgFunc_HUDColor( const char *pszName, int iSize, void *pbuf);
int _cdecl MsgFunc_SetFog( const char *pszName, int iSize, void *pbuf );
int _cdecl MsgFunc_SetSky( const char *pszName, int iSize, void *pbuf );
int _cdecl MsgFunc_CamData( const char *pszName, int iSize, void *pbuf );
int _cdecl MsgFunc_SetBody( const char *pszName, int iSize, void *pbuf );
int _cdecl MsgFunc_SetSkin( const char *pszName, int iSize, void *pbuf );
int _cdecl MsgFunc_AddScreen( const char *pszName, int iSize, void *pbuf );
int _cdecl MsgFunc_AddMirror( const char *pszName, int iSize, void *pbuf );
int _cdecl MsgFunc_AddPortal( const char *pszName, int iSize, void *pbuf );
int _cdecl MsgFunc_Particle( const char *pszName, int iSize, void *pbuf );
// from m_screenInfo
byte charWidths[256];
int iCharHeight;
qword m_iWeaponBits;
int m_fPlayerDead;
int m_iIntermission;
RainData Rain; // buz rain
// fog stuff
Vector m_FogColor;
float m_fStartDist;
float m_fEndDist;
int m_iFinalEndDist;
float m_fFadeDuration;
// sprite indexes
int m_HUD_number_0;
// screen shake handler
ScreenShake m_Shake;
// error sprite
int m_HUD_error;
HSPRITE m_hHudError;
void AddHudElem( CHudBase *p );
float GetSensitivity() { return m_flMouseSensitivity; }
};
extern CHud gHUD;

1189
client/hud/hud_ammo.cpp Normal file

File diff suppressed because it is too large Load Diff

59
client/hud/hud_ammo.h Normal file
View File

@ -0,0 +1,59 @@
/***
*
* 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.
*
****/
#ifndef __AMMO_H__
#define __AMMO_H__
#define MAX_WEAPON_NAME 128
#define MAX_WEAPON_SLOTS 5 // hud item selection slots
#define WEAPON_FLAGS_SELECTONEMPTY 1
#define WEAPON_IS_ONTARGET 0x40
struct WEAPON
{
char szName[MAX_WEAPON_NAME];
int iAmmoType;
int iAmmo2Type;
int iMax1;
int iMax2;
int iSlot;
int iSlotPos;
int iFlags;
int iId;
int iClip;
int iCount; // # of itesm in plist
HSPRITE hActive;
wrect_t rcActive;
HSPRITE hInactive;
wrect_t rcInactive;
HSPRITE hAmmo;
wrect_t rcAmmo;
HSPRITE hAmmo2;
wrect_t rcAmmo2;
HSPRITE hCrosshair;
wrect_t rcCrosshair;
HSPRITE hAutoaim;
wrect_t rcAutoaim;
HSPRITE hZoomedCrosshair;
wrect_t rcZoomedCrosshair;
HSPRITE hZoomedAutoaim;
wrect_t rcZoomedAutoaim;
};
typedef int AMMO;
#endif

View File

@ -0,0 +1,186 @@
/***
*
* 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.
*
****/
//
// ammohistory.cpp
//
#include "extdll.h"
#include "hud_iface.h"
#include "hud.h"
#include "hud_ammohistory.h"
HistoryResource gHR;
#define AMMO_PICKUP_GAP (gHR.iHistoryGap+5)
#define AMMO_PICKUP_PICK_HEIGHT (32 + (gHR.iHistoryGap * 2))
#define AMMO_PICKUP_HEIGHT_MAX (SCREEN_HEIGHT - 100)
#define MAX_ITEM_NAME 32
int HISTORY_DRAW_TIME = 5;
// keep a list of items
struct ITEM_INFO
{
char szName[MAX_ITEM_NAME];
HSPRITE spr;
wrect_t rect;
};
void HistoryResource :: AddToHistory( int iType, int iId, int iCount )
{
if ( iType == HISTSLOT_AMMO && !iCount )
return; // no amount, so don't add
if ( (((AMMO_PICKUP_GAP * iCurrentHistorySlot) + AMMO_PICKUP_PICK_HEIGHT) > AMMO_PICKUP_HEIGHT_MAX) || (iCurrentHistorySlot >= MAX_HISTORY) )
{ // the pic would have to be drawn too high
// so start from the bottom
iCurrentHistorySlot = 0;
}
HIST_ITEM *freeslot = &rgAmmoHistory[iCurrentHistorySlot++]; // default to just writing to the first slot
HISTORY_DRAW_TIME = CVAR_GET_FLOAT( "hud_drawhistory_time" );
freeslot->type = iType;
freeslot->iId = iId;
freeslot->iCount = iCount;
freeslot->DisplayTime = gHUD.m_flTime + HISTORY_DRAW_TIME;
}
void HistoryResource :: AddToHistory( int iType, const char *szName, int iCount )
{
if ( iType != HISTSLOT_ITEM )
return;
if ( (((AMMO_PICKUP_GAP * iCurrentHistorySlot) + AMMO_PICKUP_PICK_HEIGHT) > AMMO_PICKUP_HEIGHT_MAX) || (iCurrentHistorySlot >= MAX_HISTORY) )
{ // the pic would have to be drawn too high
// so start from the bottom
iCurrentHistorySlot = 0;
}
HIST_ITEM *freeslot = &rgAmmoHistory[iCurrentHistorySlot++]; // default to just writing to the first slot
// I am really unhappy with all the code in this file
int i = gHUD.GetSpriteIndex( szName );
if ( i == -1 )
return; // unknown sprite name, don't add it to history
freeslot->iId = i;
freeslot->type = iType;
freeslot->iCount = iCount;
HISTORY_DRAW_TIME = CVAR_GET_FLOAT( "hud_drawhistory_time" );
freeslot->DisplayTime = gHUD.m_flTime + HISTORY_DRAW_TIME;
}
void HistoryResource :: CheckClearHistory( void )
{
for ( int i = 0; i < MAX_HISTORY; i++ )
{
if ( rgAmmoHistory[i].type )
return;
}
iCurrentHistorySlot = 0;
}
//
// Draw Ammo pickup history
//
int HistoryResource :: DrawAmmoHistory( float flTime )
{
for ( int i = 0; i < MAX_HISTORY; i++ )
{
if ( rgAmmoHistory[i].type )
{
rgAmmoHistory[i].DisplayTime = min( rgAmmoHistory[i].DisplayTime, gHUD.m_flTime + HISTORY_DRAW_TIME );
if ( rgAmmoHistory[i].DisplayTime <= flTime )
{ // pic drawing time has expired
memset( &rgAmmoHistory[i], 0, sizeof(HIST_ITEM) );
CheckClearHistory();
}
else if ( rgAmmoHistory[i].type == HISTSLOT_AMMO )
{
wrect_t rcPic;
HSPRITE *spr = gWR.GetAmmoPicFromWeapon( rgAmmoHistory[i].iId, rcPic );
int r, g, b;
UnpackRGB(r,g,b, gHUD.m_iHUDColor);
float scale = (rgAmmoHistory[i].DisplayTime - flTime) * 80;
ScaleColors(r, g, b, min(scale, 255) );
// Draw the pic
int ypos = SCREEN_HEIGHT - (AMMO_PICKUP_PICK_HEIGHT + (AMMO_PICKUP_GAP * i));
int xpos = SCREEN_WIDTH - 24;
if ( spr && *spr ) // weapon isn't loaded yet so just don't draw the pic
{ // the dll has to make sure it has sent info the weapons you need
SPR_Set( *spr, r, g, b );
SPR_DrawAdditive( 0, xpos, ypos, &rcPic );
}
// Draw the number
gHUD.DrawHudNumberString( xpos - 10, ypos, xpos - 100, rgAmmoHistory[i].iCount, r, g, b );
}
else if ( rgAmmoHistory[i].type == HISTSLOT_WEAP )
{
WEAPON *weap = gWR.GetWeapon( rgAmmoHistory[i].iId );
if ( !weap )
return 1; // we don't know about the weapon yet, so don't draw anything
int r, g, b;
UnpackRGB(r,g,b, gHUD.m_iHUDColor);
if ( !gWR.HasAmmo( weap ) )
UnpackRGB(r,g,b, RGB_REDISH); // if the weapon doesn't have ammo, display it as red
float scale = (rgAmmoHistory[i].DisplayTime - flTime) * 80;
ScaleColors(r, g, b, min(scale, 255) );
int ypos = SCREEN_HEIGHT - (AMMO_PICKUP_PICK_HEIGHT + (AMMO_PICKUP_GAP * i));
int xpos = SCREEN_WIDTH - (weap->rcInactive.right - weap->rcInactive.left);
SPR_Set( weap->hInactive, r, g, b );
SPR_DrawAdditive( 0, xpos, ypos, &weap->rcInactive );
}
else if ( rgAmmoHistory[i].type == HISTSLOT_ITEM )
{
int r, g, b;
if ( !rgAmmoHistory[i].iId )
continue; // sprite not loaded
wrect_t rect = gHUD.GetSpriteRect( rgAmmoHistory[i].iId );
UnpackRGB(r,g,b, gHUD.m_iHUDColor);
float scale = (rgAmmoHistory[i].DisplayTime - flTime) * 80;
ScaleColors(r, g, b, min(scale, 255) );
int ypos = SCREEN_HEIGHT - (AMMO_PICKUP_PICK_HEIGHT + (AMMO_PICKUP_GAP * i));
int xpos = SCREEN_WIDTH - (rect.right - rect.left) - 10;
SPR_Set( gHUD.GetSprite( rgAmmoHistory[i].iId ), r, g, b );
SPR_DrawAdditive( 0, xpos, ypos, &rect );
}
}
}
return 1;
}

View File

@ -0,0 +1,134 @@
/***
*
* 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.
*
****/
//
// ammohistory.h
//
// this is the max number of items in each bucket
#define MAX_WEAPON_POSITIONS MAX_WEAPON_SLOTS
class WeaponsResource
{
private:
// Information about weapons & ammo
WEAPON rgWeapons[MAX_WEAPONS]; // Weapons Array
// counts of weapons * ammo
// The slots currently in use by weapons. The value is a pointer to the weapon;
// if it's NULL, no weapon is there
WEAPON* rgSlots[MAX_WEAPON_SLOTS+1][MAX_WEAPON_POSITIONS+1];
int riAmmo[MAX_AMMO_SLOTS]; // count of each ammo type
public:
void Init( void )
{
memset( rgWeapons, 0, sizeof rgWeapons );
Reset();
}
void Reset( void )
{
iOldWeaponBits = 0;
memset( rgSlots, 0, sizeof rgSlots );
memset( riAmmo, 0, sizeof riAmmo );
}
///// WEAPON /////
int iOldWeaponBits;
WEAPON *GetWeapon( int iId ) { return &rgWeapons[iId]; }
void AddWeapon( WEAPON *wp )
{
rgWeapons[ wp->iId ] = *wp;
LoadWeaponSprites( &rgWeapons[ wp->iId ] );
}
void PickupWeapon( WEAPON *wp )
{
rgSlots[ wp->iSlot ][ wp->iSlotPos ] = wp;
}
void DropWeapon( WEAPON *wp )
{
rgSlots[ wp->iSlot ][ wp->iSlotPos ] = NULL;
}
void DropAllWeapons( void )
{
for ( int i = 0; i < MAX_WEAPONS; i++ )
{
if ( rgWeapons[i].iId )
DropWeapon( &rgWeapons[i] );
}
}
WEAPON* GetWeaponSlot( int slot, int pos ) { return rgSlots[slot][pos]; }
void LoadWeaponSprite( WEAPON *p, const char *type );
void LoadWeaponSprites( WEAPON* wp );
void LoadAllWeaponSprites( void );
WEAPON* GetFirstPos( int iSlot );
void SelectSlot( int iSlot, int fAdvance, int iDirection );
WEAPON* GetNextActivePos( int iSlot, int iSlotPos );
int HasAmmo( WEAPON *p );
///// AMMO /////
AMMO GetAmmo( int iId ) { return iId; }
void SetAmmo( int iId, int iCount ) { riAmmo[ iId ] = iCount; }
int CountAmmo( int iId );
HSPRITE* GetAmmoPicFromWeapon( int iAmmoId, wrect_t& rect );
};
#define MAX_HISTORY 12
enum
{
HISTSLOT_EMPTY,
HISTSLOT_AMMO,
HISTSLOT_WEAP,
HISTSLOT_ITEM,
};
class HistoryResource
{
private:
struct HIST_ITEM
{
int type;
float DisplayTime; // the time at which this item should be removed from the history
int iCount;
int iId;
};
HIST_ITEM rgAmmoHistory[MAX_HISTORY];
public:
void Init( void ) { Reset(); }
void Reset( void ) { memset( rgAmmoHistory, 0, sizeof rgAmmoHistory ); }
int iHistoryGap;
int iCurrentHistorySlot;
void AddToHistory( int iType, int iId, int iCount = 0 );
void AddToHistory( int iType, const char *szName, int iCount = 0 );
void CheckClearHistory( void );
int DrawAmmoHistory( float flTime );
};
extern WeaponsResource gWR;
extern HistoryResource gHR;

135
client/hud/hud_battery.cpp Normal file
View File

@ -0,0 +1,135 @@
/***
*
* 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.
*
****/
//
// battery.cpp
//
// implementation of CHudBattery class
//
#include "extdll.h"
#include "hud_iface.h"
#include "hud.h"
DECLARE_MESSAGE( m_Battery, Battery )
int CHudBattery :: Init( void )
{
m_iBat = 0;
m_fFade = 0;
m_iFlags = 0;
HOOK_MESSAGE( Battery );
gHUD.AddHudElem( this );
return 1;
}
int CHudBattery :: VidInit( void )
{
int HUD_suit_empty = gHUD.GetSpriteIndex( "suit_empty" );
int HUD_suit_full = gHUD.GetSpriteIndex( "suit_full" );
m_hSprite1 = m_hSprite2 = 0; // delaying get sprite handles until we know the sprites are loaded
m_prc1 = &gHUD.GetSpriteRect( HUD_suit_empty );
m_prc2 = &gHUD.GetSpriteRect( HUD_suit_full );
m_iHeight = m_prc2->bottom - m_prc1->top;
m_fFade = 0;
return 1;
};
int CHudBattery:: MsgFunc_Battery( const char *pszName, int iSize, void *pbuf )
{
m_iFlags |= HUD_ACTIVE;
BEGIN_READ( pszName, iSize, pbuf );
int x = READ_SHORT();
if( x != m_iBat )
{
m_fFade = FADE_TIME;
m_iBat = x;
}
END_READ();
return 1;
}
int CHudBattery :: Draw( float flTime )
{
if( gHUD.m_iHideHUDDisplay & HIDEHUD_HEALTH )
return 1;
int r, g, b, x, y, a;
wrect_t rc;
rc = *m_prc2;
// battery can go from 0 to 100 so * 0.01 goes from 0 to 1
rc.top += m_iHeight * ((float)(100-(min(100,m_iBat))) * 0.01);
UnpackRGB( r, g, b, gHUD.m_iHUDColor );
if(!( gHUD.m_iHideHUDDisplay & ITEM_SUIT ))
return 1;
// Has health changed? Flash the health #
if( m_fFade )
{
if( m_fFade > FADE_TIME )
m_fFade = FADE_TIME;
m_fFade -= (gHUD.m_flTimeDelta * 20);
if( m_fFade <= 0 )
{
a = 128;
m_fFade = 0;
}
// Fade the health number back to dim
a = MIN_ALPHA + (m_fFade/FADE_TIME) * 128;
}
else a = MIN_ALPHA;
ScaleColors( r, g, b, a );
int iOffset = (m_prc1->bottom - m_prc1->top) / 6;
y = SCREEN_HEIGHT - gHUD.m_iFontHeight - gHUD.m_iFontHeight / 2;
x = SCREEN_WIDTH / 5;
// make sure we have the right sprite handles
if( !m_hSprite1 )
m_hSprite1 = gHUD.GetSprite( gHUD.GetSpriteIndex( "suit_empty" ));
if( !m_hSprite2 )
m_hSprite2 = gHUD.GetSprite( gHUD.GetSpriteIndex( "suit_full" ));
SPR_Set( m_hSprite1, r, g, b );
SPR_DrawAdditive( 0, x, y - iOffset, m_prc1 );
if( rc.bottom > rc.top )
{
SPR_Set( m_hSprite2, r, g, b );
SPR_DrawAdditive( 0, x, y - iOffset + (rc.top - m_prc2->top), &rc );
}
x += (m_prc1->right - m_prc1->left);
x = gHUD.DrawHudNumber( x, y, DHN_3DIGITS|DHN_DRAWZERO, m_iBat, r, g, b );
return 1;
}

232
client/hud/hud_death.cpp Normal file
View File

@ -0,0 +1,232 @@
/***
*
* 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.
*
****/
//
// death notice
//
#include "extdll.h"
#include "hud_iface.h"
#include "hud.h"
DECLARE_MESSAGE( m_DeathNotice, DeathMsg );
struct DeathNoticeItem
{
char szKiller[MAX_PLAYER_NAME_LENGTH*2];
char szVictim[MAX_PLAYER_NAME_LENGTH*2];
int iId; // the index number of the associated sprite
int iSuicide;
int iTeamKill;
int iNonPlayerKill;
float flDisplayTime;
};
#define MAX_DEATHNOTICES 4
static int DEATHNOTICE_DISPLAY_TIME = 6;
#define DEATHNOTICE_TOP 32
DeathNoticeItem rgDeathNoticeList[ MAX_DEATHNOTICES + 1 ];
int CHudDeathNotice :: Init( void )
{
gHUD.AddHudElem( this );
HOOK_MESSAGE( DeathMsg );
CVAR_REGISTER( "hud_deathnotice_time", "6", 0, "time to show deathnotice" );
return 1;
}
void CHudDeathNotice :: InitHUDData( void )
{
memset( rgDeathNoticeList, 0, sizeof( rgDeathNoticeList ));
}
int CHudDeathNotice :: VidInit( void )
{
m_HUD_d_skull = gHUD.GetSpriteIndex( "d_skull" );
return 1;
}
int CHudDeathNotice :: Draw( float flTime )
{
int x, y, r, g, b;
for( int i = 0; i < MAX_DEATHNOTICES; i++ )
{
if( rgDeathNoticeList[i].iId == 0 )
break; // we've gone through them all
if( rgDeathNoticeList[i].flDisplayTime < flTime )
{
// display time has expired
// remove the current item from the list
memmove( &rgDeathNoticeList[i], &rgDeathNoticeList[i+1], sizeof(DeathNoticeItem) * (MAX_DEATHNOTICES - i));
i--; // continue on the next item; stop the counter getting incremented
continue;
}
rgDeathNoticeList[i].flDisplayTime = min( rgDeathNoticeList[i].flDisplayTime, gHUD.m_flTime + DEATHNOTICE_DISPLAY_TIME );
// Draw the death notice
y = DEATHNOTICE_TOP + (20 * i); //!!!
int id = (rgDeathNoticeList[i].iId == -1) ? m_HUD_d_skull : rgDeathNoticeList[i].iId;
x = SCREEN_WIDTH - ConsoleStringLen(rgDeathNoticeList[i].szVictim) - (gHUD.GetSpriteRect(id).right - gHUD.GetSpriteRect(id).left);
if ( !rgDeathNoticeList[i].iSuicide )
{
x -= (5 + ConsoleStringLen( rgDeathNoticeList[i].szKiller ) );
// Draw killers name
x = 5 + DrawConsoleString( x, y, rgDeathNoticeList[i].szKiller );
}
r = 255; g = 80; b = 0;
if ( rgDeathNoticeList[i].iTeamKill )
{
r = 10; g = 240; b = 10; // display it in sickly green
}
// Draw death weapon
SPR_Set( gHUD.GetSprite(id), r, g, b );
SPR_DrawAdditive( 0, x, y, &gHUD.GetSpriteRect( id ));
x += (gHUD.GetSpriteRect( id ).right - gHUD.GetSpriteRect( id ).left );
// Draw victims name
x = DrawConsoleString( x, y, rgDeathNoticeList[i].szVictim );
}
return 1;
}
// This message handler may be better off elsewhere
int CHudDeathNotice :: MsgFunc_DeathMsg( const char *pszName, int iSize, void *pbuf )
{
m_iFlags |= HUD_ACTIVE;
BEGIN_READ( pszName, iSize, pbuf );
int killer = READ_BYTE();
int victim = READ_BYTE();
char killedwith[32];
strcpy( killedwith, "d_" );
strncat( killedwith, READ_STRING(), 32 );
gHUD.m_Scoreboard.DeathMsg( killer, victim );
for ( int i = 0; i < MAX_DEATHNOTICES; i++ )
{
if ( rgDeathNoticeList[i].iId == 0 )
break;
}
if ( i == MAX_DEATHNOTICES )
{
// move the rest of the list forward to make room for this item
memmove( rgDeathNoticeList, rgDeathNoticeList+1, sizeof( DeathNoticeItem ) * MAX_DEATHNOTICES );
i = MAX_DEATHNOTICES - 1;
}
gHUD.m_Scoreboard.GetAllPlayersInfo();
char *killer_name = gHUD.m_Scoreboard.m_PlayerInfoList[ killer ].name;
char *victim_name = gHUD.m_Scoreboard.m_PlayerInfoList[ victim ].name;
if( !killer_name ) killer_name = "";
if( !victim_name ) victim_name = "";
// Is it a non-player object kill?
if ( ((char)victim) == -1 )
{
rgDeathNoticeList[i].iNonPlayerKill = TRUE;
// Store the object's name in the Victim slot (skip the d_ bit)
strcpy( rgDeathNoticeList[i].szVictim, killedwith+2 );
}
else
{
if( killer == victim || killer == 0 )
rgDeathNoticeList[i].iSuicide = TRUE;
if( !strcmp( killedwith, "d_teammate" ) )
rgDeathNoticeList[i].iTeamKill = TRUE;
}
// Find the sprite in the list
int spr = gHUD.GetSpriteIndex( killedwith );
rgDeathNoticeList[i].iId = spr;
DEATHNOTICE_DISPLAY_TIME = CVAR_GET_FLOAT( "hud_deathnotice_time" );
rgDeathNoticeList[i].flDisplayTime = gHUD.m_flTime + DEATHNOTICE_DISPLAY_TIME;
if( rgDeathNoticeList[i].iNonPlayerKill )
{
ConsolePrint( rgDeathNoticeList[i].szKiller );
ConsolePrint( " killed a " );
ConsolePrint( rgDeathNoticeList[i].szVictim );
ConsolePrint( "\n" );
}
else
{
// record the death notice in the console
if ( rgDeathNoticeList[i].iSuicide )
{
ConsolePrint( rgDeathNoticeList[i].szVictim );
if ( !strcmp( killedwith, "d_world" ) )
{
ConsolePrint( " died" );
}
else
{
ConsolePrint( " killed self" );
}
}
else if ( rgDeathNoticeList[i].iTeamKill )
{
ConsolePrint( rgDeathNoticeList[i].szKiller );
ConsolePrint( " killed his teammate " );
ConsolePrint( rgDeathNoticeList[i].szVictim );
}
else
{
ConsolePrint( rgDeathNoticeList[i].szKiller );
ConsolePrint( " killed " );
ConsolePrint( rgDeathNoticeList[i].szVictim );
}
if ( killedwith && *killedwith && (*killedwith > 13 ) && strcmp( killedwith, "d_world" ) && !rgDeathNoticeList[i].iTeamKill )
{
ConsolePrint( " with " );
// replace the code names with the 'real' names
if( !strcmp( killedwith+2, "egon" ) )
strcpy( killedwith, "d_gluon gun" );
if( !strcmp( killedwith+2, "gauss" ) )
strcpy( killedwith, "d_tau cannon" );
ConsolePrint( killedwith+2 ); // skip over the "d_" part
}
ConsolePrint( "\n" );
}
return 1;
}

View File

@ -0,0 +1,140 @@
/***
*
* 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.
*
****/
//
// flashlight.cpp
//
// implementation of CHudFlashlight class
//
#include "extdll.h"
#include "hud_iface.h"
#include "hud.h"
DECLARE_MESSAGE( m_Flash, FlashBat )
DECLARE_MESSAGE( m_Flash, Flashlight )
int CHudFlashlight :: Init( void )
{
m_fFade = 0;
m_fOn = 0;
HOOK_MESSAGE( Flashlight );
HOOK_MESSAGE( FlashBat );
m_iFlags |= HUD_ACTIVE;
gHUD.AddHudElem( this );
return 1;
}
void CHudFlashlight :: Reset( void )
{
m_fFade = 0;
m_fOn = 0;
}
int CHudFlashlight :: VidInit( void )
{
int HUD_flash_empty = gHUD.GetSpriteIndex( "flash_empty" );
int HUD_flash_full = gHUD.GetSpriteIndex( "flash_full" );
int HUD_flash_beam = gHUD.GetSpriteIndex( "flash_beam" );
m_hSprite1 = gHUD.GetSprite( HUD_flash_empty );
m_hSprite2 = gHUD.GetSprite( HUD_flash_full );
m_hBeam = gHUD.GetSprite( HUD_flash_beam );
m_prc1 = &gHUD.GetSpriteRect( HUD_flash_empty );
m_prc2 = &gHUD.GetSpriteRect( HUD_flash_full );
m_prcBeam = &gHUD.GetSpriteRect( HUD_flash_beam );
m_iWidth = m_prc2->right - m_prc2->left;
return 1;
}
int CHudFlashlight:: MsgFunc_FlashBat(const char *pszName, int iSize, void *pbuf )
{
BEGIN_READ( pszName, iSize, pbuf );
int x = READ_BYTE();
m_iBat = x;
m_flBat = ((float)x) / 100.0;
END_READ();
return 1;
}
int CHudFlashlight :: MsgFunc_Flashlight( const char *pszName, int iSize, void *pbuf )
{
BEGIN_READ( pszName, iSize, pbuf );
m_fOn = READ_BYTE();
int x = READ_BYTE();
m_iBat = x;
m_flBat = ((float)x) / 100.0;
END_READ();
return 1;
}
int CHudFlashlight :: Draw( float flTime )
{
if( gHUD.m_iHideHUDDisplay & (HIDEHUD_FLASHLIGHT|HIDEHUD_ALL) )
return 1;
int r, g, b, x, y, a;
wrect_t rc;
if(!( gHUD.m_iHideHUDDisplay & ITEM_SUIT ))
return 1;
if( m_fOn )
a = 225;
else a = MIN_ALPHA;
if( m_flBat < 0.20 )
UnpackRGB( r, g, b, RGB_REDISH );
else UnpackRGB( r, g, b, gHUD.m_iHUDColor );
ScaleColors( r, g, b, a );
y = (m_prc1->bottom - m_prc2->top) / 2;
x = SCREEN_WIDTH - m_iWidth - m_iWidth / 2;
// draw the flashlight casing
SPR_Set( m_hSprite1, r, g, b );
SPR_DrawAdditive( 0, x, y, m_prc1 );
if( m_fOn )
{
// draw the flashlight beam
x = SCREEN_WIDTH - m_iWidth / 2;
SPR_Set( m_hBeam, r, g, b );
SPR_DrawAdditive( 0, x, y, m_prcBeam );
}
// draw the flashlight energy level
x = SCREEN_WIDTH - m_iWidth - m_iWidth / 2;
int iOffset = m_iWidth * (1.0 - m_flBat);
if( iOffset < m_iWidth )
{
rc = *m_prc2;
rc.left += iOffset;
SPR_Set( m_hSprite2, r, g, b );
SPR_DrawAdditive( 0, x + iOffset, y, &rc );
}
return 1;
}

177
client/hud/hud_geiger.cpp Normal file
View File

@ -0,0 +1,177 @@
/***
*
* 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.
*
****/
//
// Geiger.cpp
//
// implementation of CHudAmmo class
//
#include "extdll.h"
#include "hud_iface.h"
#include "hud.h"
DECLARE_MESSAGE( m_Geiger, Geiger )
int CHudGeiger::Init( void )
{
HOOK_MESSAGE( Geiger );
m_iGeigerRange = 0;
m_iFlags = 0;
gHUD.AddHudElem( this );
return 1;
}
int CHudGeiger::VidInit( void )
{
return 1;
}
int CHudGeiger::MsgFunc_Geiger( const char *pszName, int iSize, void *pbuf )
{
BEGIN_READ( pszName, iSize, pbuf );
// update geiger data
m_iGeigerRange = READ_BYTE();
m_iGeigerRange = m_iGeigerRange << 2;
m_iFlags |= HUD_ACTIVE;
END_READ();
return 1;
}
int CHudGeiger::Draw( float flTime )
{
int pct;
float flvol;
int rg[3];
int i;
if( m_iGeigerRange < 1000 && m_iGeigerRange > 0 )
{
// peicewise linear is better than continuous formula for this
if( m_iGeigerRange > 800 )
{
pct = 0; //ALERT( at_console, "range > 800\n" );
}
else if( m_iGeigerRange > 600 )
{
pct = 2;
flvol = 0.4; //ALERT( at_console, "range > 600\n" );
rg[0] = 1;
rg[1] = 1;
i = 2;
}
else if( m_iGeigerRange > 500 )
{
pct = 4;
flvol = 0.5; //ALERT( at_console, "range > 500\n" );
rg[0] = 1;
rg[1] = 2;
i = 2;
}
else if( m_iGeigerRange > 400 )
{
pct = 8;
flvol = 0.6; //ALERT( at_console, "range > 400\n" );
rg[0] = 1;
rg[1] = 2;
rg[2] = 3;
i = 3;
}
else if( m_iGeigerRange > 300 )
{
pct = 8;
flvol = 0.7; //ALERT( at_console, "range > 300\n" );
rg[0] = 2;
rg[1] = 3;
rg[2] = 4;
i = 3;
}
else if( m_iGeigerRange > 200 )
{
pct = 28;
flvol = 0.78; //ALERT( at_console, "range > 200\n" );
rg[0] = 2;
rg[1] = 3;
rg[2] = 4;
i = 3;
}
else if( m_iGeigerRange > 150 )
{
pct = 40;
flvol = 0.80; //ALERT( at_console, "range > 150\n" );
rg[0] = 3;
rg[1] = 4;
rg[2] = 5;
i = 3;
}
else if( m_iGeigerRange > 100 )
{
pct = 60;
flvol = 0.85; //ALERT( at_console, "range > 100\n" );
rg[0] = 3;
rg[1] = 4;
rg[2] = 5;
i = 3;
}
else if( m_iGeigerRange > 75 )
{
pct = 80;
flvol = 0.9;
rg[0] = 4;
rg[1] = 5;
rg[2] = 6;
i = 3;
}
else if( m_iGeigerRange > 50 )
{
pct = 90;
flvol = 0.95;
rg[0] = 5;
rg[1] = 6;
i = 2;
}
else
{
pct = 95;
flvol = 1.0;
rg[0] = 5;
rg[1] = 6;
i = 2;
}
flvol = flvol * RANDOM_FLOAT( 0.25, 0.5 );
if( RANDOM_LONG( 0, 128 ) < pct )
{
char sz[256];
int j = RANDOM_LONG( 0, 2 );
if( i > 2 )
j += RANDOM_LONG( 0, 2 );
// player/geiger6.wav isn't used ?
sprintf( sz, "player/geiger%d.wav", j + 1 );
CL_PlaySound( sz, flvol );
}
}
return 1;
}

448
client/hud/hud_health.cpp Normal file
View File

@ -0,0 +1,448 @@
/***
*
* 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.
*
****/
//
// Health.cpp
//
// implementation of CHudHealth class
//
#include "extdll.h"
#include "hud_iface.h"
#include "hud.h"
DECLARE_MESSAGE( m_Health, Health )
DECLARE_MESSAGE( m_Health, Damage )
#define PAIN_NAME "hud_pain"
#define DAMAGE_NAME "sprites/%d_dmg.spr"
int giDmgHeight, giDmgWidth;
int giDmgFlags[NUM_DMG_TYPES] =
{
DMG_POISON,
DMG_ACID,
DMG_FREEZE|DMG_SLOWFREEZE,
DMG_DROWN,
DMG_BURN|DMG_SLOWBURN,
DMG_NERVEGAS,
DMG_RADIATION,
DMG_SHOCK,
DMG_NUCLEAR
};
int CHudHealth :: Init( void )
{
HOOK_MESSAGE( Health );
HOOK_MESSAGE( Damage );
m_iHealth = 100;
m_fFade = 0;
m_iFlags = 0;
m_bitsDamage = 0;
m_fAttackFront = m_fAttackRear = m_fAttackRight = m_fAttackLeft = 0;
giDmgHeight = 0;
giDmgWidth = 0;
memset( m_dmg, 0, sizeof(DAMAGE_IMAGE) * NUM_DMG_TYPES );
gHUD.AddHudElem( this );
return 1;
}
void CHudHealth :: Reset( void )
{
// make sure the pain compass is cleared when the player respawns
m_fAttackFront = m_fAttackRear = m_fAttackRight = m_fAttackLeft = 0;
// force all the flashing damage icons to expire
m_bitsDamage = 0;
for( int i = 0; i < NUM_DMG_TYPES; i++ )
{
m_dmg[i].fExpire = 0;
}
}
int CHudHealth :: VidInit( void )
{
m_hSprite = 0;
m_HUD_dmg_bio = gHUD.GetSpriteIndex( "dmg_bio" ) + 1;
m_HUD_cross = gHUD.GetSpriteIndex( "cross" );
giDmgHeight = gHUD.GetSpriteRect(m_HUD_dmg_bio).right - gHUD.GetSpriteRect(m_HUD_dmg_bio).left;
giDmgWidth = gHUD.GetSpriteRect(m_HUD_dmg_bio).bottom - gHUD.GetSpriteRect(m_HUD_dmg_bio).top;
return 1;
}
int CHudHealth :: MsgFunc_Health( const char *pszName, int iSize, void *pbuf )
{
// TODO: update local health data
BEGIN_READ( pszName, iSize, pbuf );
int x = READ_BYTE();
m_iFlags |= HUD_ACTIVE;
// only update the fade if we've changed health
if( x != m_iHealth )
{
m_fFade = FADE_TIME;
m_iHealth = x;
}
END_READ();
return 1;
}
int CHudHealth :: MsgFunc_Damage( const char *pszName, int iSize, void *pbuf )
{
BEGIN_READ( pszName, iSize, pbuf );
int armor = READ_BYTE(); // armor
int damageTaken = READ_BYTE(); // health
long bitsDamage = READ_LONG(); // damage bits
vec3_t vecFrom;
for( int i = 0 ; i < 3 ; i++)
vecFrom[i] = READ_COORD();
UpdateTiles( gHUD.m_flTime, bitsDamage );
// actually took damage?
if( damageTaken > 0 || armor > 0 )
CalcDamageDirection(vecFrom);
END_READ();
return 1;
}
// Returns back a color from the
// Green <-> Yellow <-> Red ramp
void CHudHealth :: GetPainColor( int &r, int &g, int &b )
{
int iHealth = m_iHealth;
if( iHealth > 25 )
iHealth -= 25;
else if( iHealth < 0 )
iHealth = 0;
if( m_iHealth > 25 )
{
UnpackRGB( r, g, b, gHUD.m_iHUDColor );
}
else
{
r = 250;
g = 0;
b = 0;
}
}
int CHudHealth :: Draw( float flTime )
{
int r, g, b;
int a = 0, x, y;
int HealthWidth;
if((gHUD.m_iHideHUDDisplay & HIDEHUD_HEALTH) || IsSpectateOnly())
return 1;
if( !m_hSprite )
m_hSprite = LOAD_SHADER( PAIN_NAME );
// has health changed? Flash the health #
if( m_fFade )
{
m_fFade -= (gHUD.m_flTimeDelta * 20);
if( m_fFade <= 0 )
{
a = MIN_ALPHA;
m_fFade = 0;
}
// fade the health number back to dim
a = MIN_ALPHA + (m_fFade/FADE_TIME) * 128;
}
else a = MIN_ALPHA;
// if health is getting low, make it bright red
if( m_iHealth <= 15 ) a = 255;
GetPainColor( r, g, b );
ScaleColors( r, g, b, a );
// only draw health if we have the suit.
if( gHUD.m_iHideHUDDisplay & ITEM_SUIT )
{
HealthWidth = gHUD.GetSpriteRect(gHUD.m_HUD_number_0).right - gHUD.GetSpriteRect(gHUD.m_HUD_number_0).left;
int CrossWidth = gHUD.GetSpriteRect(m_HUD_cross).right - gHUD.GetSpriteRect(m_HUD_cross).left;
y = SCREEN_HEIGHT - gHUD.m_iFontHeight - gHUD.m_iFontHeight / 2;
x = CrossWidth / 2;
SPR_Set( gHUD.GetSprite( m_HUD_cross ), r, g, b );
SPR_DrawAdditive( 0, x, y, &gHUD.GetSpriteRect( m_HUD_cross ));
x = CrossWidth + HealthWidth / 2;
x = gHUD.DrawHudNumber( x, y, DHN_3DIGITS | DHN_DRAWZERO, m_iHealth, r, g, b );
x += HealthWidth / 2;
int iHeight = gHUD.m_iFontHeight;
int iWidth = HealthWidth / 10;
UnpackRGB( r, g, b, gHUD.m_iHUDColor );
FillRGBA( x, y, iWidth, iHeight, r, g, b, a );
}
DrawDamage( flTime );
return DrawPain( flTime );
}
void CHudHealth :: CalcDamageDirection( Vector vecFrom )
{
Vector forward, right, up;
float side, front;
Vector vecOrigin, vecAngles;
if( vecFrom == Vector( 0, 0, 0 ))
{
m_fAttackFront = m_fAttackRear = m_fAttackRight = m_fAttackLeft = 0;
return;
}
memcpy( vecOrigin, gHUD.m_vecOrigin, sizeof( vec3_t ));
memcpy( vecAngles, gHUD.m_vecAngles, sizeof( vec3_t ));
vecFrom -= vecOrigin;
float flDistToTarget = vecFrom.Length();
vecFrom = vecFrom.Normalize();
AngleVectors( vecAngles, forward, right, up );
front = vecFrom.Dot( right );
side = vecFrom.Dot( forward );
if( flDistToTarget <= 50 )
{
m_fAttackFront = m_fAttackRear = m_fAttackRight = m_fAttackLeft = 1;
}
else
{
if( side > 0 )
{
if( side > 0.3 )
m_fAttackFront = max( m_fAttackFront, side );
}
else
{
float f = fabs( side );
if( f > 0.3 )
m_fAttackRear = max( m_fAttackRear, f );
}
if( front > 0 )
{
if( front > 0.3 )
m_fAttackRight = max( m_fAttackRight, front );
}
else
{
float f = fabs( front );
if( f > 0.3 )
m_fAttackLeft = max( m_fAttackLeft, f );
}
}
}
int CHudHealth :: DrawPain( float flTime )
{
if(!( m_fAttackFront || m_fAttackRear || m_fAttackLeft || m_fAttackRight ))
return 1;
int r, g, b;
int x, y, a, shade;
// TODO: get the shift value of the health
a = 255; // max brightness until then
float fFade = gHUD.m_flTimeDelta * 2;
// SPR_Draw top
if( m_fAttackFront > 0.4 )
{
GetPainColor( r, g, b );
shade = a * max( m_fAttackFront, 0.5 );
ScaleColors( r, g, b, shade );
SPR_Set( m_hSprite, r, g, b );
x = SCREEN_WIDTH / 2 - SPR_Width( m_hSprite, 0 ) / 2;
y = SCREEN_HEIGHT / 2 - SPR_Height( m_hSprite, 0 ) * 3;
SPR_DrawAdditive( 0, x, y, NULL );
m_fAttackFront = max( 0, m_fAttackFront - fFade );
}
else m_fAttackFront = 0;
if( m_fAttackRight > 0.4 )
{
GetPainColor( r, g, b );
shade = a * max( m_fAttackRight, 0.5 );
ScaleColors( r, g, b, shade );
SPR_Set( m_hSprite, r, g, b );
x = SCREEN_WIDTH / 2 + SPR_Width( m_hSprite, 1 ) * 2;
y = SCREEN_HEIGHT / 2 - SPR_Height( m_hSprite,1 ) / 2;
SPR_DrawAdditive( 1, x, y, NULL );
m_fAttackRight = max( 0, m_fAttackRight - fFade );
}
else m_fAttackRight = 0;
if( m_fAttackRear > 0.4 )
{
GetPainColor( r, g, b );
shade = a * max( m_fAttackRear, 0.5 );
ScaleColors(r, g, b, shade);
SPR_Set(m_hSprite, r, g, b );
x = SCREEN_WIDTH / 2 - SPR_Width( m_hSprite, 2 ) / 2;
y = SCREEN_HEIGHT / 2 + SPR_Height( m_hSprite, 2 ) * 2;
SPR_DrawAdditive( 2, x, y, NULL);
m_fAttackRear = max( 0, m_fAttackRear - fFade );
}
else m_fAttackRear = 0;
if( m_fAttackLeft > 0.4 )
{
GetPainColor( r, g, b );
shade = a * max( m_fAttackLeft, 0.5 );
ScaleColors( r, g, b, shade );
SPR_Set( m_hSprite, r, g, b );
x = SCREEN_WIDTH / 2 - SPR_Width( m_hSprite, 3 ) * 3;
y = SCREEN_HEIGHT / 2 - SPR_Height( m_hSprite,3 ) / 2;
SPR_DrawAdditive( 3, x, y, NULL );
m_fAttackLeft = max( 0, m_fAttackLeft - fFade );
}
else m_fAttackLeft = 0;
return 1;
}
int CHudHealth :: DrawDamage( float flTime )
{
int r, g, b, a;
DAMAGE_IMAGE *pdmg;
if( !m_bitsDamage )
return 1;
UnpackRGB( r, g, b, gHUD.m_iHUDColor );
a = (int)(fabs(sin( flTime * 2)) * 256.0 );
ScaleColors( r, g, b, a );
// draw all the items
for( int i = 0; i < NUM_DMG_TYPES; i++ )
{
if( m_bitsDamage & giDmgFlags[i] )
{
pdmg = &m_dmg[i];
SPR_Set( gHUD.GetSprite( m_HUD_dmg_bio + i ), r, g, b );
SPR_DrawAdditive( 0, pdmg->x, pdmg->y, &gHUD.GetSpriteRect( m_HUD_dmg_bio + i ));
}
}
// check for bits that should be expired
for( i = 0; i < NUM_DMG_TYPES; i++ )
{
DAMAGE_IMAGE *pdmg = &m_dmg[i];
if( m_bitsDamage & giDmgFlags[i] )
{
pdmg->fExpire = min( flTime + DMG_IMAGE_LIFE, pdmg->fExpire );
// when the time has expired and the flash is at the low point of the cycle
if( pdmg->fExpire <= flTime && a < 40 )
{
pdmg->fExpire = 0;
int y = pdmg->y;
pdmg->x = pdmg->y = 0;
// move everyone above down
for( int j = 0; j < NUM_DMG_TYPES; j++ )
{
pdmg = &m_dmg[j];
if(( pdmg->y ) && ( pdmg->y < y ))
pdmg->y += giDmgHeight;
}
m_bitsDamage &= ~giDmgFlags[i]; // clear the bits
}
}
}
return 1;
}
void CHudHealth :: UpdateTiles( float flTime, long bitsDamage )
{
DAMAGE_IMAGE *pdmg;
// Which types are new?
long bitsOn = ~m_bitsDamage & bitsDamage;
for( int i = 0; i < NUM_DMG_TYPES; i++ )
{
pdmg = &m_dmg[i];
// is this one already on?
if( m_bitsDamage & giDmgFlags[i] )
{
pdmg->fExpire = flTime + DMG_IMAGE_LIFE; // extend the duration
if( !pdmg->fBaseline )
pdmg->fBaseline = flTime;
}
// are we just turning it on?
if( bitsOn & giDmgFlags[i] )
{
// put this one at the bottom
pdmg->x = giDmgWidth / 8;
pdmg->y = SCREEN_HEIGHT - giDmgHeight * 2;
pdmg->fExpire=flTime + DMG_IMAGE_LIFE;
// move everyone else up
for( int j = 0; j < NUM_DMG_TYPES; j++ )
{
if( j == i ) continue;
pdmg = &m_dmg[j];
if( pdmg->y )
pdmg->y -= giDmgHeight;
}
pdmg = &m_dmg[i];
}
}
// damage bits are only turned on here; they are turned off when the draw time has expired (in DrawDamage())
m_bitsDamage |= bitsDamage;
}

106
client/hud/hud_health.h Normal file
View File

@ -0,0 +1,106 @@
/***
*
* 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.
*
****/
#define DMG_IMAGE_LIFE 2 // seconds that image is up
#define DMG_IMAGE_POISON 0
#define DMG_IMAGE_ACID 1
#define DMG_IMAGE_COLD 2
#define DMG_IMAGE_DROWN 3
#define DMG_IMAGE_BURN 4
#define DMG_IMAGE_NERVE 5
#define DMG_IMAGE_RAD 6
#define DMG_IMAGE_SHOCK 7
//tf defines
#define DMG_IMAGE_CALTROP 8
#define DMG_IMAGE_TRANQ 9
#define DMG_IMAGE_CONCUSS 10
#define DMG_IMAGE_HALLUC 11
#define NUM_DMG_TYPES 12
// instant damage
#define DMG_GENERIC 0 // generic damage was done
#define DMG_CRUSH (1 << 0) // crushed by falling or moving object
#define DMG_BULLET (1 << 1) // shot
#define DMG_SLASH (1 << 2) // cut, clawed, stabbed
#define DMG_BURN (1 << 3) // heat burned
#define DMG_FREEZE (1 << 4) // frozen
#define DMG_FALL (1 << 5) // fell too far
#define DMG_BLAST (1 << 6) // explosive blast damage
#define DMG_CLUB (1 << 7) // crowbar, punch, headbutt
#define DMG_SHOCK (1 << 8) // electric shock
#define DMG_SONIC (1 << 9) // sound pulse shockwave
#define DMG_ENERGYBEAM (1 << 10) // laser or other high energy beam
#define DMG_NEVERGIB (1 << 12) // with this bit OR'd in, no damage type will be able to gib victims upon death
#define DMG_ALWAYSGIB (1 << 13) // with this bit OR'd in, any damage type can be made to gib victims upon death.
// time-based damage
//mask off TF-specific stuff too
#define DMG_TIMEBASED (~(0xff003fff)) // mask for time-based damage
#define DMG_DROWN (1 << 14) // Drowning
#define DMG_FIRSTTIMEBASED DMG_DROWN
#define DMG_PARALYZE (1 << 15) // slows affected creature down
#define DMG_NERVEGAS (1 << 16) // nerve toxins, very bad
#define DMG_POISON (1 << 17) // blood poisioning
#define DMG_RADIATION (1 << 18) // radiation exposure
#define DMG_DROWNRECOVER (1 << 19) // drowning recovery
#define DMG_ACID (1 << 20) // toxic chemicals or acid burns
#define DMG_SLOWBURN (1 << 21) // in an oven
#define DMG_SLOWFREEZE (1 << 22) // in a subzero freezer
#define DMG_MORTAR (1 << 23) // Hit by air raid (done to distinguish grenade from mortar)
#define DMG_NUCLEAR (1 << 24) // Players hit by this begin to burn
typedef struct
{
float fExpire;
float fBaseline;
int x, y;
} DAMAGE_IMAGE;
//
//-----------------------------------------------------
//
class CHudHealth: public CHudBase
{
public:
virtual int Init( void );
virtual int VidInit( void );
virtual int Draw( float fTime );
virtual void Reset( void );
int MsgFunc_Health( const char *pszName, int iSize, void *pbuf );
int MsgFunc_Damage( const char *pszName, int iSize, void *pbuf );
int m_iHealth;
int m_HUD_dmg_bio;
int m_HUD_cross;
float m_fAttackFront, m_fAttackRear, m_fAttackLeft, m_fAttackRight;
void GetPainColor( int &r, int &g, int &b );
float m_fFade;
private:
HSPRITE m_hSprite;
HSPRITE m_hDamage;
DAMAGE_IMAGE m_dmg[NUM_DMG_TYPES];
int m_bitsDamage;
int DrawPain( float fTime );
int DrawDamage( float fTime );
void CalcDamageDirection( vec3_t vecFrom );
void UpdateTiles( float fTime, long bits );
};

150
client/hud/hud_icons.cpp Normal file
View File

@ -0,0 +1,150 @@
/***
*
* 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.
*
****/
//
// status_icons.cpp
//
#include "extdll.h"
#include "hud_iface.h"
#include "hud.h"
DECLARE_MESSAGE( m_StatusIcons, StatusIcon );
int CHudStatusIcons::Init( void )
{
HOOK_MESSAGE( StatusIcon );
gHUD.AddHudElem( this );
Reset();
return 1;
}
int CHudStatusIcons::VidInit( void )
{
return 1;
}
void CHudStatusIcons::Reset( void )
{
memset( m_IconList, 0, sizeof m_IconList );
m_iFlags &= ~HUD_ACTIVE;
}
// Draw status icons along the left-hand side of the screen
int CHudStatusIcons::Draw( float flTime )
{
// find starting position to draw from, along right-hand side of screen
int x = 5;
int y = SCREEN_HEIGHT / 2;
// loop through icon list, and draw any valid icons drawing up from the middle of screen
for( int i = 0; i < MAX_ICONSPRITES; i++ )
{
if ( m_IconList[i].spr )
{
y -= ( m_IconList[i].rc.bottom - m_IconList[i].rc.top ) + 5;
SPR_Set( m_IconList[i].spr, m_IconList[i].r, m_IconList[i].g, m_IconList[i].b );
SPR_DrawAdditive( 0, x, y, &m_IconList[i].rc );
}
}
return 1;
}
// Message handler for StatusIcon message
// accepts five values:
// byte : TRUE = ENABLE icon, FALSE = DISABLE icon
// string : the sprite name to display
// byte : red
// byte : green
// byte : blue
int CHudStatusIcons::MsgFunc_StatusIcon( const char *pszName, int iSize, void *pbuf )
{
BEGIN_READ( pszName, iSize, pbuf );
int ShouldEnable = READ_BYTE();
char *pszIconName = READ_STRING();
if ( ShouldEnable )
{
int r = READ_BYTE();
int g = READ_BYTE();
int b = READ_BYTE();
EnableIcon( pszIconName, r, g, b );
m_iFlags |= HUD_ACTIVE;
}
else
{
DisableIcon( pszIconName );
}
END_READ();
return 1;
}
// add the icon to the icon list, and set it's drawing color
void CHudStatusIcons::EnableIcon( char *pszIconName, byte red, byte green, byte blue )
{
// check to see if the sprite is in the current list
for( int i = 0; i < MAX_ICONSPRITES; i++ )
{
if ( !stricmp( m_IconList[i].szSpriteName, pszIconName ) )
break;
}
if( i == MAX_ICONSPRITES )
{
// icon not in list, so find an empty slot to add to
for( i = 0; i < MAX_ICONSPRITES; i++ )
{
if( !m_IconList[i].spr )
break;
}
}
// if we've run out of space in the list, overwrite the first icon
if( i == MAX_ICONSPRITES )
{
i = 0;
}
// Load the sprite and add it to the list
// the sprite must be listed in hud.txt
int spr_index = gHUD.GetSpriteIndex( pszIconName );
m_IconList[i].spr = gHUD.GetSprite( spr_index );
m_IconList[i].rc = gHUD.GetSpriteRect( spr_index );
m_IconList[i].r = red;
m_IconList[i].g = green;
m_IconList[i].b = blue;
strcpy( m_IconList[i].szSpriteName, pszIconName );
// HACKHACK: Play Timer sound when a grenade icon is played (in 0.8 seconds)
if( strstr( m_IconList[i].szSpriteName, "grenade" ))
{
CL_PlaySound( "weapons/timer.wav", 1.0 );
}
}
void CHudStatusIcons::DisableIcon( char *pszIconName )
{
// find the sprite is in the current list
for( int i = 0; i < MAX_ICONSPRITES; i++ )
{
if( !stricmp( m_IconList[i].szSpriteName, pszIconName ) )
{
// clear the item from the list
memset( &m_IconList[i], 0, sizeof icon_sprite_t );
return;
}
}
}

154
client/hud/hud_iface.h Normal file
View File

@ -0,0 +1,154 @@
//=======================================================================
// Copyright XashXT Group 2008 ©
// hud_iface.h - client exported funcs
//=======================================================================
#ifndef HUD_IFACE_H
#define HUD_IFACE_H
extern cl_enginefuncs_t g_engfuncs;
#include "enginecallback.h"
extern int HUD_VidInit( void );
extern void HUD_Init( void );
extern int HUD_Redraw( float flTime, int intermission );
extern int HUD_UpdateClientData( ref_params_t *parms, float flTime );
extern void HUD_Reset( void );
extern void HUD_Frame( double time );
extern void HUD_Shutdown( void );
extern void HUD_DrawNormalTriangles( void );
extern void HUD_DrawTransparentTriangles( void );
extern void HUD_CreateEntities( void );
extern void HUD_StudioEvent( const dstudioevent_t *event, edict_t *entity );
extern void V_CalcRefdef( ref_params_t *parms );
typedef struct rect_s
{
int left;
int right;
int top;
int bottom;
} wrect_t;
typedef HMODULE dllhandle_t;
typedef struct dllfunction_s
{
const char *name;
void **funcvariable;
} dllfunction_t;
// cvar flags
#define CVAR_ARCHIVE BIT(0) // set to cause it to be saved to vars.rc
#define CVAR_USERINFO BIT(1) // added to userinfo when changed
#define CVAR_SERVERINFO BIT(2) // added to serverinfo when changed
// macros to hook function calls into the HUD object
#define HOOK_MESSAGE( x ) (*g_engfuncs.pfnHookUserMsg)( #x, __MsgFunc_##x );
#define DECLARE_MESSAGE( y, x ) int __MsgFunc_##x(const char *pszName, int iSize, void *pbuf) \
{ \
return gHUD.##y.MsgFunc_##x(pszName, iSize, pbuf ); \
}
#define DECLARE_HUDMESSAGE( x ) int __MsgFunc_##x( const char *pszName, int iSize, void *pbuf ) \
{ \
return gHUD.MsgFunc_##x(pszName, iSize, pbuf ); \
}
#define HOOK_COMMAND( x, y ) (*g_engfuncs.pfnAddCommand)( x, __CmdFunc_##y, "user-defined command" );
#define DECLARE_COMMAND( y, x ) void __CmdFunc_##x( void ) \
{ \
gHUD.##y.UserCmd_##x( ); \
}
inline void UnpackRGB( int &r, int &g, int &b, dword ulRGB )
{
r = (ulRGB & 0xFF0000) >>16;\
g = (ulRGB & 0xFF00) >> 8;\
b = ulRGB & 0xFF;\
}
inline void ScaleColors( int &r, int &g, int &b, int a )
{
float x = (float)a / 255;
r = (int)(r * x);
g = (int)(g * x);
b = (int)(b * x);
}
inline int ConsoleStringLen( const char *string )
{
// console using fixed font size
return strlen( string ) * SMALLCHAR_WIDTH;
}
inline void GetConsoleStringSize( const char *string, int *width, int *height )
{
// console using fixed font size
if( width ) *width = ConsoleStringLen( string );
if( height ) *height = SMALLCHAR_HEIGHT;
}
// simple huh ?
inline int DrawConsoleString( int x, int y, const char *string )
{
DrawString( x, y, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, string );
return ConsoleStringLen( string );
}
inline void ConsolePrint( const char *string )
{
ALERT( at_console, "%s", string );
}
// returns the players name of entity no.
inline void GetPlayerInfo( int ent_num, hud_player_info_t *pinfo )
{
GET_PLAYER_INFO( ent_num, pinfo );
}
inline client_textmessage_t *TextMessageGet( const char *pName )
{
return GET_GAME_MESSAGE( pName );
}
// message reading
extern void BEGIN_READ( const char *pszName, int iSize, void *pBuf );
extern int READ_CHAR( void );
extern int READ_BYTE( void );
extern int READ_SHORT( void );
extern int READ_WORD( void );
extern int READ_LONG( void );
extern float READ_FLOAT( void );
extern char* READ_STRING( void );
extern float READ_COORD( void );
extern float READ_ANGLE( void );
extern float READ_ANGLE16( void );
extern void END_READ( void );
// drawing stuff
extern int SPR_Frames( HSPRITE hPic );
extern int SPR_Height( HSPRITE hPic, int frame );
extern int SPR_Width( HSPRITE hPic, int frame );
extern void SPR_Set( HSPRITE hPic, int r, int g, int b );
extern void SPR_Draw( int frame, int x, int y, const wrect_t *prc );
extern void SPR_Draw( int frame, int x, int y, int width, int height );
extern void SPR_DrawHoles( int frame, int x, int y, const wrect_t *prc );
extern void SPR_DrawHoles( int frame, int x, int y, int width, int height );
extern void SPR_DrawAdditive( int frame, int x, int y, const wrect_t *prc );
extern void SetCrosshair( HSPRITE hspr, wrect_t rc, int r, int g, int b );
extern void DrawCrosshair( void );
extern void SetScreenFade( Vector fadeColor, float alpha, float duration, float holdTime, int fadeFlags );
extern void DrawScreenFade( void );
// stdio stuff
extern char *va( const char *format, ... );
// dlls stuff
BOOL Sys_LoadLibrary( const char* dllname, dllhandle_t* handle, const dllfunction_t *fcts );
void* Sys_GetProcAddress( dllhandle_t handle, const char* name );
void Sys_UnloadLibrary( dllhandle_t* handle );
#endif//HUD_IFACE_H

172
client/hud/hud_menu.cpp Normal file
View File

@ -0,0 +1,172 @@
/***
*
* 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.
*
****/
//
// menu.cpp
//
// generic menu handler
//
#include "extdll.h"
#include "hud_iface.h"
#include "hud.h"
#define MAX_MENU_STRING 512
char g_szMenuString[MAX_MENU_STRING];
char g_szPrelocalisedMenuString[MAX_MENU_STRING];
DECLARE_MESSAGE( m_Menu, ShowMenu );
int CHudMenu :: Init( void )
{
gHUD.AddHudElem( this );
HOOK_MESSAGE( ShowMenu );
InitHUDData();
return 1;
}
void CHudMenu :: InitHUDData( void )
{
m_fMenuDisplayed = 0;
m_bitsValidSlots = 0;
Reset();
}
void CHudMenu :: Reset( void )
{
g_szPrelocalisedMenuString[0] = 0;
m_fWaitingForMore = FALSE;
}
int CHudMenu :: VidInit( void )
{
return 1;
}
int CHudMenu :: Draw( float flTime )
{
// check for if menu is set to disappear
if( m_flShutoffTime > 0 )
{
if( m_flShutoffTime <= gHUD.m_flTime )
{
// times up, shutoff
m_fMenuDisplayed = 0;
m_iFlags &= ~HUD_ACTIVE;
return 1;
}
}
// don't draw the menu if the scoreboard is being shown
if( gHUD.m_Scoreboard.m_iShowscoresHeld )
return 1;
// draw the menu, along the left-hand side of the screen
// count the number of newlines
int nlc = 0;
for( int i = 0; i < MAX_MENU_STRING && g_szMenuString[i] != '\0'; i++ )
{
if( g_szMenuString[i] == '\n' )
nlc++;
}
// center it
int y = (SCREEN_HEIGHT/2) - ((nlc/2)*12) - 40; // make sure it is above the say text
int x = 20;
i = 0;
while( i < MAX_MENU_STRING && g_szMenuString[i] != '\0' )
{
gHUD.DrawHudString( x, y, 320, g_szMenuString + i, 255, 255, 255 );
y += 12;
while( i < MAX_MENU_STRING && g_szMenuString[i] != '\0' && g_szMenuString[i] != '\n' )
i++;
if( g_szMenuString[i] == '\n' )
i++;
}
return 1;
}
// selects an item from the menu
void CHudMenu :: SelectMenuItem( int menu_item )
{
// if menu_item is in a valid slot, send a menuselect command to the server
if( (menu_item > 0) && (m_bitsValidSlots & (1<<(menu_item - 1))) )
{
char szbuf[32];
sprintf( szbuf, "menuselect %d\n", menu_item );
CLIENT_COMMAND( szbuf );
// remove the menu
m_fMenuDisplayed = 0;
m_iFlags &= ~HUD_ACTIVE;
}
}
// Message handler for ShowMenu message
// takes four values:
// short: a bitfield of keys that are valid input
// char : the duration, in seconds, the menu should stay up. -1 means is stays until something is chosen.
// byte : a boolean, TRUE if there is more string yet to be received before displaying the menu, FALSE if it's the last string
// string: menu string to display
// if this message is never received, then scores will simply be the combined totals of the players.
int CHudMenu :: MsgFunc_ShowMenu( const char *pszName, int iSize, void *pbuf )
{
BEGIN_READ( pszName, iSize, pbuf );
m_bitsValidSlots = READ_SHORT();
int DisplayTime = READ_CHAR();
int NeedMore = READ_BYTE();
if( DisplayTime > 0 )
m_flShutoffTime = DisplayTime + gHUD.m_flTime;
else m_flShutoffTime = -1;
if( m_bitsValidSlots )
{
if( !m_fWaitingForMore ) // this is the start of a new menu
{
strncpy( g_szPrelocalisedMenuString, READ_STRING(), MAX_MENU_STRING );
}
else
{
// append to the current menu string
strncat( g_szPrelocalisedMenuString, READ_STRING(), MAX_MENU_STRING - strlen(g_szPrelocalisedMenuString) );
}
g_szPrelocalisedMenuString[MAX_MENU_STRING-1] = 0; // ensure null termination (strncat/strncpy does not)
if( !NeedMore )
{
// we have the whole string, so we can localise it now
strcpy( g_szMenuString, gHUD.m_TextMessage.BufferedLocaliseTextString( g_szPrelocalisedMenuString ) );
}
m_fMenuDisplayed = 1;
m_iFlags |= HUD_ACTIVE;
}
else
{
m_fMenuDisplayed = 0; // no valid slots means that the menu should be turned off
m_iFlags &= ~HUD_ACTIVE;
}
m_fWaitingForMore = NeedMore;
return 1;
}

514
client/hud/hud_message.cpp Normal file
View File

@ -0,0 +1,514 @@
/***
*
* 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.
*
****/
//
// Message.cpp
//
// implementation of CHudMessage class
//
#include "extdll.h"
#include "hud_iface.h"
#include "hud.h"
DECLARE_MESSAGE( m_Message, HudText )
DECLARE_MESSAGE( m_Message, GameTitle )
// 1 Global client_textmessage_t for custom messages that aren't in the titles.txt
client_textmessage_t g_pCustomMessage;
char *g_pCustomName = "Custom";
char g_pCustomText[1024];
int CHudMessage :: Init( void )
{
HOOK_MESSAGE( HudText );
HOOK_MESSAGE( GameTitle );
gHUD.AddHudElem( this );
Reset();
return 1;
}
int CHudMessage :: VidInit( void )
{
HUD_Logo = gHUD.GetSpriteIndex( "logo" );
return 1;
}
void CHudMessage :: Reset( void )
{
memset( m_pMessages, 0, sizeof( m_pMessages[0] ) * maxHUDMessages );
memset( m_startTime, 0, sizeof( m_startTime[0] ) * maxHUDMessages );
m_gameTitleTime = 0;
m_pGameTitle = NULL;
}
float CHudMessage :: FadeBlend( float fadein, float fadeout, float hold, float localTime )
{
float fadeTime = fadein + hold;
float fadeBlend;
if( localTime < 0 )
return 0;
if( localTime < fadein )
{
fadeBlend = 1 - ((fadein - localTime) / fadein);
}
else if( localTime > fadeTime )
{
if( fadeout > 0 )
fadeBlend = 1 - ((localTime - fadeTime) / fadeout);
else fadeBlend = 0;
}
else fadeBlend = 1;
return fadeBlend;
}
int CHudMessage::XPosition( float x, int width, int totalWidth )
{
int xPos;
if( x == -1 )
{
xPos = (SCREEN_WIDTH - width) / 2;
}
else
{
if ( x < 0 )
xPos = (1.0 + x) * SCREEN_WIDTH - totalWidth; // Alight right
else
xPos = x * SCREEN_WIDTH;
}
if( xPos + width > SCREEN_WIDTH )
xPos = SCREEN_WIDTH - width;
else if( xPos < 0 )
xPos = 0;
return xPos;
}
int CHudMessage::YPosition( float y, int height )
{
int yPos;
if( y == -1 ) // Centered?
yPos = (SCREEN_HEIGHT - height) * 0.5;
else
{
// Alight bottom?
if( y < 0 )
yPos = (1.0 + y) * SCREEN_HEIGHT - height; // Alight bottom
else // align top
yPos = y * SCREEN_HEIGHT;
}
if( yPos + height > SCREEN_HEIGHT )
yPos = SCREEN_HEIGHT - height;
else if( yPos < 0 )
yPos = 0;
return yPos;
}
void CHudMessage :: MessageScanNextChar( void )
{
int srcRed, srcGreen, srcBlue, destRed, destGreen, destBlue;
int blend;
srcRed = m_parms.pMessage->r1;
srcGreen = m_parms.pMessage->g1;
srcBlue = m_parms.pMessage->b1;
blend = 0; // Pure source
switch( m_parms.pMessage->effect )
{
case 0: // Fade-in / Fade-out
case 1:
destRed = destGreen = destBlue = 0;
blend = m_parms.fadeBlend;
break;
case 2:
m_parms.charTime += m_parms.pMessage->fadein;
if( m_parms.charTime > m_parms.time )
{
srcRed = srcGreen = srcBlue = 0;
blend = 0; // pure source
}
else
{
float deltaTime = m_parms.time - m_parms.charTime;
destRed = destGreen = destBlue = 0;
if( m_parms.time > m_parms.fadeTime )
{
blend = m_parms.fadeBlend;
}
else if( deltaTime > m_parms.pMessage->fxtime )
blend = 0; // pure dest
else
{
destRed = m_parms.pMessage->r2;
destGreen = m_parms.pMessage->g2;
destBlue = m_parms.pMessage->b2;
blend = 255 - (deltaTime * (1.0 / m_parms.pMessage->fxtime) * 255.0 + 0.5);
}
}
break;
}
if( blend > 255 )
blend = 255;
else if( blend < 0 )
blend = 0;
m_parms.r = ((srcRed * (255-blend)) + (destRed * blend)) >> 8;
m_parms.g = ((srcGreen * (255-blend)) + (destGreen * blend)) >> 8;
m_parms.b = ((srcBlue * (255-blend)) + (destBlue * blend)) >> 8;
if( m_parms.pMessage->effect == 1 && m_parms.charTime != 0 )
{
if( m_parms.x >= 0 && m_parms.y >= 0 && (m_parms.x + gHUD.charWidths[ m_parms.text ]) <= SCREEN_WIDTH )
TextMessageDrawChar( m_parms.x, m_parms.y, m_parms.text, m_parms.pMessage->r2, m_parms.pMessage->g2, m_parms.pMessage->b2 );
}
}
void CHudMessage :: MessageScanStart( void )
{
switch( m_parms.pMessage->effect )
{
case 1: // Fade-in / out with flicker
case 0:
m_parms.fadeTime = m_parms.pMessage->fadein + m_parms.pMessage->holdtime;
if( m_parms.time < m_parms.pMessage->fadein )
{
m_parms.fadeBlend = ((m_parms.pMessage->fadein - m_parms.time) * (1.0/m_parms.pMessage->fadein) * 255);
}
else if( m_parms.time > m_parms.fadeTime )
{
if ( m_parms.pMessage->fadeout > 0 )
m_parms.fadeBlend = (((m_parms.time - m_parms.fadeTime) / m_parms.pMessage->fadeout) * 255);
else
m_parms.fadeBlend = 255; // Pure dest (off)
}
else m_parms.fadeBlend = 0; // Pure source (on)
m_parms.charTime = 0;
if( m_parms.pMessage->effect == 1 && (rand() % 100 ) < 10 )
m_parms.charTime = 1;
break;
case 2:
m_parms.fadeTime = (m_parms.pMessage->fadein * m_parms.length) + m_parms.pMessage->holdtime;
if( m_parms.time > m_parms.fadeTime && m_parms.pMessage->fadeout > 0 )
m_parms.fadeBlend = (((m_parms.time - m_parms.fadeTime) / m_parms.pMessage->fadeout) * 255);
else m_parms.fadeBlend = 0;
break;
}
}
void CHudMessage :: MessageDrawScan( client_textmessage_t *pMessage, float time )
{
int i, j, length, width;
const char *pText;
unsigned char line[80];
pText = pMessage->pMessage;
// Count lines
m_parms.lines = 1;
m_parms.time = time;
m_parms.pMessage = pMessage;
length = 0;
width = 0;
m_parms.totalWidth = 0;
while( *pText )
{
if ( *pText == '\n' )
{
m_parms.lines++;
if ( width > m_parms.totalWidth )
m_parms.totalWidth = width;
width = 0;
}
else width += gHUD.charWidths[*pText];
pText++;
length++;
}
m_parms.length = length;
m_parms.totalHeight = (m_parms.lines * gHUD.iCharHeight);
m_parms.y = YPosition( pMessage->y, m_parms.totalHeight );
pText = pMessage->pMessage;
m_parms.charTime = 0;
MessageScanStart();
for( i = 0; i < m_parms.lines; i++ )
{
m_parms.lineLength = 0;
m_parms.width = 0;
while( *pText && *pText != '\n' )
{
unsigned char c = *pText;
line[m_parms.lineLength] = c;
m_parms.width += gHUD.charWidths[c];
m_parms.lineLength++;
pText++;
}
pText++; // Skip LineFeed
line[m_parms.lineLength] = 0;
m_parms.x = XPosition( pMessage->x, m_parms.width, m_parms.totalWidth );
for ( j = 0; j < m_parms.lineLength; j++ )
{
m_parms.text = line[j];
int next = m_parms.x + gHUD.charWidths[ m_parms.text ];
MessageScanNextChar();
if ( m_parms.x >= 0 && m_parms.y >= 0 && next <= SCREEN_WIDTH )
TextMessageDrawChar( m_parms.x, m_parms.y, m_parms.text, m_parms.r, m_parms.g, m_parms.b );
m_parms.x = next;
}
m_parms.y += gHUD.iCharHeight;
}
}
int CHudMessage :: Draw( float fTime )
{
int i, drawn;
client_textmessage_t *pMessage;
float endTime;
drawn = 0;
if( m_gameTitleTime > 0 )
{
float localTime = gHUD.m_flTime - m_gameTitleTime;
float brightness;
// Maybe timer isn't set yet
if( m_gameTitleTime > gHUD.m_flTime )
m_gameTitleTime = gHUD.m_flTime;
if( localTime > (m_pGameTitle->fadein + m_pGameTitle->holdtime + m_pGameTitle->fadeout) )
m_gameTitleTime = 0;
else
{
brightness = FadeBlend( m_pGameTitle->fadein, m_pGameTitle->fadeout, m_pGameTitle->holdtime, localTime );
int fullWidth = gHUD.GetSpriteRect(HUD_Logo).right - gHUD.GetSpriteRect(HUD_Logo).left;
int fullHeight = gHUD.GetSpriteRect(HUD_Logo).bottom - gHUD.GetSpriteRect(HUD_Logo).top;
int x = XPosition( m_pGameTitle->x, fullWidth, fullWidth );
int y = YPosition( m_pGameTitle->y, fullHeight);
SPR_Set( gHUD.GetSprite(HUD_Logo), brightness * m_pGameTitle->r1, brightness * m_pGameTitle->g1, brightness * m_pGameTitle->b1 );
SPR_DrawAdditive( 0, x, y, &gHUD.GetSpriteRect( HUD_Logo ));
drawn = 1;
}
}
// fixup level transitions
for( i = 0; i < maxHUDMessages; i++ )
{
// assume m_parms.time contains last time
if( m_pMessages[i] )
{
pMessage = m_pMessages[i];
if( m_startTime[i] > gHUD.m_flTime )
{
// Server takes 0.2 seconds to spawn, adjust for this
m_startTime[i] = gHUD.m_flTime + m_parms.time - m_startTime[i] + 0.2;
}
}
}
for( i = 0; i < maxHUDMessages; i++ )
{
if ( m_pMessages[i] )
{
pMessage = m_pMessages[i];
// This is when the message is over
switch( pMessage->effect )
{
case 0:
case 1:
endTime = m_startTime[i] + pMessage->fadein + pMessage->fadeout + pMessage->holdtime;
break;
// Fade in is per character in scanning messages
case 2:
endTime = m_startTime[i] + (pMessage->fadein * strlen( pMessage->pMessage )) + pMessage->fadeout + pMessage->holdtime;
break;
}
if( fTime <= endTime )
{
float messageTime = fTime - m_startTime[i];
// Draw the message
// effect 0 is fade in/fade out
// effect 1 is flickery credits
// effect 2 is write out (training room)
MessageDrawScan( pMessage, messageTime );
drawn++;
}
else
{
// The message is over
m_pMessages[i] = NULL;
}
}
}
// Remember the time -- to fix up level transitions
m_parms.time = gHUD.m_flTime;
// Don't call until we get another message
if( !drawn ) m_iFlags &= ~HUD_ACTIVE;
return 1;
}
void CHudMessage :: MessageAdd( const char *pName, float time )
{
int i, j;
client_textmessage_t *tempMessage;
for( i = 0; i < maxHUDMessages; i++ )
{
if( !m_pMessages[i] )
{
// Trim off a leading # if it's there
if( pName[0] == '#' )
tempMessage = TextMessageGet( pName+1 );
else tempMessage = TextMessageGet( pName );
// If we couldnt find it in the titles.txt, just create it
if( !tempMessage )
{
g_pCustomMessage.effect = 2;
g_pCustomMessage.r1 = g_pCustomMessage.g1 = g_pCustomMessage.b1 = g_pCustomMessage.a1 = 100;
g_pCustomMessage.r2 = 240;
g_pCustomMessage.g2 = 110;
g_pCustomMessage.b2 = 0;
g_pCustomMessage.a2 = 0;
g_pCustomMessage.x = -1; // Centered
g_pCustomMessage.y = 0.7;
g_pCustomMessage.fadein = 0.01;
g_pCustomMessage.fadeout = 1.5;
g_pCustomMessage.fxtime = 0.25;
g_pCustomMessage.holdtime = 5;
g_pCustomMessage.pName = g_pCustomName;
strcpy( g_pCustomText, pName );
g_pCustomMessage.pMessage = g_pCustomText;
tempMessage = &g_pCustomMessage;
}
for( j = 0; j < maxHUDMessages; j++ )
{
if( m_pMessages[j] )
{
// is this message already in the list
if( !strcmp( tempMessage->pMessage, m_pMessages[j]->pMessage ) )
{
return;
}
// get rid of any other messages in same location (only one displays at a time)
if( fabs( tempMessage->y - m_pMessages[j]->y ) < 0.0001 )
{
if ( fabs( tempMessage->x - m_pMessages[j]->x ) < 0.0001 )
{
m_pMessages[j] = NULL;
}
}
}
}
m_pMessages[i] = tempMessage;
m_startTime[i] = time;
return;
}
}
}
int CHudMessage::MsgFunc_HudText( const char *pszName, int iSize, void *pbuf )
{
BEGIN_READ( pszName, iSize, pbuf );
char *pString = READ_STRING();
MessageAdd( pString, gHUD.m_flTime );
// Remember the time -- to fix up level transitions
m_parms.time = gHUD.m_flTime;
// Turn on drawing
if(!( m_iFlags & HUD_ACTIVE ))
m_iFlags |= HUD_ACTIVE;
END_READ();
return 1;
}
int CHudMessage::MsgFunc_GameTitle( const char *pszName, int iSize, void *pbuf )
{
m_pGameTitle = TextMessageGet( "GAMETITLE" );
if( m_pGameTitle != NULL )
{
m_gameTitleTime = gHUD.m_flTime;
// Turn on drawing
if(!( m_iFlags & HUD_ACTIVE ))
m_iFlags |= HUD_ACTIVE;
}
return 1;
}
void CHudMessage :: MessageAdd( client_textmessage_t *newMessage )
{
m_parms.time = gHUD.m_flTime;
// Turn on drawing
if(!( m_iFlags & HUD_ACTIVE ))
m_iFlags |= HUD_ACTIVE;
for( int i = 0; i < maxHUDMessages; i++ )
{
if( !m_pMessages[i] )
{
m_pMessages[i] = newMessage;
m_startTime[i] = gHUD.m_flTime;
return;
}
}
}

132
client/hud/hud_motd.cpp Normal file
View File

@ -0,0 +1,132 @@
/***
*
* Copyright (c) 1999, 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.
*
****/
//
//
// for displaying a server-sent message of the day
//
#include "extdll.h"
#include "hud_iface.h"
#include "hud.h"
DECLARE_MESSAGE( m_MOTD, MOTD );
int CHudMOTD :: MOTD_DISPLAY_TIME;
int CHudMOTD :: Init( void )
{
gHUD.AddHudElem( this );
HOOK_MESSAGE( MOTD );
CVAR_REGISTER( "motd_display_time", "6", 0, "time to show message of the day" );
m_iFlags &= ~HUD_ACTIVE; // start out inactive
m_szMOTD[0] = 0;
return 1;
}
int CHudMOTD :: VidInit( void )
{
// Load sprites here
return 1;
}
void CHudMOTD :: Reset( void )
{
m_iFlags &= ~HUD_ACTIVE; // start out inactive
m_szMOTD[0] = 0;
m_iLines = 0;
m_flActiveTill = 0;
}
#define LINE_HEIGHT 13
int CHudMOTD :: Draw( float fTime )
{
// Draw MOTD line-by-line
if( m_flActiveTill < gHUD.m_flTime )
{
// finished with MOTD, disable it
m_szMOTD[0] = 0;
m_iLines = 0;
m_iFlags &= ~HUD_ACTIVE;
return 1;
}
// cap activetill time to the display time
m_flActiveTill = min( gHUD.m_flTime + MOTD_DISPLAY_TIME, m_flActiveTill );
// find the top of where the MOTD should be drawn, so the whole thing is centered in the screen
int ypos = max(((SCREEN_HEIGHT - (m_iLines * LINE_HEIGHT)) / 2) - 40, 30 ); // shift it up slightly
char *ch = m_szMOTD;
while( *ch )
{
int line_length = 0; // count the length of the current line
for( char *next_line = ch; *next_line != '\n' && *next_line != 0; next_line++ )
line_length += gHUD.charWidths[ *next_line ];
char *top = next_line;
if( *top == '\n' )
*top = 0;
else
top = NULL;
// find where to start drawing the line
int xpos = (SCREEN_WIDTH - line_length) / 2;
gHUD.DrawHudString( xpos, ypos, SCREEN_WIDTH, ch, 255, 180, 0 );
ypos += LINE_HEIGHT;
if( top ) *top = '\n'; // restore
ch = next_line;
if( *ch == '\n' )
ch++;
if ( ypos > ( SCREEN_HEIGHT - 20 ))
break; // don't let it draw too low
}
return 1;
}
int CHudMOTD :: MsgFunc_MOTD( const char *pszName, int iSize, void *pbuf )
{
if( m_iFlags & HUD_ACTIVE )
{
Reset(); // clear the current MOTD in prep for this one
}
BEGIN_READ( pszName, iSize, pbuf );
int is_finished = READ_BYTE();
strcat( m_szMOTD, READ_STRING() );
if( is_finished )
{
m_iFlags |= HUD_ACTIVE;
MOTD_DISPLAY_TIME = CVAR_GET_FLOAT( "motd_display_time" );
m_flActiveTill = gHUD.m_flTime + MOTD_DISPLAY_TIME;
for( char *sz = m_szMOTD; *sz != 0; sz++ ) // count the number of lines in the MOTD
{
if( *sz == '\n' )
m_iLines++;
}
}
return 1;
}

426
client/hud/hud_msg.cpp Normal file
View File

@ -0,0 +1,426 @@
/***
*
* 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.
*
****/
//
// hud_msg.cpp
//
#include "extdll.h"
#include "hud_iface.h"
#include "hud.h"
// CHud message handlers
DECLARE_HUDMESSAGE( HUDColor );
DECLARE_HUDMESSAGE( SetFog );
DECLARE_HUDMESSAGE( SetSky );
DECLARE_HUDMESSAGE( RainData );
DECLARE_HUDMESSAGE( SetBody );
DECLARE_HUDMESSAGE( SetSkin );
DECLARE_HUDMESSAGE( ResetHUD );
DECLARE_HUDMESSAGE( InitHUD );
DECLARE_HUDMESSAGE( ViewMode );
DECLARE_HUDMESSAGE( Particle );
DECLARE_HUDMESSAGE( SetFOV );
DECLARE_HUDMESSAGE( Concuss );
DECLARE_HUDMESSAGE( GameMode );
DECLARE_HUDMESSAGE( CamData );
DECLARE_HUDMESSAGE( AddMirror );
DECLARE_HUDMESSAGE( AddScreen );
DECLARE_HUDMESSAGE( AddPortal );
DECLARE_HUDMESSAGE( ServerName );
DECLARE_HUDMESSAGE( ScreenShake );
int CHud :: InitMessages( void )
{
HOOK_MESSAGE( ResetHUD );
HOOK_MESSAGE( GameMode );
HOOK_MESSAGE( ServerName );
HOOK_MESSAGE( InitHUD );
HOOK_MESSAGE( ViewMode );
HOOK_MESSAGE( SetFOV );
HOOK_MESSAGE( Concuss );
HOOK_MESSAGE( HUDColor );
HOOK_MESSAGE( Particle );
HOOK_MESSAGE( SetFog );
HOOK_MESSAGE( SetSky );
HOOK_MESSAGE( CamData );
HOOK_MESSAGE( RainData );
HOOK_MESSAGE( SetBody );
HOOK_MESSAGE( SetSkin );
HOOK_MESSAGE( AddMirror);
HOOK_MESSAGE( AddScreen );
HOOK_MESSAGE( AddPortal );
viewEntityIndex = 0; // trigger_viewset stuff
viewFlags = 0;
m_iFOV = 0;
m_iHUDColor = RGB_YELLOWISH; // 255,160,0
CVAR_REGISTER( "zoom_sensitivity_ratio", "1.2", 0, "mouse sensitivity when zooming" );
CVAR_REGISTER( "default_fov", "90", 0, "default client fov" );
CVAR_REGISTER( "hud_draw", "1", CVAR_ARCHIVE, "hud drawing modes" );
// clear any old HUD list
if( m_pHudList )
{
HUDLIST *pList;
while( m_pHudList )
{
pList = m_pHudList;
m_pHudList = m_pHudList->pNext;
FREE( pList );
}
m_pHudList = NULL;
}
m_flTime = 1.0;
return 1;
}
int CHud :: MsgFunc_ResetHUD(const char *pszName, int iSize, void *pbuf )
{
// clear all hud data
HUDLIST *pList = m_pHudList;
while( pList )
{
if( pList->p ) pList->p->Reset();
pList = pList->pNext;
}
// reset sensitivity
m_flMouseSensitivity = 0;
// reset concussion effect
m_iConcussionEffect = 0;
// reset fog
m_fStartDist = 0;
m_fEndDist = 0;
return 1;
}
int CHud :: MsgFunc_ViewMode( const char *pszName, int iSize, void *pbuf )
{
// CAM_ToFirstPerson();
return 1;
}
int CHud :: MsgFunc_InitHUD( const char *pszName, int iSize, void *pbuf )
{
m_fStartDist = 0;
m_fEndDist = 0;
m_iSkyMode = SKY_OFF;
// prepare all hud data
HUDLIST *pList = m_pHudList;
while (pList)
{
if ( pList->p ) pList->p->InitHUDData();
pList = pList->pNext;
}
return 1;
}
int CHud::MsgFunc_SetFOV( const char *pszName, int iSize, void *pbuf )
{
BEGIN_READ( pszName, iSize, pbuf );
int newfov = READ_BYTE();
int def_fov = CVAR_GET_FLOAT( "default_fov" );
if( newfov == 0 )
{
m_iFOV = def_fov;
}
else
{
m_iFOV = newfov;
}
if( m_iFOV == def_fov )
{
m_flMouseSensitivity = 0;
}
else
{
// set a new sensitivity that is proportional to the change from the FOV default
m_flMouseSensitivity = CVAR_GET_FLOAT( "sensitivity" ) * ( (float)newfov / (float)def_fov );
m_flMouseSensitivity *= CVAR_GET_FLOAT( "zoom_sensitivity_ratio" ); // apply zoom factor
}
END_READ();
return 1;
}
int CHud::MsgFunc_HUDColor(const char *pszName, int iSize, void *pbuf)
{
BEGIN_READ( pszName, iSize, pbuf );
m_iHUDColor = READ_LONG();
END_READ();
return 1;
}
int CHud :: MsgFunc_SetFog( const char *pszName, int iSize, void *pbuf )
{
BEGIN_READ( pszName, iSize, pbuf );
m_FogColor.x = (float)(READ_BYTE() / 255.0f);
m_FogColor.y = (float)(READ_BYTE() / 255.0f);
m_FogColor.z = (float)(READ_BYTE() / 255.0f);
m_fStartDist = READ_SHORT();
m_fEndDist = READ_SHORT();
END_READ();
return 1;
}
int CHud :: MsgFunc_SetSky( const char *pszName, int iSize, void *pbuf )
{
BEGIN_READ( pszName, iSize, pbuf );
m_iSkyMode = READ_BYTE();
m_vecSkyPos[0] = READ_COORD();
m_vecSkyPos[1] = READ_COORD();
m_vecSkyPos[2] = READ_COORD();
END_READ();
return 1;
}
int CHud :: MsgFunc_GameMode( const char *pszName, int iSize, void *pbuf )
{
BEGIN_READ( pszName, iSize, pbuf );
m_Teamplay = READ_BYTE();
END_READ();
return 1;
}
int CHud :: MsgFunc_Damage( const char *pszName, int iSize, void *pbuf )
{
int armor, blood;
int i;
float count;
BEGIN_READ( pszName, iSize, pbuf );
armor = READ_BYTE();
blood = READ_BYTE();
float from[3];
for( i = 0; i < 3; i++ )
from[i] = READ_COORD();
count = (blood * 0.5) + (armor * 0.5);
if( count < 10 ) count = 10;
END_READ();
// TODO: kick viewangles, show damage visually
return 1;
}
int CHud :: MsgFunc_Concuss( const char *pszName, int iSize, void *pbuf )
{
BEGIN_READ( pszName, iSize, pbuf );
m_iConcussionEffect = READ_BYTE();
if( m_iConcussionEffect )
m_StatusIcons.EnableIcon( "dmg_concuss", 255, 160, 0 );
else m_StatusIcons.DisableIcon( "dmg_concuss" );
END_READ();
return 1;
}
int CHud :: MsgFunc_CamData( const char *pszName, int iSize, void *pbuf )
{
BEGIN_READ( pszName, iSize, pbuf );
gHUD.viewEntityIndex = READ_SHORT();
gHUD.viewFlags = READ_SHORT();
if( gHUD.viewFlags )
m_iCameraMode = 1;
else m_iCameraMode = m_iLastCameraMode;
END_READ();
return 1;
}
int CHud :: MsgFunc_RainData( const char *pszName, int iSize, void *pbuf )
{
BEGIN_READ( pszName, iSize, pbuf );
Rain.dripsPerSecond = READ_SHORT();
Rain.distFromPlayer = READ_COORD();
Rain.windX = READ_COORD();
Rain.windY = READ_COORD();
Rain.randX = READ_COORD();
Rain.randY = READ_COORD();
Rain.weatherMode = READ_SHORT();
Rain.globalHeight= READ_COORD(); // FIXME: calc on client side ?
END_READ();
return 1;
}
int CHud :: MsgFunc_SetBody( const char *pszName, int iSize, void *pbuf )
{
BEGIN_READ( pszName, iSize, pbuf );
edict_t *viewmodel = GetViewModel();
int body = READ_BYTE();
if( viewmodel )
viewmodel->v.body = body;
END_READ();
return 1;
}
int CHud :: MsgFunc_SetSkin( const char *pszName, int iSize, void *pbuf )
{
BEGIN_READ( pszName, iSize, pbuf );
edict_t *viewmodel = GetViewModel();
int skin = READ_BYTE();
if( viewmodel )
viewmodel->v.skin = skin;
END_READ();
return 1;
}
int CHud :: MsgFunc_AddScreen( const char *pszName, int iSize, void *pbuf )
{
BEGIN_READ( pszName, iSize, pbuf );
// AddScreenBrushEntity( READ_BYTE() );
END_READ();
return 1;
}
int CHud :: MsgFunc_AddMirror( const char *pszName, int iSize, void *pbuf )
{
BEGIN_READ( pszName, iSize, pbuf );
// AddMirrorBrushEntity( READ_BYTE() );
END_READ();
return 1;
}
int CHud :: MsgFunc_AddPortal( const char *pszName, int iSize, void *pbuf )
{
BEGIN_READ( pszName, iSize, pbuf );
// AddPortalBrushEntity( READ_BYTE() );
END_READ();
return 1;
}
int CHud :: MsgFunc_Particle( const char *pszName, int iSize, void *pbuf )
{
BEGIN_READ( pszName, iSize, pbuf );
int idx = READ_BYTE();
char *sz = READ_STRING();
// CreateAurora( idx, sz );
END_READ();
return 1;
}
int CHud::MsgFunc_ServerName( const char *pszName, int iSize, void *pbuf )
{
char m_szServerName[32];
BEGIN_READ( pszName, iSize, pbuf );
strncpy( m_szServerName, READ_STRING(), 32 );
END_READ();
return 1;
}
int CHud::MsgFunc_ScreenShake( const char *pszName, int iSize, void *pbuf )
{
BEGIN_READ( pszName, iSize, pbuf );
ShakeCommand_t eCommand = (ShakeCommand_t)READ_BYTE();
float amplitude = READ_FLOAT();
float frequency = READ_FLOAT();
float duration = READ_FLOAT();
if( eCommand == SHAKE_STOP )
{
m_Shake.amplitude = 0;
m_Shake.frequency = 0;
m_Shake.duration = 0;
}
else
{
if(( eCommand == SHAKE_START) || ( eCommand == SHAKE_FREQUENCY ))
{
m_Shake.frequency = frequency;
}
if(( eCommand == SHAKE_START) || ( eCommand == SHAKE_AMPLITUDE ))
{
// don't overwrite larger existing shake unless we are told to.
if(( amplitude > m_Shake.amplitude ) || ( eCommand == SHAKE_AMPLITUDE ))
{
m_Shake.amplitude = amplitude;
}
}
// only reset the timer for a new shake.
if( eCommand == SHAKE_START )
{
m_Shake.duration = duration;
m_Shake.nextShake = 0;
m_Shake.time = m_flTime + duration;
}
}
END_READ();
return 1;
}

226
client/hud/hud_saytext.cpp Normal file
View File

@ -0,0 +1,226 @@
/***
*
* 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.
*
****/
//
// saytext.cpp
//
// implementation of CHudSayText class
//
#include "extdll.h"
#include "hud_iface.h"
#include "hud.h"
#define MAX_LINES 5
#define MAX_CHARS_PER_LINE 256 /* it can be less than this, depending on char size */
// allow 20 pixels on either side of the text
#define MAX_LINE_WIDTH ( SCREEN_WIDTH - 40 )
#define LINE_START 10
static float SCROLL_SPEED = 5;
static char g_szLineBuffer[ MAX_LINES + 1 ][ MAX_CHARS_PER_LINE ];
static float flScrollTime = 0; // the time at which the lines next scroll up
static int Y_START = 0;
static int line_height = 0;
DECLARE_MESSAGE( m_SayText, SayText );
int CHudSayText :: Init( void )
{
gHUD.AddHudElem( this );
HOOK_MESSAGE( SayText );
InitHUDData();
CVAR_REGISTER( "hud_saytext_time", "5", 0, "time to show message text" );
m_iFlags |= HUD_INTERMISSION; // is always drawn during an intermission
return 1;
}
void CHudSayText :: InitHUDData( void )
{
memset( g_szLineBuffer, 0, sizeof g_szLineBuffer );
}
int CHudSayText :: VidInit( void )
{
return 1;
}
void ScrollTextUp( void )
{
ConsolePrint( g_szLineBuffer[0] ); // move the first line into the console buffer
memmove( g_szLineBuffer[0], g_szLineBuffer[1], sizeof(g_szLineBuffer) - sizeof(g_szLineBuffer[0]) ); // overwrite the first line
if( g_szLineBuffer[0][0] == ' ' ) // also scroll up following lines
{
g_szLineBuffer[0][0] = 2;
ScrollTextUp();
}
}
int CHudSayText :: Draw( float flTime )
{
int y = Y_START;
// make sure the scrolltime is within reasonable bounds, to guard against the clock being reset
flScrollTime = min( flScrollTime, flTime + CVAR_GET_FLOAT( "hud_saytext_time" ));
// make sure the scrolltime is within reasonable bounds, to guard against the clock being reset
flScrollTime = min( flScrollTime, flTime + CVAR_GET_FLOAT( "hud_saytext_time" ));
if( flScrollTime <= flTime )
{
if( *g_szLineBuffer[0] )
{
flScrollTime = flTime + CVAR_GET_FLOAT( "hud_saytext_time" );
// push the console up
ScrollTextUp();
}
else
{
// buffer is empty, just disable drawing of this section
m_iFlags &= ~HUD_ACTIVE;
}
}
for ( int i = 0; i < MAX_LINES; i++ )
{
if ( *g_szLineBuffer[i] )
DrawConsoleString( LINE_START, y, g_szLineBuffer[i] );
y += line_height;
}
return 1;
}
int CHudSayText :: MsgFunc_SayText( const char *pszName, int iSize, void *pbuf )
{
BEGIN_READ( pszName, iSize, pbuf );
int client_index = READ_BYTE(); // the client who spoke the message
SayTextPrint( READ_STRING(), iSize - 1, client_index );
END_READ();
return 1;
}
void CHudSayText :: SayTextPrint( const char *pszBuf, int iBufSize, int clientIndex )
{
// find an empty string slot
for( int i = 0; i < MAX_LINES; i++ )
{
if( !*g_szLineBuffer[i] )
break;
}
if( i == MAX_LINES )
{
// force scroll buffer up
ScrollTextUp();
i = MAX_LINES - 1;
}
strncpy( g_szLineBuffer[i], pszBuf, max(iBufSize -1, MAX_CHARS_PER_LINE-1) );
// make sure the text fits in one line
EnsureTextFitsInOneLineAndWrapIfHaveTo( i );
// set scroll time
if( i == 0 )
{
flScrollTime = gHUD.m_flTime + CVAR_GET_FLOAT( "hud_saytext_time" );
}
m_iFlags |= HUD_ACTIVE;
CL_PlaySound( "misc/talk.wav", 1.0f );
Y_START = SCREEN_HEIGHT - 60;
Y_START -= (line_height * (MAX_LINES+1));
}
void CHudSayText :: EnsureTextFitsInOneLineAndWrapIfHaveTo( int line )
{
int line_width = 0;
GetConsoleStringSize( g_szLineBuffer[line], &line_width, &line_height );
if((line_width + LINE_START) > MAX_LINE_WIDTH )
{
// string is too long to fit on line
// scan the string until we find what word is too long, and wrap the end of the sentence after the word
int length = LINE_START;
int tmp_len = 0;
char *last_break = NULL;
for ( char *x = g_szLineBuffer[line]; *x != 0; x++ )
{
char buf[2];
buf[1] = 0;
if( *x == ' ' && x != g_szLineBuffer[line] ) // store each line break, except for the very first character
last_break = x;
buf[0] = *x; // get the length of the current character
GetConsoleStringSize( buf, &tmp_len, &line_height );
length += tmp_len;
if( length > MAX_LINE_WIDTH )
{
// needs to be broken up
if( !last_break )
last_break = x-1;
x = last_break;
// find an empty string slot
for( int j = 0; j < MAX_LINES; j++ )
{
if( !*g_szLineBuffer[j] )
break;
}
if( j == MAX_LINES )
{
j = MAX_LINES - 1;
}
// copy remaining string into next buffer, making sure it starts with a space character
if( (char)*last_break == (char)' ' )
{
int linelen = strlen(g_szLineBuffer[j]);
int remaininglen = strlen(last_break);
if( (linelen - remaininglen) <= MAX_CHARS_PER_LINE )
strcat( g_szLineBuffer[j], last_break );
}
else
{
if( (strlen(g_szLineBuffer[j]) - strlen(last_break) - 2) < MAX_CHARS_PER_LINE )
{
strcat( g_szLineBuffer[j], " " );
strcat( g_szLineBuffer[j], last_break );
}
}
*last_break = 0; // cut off the last string
EnsureTextFitsInOneLineAndWrapIfHaveTo( j );
break;
}
}
}
}

View File

@ -0,0 +1,505 @@
/***
*
* Copyright (c) 1999, 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.
*
****/
//
// Scoreboard.cpp
//
// implementation of CHudScoreboard class
//
#include "extdll.h"
#include "hud_iface.h"
#include "hud.h"
DECLARE_COMMAND( m_Scoreboard, ShowScores );
DECLARE_COMMAND( m_Scoreboard, HideScores );
DECLARE_MESSAGE( m_Scoreboard, ScoreInfo );
DECLARE_MESSAGE( m_Scoreboard, TeamInfo );
DECLARE_MESSAGE( m_Scoreboard, TeamScore );
int CHudScoreboard :: Init( void )
{
gHUD.AddHudElem( this );
// Hook messages & commands here
HOOK_COMMAND( "+showscores", ShowScores );
HOOK_COMMAND( "-showscores", HideScores );
HOOK_MESSAGE( ScoreInfo );
HOOK_MESSAGE( TeamScore );
HOOK_MESSAGE( TeamInfo );
InitHUDData();
return 1;
}
int CHudScoreboard :: VidInit( void )
{
// Load sprites here
return 1;
}
void CHudScoreboard :: InitHUDData( void )
{
memset( m_PlayerExtraInfo, 0, sizeof m_PlayerExtraInfo );
m_iLastKilledBy = 0;
m_fLastKillTime = 0;
m_iPlayerNum = 0;
m_iNumTeams = 0;
memset( m_TeamInfo, 0, sizeof m_TeamInfo );
m_iFlags &= ~HUD_ACTIVE; // starts out inactive
m_iFlags |= HUD_INTERMISSION; // is always drawn during an intermission
}
/* The scoreboard
We have a minimum width of 1-320 - we could have the field widths scale with it?
*/
// X positions
// relative to the side of the scoreboard
#define NAME_RANGE_MIN 20
#define NAME_RANGE_MAX 145
#define KILLS_RANGE_MIN 130
#define KILLS_RANGE_MAX 170
#define DIVIDER_POS 180
#define DEATHS_RANGE_MIN 185
#define DEATHS_RANGE_MAX 210
#define PING_RANGE_MIN 245
#define PING_RANGE_MAX 295
#define SCOREBOARD_WIDTH 320
// Y positions
#define ROW_GAP 13
#define ROW_RANGE_MIN 15
#define ROW_RANGE_MAX ( SCREEN_HEIGHT - 50 )
int CHudScoreboard :: Draw( float fTime )
{
if( !m_iShowscoresHeld && gHUD.m_Health.m_iHealth > 0 && !gHUD.m_iIntermission )
return 1;
GetAllPlayersInfo();
// just sort the list on the fly
// list is sorted first by frags, then by deaths
float list_slot = 0;
int xpos_rel = (SCREEN_WIDTH - SCOREBOARD_WIDTH) / 2;
// print the heading line
int ypos = ROW_RANGE_MIN + (list_slot * ROW_GAP);
int xpos = NAME_RANGE_MIN + xpos_rel;
if ( !gHUD.m_Teamplay )
gHUD.DrawHudString( xpos, ypos, NAME_RANGE_MAX + xpos_rel, "Player", 255, 140, 0 );
else
gHUD.DrawHudString( xpos, ypos, NAME_RANGE_MAX + xpos_rel, "Teams", 255, 140, 0 );
gHUD.DrawHudStringReverse( KILLS_RANGE_MAX + xpos_rel, ypos, 0, "frags", 255, 140, 0 );
gHUD.DrawHudString( DIVIDER_POS + xpos_rel, ypos, SCREEN_WIDTH, "/", 255, 140, 0 );
gHUD.DrawHudString( DEATHS_RANGE_MIN + xpos_rel + 5, ypos, SCREEN_WIDTH, "deaths", 255, 140, 0 );
gHUD.DrawHudString( PING_RANGE_MAX + xpos_rel - 35, ypos, SCREEN_WIDTH, "ping", 255, 140, 0 );
list_slot += 1.2;
ypos = ROW_RANGE_MIN + (list_slot * ROW_GAP);
xpos = NAME_RANGE_MIN + xpos_rel;
FillRGBA( xpos - 5, ypos, PING_RANGE_MAX - 5, 1, 255, 140, 0, 255); // draw the seperator line
list_slot += 0.8;
if ( !gHUD.m_Teamplay )
{
// it's not teamplay, so just draw a simple player list
DrawPlayers( xpos_rel, list_slot );
return 1;
}
// clear out team scores
for ( int i = 1; i <= m_iNumTeams; i++ )
{
if ( !m_TeamInfo[i].scores_overriden )
m_TeamInfo[i].frags = m_TeamInfo[i].deaths = 0;
m_TeamInfo[i].ping = m_TeamInfo[i].packetloss = 0;
}
// recalc the team scores, then draw them
for ( i = 1; i < MAX_PLAYERS; i++ )
{
if ( m_PlayerInfoList[i].name == NULL )
continue; // empty player slot, skip
if ( m_PlayerExtraInfo[i].teamname[0] == 0 )
continue; // skip over players who are not in a team
// find what team this player is in
for ( int j = 1; j <= m_iNumTeams; j++ )
{
if ( !stricmp( m_PlayerExtraInfo[i].teamname, m_TeamInfo[j].name ) )
break;
}
if ( j > m_iNumTeams ) // player is not in a team, skip to the next guy
continue;
if ( !m_TeamInfo[j].scores_overriden )
{
m_TeamInfo[j].frags += m_PlayerExtraInfo[i].frags;
m_TeamInfo[j].deaths += m_PlayerExtraInfo[i].deaths;
}
m_TeamInfo[j].ping += m_PlayerInfoList[i].ping;
m_TeamInfo[j].packetloss += m_PlayerInfoList[i].packetloss;
if ( m_PlayerInfoList[i].thisplayer )
m_TeamInfo[j].ownteam = TRUE;
else
m_TeamInfo[j].ownteam = FALSE;
}
// find team ping/packetloss averages
for ( i = 1; i <= m_iNumTeams; i++ )
{
m_TeamInfo[i].already_drawn = FALSE;
if ( m_TeamInfo[i].players > 0 )
{
m_TeamInfo[i].ping /= m_TeamInfo[i].players; // use the average ping of all the players in the team as the teams ping
m_TeamInfo[i].packetloss /= m_TeamInfo[i].players; // use the average ping of all the players in the team as the teams ping
}
}
// Draw the teams
while ( 1 )
{
int highest_frags = -99999; int lowest_deaths = 99999;
int best_team = 0;
for ( i = 1; i <= m_iNumTeams; i++ )
{
if ( m_TeamInfo[i].players < 0 )
continue;
if ( !m_TeamInfo[i].already_drawn && m_TeamInfo[i].frags >= highest_frags )
{
if ( m_TeamInfo[i].frags > highest_frags || m_TeamInfo[i].deaths < lowest_deaths )
{
best_team = i;
lowest_deaths = m_TeamInfo[i].deaths;
highest_frags = m_TeamInfo[i].frags;
}
}
}
// draw the best team on the scoreboard
if ( !best_team )
break;
// draw out the best team
team_info_t *team_info = &m_TeamInfo[best_team];
ypos = ROW_RANGE_MIN + (list_slot * ROW_GAP);
// check we haven't drawn too far down
if ( ypos > ROW_RANGE_MAX ) // don't draw to close to the lower border
break;
xpos = NAME_RANGE_MIN + xpos_rel;
int r = 255, g = 225, b = 55; // draw the stuff kinda yellowish
if ( team_info->ownteam ) // if it is their team, draw the background different color
{
// overlay the background in blue, then draw the score text over it
FillRGBA( NAME_RANGE_MIN + xpos_rel - 5, ypos, PING_RANGE_MAX - 5, ROW_GAP, 0, 0, 255, 70 );
}
// draw their name (left to right)
gHUD.DrawHudString( xpos, ypos, NAME_RANGE_MAX + xpos_rel, team_info->name, r, g, b );
// draw kills (right to left)
xpos = KILLS_RANGE_MAX + xpos_rel;
gHUD.DrawHudNumberString( xpos, ypos, KILLS_RANGE_MIN + xpos_rel, team_info->frags, r, g, b );
// draw divider
xpos = DIVIDER_POS + xpos_rel;
gHUD.DrawHudString( xpos, ypos, xpos + 20, "/", r, g, b );
// draw deaths
xpos = DEATHS_RANGE_MAX + xpos_rel;
gHUD.DrawHudNumberString( xpos, ypos, DEATHS_RANGE_MIN + xpos_rel, team_info->deaths, r, g, b );
// draw ping
// draw ping & packetloss
static char buf[64];
sprintf( buf, "%d", team_info->ping );
xpos = ((PING_RANGE_MAX - PING_RANGE_MIN) / 2) + PING_RANGE_MIN + xpos_rel + 25;
UnpackRGB( r, g, b, RGB_YELLOWISH );
gHUD.DrawHudStringReverse( xpos, ypos, xpos - 50, buf, r, g, b );
team_info->already_drawn = TRUE; // set the already_drawn to be TRUE, so this team won't get drawn again
list_slot++;
// draw all the players that belong to this team, indented slightly
list_slot = DrawPlayers( xpos_rel, list_slot, 10, team_info->name );
}
// draw all the players who are not in a team
list_slot += 0.5;
DrawPlayers( xpos_rel, list_slot, 0, "" );
return 1;
}
// returns the ypos where it finishes drawing
int CHudScoreboard :: DrawPlayers( int xpos_rel, float list_slot, int nameoffset, char *team )
{
// draw the players, in order, and restricted to team if set
while ( 1 )
{
// Find the top ranking player
int highest_frags = -99999; int lowest_deaths = 99999;
int best_player = 0;
for ( int i = 1; i < MAX_PLAYERS; i++ )
{
if ( m_PlayerInfoList[i].name && m_PlayerExtraInfo[i].frags >= highest_frags )
{
if ( !(team && stricmp(m_PlayerExtraInfo[i].teamname, team)) ) // make sure it is the specified team
{
extra_player_info_t *pl_info = &m_PlayerExtraInfo[i];
if ( pl_info->frags > highest_frags || pl_info->deaths < lowest_deaths )
{
best_player = i;
lowest_deaths = pl_info->deaths;
highest_frags = pl_info->frags;
}
}
}
}
if ( !best_player )
break;
// draw out the best player
hud_player_info_t *pl_info = &m_PlayerInfoList[best_player];
int ypos = ROW_RANGE_MIN + (list_slot * ROW_GAP);
// check we haven't drawn too far down
if ( ypos > ROW_RANGE_MAX ) // don't draw to close to the lower border
break;
int xpos = NAME_RANGE_MIN + xpos_rel;
int r = 255, g = 255, b = 255;
if ( best_player == m_iLastKilledBy && m_fLastKillTime && m_fLastKillTime > gHUD.m_flTime )
{
if ( pl_info->thisplayer )
{ // green is the suicide color? i wish this could do grey...
FillRGBA( NAME_RANGE_MIN + xpos_rel - 5, ypos, PING_RANGE_MAX - 5, ROW_GAP, 80, 155, 0, 70 );
}
else
{ // Highlight the killers name - overlay the background in red, then draw the score text over it
FillRGBA( NAME_RANGE_MIN + xpos_rel - 5, ypos, PING_RANGE_MAX - 5, ROW_GAP, 255, 0, 0, ((float)15 * (float)(m_fLastKillTime - gHUD.m_flTime)) );
}
}
else if ( pl_info->thisplayer ) // if it is their name, draw it a different color
{
// overlay the background in blue, then draw the score text over it
FillRGBA( NAME_RANGE_MIN + xpos_rel - 5, ypos, PING_RANGE_MAX - 5, ROW_GAP, 0, 0, 255, 70 );
}
// draw their name (left to right)
gHUD.DrawHudString( xpos + nameoffset, ypos, NAME_RANGE_MAX + xpos_rel, pl_info->name, r, g, b );
// draw kills (right to left)
xpos = KILLS_RANGE_MAX + xpos_rel;
gHUD.DrawHudNumberString( xpos, ypos, KILLS_RANGE_MIN + xpos_rel, m_PlayerExtraInfo[best_player].frags, r, g, b );
// draw divider
xpos = DIVIDER_POS + xpos_rel;
gHUD.DrawHudString( xpos, ypos, xpos + 20, "/", r, g, b );
// draw deaths
xpos = DEATHS_RANGE_MAX + xpos_rel;
gHUD.DrawHudNumberString( xpos, ypos, DEATHS_RANGE_MIN + xpos_rel, m_PlayerExtraInfo[best_player].deaths, r, g, b );
// draw ping & packetloss
static char buf[64];
sprintf( buf, "%d", m_PlayerInfoList[best_player].ping );
xpos = ((PING_RANGE_MAX - PING_RANGE_MIN) / 2) + PING_RANGE_MIN + xpos_rel + 25;
gHUD.DrawHudStringReverse( xpos, ypos, xpos - 50, buf, r, g, b );
pl_info->name = NULL; // set the name to be NULL, so this client won't get drawn again
list_slot++;
}
return list_slot;
}
void CHudScoreboard :: GetAllPlayersInfo( void )
{
for ( int i = 1; i < MAX_PLAYERS; i++ )
{
GetPlayerInfo( i, &m_PlayerInfoList[i] );
if ( m_PlayerInfoList[i].thisplayer )
m_iPlayerNum = i; // !!!HACK: this should be initialized elsewhere... maybe gotten from the engine
}
}
int CHudScoreboard :: MsgFunc_ScoreInfo( const char *pszName, int iSize, void *pbuf )
{
m_iFlags |= HUD_ACTIVE;
BEGIN_READ( pszName, iSize, pbuf );
short cl = READ_BYTE();
short frags = READ_SHORT();
short deaths = READ_SHORT();
if( cl > 0 && cl <= MAX_PLAYERS )
{
m_PlayerExtraInfo[cl].frags = frags;
m_PlayerExtraInfo[cl].deaths = deaths;
}
END_READ();
return 1;
}
// Message handler for TeamInfo message
// accepts two values:
// byte: client number
// string: client team name
int CHudScoreboard :: MsgFunc_TeamInfo( const char *pszName, int iSize, void *pbuf )
{
BEGIN_READ( pszName, iSize, pbuf );
short cl = READ_BYTE();
if( cl > 0 && cl <= MAX_PLAYERS )
{
// set the players team
strncpy( m_PlayerExtraInfo[cl].teamname, READ_STRING(), MAX_TEAM_NAME );
}
// rebuild the list of teams
// clear out player counts from teams
for ( int i = 1; i <= m_iNumTeams; i++ )
{
m_TeamInfo[i].players = 0;
}
// rebuild the team list
GetAllPlayersInfo();
m_iNumTeams = 0;
for ( i = 1; i < MAX_PLAYERS; i++ )
{
if ( m_PlayerInfoList[i].name == NULL )
continue;
if ( m_PlayerExtraInfo[i].teamname[0] == 0 )
continue; // skip over players who are not in a team
// is this player in an existing team?
for ( int j = 1; j <= m_iNumTeams; j++ )
{
if ( m_TeamInfo[j].name[0] == '\0' )
break;
if ( !stricmp( m_PlayerExtraInfo[i].teamname, m_TeamInfo[j].name ) )
break;
}
if ( j > m_iNumTeams )
{ // they aren't in a listed team, so make a new one
// search through for an empty team slot
for ( int j = 1; j <= m_iNumTeams; j++ )
{
if ( m_TeamInfo[j].name[0] == '\0' )
break;
}
m_iNumTeams = max( j, m_iNumTeams );
strncpy( m_TeamInfo[j].name, m_PlayerExtraInfo[i].teamname, MAX_TEAM_NAME );
m_TeamInfo[j].players = 0;
}
m_TeamInfo[j].players++;
}
// clear out any empty teams
for ( i = 1; i <= m_iNumTeams; i++ )
{
if ( m_TeamInfo[i].players < 1 )
memset( &m_TeamInfo[i], 0, sizeof(team_info_t) );
}
END_READ();
return 1;
}
// Message handler for TeamScore message
// accepts three values:
// string: team name
// short: teams kills
// short: teams deaths
// if this message is never received, then scores will simply be the combined totals of the players.
int CHudScoreboard :: MsgFunc_TeamScore( const char *pszName, int iSize, void *pbuf )
{
BEGIN_READ( pszName, iSize, pbuf );
char *TeamName = READ_STRING();
// find the team matching the name
for ( int i = 1; i <= m_iNumTeams; i++ )
{
if ( !stricmp( TeamName, m_TeamInfo[i].name ) )
break;
}
if ( i > m_iNumTeams )
return 1;
// use this new score data instead of combined player scores
m_TeamInfo[i].scores_overriden = TRUE;
m_TeamInfo[i].frags = READ_SHORT();
m_TeamInfo[i].deaths = READ_SHORT();
END_READ();
return 1;
}
void CHudScoreboard :: DeathMsg( int killer, int victim )
{
// if we were the one killed, or the world killed us, set the scoreboard to indicate suicide
if ( victim == m_iPlayerNum || killer == 0 )
{
m_iLastKilledBy = killer ? killer : m_iPlayerNum;
m_fLastKillTime = gHUD.m_flTime + 10; // display who we were killed by for 10 seconds
if ( killer == m_iPlayerNum )
m_iLastKilledBy = m_iPlayerNum;
}
}
void CHudScoreboard :: UserCmd_ShowScores( void )
{
m_iShowscoresHeld = TRUE;
}
void CHudScoreboard :: UserCmd_HideScores( void )
{
m_iShowscoresHeld = FALSE;
}

357
client/hud/hud_sound.cpp Normal file
View File

@ -0,0 +1,357 @@
//=======================================================================
// Copyright (C) XashXT Group 2006
//=======================================================================
#include "extdll.h"
#include "hud_iface.h"
#include "hud.h"
/*
====================
FMOD definitions
====================
*/
enum FSOUND_OUTPUTTYPES
{
FSOUND_OUTPUT_NOSOUND, /* NoSound driver, all calls to this succeed but do nothing. */
FSOUND_OUTPUT_WINMM, /* Windows Multimedia driver. */
FSOUND_OUTPUT_DSOUND, /* DirectSound driver. You need this to get EAX or EAX2 support. */
FSOUND_OUTPUT_A3D, /* A3D driver. You need this to get geometry support. */
FSOUND_OUTPUT_OSS, /* Linux/Unix OSS (Open Sound System) driver, i.e. the kernel sound drivers. */
FSOUND_OUTPUT_ESD, /* Linux/Unix ESD (Enlightment Sound Daemon) driver. */
FSOUND_OUTPUT_ALSA /* Linux Alsa driver. */
};
enum FMOD_ERRORS
{
FMOD_ERR_NONE, /* No errors */
FMOD_ERR_BUSY, /* Cannot call this command after FSOUND_Init. Call FSOUND_Close first. */
FMOD_ERR_UNINITIALIZED, /* This command failed because FSOUND_Init or FSOUND_SetOutput was not called */
FMOD_ERR_INIT, /* Error initializing output device. */
FMOD_ERR_ALLOCATED, /* Error initializing output device, but more specifically, the output device is already in use and cannot be reused. */
FMOD_ERR_PLAY, /* Playing the sound failed. */
FMOD_ERR_OUTPUT_FORMAT, /* Soundcard does not support the features needed for this soundsystem (16bit stereo output) */
FMOD_ERR_COOPERATIVELEVEL, /* Error setting cooperative level for hardware. */
FMOD_ERR_CREATEBUFFER, /* Error creating hardware sound buffer. */
FMOD_ERR_FILE_NOTFOUND, /* File not found */
FMOD_ERR_FILE_FORMAT, /* Unknown file format */
FMOD_ERR_FILE_BAD, /* Error loading file */
FMOD_ERR_MEMORY, /* Not enough memory or resources */
FMOD_ERR_VERSION, /* The version number of this file format is not supported */
FMOD_ERR_INVALID_PARAM, /* An invalid parameter was passed to this function */
FMOD_ERR_NO_EAX, /* Tried to use an EAX command on a non EAX enabled channel or output. */
FMOD_ERR_CHANNEL_ALLOC, /* Failed to allocate a new channel */
FMOD_ERR_RECORD, /* Recording is not supported on this machine */
FMOD_ERR_MEDIAPLAYER, /* Windows Media Player not installed so cannot play wma or use internet streaming. */
FMOD_ERR_CDDEVICE /* An error occured trying to open the specified CD device */
};
static char *FMOD_ErrorString( int errcode )
{
switch (errcode)
{
case FMOD_ERR_NONE: return "No errors";
case FMOD_ERR_BUSY: return "Cannot call this command after FSOUND_Init. Call FSOUND_Close first.";
case FMOD_ERR_UNINITIALIZED: return "This command failed because FSOUND_Init was not called";
case FMOD_ERR_PLAY: return "Playing the sound failed.";
case FMOD_ERR_INIT: return "Error initializing output device.";
case FMOD_ERR_ALLOCATED: return "The output device is already in use and cannot be reused.";
case FMOD_ERR_OUTPUT_FORMAT: return "Soundcard does not support the features needed for this soundsystem (16bit stereo output)";
case FMOD_ERR_COOPERATIVELEVEL: return "Error setting cooperative level for hardware.";
case FMOD_ERR_CREATEBUFFER: return "Error creating hardware sound buffer.";
case FMOD_ERR_FILE_NOTFOUND: return "File not found";
case FMOD_ERR_FILE_FORMAT: return "Unknown file format";
case FMOD_ERR_FILE_BAD: return "Error loading file";
case FMOD_ERR_MEMORY: return "Not enough memory ";
case FMOD_ERR_VERSION: return "The version number of this file format is not supported";
case FMOD_ERR_INVALID_PARAM: return "An invalid parameter was passed to this function";
case FMOD_ERR_NO_EAX: return "Tried to use an EAX command on a non EAX enabled channel or output.";
case FMOD_ERR_CHANNEL_ALLOC: return "Failed to allocate a new channel";
case FMOD_ERR_RECORD: return "Recording not supported on this device";
case FMOD_ERR_MEDIAPLAYER: return "Required Mediaplayer codec is not installed";
default : return "Unknown error";
};
}
#define FSOUND_LOOP_OFF 0x00000001
#define FSOUND_LOOP_NORMAL 0x00000002
#define FSOUND_LOADMEMORY 0x00008000
#define FSOUND_MPEGACCURATE 0x00020000
enum FSOUND_MIXERTYPES
{
FSOUND_MIXER_AUTODETECT, /* CE/PS2/GC Only - Non interpolating/low quality mixer. */
FSOUND_MIXER_BLENDMODE, /* Removed / obsolete. */
FSOUND_MIXER_MMXP5, /* Removed / obsolete. */
FSOUND_MIXER_MMXP6, /* Removed / obsolete. */
FSOUND_MIXER_QUALITY_AUTODETECT,/* All platforms - Autodetect the fastest quality mixer based on your cpu. */
FSOUND_MIXER_QUALITY_FPU, /* Win32/Linux only - Interpolating/volume ramping FPU mixer. */
FSOUND_MIXER_QUALITY_MMXP5, /* Win32/Linux only - Interpolating/volume ramping P5 MMX mixer. */
FSOUND_MIXER_QUALITY_MMXP6, /* Win32/Linux only - Interpolating/volume ramping ppro+ MMX mixer. */
FSOUND_MIXER_MONO, /* CE/PS2/GC only - MONO non interpolating/low quality mixer. For speed*/
FSOUND_MIXER_QUALITY_MONO, /* CE/PS2/GC only - MONO Interpolating mixer. For speed */
FSOUND_MIXER_MAX
};
/*
====================
Cl definitions
====================
*/
static int (_stdcall *qfmod_geterror)();
static float (_stdcall *qfmod_getversion)();
static signed char (_stdcall *qfmod_setoutput)(int outputtype);
static signed char (_stdcall *qfmod_setdriver)(int driver);
static signed char (_stdcall *qfmod_setmixer)(int mixer);
static signed char (_stdcall *qfmod_setbuffersize)(int len_ms);
static signed char (_stdcall *qfmod_init)(int mixrate, int maxsoftwarechannels, unsigned int flags);
static void (_stdcall *qfmod_close)();
static int (_stdcall *qfmod_getmixer)();
static signed char (_stdcall *qfmod_freesong)(void *mod);
static signed char (_stdcall *qfmod_playsong)(void *mod);
static signed char (_stdcall *qfmod_stopsong)(void *mod);
static void (_stdcall *qfmod_stopallsongs)();
static void* (_stdcall *qfmod_loadsong)(const char *name);
static void* (_stdcall *qfmod_loadsongmemory)(void *data, int length);
static signed char (_stdcall *qfmod_setmodpause)(void *data, signed char pause);
static signed char (_stdcall *qfmod_getmodpause)(void *data);
static void* (_stdcall *qfmod_loadstream)(const char *data, unsigned int mode, int memlength);
static int (_stdcall *qfmod_playstream)(int channel, void *data);
static signed char (_stdcall *qfmod_freestream)(void *data);
static signed char (_stdcall *qfmod_getstreampause)(int channel);
static int (_stdcall *qfmod_getstreampos)(void *data);
static signed char (_stdcall *qfmod_setstreampause)(int channel, signed char paused);
static signed char (_stdcall *qfmod_setstreampos)(void *data, unsigned int pos);
static signed char (_stdcall *qfmod_stopstream)(void *data);
static signed char (_stdcall *qfmod_setvolume)(int channel, int vol);
static dllfunction_t fmodfuncs[] =
{
{"_FSOUND_GetError@0", (void **) &qfmod_geterror},
{"_FSOUND_GetVersion@0", (void **) &qfmod_getversion},
{"_FSOUND_SetOutput@4", (void **) &qfmod_setoutput},
{"_FSOUND_SetDriver@4", (void **) &qfmod_setdriver},
{"_FSOUND_SetMixer@4", (void **) &qfmod_setmixer},
{"_FSOUND_SetBufferSize@4", (void **) &qfmod_setbuffersize},
{"_FSOUND_Init@12", (void **) &qfmod_init},
{"_FSOUND_Close@0", (void **) &qfmod_close},
{"_FSOUND_GetMixer@0", (void **) &qfmod_getmixer},
{"_FMUSIC_FreeSong@4", (void **) &qfmod_freesong},
{"_FMUSIC_PlaySong@4", (void **) &qfmod_playsong},
{"_FMUSIC_StopSong@4", (void **) &qfmod_stopsong},
{"_FMUSIC_StopAllSongs@0", (void **) &qfmod_stopallsongs},
{"_FMUSIC_LoadSong@4", (void **) &qfmod_loadsong},
{"_FMUSIC_LoadSongMemory@8", (void **) &qfmod_loadsongmemory},
{"_FMUSIC_SetPaused@8", (void **) &qfmod_setmodpause},
{"_FMUSIC_GetPaused@4", (void **) &qfmod_getmodpause},
{"_FSOUND_Stream_OpenFile@12",(void **) &qfmod_loadstream},
{"_FSOUND_Stream_Play@8", (void **) &qfmod_playstream},
{"_FSOUND_Stream_Close@4", (void **) &qfmod_freestream},
{"_FSOUND_GetPaused@4", (void **) &qfmod_getstreampause},
{"_FSOUND_Stream_GetPosition@4",(void **) &qfmod_getstreampos},
{"_FSOUND_SetPaused@8", (void **) &qfmod_setstreampause},
{"_FSOUND_Stream_SetPosition@8",(void **) &qfmod_setstreampos},
{"_FSOUND_Stream_Stop@4", (void **) &qfmod_stopstream},
{"_FSOUND_SetVolume@8", (void **) &qfmod_setvolume},
{NULL, NULL}
};
DECLARE_MESSAGE(m_Sound, Fsound)
#define STREAM 1
#define TRACK 2
// Handle for fmod
static dllhandle_t fmod_dll = NULL;
void *fmod_data = NULL;
static char songname[256];
int last_state = 0;
int CheckFormat( BOOL skip_buffer )
{
int i;
// check buffer
if( !fmod_data && !skip_buffer ) return false;
// detect of music type
for( i = 0; songname[i]; i++ )
{
if( songname[i] == '.' )//found extension
{
if( !strncmp( &songname[i+1], "mp3", 3 )) return STREAM;
else if( !strncmp(&songname[i+1], "wma", 3 )) return STREAM;
else if( !strncmp(&songname[i+1], "ogg", 3 )) return STREAM;
else if( !strncmp(&songname[i+1], "xm", 2 )) return TRACK;
else if( !strncmp(&songname[i+1], "it", 2 )) return TRACK;
else if( !strncmp(&songname[i+1], "s3m", 3 )) return TRACK;
else return false;
}
}
return false;
}
int CHudSound :: Init( void )
{
m_iStatus = 0;
m_iTime = 0;
HOOK_MESSAGE( Fsound );
m_iFlags |= HUD_ACTIVE;
gHUD.AddHudElem( this );
// already loaded?
if( fmod_dll ) return 1;
if( Sys_LoadLibrary( "fmod.dll", &fmod_dll, fmodfuncs ))
{
if( qfmod_getversion() > 3.4f && qfmod_getversion() < 3.3f )
{
Sys_UnloadLibrary( &fmod_dll ); //free library
ALERT( at_warning, "Invalid fmod version: %g\n", qfmod_getversion());
return 1;
}
qfmod_setbuffersize( 100 );
qfmod_setoutput( FSOUND_OUTPUT_DSOUND );
qfmod_setdriver( 0 );
if( !qfmod_init( 48000, 32, 0 ))
{
ALERT( at_error, "%s\n", FMOD_ErrorString( qfmod_geterror() ));
return 1;
}
qfmod_setmixer( FSOUND_MIXER_AUTODETECT );
}
else ALERT( at_error, "fmod.dll not installed\n" );
return 1;
}
int CHudSound :: VidInit( void )
{
// MsgFunc_Fsound( 0, 0, NULL );
if( fmod_dll ) qfmod_stopallsongs(); // stop all songs
return 1;
}
int CHudSound :: MsgFunc_Fsound( const char *pszName, int iSize, void *pbuf )
{
if( !fmod_dll ) return 1;
BEGIN_READ( pszName, iSize, pbuf );
strcpy( songname, READ_STRING( )); // songname
m_iTime = READ_SHORT(); // song position
m_iStatus = READ_BYTE();
if( m_iStatus & 1 ) PlayStream( songname );
else if( fmod_data )
{
if( CheckFormat( FALSE ) == TRACK ) qfmod_freesong( fmod_data );
else if( CheckFormat( FALSE ) == STREAM ) qfmod_freestream( fmod_data );
memset( fmod_data, 0, sizeof( fmod_data ));
memset( (char*)songname, 0, sizeof( songname ));
m_iTime = 0;
m_iStatus = 0;
}
END_READ();
return 1;
}
int CHudSound :: PlayStream( const char* name )
{
int filesize;
char *data;
if( !fmod_dll ) return false;
// Load the file
data = (char *)LOAD_FILE( name, &filesize );
if( !data )
{
ALERT( at_console, "couldn't load %s\n", name );
return false;
}
// try to open this file as stream default
int flags;
if( m_iStatus & 2 ) flags = FSOUND_LOADMEMORY|FSOUND_MPEGACCURATE|FSOUND_LOOP_NORMAL;
else flags = FSOUND_LOADMEMORY|FSOUND_MPEGACCURATE|FSOUND_LOOP_OFF;
fmod_data = qfmod_loadstream( data, flags, filesize );
if( !fmod_data ) // may be it's tracker?
{
// check for .xm .it .s3m headers
if( memcmp( data, "Extended Module:", 16 ) && memcmp( data, "IMPM", 4 ) && memcmp( data + 44, "SCRM", 4 ))
{
for( int i = 0; name[i]; i++ )
{
if( name[i] == '.' ) // found extension
{
ALERT( at_console, "%s is not a %s file\n", name, &name[i+1] );
break;
}
}
FREE_FILE( data );
return 1;
}
// it's tracker, try to load
fmod_data = qfmod_loadsongmemory( data, filesize );
if( !fmod_data ) // what's hell?
{
ALERT( at_console, "%s\n", FMOD_ErrorString(qfmod_geterror()));
FREE_FILE( data );
return 1;
}
qfmod_playsong( fmod_data );
FREE_FILE( data );
return 1;
}
qfmod_playstream( 0, fmod_data );
FREE_FILE( data );
m_iStatus = 1;
return 1;
}
int CHudSound :: Draw( float flTime )
{
int pause = CVAR_GET_FLOAT( "paused" ); // engine cvar
if( fmod_dll && fmod_data )
{
if( last_state != pause )
{
// detect of music type
if( CheckFormat( FALSE ) == TRACK )
qfmod_setmodpause( fmod_data, !qfmod_getmodpause( fmod_data ));
else if( CheckFormat( FALSE ) == STREAM )
qfmod_setstreampause( 0, !qfmod_getstreampause( 0 ));
last_state = pause;
}
}
return 1;
}
int CHudSound :: Close( void )
{
if( fmod_dll )
{
qfmod_stopallsongs(); // stop all songs
qfmod_close();
Sys_UnloadLibrary( &fmod_dll ); // free library
}
return 1;
}

View File

@ -0,0 +1,246 @@
/***
*
* 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.
*
****/
//
// statusbar.cpp
//
// generic text status bar, set by game dll
// runs across bottom of screen
//
#include "extdll.h"
#include "hud_iface.h"
#include "hud.h"
DECLARE_MESSAGE( m_StatusBar, StatusText );
DECLARE_MESSAGE( m_StatusBar, StatusValue );
#define STATUSBAR_ID_LINE 1
int CHudStatusBar :: Init( void )
{
gHUD.AddHudElem( this );
HOOK_MESSAGE( StatusText );
HOOK_MESSAGE( StatusValue );
Reset();
CVAR_REGISTER( "hud_centerid", "0", CVAR_ARCHIVE, "disables center id" );
return 1;
}
int CHudStatusBar :: VidInit( void )
{
// Load sprites here
return 1;
}
void CHudStatusBar :: Reset( void )
{
int i = 0;
m_iFlags &= ~HUD_ACTIVE; // start out inactive
for ( i = 0; i < MAX_STATUSBAR_LINES; i++ )
m_szStatusText[i][0] = 0;
memset( m_iStatusValues, 0, sizeof m_iStatusValues );
m_iStatusValues[0] = 1; // 0 is the special index, which always returns true
}
void CHudStatusBar :: ParseStatusString( int line_num )
{
// localise string first
char szBuffer[MAX_STATUSTEXT_LENGTH];
memset( szBuffer, 0, sizeof szBuffer );
gHUD.m_TextMessage.LocaliseTextString( m_szStatusText[line_num], szBuffer, MAX_STATUSTEXT_LENGTH );
// parse m_szStatusText & m_iStatusValues into m_szStatusBar
memset( m_szStatusBar[line_num], 0, MAX_STATUSTEXT_LENGTH );
char *src = szBuffer;
char *dst = m_szStatusBar[line_num];
char *src_start = src, *dst_start = dst;
while ( *src != 0 )
{
while ( *src == '\n' )
src++; // skip over any newlines
if ( ((src - src_start) >= MAX_STATUSTEXT_LENGTH) || ((dst - dst_start) >= MAX_STATUSTEXT_LENGTH) )
break;
int index = atoi( src );
// should we draw this line?
if ( (index >= 0 && index < MAX_STATUSBAR_VALUES) && (m_iStatusValues[index] != 0) )
{ // parse this line and append result to the status bar
while ( *src >= '0' && *src <= '9' )
src++;
if ( *src == '\n' || *src == 0 )
continue; // no more left in this text line
// copy the text, char by char, until we hit a % or a \n
while ( *src != '\n' && *src != 0 )
{
if ( *src != '%' )
{ // just copy the character
*dst = *src;
dst++, src++;
}
else
{
// get the descriptor
char valtype = *(++src); // move over %
// if it's a %, draw a % sign
if ( valtype == '%' )
{
*dst = valtype;
dst++, src++;
continue;
}
// move over descriptor, then get and move over the index
index = atoi( ++src );
while ( *src >= '0' && *src <= '9' )
src++;
if ( index >= 0 && index < MAX_STATUSBAR_VALUES )
{
int indexval = m_iStatusValues[index];
// get the string to substitute in place of the %XX
char szRepString[MAX_PLAYER_NAME_LENGTH];
switch ( valtype )
{
case 'p': // player name
GetPlayerInfo( indexval, &gHUD.m_Scoreboard.m_PlayerInfoList[indexval] );
if ( gHUD.m_Scoreboard.m_PlayerInfoList[indexval].name != NULL )
{
strncpy( szRepString, gHUD.m_Scoreboard.m_PlayerInfoList[indexval].name, MAX_PLAYER_NAME_LENGTH );
}
else
{
strcpy( szRepString, "******" );
}
break;
case 'i': // number
sprintf( szRepString, "%d", indexval );
break;
default:
szRepString[0] = 0;
}
for ( char *cp = szRepString; *cp != 0 && ((dst - dst_start) < MAX_STATUSTEXT_LENGTH); cp++, dst++ )
*dst = *cp;
}
}
}
}
else
{
// skip to next line of text
while ( *src != 0 && *src != '\n' )
src++;
}
}
}
int CHudStatusBar :: Draw( float fTime )
{
if ( m_bReparseString )
{
for ( int i = 0; i < MAX_STATUSBAR_LINES; i++ )
ParseStatusString( i );
m_bReparseString = FALSE;
}
// Draw the status bar lines
for ( int i = 0; i < MAX_STATUSBAR_LINES; i++ )
{
int TextHeight, TextWidth;
GetConsoleStringSize( m_szStatusBar[i], &TextWidth, &TextHeight );
int Y_START = SCREEN_HEIGHT - 45;
int x = 4;
int y = Y_START - ( 4 + TextHeight * i ); // draw along bottom of screen
// let user set status ID bar centering
if ( (i == STATUSBAR_ID_LINE) && CVAR_GET_FLOAT( "hud_centerid" ))
{
x = max( 0, max( 2, (SCREEN_WIDTH - TextWidth)) / 2 );
y = (SCREEN_HEIGHT / 2) + (TextHeight * CVAR_GET_FLOAT( "hud_centerid" ));
}
DrawConsoleString( x, y, m_szStatusBar[i] );
}
return 1;
}
// Message handler for StatusText message
// accepts two values:
// byte: line number of status bar text
// string: status bar text
// this string describes how the status bar should be drawn
// a semi-regular expression:
// ( slotnum ([a..z] [%pX] [%iX])*)*
// where slotnum is an index into the Value table (see below)
// if slotnum is 0, the string is always drawn
// if StatusValue[slotnum] != 0, the following string is drawn, upto the next newline - otherwise the text is skipped upto next newline
// %pX, where X is an integer, will substitute a player name here, getting the player index from StatusValue[X]
// %iX, where X is an integer, will substitute a number here, getting the number from StatusValue[X]
int CHudStatusBar :: MsgFunc_StatusText( const char *pszName, int iSize, void *pbuf )
{
BEGIN_READ( pszName, iSize, pbuf );
int line = READ_BYTE();
if ( line < 0 || line >= MAX_STATUSBAR_LINES )
return 1;
strncpy( m_szStatusText[line], READ_STRING(), MAX_STATUSTEXT_LENGTH );
m_szStatusText[line][MAX_STATUSTEXT_LENGTH-1] = 0; // ensure it's null terminated ( strncpy() won't null terminate if read string too long)
if( m_szStatusText[0] == 0 )
m_iFlags &= ~HUD_ACTIVE;
else m_iFlags |= HUD_ACTIVE; // we have status text, so turn on the status bar
m_bReparseString = TRUE;
END_READ();
return 1;
}
// Message handler for StatusText message
// accepts two values:
// byte: index into the status value array
// short: value to store
int CHudStatusBar :: MsgFunc_StatusValue( const char *pszName, int iSize, void *pbuf )
{
BEGIN_READ( pszName, iSize, pbuf );
int index = READ_BYTE();
if( index < 1 || index >= MAX_STATUSBAR_VALUES )
return 1; // index out of range
m_iStatusValues[index] = READ_SHORT();
m_bReparseString = TRUE;
END_READ();
return 1;
}

204
client/hud/hud_text.cpp Normal file
View File

@ -0,0 +1,204 @@
/***
*
* 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.
*
****/
//
// text_message.cpp
//
// implementation of CHudTextMessage class
//
// this class routes messages through titles.txt for localisation
//
#include "extdll.h"
#include "hud_iface.h"
#include "hud.h"
DECLARE_MESSAGE( m_TextMessage, TextMsg );
int CHudTextMessage::Init(void)
{
HOOK_MESSAGE( TextMsg );
gHUD.AddHudElem( this );
Reset();
return 1;
};
// Searches through the string for any msg names (indicated by a '#')
// any found are looked up in titles.txt and the new message substituted
// the new value is pushed into dst_buffer
char *CHudTextMessage::LocaliseTextString( const char *msg, char *dst_buffer, int buffer_size )
{
char *dst = dst_buffer;
for ( char *src = (char*)msg; *src != 0 && buffer_size > 0; buffer_size-- )
{
if ( *src == '#' )
{
// cut msg name out of string
static char word_buf[255];
char *wdst = word_buf, *word_start = src;
for ( ++src ; (*src >= 'A' && *src <= 'z') || (*src >= '0' && *src <= '9'); wdst++, src++ )
{
*wdst = *src;
}
*wdst = 0;
// lookup msg name in titles.txt
client_textmessage_t *clmsg = TextMessageGet( word_buf );
if ( !clmsg || !(clmsg->pMessage) )
{
src = word_start;
*dst = *src;
dst++, src++;
continue;
}
// copy string into message over the msg name
for ( char *wsrc = (char*)clmsg->pMessage; *wsrc != 0; wsrc++, dst++ )
{
*dst = *wsrc;
}
*dst = 0;
}
else
{
*dst = *src;
dst++, src++;
*dst = 0;
}
}
dst_buffer[buffer_size-1] = 0; // ensure null termination
return dst_buffer;
}
// As above, but with a local static buffer
char *CHudTextMessage::BufferedLocaliseTextString( const char *msg )
{
static char dst_buffer[1024];
LocaliseTextString( msg, dst_buffer, 1024 );
return dst_buffer;
}
// Simplified version of LocaliseTextString; assumes string is only one word
char *CHudTextMessage::LookupString( const char *msg, int *msg_dest )
{
if ( !msg )
return "";
// '#' character indicates this is a reference to a string in titles.txt, and not the string itself
if ( msg[0] == '#' )
{
// this is a message name, so look up the real message
client_textmessage_t *clmsg = TextMessageGet( msg+1 );
if ( !clmsg || !(clmsg->pMessage) )
return (char*)msg; // lookup failed, so return the original string
if ( msg_dest )
{
// check to see if titles.txt info overrides msg destination
// if clmsg->effect is less than 0, then clmsg->effect holds -1 * message_destination
if ( clmsg->effect < 0 ) //
*msg_dest = -clmsg->effect;
}
return (char*)clmsg->pMessage;
}
else
{ // nothing special about this message, so just return the same string
return (char*)msg;
}
}
void StripEndNewlineFromString( char *str )
{
int s = strlen( str ) - 1;
if ( str[s] == '\n' || str[s] == '\r' )
str[s] = 0;
}
// converts all '\r' characters to '\n', so that the engine can deal with the properly
// returns a pointer to str
char* ConvertCRtoNL( char *str )
{
for ( char *ch = str; *ch != 0; ch++ )
if ( *ch == '\r' )
*ch = '\n';
return str;
}
// Message handler for text messages
// displays a string, looking them up from the titles.txt file, which can be localised
// parameters:
// byte: message direction ( HUD_PRINTCONSOLE, HUD_PRINTNOTIFY, HUD_PRINTCENTER, HUD_PRINTTALK )
// string: message
// optional parameters:
// string: message parameter 1
// string: message parameter 2
// string: message parameter 3
// string: message parameter 4
// any string that starts with the character '#' is a message name, and is used to look up the real message in titles.txt
// the next (optional) one to four strings are parameters for that string (which can also be message names if they begin with '#')
int CHudTextMessage::MsgFunc_TextMsg( const char *pszName, int iSize, void *pbuf )
{
BEGIN_READ( pszName, iSize, pbuf );
int msg_dest = READ_BYTE();
static char szBuf[6][128];
char *msg_text = LookupString( READ_STRING(), &msg_dest );
msg_text = strcpy( szBuf[0], msg_text );
// keep reading strings and using C format strings for subsituting the strings into the localised text string
char *sstr1 = LookupString( READ_STRING() );
sstr1 = strcpy( szBuf[1], sstr1 );
StripEndNewlineFromString( sstr1 ); // these strings are meant for subsitution into the main strings, so cull the automatic end newlines
char *sstr2 = LookupString( READ_STRING() );
sstr2 = strcpy( szBuf[2], sstr2 );
StripEndNewlineFromString( sstr2 );
char *sstr3 = LookupString( READ_STRING() );
sstr3 = strcpy( szBuf[3], sstr3 );
StripEndNewlineFromString( sstr3 );
char *sstr4 = LookupString( READ_STRING() );
sstr4 = strcpy( szBuf[4], sstr4 );
StripEndNewlineFromString( sstr4 );
char *psz = szBuf[5];
switch ( msg_dest )
{
case HUD_PRINTCENTER:
sprintf( psz, msg_text, sstr1, sstr2, sstr3, sstr4 );
CenterPrint( ConvertCRtoNL( psz ), SCREEN_HEIGHT / 2, BIGCHAR_WIDTH );
break;
case HUD_PRINTNOTIFY:
psz[0] = 1; // mark this message to go into the notify buffer
sprintf( psz+1, msg_text, sstr1, sstr2, sstr3, sstr4 );
ConsolePrint( ConvertCRtoNL( psz ) );
break;
case HUD_PRINTTALK:
sprintf( psz, msg_text, sstr1, sstr2, sstr3, sstr4 );
gHUD.m_SayText.SayTextPrint( ConvertCRtoNL( psz ), 128 );
break;
case HUD_PRINTCONSOLE:
sprintf( psz, msg_text, sstr1, sstr2, sstr3, sstr4 );
ConsolePrint( ConvertCRtoNL( psz ) );
break;
}
END_READ();
return 1;
}

79
client/hud/hud_train.cpp Normal file
View File

@ -0,0 +1,79 @@
/***
*
* 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.
*
****/
//
// Train.cpp
//
// implementation of CHudAmmo class
//
#include "extdll.h"
#include "hud_iface.h"
#include "hud.h"
DECLARE_MESSAGE( m_Train, Train )
int CHudTrain :: Init( void )
{
HOOK_MESSAGE( Train );
m_iPos = 0;
m_iFlags = 0;
gHUD.AddHudElem( this );
return 1;
}
int CHudTrain::VidInit( void )
{
m_hSprite = 0;
return 1;
}
int CHudTrain::Draw(float fTime)
{
if( !m_hSprite )
m_hSprite = LOAD_SHADER( "hud_train" );
if( m_iPos )
{
int r, g, b, x, y;
UnpackRGB( r, g, b, gHUD.m_iHUDColor );
SPR_Set( m_hSprite, r, g, b );
// This should show up to the right and part way up the armor number
y = SCREEN_HEIGHT - SPR_Height( m_hSprite, 0 ) - gHUD.m_iFontHeight;
x = SCREEN_WIDTH / 3 + SPR_Width( m_hSprite, 0 ) / 4;
SPR_DrawAdditive( m_iPos - 1, x, y, NULL);
}
return 1;
}
int CHudTrain::MsgFunc_Train( const char *pszName, int iSize, void *pbuf )
{
BEGIN_READ( pszName, iSize, pbuf );
// update Train data
m_iPos = READ_BYTE();
if( m_iPos )
m_iFlags |= HUD_ACTIVE;
else m_iFlags &= ~HUD_ACTIVE;
END_READ();
return 1;
}

356
client/hud/hud_utils.cpp Normal file
View File

@ -0,0 +1,356 @@
//=======================================================================
// Copyright XashXT Group 2008 ©
// hud_utils.cpp - client game utilities code
//=======================================================================
#include "extdll.h"
#include "hud_iface.h"
#include "hud.h"
// NOTE: modify these functions with caution
typedef struct
{
char *name;
byte *buf;
int size;
int read;
BOOL badRead;
char string[2048]; // using for store strings
} user_message_t;
static user_message_t gMsg;
void BEGIN_READ( const char *pszName, int iSize, void *pBuf )
{
memset( &gMsg, 0, sizeof( gMsg ));
gMsg.size = iSize;
gMsg.buf = (byte *)pBuf;
}
void END_READ( void )
{
if( gMsg.badRead )
{
ALERT( at_console, "%s was received with errors\n", gMsg.name );
}
}
int READ_CHAR( void )
{
int c;
if( gMsg.read + 1 > gMsg.size )
{
gMsg.badRead = true;
return -1;
}
c = (signed char)gMsg.buf[gMsg.read];
gMsg.read++;
return c;
}
int READ_BYTE( void )
{
int c;
if( gMsg.read+1 > gMsg.size )
{
gMsg.badRead = true;
return -1;
}
c = (unsigned char)gMsg.buf[gMsg.read];
gMsg.read++;
return c;
}
int READ_SHORT( void )
{
int c;
if( gMsg.read + 2 > gMsg.size )
{
gMsg.badRead = true;
return -1;
}
c = (short)( gMsg.buf[gMsg.read] + ( gMsg.buf[gMsg.read+1] << 8 ));
gMsg.read += 2;
return c;
}
int READ_WORD( void ) { return READ_SHORT(); }
int READ_LONG( void )
{
int c;
if( gMsg.read + 4 > gMsg.size )
{
gMsg.badRead = true;
return -1;
}
c = gMsg.buf[gMsg.read]+(gMsg.buf[gMsg.read+1]<<8)+(gMsg.buf[gMsg.read+2]<<16)+(gMsg.buf[gMsg.read+3]<<24);
gMsg.read += 4;
return c;
}
float READ_FLOAT( void )
{
union { float f; int l; } dat;
dat.l = READ_LONG();
return dat.f;
}
char* READ_STRING( void )
{
int l, c;
gMsg.string[0] = 0;
l = 0;
do
{
if( gMsg.read+1 > gMsg.size ) break; // no more characters
c = READ_CHAR();
if( c == -1 || c == '\0' )
break;
// translate all fmt spec to avoid crash bugs
if( c == '%' ) c = '.';
gMsg.string[l] = c;
l++;
} while( l < sizeof( gMsg.string ) - 1 );
gMsg.string[l] = 0; // terminator
return gMsg.string;
}
//
// Xash3D network specs. Don't modify!
//
float READ_COORD( void )
{
return READ_FLOAT();
}
float READ_ANGLE( void )
{
return READ_FLOAT();
}
float READ_ANGLE16( void )
{
return (float)(READ_SHORT() * (360.0 / 65536));
}
//
// Sprites draw stuff
//
typedef struct
{
// temp handle
HSPRITE hSprite;
// crosshair members
HSPRITE hCrosshair;
wrect_t rcCrosshair;
Vector rgbCrosshair;
} draw_stuff_t;
typedef struct
{
float fadeSpeed; // How fast to fade (tics / second) (+ fade in, - fade out)
float fadeEnd; // When the fading hits maximum
float fadeTotalEnd; // Total End Time of the fade (used for FFADE_OUT)
float fadeReset; // When to reset to not fading (for fadeout and hold)
Vector fadeColor;
float fadealpha;
int fadeFlags; // Fading flags
} screenfade_t;
static draw_stuff_t ds;
static screenfade_t sf;
int SPR_Frames( HSPRITE hPic )
{
// FIXME: engfuncs GetImageFrames
return 1;
}
int SPR_Height( HSPRITE hPic, int frame )
{
int Height;
GetImageSize( NULL, &Height, hPic );
return Height;
}
int SPR_Width( HSPRITE hPic, int frame )
{
int Width;
GetImageSize( &Width, NULL, hPic );
return Width;
}
void SPR_Set( HSPRITE hPic, int r, int g, int b )
{
ds.hSprite = hPic;
SetColor((r / 255.0f), (g / 255.0f), (b / 255.0f), 1.0f );
}
void SPR_Draw( int frame, int x, int y, const wrect_t *prc )
{
// FIXME: switch rendermode
DrawImage( ds.hSprite, x, y, prc->right, prc->bottom, frame );
}
void SPR_Draw( int frame, int x, int y, int width, int height )
{
// FIXME: switch rendermode
DrawImage( ds.hSprite, x, y, width, height, frame );
}
void SPR_DrawHoles( int frame, int x, int y, const wrect_t *prc )
{
// FIXME: switch rendermode
DrawImage( ds.hSprite, x, y, prc->right, prc->bottom, frame );
}
void SPR_DrawHoles( int frame, int x, int y, int width, int height )
{
// FIXME: switch rendermode
DrawImage( ds.hSprite, x, y, width, height, frame );
}
void SPR_DrawAdditive( int frame, int x, int y, const wrect_t *prc )
{
// FIXME: switch rendermode
DrawImage( ds.hSprite, x, y, prc->right, prc->bottom, frame );
}
void SetCrosshair( HSPRITE hspr, wrect_t rc, int r, int g, int b )
{
ds.rgbCrosshair.x = (float)(r / 255.0f);
ds.rgbCrosshair.y = (float)(g / 255.0f);
ds.rgbCrosshair.z = (float)(b / 255.0f);
ds.hCrosshair = hspr;
ds.rcCrosshair = rc;
}
void DrawCrosshair( void )
{
if( ds.hCrosshair == 0 ) return;
// find center of screen
int x = (SCREEN_WIDTH - ds.rcCrosshair.right) / 2;
int y = (SCREEN_HEIGHT - ds.rcCrosshair.bottom) / 2;
// FIXME: apply crosshair angles
SetColor( ds.rgbCrosshair.x, ds.rgbCrosshair.y, ds.rgbCrosshair.z, 1.0f );
DrawImage( ds.hCrosshair, x, y, ds.rcCrosshair.right, ds.rcCrosshair.bottom, 0 );
}
void SetScreenFade( Vector fadeColor, float alpha, float duration, float holdTime, int fadeFlags )
{
sf.fadeColor = fadeColor;
sf.fadealpha = alpha;
sf.fadeFlags = fadeFlags;
sf.fadeEnd = gHUD.m_flTime + duration;
sf.fadeTotalEnd = sf.fadeEnd + holdTime;
sf.fadeSpeed = duration / gHUD.m_flTimeDelta;
}
void DrawScreenFade( void )
{
// FIXME: implement
}
/*
====================
Sys LoadGameDLL
====================
*/
BOOL Sys_LoadLibrary( const char* dllname, dllhandle_t* handle, const dllfunction_t *fcts )
{
const dllfunction_t *gamefunc;
char dllpath[256], gamedir[256];
dllhandle_t dllhandle = 0;
if( handle == NULL ) return false;
// Initializations
for( gamefunc = fcts; gamefunc && gamefunc->name != NULL; gamefunc++ )
*gamefunc->funcvariable = NULL;
GET_GAME_DIR( gamedir );
sprintf( dllpath, "%s/bin/%s", gamedir, dllname );
dllhandle = LoadLibrary( dllpath );
// No DLL found
if( !dllhandle ) return false;
// Get the function adresses
for( gamefunc = fcts; gamefunc && gamefunc->name != NULL; gamefunc++ )
if(!( *gamefunc->funcvariable = (void *) Sys_GetProcAddress( dllhandle, gamefunc->name )))
{
Sys_UnloadLibrary( &dllhandle );
return false;
}
ALERT( at_loading, "%s loaded succesfully!\n", dllname );
*handle = dllhandle;
return true;
}
void Sys_UnloadLibrary( dllhandle_t *handle )
{
if( handle == NULL || *handle == NULL )
return;
FreeLibrary( *handle );
*handle = NULL;
}
void* Sys_GetProcAddress( dllhandle_t handle, const char* name )
{
return (void *)GetProcAddress( handle, name );
}
/*
============
va
does a varargs printf into a temp buffer, so I don't need to have
varargs versions of all text functions.
FIXME: make this buffer size safe someday
============
*/
char *va( const char *format, ... )
{
va_list argptr;
static char string[16][1024], *s;
static int stringindex = 0;
s = string[stringindex];
stringindex = (stringindex + 1) & 15;
va_start( argptr, format );
_vsnprintf( s, sizeof( string[0] ), format, argptr );
va_end( argptr );
return s;
}

141
client/hud/hud_warhead.cpp Normal file
View File

@ -0,0 +1,141 @@
//=======================================================================
// Copyright (C) Shambler Team 2004
// warhead.cpp - hud for weapon_redeemer
//
//=======================================================================
#include "extdll.h"
#include "hud_iface.h"
#include "hud.h"
#define GUIDE_S SPR_Width( m_hCrosshair, 0 )
#define READOUT_S 128
DECLARE_MESSAGE( m_Redeemer, WarHUD )
int CHudRedeemer::Init( void )
{
m_iHudMode = 0;
HOOK_MESSAGE( WarHUD );
m_iFlags |= HUD_ACTIVE;
gHUD.AddHudElem( this );
return 1;
}
int CHudRedeemer :: VidInit( void )
{
m_hSprite = LOAD_SHADER( "wh_readout" );
m_hCrosshair = LOAD_SHADER( "wh_guidedx");
m_hStatic = LOAD_SHADER( "wh_static");
m_hCamera = LOAD_SHADER( "wh_camera");
m_hCamRec = LOAD_SHADER( "wh_cam_rec");
m_iHudMode = 0;
return 1;
}
int CHudRedeemer :: MsgFunc_WarHUD( const char *pszName, int iSize, void *pbuf )
{
BEGIN_READ( pszName, iSize, pbuf );
m_iHudMode = READ_BYTE();
END_READ();
return 1;
}
int CHudRedeemer :: Draw( float flTime )
{
int x, y, w, h;
wrect_t rc;
int frame;
// setup screen rectangle
rc.left = rc.top = 0;
rc.right = SCREEN_WIDTH;
rc.bottom = SCREEN_HEIGHT;
if( m_iHudMode == 1 ) // draw crosshair and readout
{
y = (SCREEN_WIDTH - GUIDE_S) / 2;
x = (SCREEN_HEIGHT - GUIDE_S) / 2;
SPR_Set( m_hCrosshair, 255, 128, 128 );
SPR_DrawAdditive( 0, y, x, NULL);
int yOffset = ((int)(flTime * 850) % READOUT_S) - READOUT_S;
SPR_Set( m_hSprite, 255, 128, 128 );
for( ; yOffset < SCREEN_HEIGHT; yOffset += READOUT_S )
SPR_DrawAdditive( 0, 0, yOffset, NULL );
SetScreenFade( Vector( 1, 0, 0 ), 0.25, 0, 0, FFADE_STAYOUT ); // enable red fade
}
else if( m_iHudMode == 2 ) // draw alpha noise
{
SPR_Set(m_hStatic, 255, 255, 255 );
// play at 15fps
frame = (int)(flTime * 15) % SPR_Frames( m_hStatic );
y = x = 0;
w = SPR_Width( m_hStatic, 0);
h = SPR_Height( m_hStatic, 0);
for( y = -(rand() % h); y < SCREEN_HEIGHT; y += h )
for( x = -(rand() % w); x < SCREEN_WIDTH; x += w )
SPR_DrawAdditive ( frame, x, y, NULL );
y = (SCREEN_WIDTH - GUIDE_S) / 2;
x = (SCREEN_HEIGHT - GUIDE_S) / 2;
SPR_Set( m_hCrosshair, 255, 128, 128 );
SPR_DrawAdditive( 0, y, x, NULL );
int yOffset = ((int)(flTime * 850) % READOUT_S) - READOUT_S;
SPR_Set(m_hSprite, 255, 128, 128 );
for( ; yOffset < SCREEN_HEIGHT; yOffset += READOUT_S )
SPR_DrawAdditive( 0, 0, yOffset, NULL );
SetScreenFade( Vector( 1, 0, 0 ), 0.25, 0, 0, FFADE_STAYOUT ); // enable red fade
}
else if( m_iHudMode == 3 ) // draw static noise
{
// play at 25fps
frame = (int)(flTime * 25) % SPR_Frames( m_hStatic );
SPR_Set( m_hStatic, 255, 255, 255 );
SPR_Draw( frame, 0, 0, &rc );
// disable fade
SetScreenFade( Vector( 1, 1, 1 ), 0, 0, 0, FFADE_OUT );
}
else if( m_iHudMode == 4 ) // draw videocamera screen
{
// play at 15fps
frame = (int)(flTime * 1.5f) % SPR_Frames( m_hCamRec );
// draw interlaces
SPR_Set( m_hCamera, 16, 96, 16 ); //, 64 ); // give this value from breaklight.bsp (xash03)
SPR_DrawAdditive( frame, 0, 0, &rc );
// draw recorder icon
SPR_Set( m_hCamRec, 255, 0, 0 ); //, 255 ); // give this value from breaklight.bsp (xash03)
float scale = 2.5f; // REC[*] sprite scale
wrect_t m_rcCamRec;
// calculating pos with different resolutions
w = SPR_Width( m_hCamRec, 0 ) * scale;
h = SPR_Height( m_hCamRec, 0 ) * scale;
x = SCREEN_WIDTH - (w * 1.5f);
y = w * 0.4f;
m_rcCamRec.left = x;
m_rcCamRec.right = x + w;
m_rcCamRec.top = y;
m_rcCamRec.bottom = y + h;
SPR_DrawAdditive( frame, 0, 0, &m_rcCamRec );
// disable fade
SetScreenFade( Vector( 1, 1, 1 ), 0, 0, 0, FFADE_OUT );
}
return 1;
}

80
client/hud/hud_zoom.cpp Normal file
View File

@ -0,0 +1,80 @@
//=======================================================================
// Copyright (C) Shambler Team 2004
// warhead.cpp - hud for weapon_redeemer
//
//=======================================================================
#include "extdll.h"
#include "hud_iface.h"
#include "hud.h"
void DrawQuad(float xmin, float ymin, float xmax, float ymax)
{
//top left
g_engfuncs.pTriAPI->TexCoord2f(0,0);
g_engfuncs.pTriAPI->Vertex3f(xmin, ymin, 0);
//bottom left
g_engfuncs.pTriAPI->TexCoord2f(0,1);
g_engfuncs.pTriAPI->Vertex3f(xmin, ymax, 0);
//bottom right
g_engfuncs.pTriAPI->TexCoord2f(1,1);
g_engfuncs.pTriAPI->Vertex3f(xmax, ymax, 0);
//top right
g_engfuncs.pTriAPI->TexCoord2f(1,0);
g_engfuncs.pTriAPI->Vertex3f(xmax, ymin, 0);
}
DECLARE_MESSAGE( m_Zoom, ZoomHUD )
int CHudZoom :: Init( void )
{
m_iHudMode = 0;
HOOK_MESSAGE( ZoomHUD );
m_iFlags |= HUD_ACTIVE;
gHUD.AddHudElem( this );
return 1;
}
int CHudZoom::VidInit( void )
{
m_hLines = LOAD_SHADER( "sniper_lines" );
m_hCrosshair = LOAD_SHADER( "sniper_zoom" );
m_iHudMode = 0;
return 1;
}
int CHudZoom :: MsgFunc_ZoomHUD( const char *pszName, int iSize, void *pbuf )
{
BEGIN_READ( pszName, iSize, pbuf );
m_iHudMode = READ_BYTE();
END_READ();
return 1;
}
int CHudZoom :: Draw( float flTime )
{
if( !m_hLines || !m_hCrosshair ) return 0;
if( !m_iHudMode ) return 0; // draw scope
float left = (SCREEN_WIDTH - SCREEN_HEIGHT)/2;
float right = left + SCREEN_HEIGHT;
float centerx = SCREEN_WIDTH / 2;
float centery = SCREEN_HEIGHT / 2;
// draw crosshair
SPR_Set( m_hCrosshair, 255, 255, 255 );
SPR_DrawHoles( 0, left, 0, centerx, centery);
SPR_DrawHoles( 1, centerx, 0, right, centery);
SPR_DrawHoles( 2, centerx, centery, right, SCREEN_HEIGHT);
SPR_DrawHoles( 3, left, centery, centerx, SCREEN_HEIGHT);
// draw side-lines
SPR_Set( m_hLines, 255, 255, 255 );
SPR_Draw( 0, 0, 0, left, SCREEN_HEIGHT );
SPR_Draw( 0, right, 0, SCREEN_WIDTH, SCREEN_HEIGHT );
return 1;
}

348
client/r_main.h Normal file
View File

@ -0,0 +1,348 @@
//=======================================================================
// Copyright (C) XashXT Group 2006
//=======================================================================
#pragma once
#define WIN32_LEAN_AND_MEAN
#define GLEW_STATIC
#include "windows.h"
#include "glew.h"
#include <gl\glu.h>
#include "cva.h"
#include "ref_params.h"
#include "com_model.h"
#pragma warning(disable:4244)
#define SAFE_GET_PROC( func, type, name) if (!func) func = (type) wglGetProcAddress( name ); \
assert(func != NULL)
#define VIEWPORT_SIZE 512
#define GL_TEXTURE_NUM_BASE (1<<25)
#define SURF_PLANEBACK 2
#define SURF_DRAWTURB 0x10
#define SURF_MIRROR (1<<16)
#define SURF_WARPMIRROR (1<<17)
#define SURF_SCREEN (1<<18)
#define SURF_PORTAL (1<<19)
// 0-2 are axial planes
#define PLANE_X 0
#define PLANE_Y 1
#define PLANE_Z 2
#define MAX_CVA_VERTS 2048
//We need the following extensions:
//ARB_multitexture
extern PFNGLACTIVETEXTUREARBPROC glActiveTextureARB;
extern PFNGLCLIENTACTIVETEXTUREARBPROC glClientActiveTextureARB;
extern PFNGLMULTITEXCOORD1FARBPROC glMultiTexCoord1fARB;
extern PFNGLMULTITEXCOORD2FARBPROC glMultiTexCoord2fARB;
extern PFNGLMULTITEXCOORD2FVARBPROC glMultiTexCoord2fvARB;
extern PFNGLMULTITEXCOORD3FARBPROC glMultiTexCoord3fARB;
extern PFNGLMULTITEXCOORD3FVARBPROC glMultiTexCoord3fvARB;
extern PFNGLMULTITEXCOORD4FARBPROC glMultiTexCoord4fARB;
extern PFNGLMULTITEXCOORD4FVARBPROC glMultiTexCoord4fvARB;
//EXT_texture_3d
extern PFNGLTEXIMAGE3DEXTPROC glTexImage3DEXT;
//ARB_texture_cube_map
//ARB_vertex_program
//ARB_fragment_program
extern PFNGLPROGRAMSTRINGARBPROC glProgramStringARB;
extern PFNGLBINDPROGRAMARBPROC glBindProgramARB;
extern PFNGLDELETEPROGRAMSARBPROC glDeleteProgramsARB;
extern PFNGLGENPROGRAMSARBPROC glGenProgramsARB;
extern PFNGLGETPROGRAMIVARBPROC glGetProgramivARB;
extern PFNGLGETPROGRAMSTRINGARBPROC glGetProgramStringARB;
extern PFNGLISPROGRAMARBPROC glIsProgramARB;
//NV_register_combiners
extern PFNGLCOMBINERPARAMETERFVNVPROC glCombinerParameterfvNV;
extern PFNGLCOMBINERPARAMETERIVNVPROC glCombinerParameterivNV;
extern PFNGLCOMBINERPARAMETERFNVPROC glCombinerParameterfNV;
extern PFNGLCOMBINERPARAMETERINVPROC glCombinerParameteriNV;
extern PFNGLCOMBINERINPUTNVPROC glCombinerInputNV;
extern PFNGLCOMBINEROUTPUTNVPROC glCombinerOutputNV;
extern PFNGLFINALCOMBINERINPUTNVPROC glFinalCombinerInputNV;
//ATI_pn_triangles
extern PFNGLPNTRIANGLESIATIPROC glPNTrianglesiATI;
extern PFNGLPNTRIANGLESFATIPROC glPNTrianglesfATI;
inline bool GLEXT_CheckExtension(const char *ext)
{
return (strstr((const char *)glGetString(GL_EXTENSIONS), ext) != NULL);
}
bool GLEXT_Setup_ARB_multitexture();
bool GLEXT_Setup_EXT_texture_3d();
bool GLEXT_Setup_ARB_texture_cubemap();
bool GLEXT_Setup_ARB_vertex_program();
bool GLEXT_Setup_ARB_fragment_program();
bool GLEXT_Setup_NV_register_combiners();
bool GLEXT_Setup_ATI_pn_triangles();
void GL_Init( void );
void GL_VidInit( void );
void GL_Shutdown( void );
void GL_PreRender( void );
void GL_Render( bool trans );
void GL_ExtractFrustum( void );
void GL_MarkTextures( void );
void R_Draw( bool transparent );
//Utils
float R_SphereInFrustum( vec3_t o, float radius );
cl_entity_t *UTIL_GetClientEntityWithServerIndex( int sv_index );
extern bool g_HardwareCapable;
extern bool g_HardwareShaderCapable;
extern int g_iTotalMirrors;
extern int g_iTotalVisibleMirrors;
extern int g_iTotalScreens;
extern int g_iTotalVisibleScreens;
extern int g_iTotalPortals;
extern int g_iTotalVisiblePortals;
extern double realtime;
extern bool g_FirstFrame;
extern bool g_RenderReady;
extern bool g_bFinalPass;
extern bool g_bEndCalc;
extern int m_RenderRefCount; //refcounter (use for debug)
//passes info
extern bool g_bMirrorShouldpass;
extern bool g_bScreenShouldpass;
extern bool g_bPortalShouldpass;
extern bool g_bSkyShouldpass;
extern bool g_bMirrorPass;
extern bool g_bPortalPass;
extern bool g_bScreenPass;
extern bool g_bSkyPass;
//base origin and angles
extern vec3_t g_vecBaseOrigin; //base origin - transformed always
extern vec3_t g_vecBaseAngles; //base angles - transformed always
extern vec3_t g_vecCurrentOrigin; //current origin
extern vec3_t g_vecCurrentAngles; //current angles
extern float r_projection_matrix[16];
extern float r_world_matrix[16];
extern unsigned int g_uiNoiseTex;
extern unsigned int g_uiNoiseTexDsDt;
extern unsigned int pTexId;
extern unsigned int AllocateTextureIndex(void);
typedef struct mirror_s
{
int index;
bool enabled;
bool visible;
float origin[3];
float angles[3];
float normal[3];
float radius;
float alpha;
float pr[16];
bool water;
bool twoside;
//CVA-related
int firstvertex;
int numvertices;
unsigned int texture;
msurface_t *surface;
int sv_entindex;
cl_entity_t *ent; //for brush model mirrors
struct mirror_s *next;
}mirror_t;
typedef struct screen_s
{
int index;
bool enabled;
bool visible;
float origin[3];
float radius;
float alpha;
//current camera member
int cam_idx;
float cam_origin[3];
float cam_angles[3];
float pr[16];
//CVA-related
int firstvertex;
int numvertices;
unsigned int texture;
msurface_t *surface;
int sv_entindex;
cl_entity_t *ent;
bool color;
struct screen_s *next;
}screen_t;
typedef struct portal_s
{
int index;
bool enabled;
bool visible;
float origin[3];
float angles[3];
float normal[3];
float radius;
float alpha;
//current camera member
int cam_idx;
float cam_origin[3];
float cam_angles[3];
float pr[16];
//CVA-related
int firstvertex;
int numvertices;
unsigned int texture;
msurface_t *surface;
int sv_entindex;
cl_entity_t *ent; //for brush model mirrors
struct portal_s *next;
}portal_t;
typedef struct cl_sbe_s
{
int index;
bool initialized;
}cl_sbe_t;
extern cvar_t *r_debug; //show renderer info
extern cvar_t *r_shadows; //original HL shadows
extern cvar_t *r_mirrors; //mirrors
extern cvar_t *r_screens; //screens
extern cvar_t *r_portals; //portals
mirror_t *R_AllocateMirror(msurface_t *surf);
void R_FreeMirrors(void);
void R_InitMirrors(void);
void R_SetupMirrorRenderPass(ref_params_t *pparams);
void R_SetupNewMirror(ref_params_t *pparams);
void R_NewMirrorRenderPass(void);
void R_RenderMirrors(bool trans);
void R_InitMirrorsForFrame(void);
void R_ResetMirrors(void);
void AddMirrorBrushEntity( int idx );
void R_DrawMirror(bool trans);
screen_t *R_AllocateScreen(msurface_t *surf);
void R_ResetScreens( void );
void R_FreeScreens( void );
void R_InitScreens( void );
void R_RenderScreens( bool trans );
void AddScreenBrushEntity( int idx );
void R_SetupScreenRenderPass(ref_params_t *pparams);
void R_SetupNewScreen(ref_params_t *pparams);
void R_NewScreenRenderPass( void );
void R_InitScreensForFrame(void);
void R_DrawScreen(bool trans);
portal_t *R_AllocatePortal(msurface_t *surf);
void R_FreePortals(void);
void R_InitPortals(void);
void R_SetupPortalRenderPass(ref_params_t *pparams);
void R_SetupNewPortal(ref_params_t *pparams);
void R_NewPortalRenderPass(void);
void R_RenderPortals(bool trans);
void R_InitPortalsForFrame(void);
void R_ResetPortals(void);
void AddPortalBrushEntity( int idx );
void R_DrawPortal(bool trans);
//Noise
void CreateNoiseTexture3D (float scale);
void CreateNoiseTextureDsDt (float scale);
//Water
void CreateWaterShader_ARB(void);
void DeleteWaterShader_ARB(void);
void BindWaterShader_ARB(mirror_t *mir);
void UnbindWaterShader_ARB(void);
//Black & white screen
void CreateScreenShader_ARB(void);
void DeleteScreenShader_ARB(void);
//Logging
void logInitLog( char *filename );
void logCloseLog();
void logPrintOpenGLInformation( void );
void logPrint( char *str );
void logPrintf( char *fmt, ...);
extern mirror_t *m_pCurrentMirror;
extern screen_t *m_pCurrentScreen;
extern portal_t *m_pCurrentPortal;
//GL state management
struct glstate_curstate_t
{
unsigned char
blend : 1,
depth_test : 1,
: 6;
};
struct glstate_curstagestate_t
{
unsigned char
texture2d : 1,
texture3d : 1,
: 6;
};
typedef struct
{
struct glstate_curstate_t curstate;
struct glstate_curstagestate_t curstagestate[8]; //assume 8 will be max texture stages
unsigned int current_texture_stage;
} glstate_t;
extern glstate_t glstate;
#define GLSTATE_ENABLE_BLEND if(!glstate.curstate.blend) { glEnable(GL_BLEND); glstate.curstate.blend=1; }
#define GLSTATE_ENABLE_DEPTH_TEST if(!glstate.curstate.depth_test) { glEnable(GL_DEPTH_TEST); glstate.curstate.depth_test=1; }
#define GLSTATE_ENABLE_TEXTURE if(!glstate.curstagestate[glstate.current_texture_stage].texture2d) { glEnable(GL_TEXTURE_2D); glstate.curstagestate[glstate.current_texture_stage].texture2d=1; }
#define GLSTATE_ENABLE_TEXTURE3D if(!glstate.curstagestate[glstate.current_texture_stage].texture3d) { glEnable(GL_TEXTURE_3D); glstate.curstagestate[glstate.current_texture_stage].texture3d=1; }
#define GLSTATE_DISABLE_BLEND if(glstate.curstate.blend) { glDisable(GL_BLEND); glstate.curstate.blend=0; }
#define GLSTATE_DISABLE_DEPTH_TEST if(glstate.curstate.depth_test) { glDisable(GL_DEPTH_TEST); glstate.curstate.depth_test=0; }
#define GLSTATE_DISABLE_TEXTURE if(glstate.curstagestate[glstate.current_texture_stage].texture2d) { glDisable(GL_TEXTURE_2D); glstate.curstagestate[glstate.current_texture_stage].texture2d=0; }
#define GLSTATE_DISABLE_TEXTURE3D if(glstate.curstagestate[glstate.current_texture_stage].texture3d) { glDisable(GL_TEXTURE_3D); glstate.curstagestate[glstate.current_texture_stage].texture3d=0; }
void GL_SelectTexture( GLenum mode );
//client sprite renderer
void R_DrawSprite ( cl_entity_t *e, HSPRITE m_hSprite );
mspriteframe_t *R_GetSpriteFrame (model_t *mod, int frame);

960
client/r_particle.cpp Normal file
View File

@ -0,0 +1,960 @@
#include "hud.h"
#include "r_main.h"
#include "r_util.h"
#include "const.h"
#include "entity_state.h"
#include "event_api.h"
#include "cl_entity.h"
#include "triangleapi.h"
#include "r_particle.h"
#include "com_model.h"
#include "pmtrace.h" // for contents and traceline
#include "pm_defs.h"
ParticleSystemManager* g_pParticleSystems = NULL;
void CreateAurora( int idx, char *file )
{
ParticleSystem *pSystem = new ParticleSystem( idx, file );
g_pParticleSystems->AddSystem(pSystem);
}
ParticleSystemManager::ParticleSystemManager( void )
{
m_pFirstSystem = NULL;
}
ParticleSystemManager::~ParticleSystemManager( void )
{
ClearSystems();
}
void ParticleSystemManager::AddSystem( ParticleSystem* pNewSystem )
{
pNewSystem->m_pNextSystem = m_pFirstSystem;
m_pFirstSystem = pNewSystem;
}
ParticleSystem *ParticleSystemManager::FindSystem( cl_entity_t* pEntity )
{
for (ParticleSystem *pSys = m_pFirstSystem; pSys; pSys = pSys->m_pNextSystem)
{
if (pEntity->index == pSys->m_iEntIndex) return pSys;
}
return NULL;
}
void ParticleSystemManager::UpdateSystems( void )
{
static float fOldTime, fTime;
fOldTime = fTime;
fTime = gEngfuncs.GetClientTime();
float frametime = fTime - fOldTime;
ParticleSystem* pSystem;
ParticleSystem* pLast = NULL;
ParticleSystem*pLastSorted = NULL;
pSystem = m_pFirstSystem;
while( pSystem )
{
if( pSystem->UpdateSystem(frametime))
{
pSystem->DrawSystem();
pLast = pSystem;
pSystem = pSystem->m_pNextSystem;
}
else // delete this system
{
if (pLast)
{
pLast->m_pNextSystem = pSystem->m_pNextSystem;
delete pSystem;
pSystem = pLast->m_pNextSystem;
}
else // deleting the first system
{
m_pFirstSystem = pSystem->m_pNextSystem;
delete pSystem;
pSystem = m_pFirstSystem;
}
}
}
gEngfuncs.pTriAPI->RenderMode(kRenderNormal);
}
void ParticleSystemManager::ClearSystems( void )
{
ParticleSystem* pSystem = m_pFirstSystem;
ParticleSystem* pTemp;
while( pSystem )
{
pTemp = pSystem->m_pNextSystem;
delete pSystem;
pSystem = pTemp;
}
m_pFirstSystem = NULL;
}
float ParticleSystem::c_fCosTable[360 + 90];
bool ParticleSystem::c_bCosTableInit = false;
ParticleType::ParticleType( ParticleType *pNext )
{
m_pSprayType = m_pOverlayType = NULL;
m_StartAngle = RandomRange(45);
m_hSprite = 0;
m_pNext = pNext;
m_szName[0] = 0;
m_StartRed = m_StartGreen = m_StartBlue = m_StartAlpha = RandomRange(1);
m_EndRed = m_EndGreen = m_EndBlue = m_EndAlpha = RandomRange(1);
m_iRenderMode = kRenderTransAdd;
m_iDrawCond = 0;
m_bEndFrame = false;
m_bIsDefined = false;
m_iCollision = 0;
}
particle* ParticleType::CreateParticle(ParticleSystem *pSys)//particle *pPart)
{
if (!pSys) return NULL;
particle *pPart = pSys->ActivateParticle();
if (!pPart) return NULL;
pPart->age = 0.0;
pPart->age_death = m_Life.GetInstance();
InitParticle(pPart, pSys);
return pPart;
}
void ParticleType::InitParticle(particle *pPart, ParticleSystem *pSys)
{
float fLifeRecip = 1/pPart->age_death;
particle *pOverlay = NULL;
if (m_pOverlayType)
{
// create an overlay for this particle
pOverlay = pSys->ActivateParticle();
if (pOverlay)
{
pOverlay->age = pPart->age;
pOverlay->age_death = pPart->age_death;
m_pOverlayType->InitParticle(pOverlay, pSys);
}
}
pPart->m_pOverlay = pOverlay;
pPart->pType = this;
pPart->velocity[0] = pPart->velocity[1] = pPart->velocity[2] = 0;
pPart->accel[0] = pPart->accel[1] = 0;
pPart->accel[2] = m_Gravity.GetInstance();
pPart->m_iEntIndex = 0;
if (m_pSprayType)
{
pPart->age_spray = 1/m_SprayRate.GetInstance();
}
else
{
pPart->age_spray = 0.0f;
}
pPart->m_fSize = m_StartSize.GetInstance();
if (m_EndSize.IsDefined())
pPart->m_fSizeStep = m_EndSize.GetOffset(pPart->m_fSize) * fLifeRecip;
else
pPart->m_fSizeStep = m_SizeDelta.GetInstance();
//pPart->m_fSizeStep = m_EndSize.GetOffset(pPart->m_fSize) * fLifeRecip;
pPart->frame = m_StartFrame.GetInstance();
if (m_EndFrame.IsDefined())
pPart->m_fFrameStep = m_EndFrame.GetOffset(pPart->frame) * fLifeRecip;
else pPart->m_fFrameStep = m_FrameRate.GetInstance();
pPart->m_fAlpha = m_StartAlpha.GetInstance();
pPart->m_fAlphaStep = m_EndAlpha.GetOffset(pPart->m_fAlpha) * fLifeRecip;
pPart->m_fRed = m_StartRed.GetInstance();
pPart->m_fRedStep = m_EndRed.GetOffset(pPart->m_fRed) * fLifeRecip;
pPart->m_fGreen = m_StartGreen.GetInstance();
pPart->m_fGreenStep = m_EndGreen.GetOffset(pPart->m_fGreen) * fLifeRecip;
pPart->m_fBlue = m_StartBlue.GetInstance();
pPart->m_fBlueStep = m_EndBlue.GetOffset(pPart->m_fBlue) * fLifeRecip;
pPart->m_fAngle = m_StartAngle.GetInstance();
pPart->m_fAngleStep = m_AngleDelta.GetInstance();
pPart->m_fDrag = m_Drag.GetInstance();
float fWindStrength = m_WindStrength.GetInstance();
float fWindYaw = m_WindYaw.GetInstance();
pPart->m_vecWind.x = fWindStrength*ParticleSystem::CosLookup(fWindYaw);
pPart->m_vecWind.y = fWindStrength*ParticleSystem::SinLookup(fWindYaw);
pPart->m_vecWind.z = 0;
}
//============================================
RandomRange::RandomRange( char *szToken )
{
char *cOneDot = NULL;
m_bDefined = true;
for (char *c = szToken; *c; c++)
{
if (*c == '.')
{
if (cOneDot != NULL)
{
// found two dots in a row - it's a range
*cOneDot = 0; // null terminate the first number
m_fMin = atof(szToken); // parse the first number
*cOneDot = '.'; // change it back, just in case
c++;
m_fMax = atof(c); // parse the second number
return;
}
else
{
cOneDot = c;
}
}
else
{
cOneDot = NULL;
}
}
// no range, just record the number
m_fMin = m_fMax = atof(szToken);
}
//============================================
ParticleSystem::ParticleSystem( int iEntIndex, char *szFilename )
{
int iParticles = 100; // default
m_iEntIndex = iEntIndex;
m_pNextSystem = NULL;
m_pFirstType = NULL;
if (!c_bCosTableInit)
{
for (int i = 0; i < 360+90; i++)
{
c_fCosTable[i] = cos(i*M_PI/180.0);
}
c_bCosTableInit = true;
}
char *szFile = (char *)gEngfuncs.COM_LoadFile( szFilename, 5, NULL);
char szToken[1024];
if (!szFile)
{
Msg("Particle %s not found.\n", szFilename );
return;
}
else
{
szFile = gEngfuncs.COM_ParseFile(szFile, szToken);
while (szFile)
{
if ( !stricmp( szToken, "particles" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
iParticles = atof(szToken);
}
else if ( !stricmp( szToken, "maintype" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
m_pMainType = AddPlaceholderType(szToken);
}
else if ( !stricmp( szToken, "attachment" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
m_iEntAttachment = atof(szToken);
}
else if ( !stricmp( szToken, "killcondition" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
if ( !stricmp( szToken, "empty" ) )
{
m_iKillCondition = CONTENTS_EMPTY;
}
else if ( !stricmp( szToken, "water" ) )
{
m_iKillCondition = CONTENTS_WATER;
}
else if ( !stricmp( szToken, "solid" ) )
{
m_iKillCondition = CONTENTS_SOLID;
}
}
else if ( !stricmp( szToken, "{" ) )
{
// parse new type
this->ParseType( szFile ); // parses the type, moves the file pointer
}
szFile = gEngfuncs.COM_ParseFile(szFile, szToken);
}
}
gEngfuncs.COM_FreeFile( szFile );
AllocateParticles(iParticles);
}
void ParticleSystem::AllocateParticles( int iParticles )
{
m_pAllParticles = new particle[iParticles];
m_pFreeParticle = m_pAllParticles;
m_pActiveParticle = NULL;
m_pMainParticle = NULL;
// initialise the linked list
particle *pLast = m_pAllParticles;
particle *pParticle = pLast+1;
for( int i = 1; i < iParticles; i++ )
{
pLast->nextpart = pParticle;
pLast = pParticle;
pParticle++;
}
pLast->nextpart = NULL;
}
ParticleSystem::~ParticleSystem( void )
{
delete[] m_pAllParticles;
ParticleType *pType = m_pFirstType;
ParticleType *pNext;
while (pType)
{
pNext = pType->m_pNext;
delete pType;
pType = pNext;
}
}
// returns the ParticleType with the given name, if there is one
ParticleType *ParticleSystem::GetType( const char *szName )
{
for (ParticleType *pType = m_pFirstType; pType; pType = pType->m_pNext)
{
if (!stricmp(pType->m_szName, szName))
return pType;
}
return NULL;
}
ParticleType *ParticleSystem::AddPlaceholderType( const char *szName )
{
m_pFirstType = new ParticleType( m_pFirstType );
strncpy(m_pFirstType->m_szName, szName, sizeof(m_pFirstType->m_szName) );
return m_pFirstType;
}
// creates a new particletype from the given file
// NB: this changes the value of szFile.
ParticleType *ParticleSystem::ParseType( char *&szFile )
{
ParticleType *pType = new ParticleType();
// parse the .aur file
char szToken[1024];
szFile = gEngfuncs.COM_ParseFile(szFile, szToken);
while ( stricmp( szToken, "}" ) )
{
if (!szFile)
break;
if ( !stricmp( szToken, "name" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
strncpy(pType->m_szName, szToken, sizeof(pType->m_szName) );
ParticleType *pTemp = GetType(szToken);
if (pTemp)
{
// there's already a type with this name
if (pTemp->m_bIsDefined)
Msg("Warning: Particle type %s is defined more than once!\n", szToken);
// copy all our data into the existing type, throw away the type we were making
*pTemp = *pType;
delete pType;
pType = pTemp;
pType->m_bIsDefined = true; // record the fact that it's defined, so we won't need to add it to the list
}
}
else if ( !stricmp( szToken, "gravity" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
pType->m_Gravity = RandomRange( szToken );
}
else if ( !stricmp( szToken, "windyaw" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
pType->m_WindYaw = RandomRange( szToken );
}
else if ( !stricmp( szToken, "windstrength" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
pType->m_WindStrength = RandomRange( szToken );
}
else if ( !stricmp( szToken, "sprite" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
pType->m_hSprite = SPR_Load( szToken );
}
else if ( !stricmp( szToken, "startalpha" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
pType->m_StartAlpha = RandomRange( szToken );
}
else if ( !stricmp( szToken, "endalpha" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
pType->m_EndAlpha = RandomRange( szToken );
}
else if ( !stricmp( szToken, "startred" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
pType->m_StartRed = RandomRange( szToken );
}
else if ( !stricmp( szToken, "endred" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
pType->m_EndRed = RandomRange( szToken );
}
else if ( !stricmp( szToken, "startgreen" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
pType->m_StartGreen = RandomRange( szToken );
}
else if ( !stricmp( szToken, "endgreen" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
pType->m_EndGreen = RandomRange( szToken );
}
else if ( !stricmp( szToken, "startblue" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
pType->m_StartBlue = RandomRange( szToken );
}
else if ( !stricmp( szToken, "endblue" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
pType->m_EndBlue = RandomRange( szToken );
}
else if ( !stricmp( szToken, "startsize" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
pType->m_StartSize = RandomRange( szToken );
}
else if ( !stricmp( szToken, "sizedelta" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
pType->m_SizeDelta = RandomRange( szToken );
}
else if ( !stricmp( szToken, "endsize" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
pType->m_EndSize = RandomRange( szToken );
}
else if ( !stricmp( szToken, "startangle" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
pType->m_StartAngle = RandomRange( szToken );
}
else if ( !stricmp( szToken, "angledelta" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
pType->m_AngleDelta = RandomRange( szToken );
}
else if ( !stricmp( szToken, "startframe" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
pType->m_StartFrame = RandomRange( szToken );
}
else if ( !stricmp( szToken, "endframe" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
pType->m_EndFrame = RandomRange( szToken );
pType->m_bEndFrame = true;
}
else if ( !stricmp( szToken, "framerate" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
pType->m_FrameRate = RandomRange( szToken );
}
else if ( !stricmp( szToken, "lifetime" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
pType->m_Life = RandomRange( szToken );
}
else if ( !stricmp( szToken, "spraytype" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
ParticleType *pTemp = GetType(szToken);
if (pTemp) pType->m_pSprayType = pTemp;
else pType->m_pSprayType = AddPlaceholderType(szToken);
}
else if ( !stricmp( szToken, "overlaytype" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
ParticleType *pTemp = GetType(szToken);
if (pTemp) pType->m_pOverlayType = pTemp;
else pType->m_pOverlayType = AddPlaceholderType(szToken);
}
else if ( !stricmp( szToken, "sprayrate" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
pType->m_SprayRate = RandomRange( szToken );
}
else if ( !stricmp( szToken, "sprayforce" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
pType->m_SprayForce = RandomRange( szToken );
}
else if ( !stricmp( szToken, "spraypitch" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
pType->m_SprayPitch = RandomRange( szToken );
}
else if ( !stricmp( szToken, "sprayyaw" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
pType->m_SprayYaw = RandomRange( szToken );
}
else if ( !stricmp( szToken, "drag" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
pType->m_Drag = RandomRange( szToken );
}
else if ( !stricmp( szToken, "bounce" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
pType->m_Bounce = RandomRange( szToken );
if (pType->m_Bounce.m_fMin != 0 || pType->m_Bounce.m_fMax != 0)
pType->m_bBouncing = true;
}
else if ( !stricmp( szToken, "bouncefriction" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
pType->m_BounceFriction = RandomRange( szToken );
}
else if ( !stricmp( szToken, "rendermode" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
if ( !stricmp( szToken, "additive" ) )
{
pType->m_iRenderMode = kRenderTransAdd;
}
else if ( !stricmp( szToken, "solid" ) )
{
pType->m_iRenderMode = kRenderTransAlpha;
}
else if ( !stricmp( szToken, "texture" ) )
{
pType->m_iRenderMode = kRenderTransTexture;
}
else if ( !stricmp( szToken, "color" ) )
{
pType->m_iRenderMode = kRenderTransColor;
}
}
else if ( !stricmp( szToken, "drawcondition" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
if ( !stricmp( szToken, "empty" ) )
{
pType->m_iDrawCond = CONTENTS_EMPTY;
}
else if ( !stricmp( szToken, "water" ) )
{
pType->m_iDrawCond = CONTENTS_WATER;
}
else if ( !stricmp( szToken, "solid" ) )
{
pType->m_iDrawCond = CONTENTS_SOLID;
}
else if ( !stricmp( szToken, "special" ) || !stricmp( szToken, "special1" ) )
{
pType->m_iDrawCond = CONTENT_SPECIAL1;
}
else if ( !stricmp( szToken, "special2" ) )
{
pType->m_iDrawCond = CONTENT_SPECIAL2;
}
else if ( !stricmp( szToken, "special3" ) )
{
pType->m_iDrawCond = CONTENT_SPECIAL3;
}
}
else if ( !stricmp( szToken, "collision" ) )
{
szFile = gEngfuncs.COM_ParseFile(szFile,szToken);
if ( !stricmp( szToken, "none" ) )
{
pType->m_iCollision = COLLISION_NONE;
}
else if ( !stricmp( szToken, "die" ) )
{
pType->m_iCollision = COLLISION_DIE;
}
else if ( !stricmp( szToken, "bounce" ) )
{
pType->m_iCollision = COLLISION_BOUNCE;
}
}
// get the next token
szFile = gEngfuncs.COM_ParseFile(szFile, szToken);
}
if (!pType->m_bIsDefined)
{
// if this is a newly-defined type, we need to add it to the list
pType->m_pNext = m_pFirstType;
m_pFirstType = pType;
pType->m_bIsDefined = true;
}
return pType;
}
particle *ParticleSystem::ActivateParticle()
{
particle* pActivated = m_pFreeParticle;
if (pActivated)
{
m_pFreeParticle = pActivated->nextpart;
pActivated->nextpart = m_pActiveParticle;
m_pActiveParticle = pActivated;
}
return pActivated;
}
extern vec3_t v_origin;
void ParticleSystem::CalculateDistance()
{
if (!m_pActiveParticle)
return;
vec3_t offset = v_origin - m_pActiveParticle->origin; // just pick one
m_fViewerDist = offset[0]*offset[0] + offset[1]*offset[1] + offset[2]*offset[2];
}
bool ParticleSystem::UpdateSystem( float frametime )
{
// the entity emitting this system
cl_entity_t *source = UTIL_GetClientEntityWithServerIndex( m_iEntIndex );
if(!source) return false;
// Don't update if the system is outside the player's PVS.
if (source->curstate.msg_time < gEngfuncs.GetClientTime())
{ //remove particles
enable = 0;
}
else enable = (source->curstate.renderfx == kRenderFxAurora);
//check for contents to remove
if(m_iKillCondition == gEngfuncs.PM_PointContents(source->curstate.origin, NULL))
{
enable = 0;
}
if (m_pMainParticle == NULL)
{
if (enable)
{
ParticleType *pType = m_pMainType;
if (pType)
{
m_pMainParticle = pType->CreateParticle(this);//m_pMainParticle);
if (m_pMainParticle)
{
m_pMainParticle->m_iEntIndex = m_iEntIndex;
m_pMainParticle->age_death = -1; // never die
}
}
}
}
else if (!enable)
{
m_pMainParticle->age_death = 0; // die now
m_pMainParticle = NULL;
}
particle* pParticle = m_pActiveParticle;
particle* pLast = NULL;
while( pParticle )
{
if( UpdateParticle( pParticle, frametime ) )
{
pLast = pParticle;
pParticle = pParticle->nextpart;
}
else // deactivate it
{
if (pLast)
{
pLast->nextpart = pParticle->nextpart;
pParticle->nextpart = m_pFreeParticle;
m_pFreeParticle = pParticle;
pParticle = pLast->nextpart;
}
else // deactivate the first particle in the list
{
m_pActiveParticle = pParticle->nextpart;
pParticle->nextpart = m_pFreeParticle;
m_pFreeParticle = pParticle;
pParticle = m_pActiveParticle;
}
}
}
return true;
}
void ParticleSystem::DrawSystem()
{
vec3_t normal, forward, right, up;
if(g_HardwareCapable)//merge particle pos for openGL
{
double r_world_matrix[16];
glGetDoublev (GL_MODELVIEW_MATRIX, r_world_matrix);
right.x = r_world_matrix[0];
right.y = r_world_matrix[4];
right.z = r_world_matrix[8];
up.x = r_world_matrix[1];
up.y = r_world_matrix[5];
up.z = r_world_matrix[9];
}
else //otherwise
{
gEngfuncs.GetViewAngles((float*)normal);
AngleVectors(normal, forward, right, up);
}
particle* pParticle = m_pActiveParticle;
for( pParticle = m_pActiveParticle; pParticle; pParticle = pParticle->nextpart )
{
DrawParticle( pParticle, right, up );
}
}
bool ParticleSystem::ParticleIsVisible( particle* part )
{
float dv[3];
bool visible = true;
VectorSubtract(part->origin, v_origin, dv);
float d = Length(dv);
if (fabs(d) > 36.0f)
{
visible = (R_SphereInFrustum(part->origin, 100 ) > 0);
}
return visible;
}
bool ParticleSystem::UpdateParticle(particle *part, float frametime)
{
if (frametime == 0 ) return true;
part->age += frametime;
cl_entity_t *source = UTIL_GetClientEntityWithServerIndex( m_iEntIndex );
// is this particle bound to an entity?
if (part->m_iEntIndex)
{
if (enable)
{
if(m_iEntAttachment)
{
part->velocity = (source->attachment[m_iEntAttachment - 1] - part->origin)/frametime;
part->origin = source->attachment[m_iEntAttachment - 1];
}
else
{
part->velocity = (source->curstate.origin - part->origin)/frametime;
part->origin = source->curstate.origin;
}
}
else
{
// entity is switched off, die
return false;
}
}
else
{
// not tied to an entity, check whether it's time to die
if (part->age_death >= 0 && part->age > part->age_death)
return false;
// apply acceleration and velocity
vec3_t vecOldPos = part->origin;
if (part->m_fDrag)
VectorMA(part->velocity, -part->m_fDrag*frametime, part->velocity - part->m_vecWind, part->velocity);
VectorMA(part->velocity, frametime, part->accel, part->velocity);
VectorMA(part->origin, frametime, part->velocity, part->origin);
if (part->pType->m_bBouncing)
{
vec3_t vecTarget;
VectorMA(part->origin, frametime, part->velocity, vecTarget);
pmtrace_t *tr = gEngfuncs.PM_TraceLine( part->origin, vecTarget, PM_TRACELINE_PHYSENTSONLY, 2, -1 );
if (tr->fraction < 1)
{
part->origin = tr->endpos;
float bounceforce = DotProduct(tr->plane.normal, part->velocity);
float newspeed = (1 - part->pType->m_BounceFriction.GetInstance());
part->velocity = part->velocity * newspeed;
VectorMA(part->velocity, -bounceforce*(newspeed+part->pType->m_Bounce.GetInstance()), tr->plane.normal, part->velocity);
}
}
}
// spray children
if (part->age_spray && part->age > part->age_spray)
{
part->age_spray = part->age + 1/part->pType->m_SprayRate.GetInstance();
//particle *pChild = ActivateParticle();
if (part->pType->m_pSprayType)
{
particle *pChild = part->pType->m_pSprayType->CreateParticle(this);
if (pChild)
{
pChild->origin = part->origin;
float fSprayForce = part->pType->m_SprayForce.GetInstance();
pChild->velocity = part->velocity;
if (fSprayForce)
{
float fSprayPitch = part->pType->m_SprayPitch.GetInstance() - source->curstate.angles.x;
float fSprayYaw = part->pType->m_SprayYaw.GetInstance() - source->curstate.angles.y;
float fSprayRoll = source->curstate.angles.z;
float fForceCosPitch = fSprayForce*CosLookup(fSprayPitch);
pChild->velocity.x += CosLookup(fSprayYaw) * fForceCosPitch;
pChild->velocity.y += SinLookup(fSprayYaw) * fForceCosPitch + SinLookup(fSprayYaw) * fSprayForce * SinLookup(fSprayRoll);
pChild->velocity.z -= SinLookup(fSprayPitch) * fSprayForce * CosLookup(fSprayRoll);
}
}
}
}
part->m_fSize += part->m_fSizeStep * frametime;
part->m_fAlpha += part->m_fAlphaStep * frametime;
part->m_fRed += part->m_fRedStep * frametime;
part->m_fGreen += part->m_fGreenStep * frametime;
part->m_fBlue += part->m_fBlueStep * frametime;
part->frame += part->m_fFrameStep * frametime;
if (part->m_fAngleStep)
{
part->m_fAngle += part->m_fAngleStep * frametime;
while (part->m_fAngle < 0) part->m_fAngle += 360;
while (part->m_fAngle > 360) part->m_fAngle -= 360;
}
return true;
}
void ParticleSystem::DrawParticle(particle *part, vec3_t &right, vec3_t &up)
{
float fSize = part->m_fSize;
vec3_t point1,point2,point3,point4;
vec3_t origin = part->origin;
// nothing to draw?
if (fSize == 0) return;
//frustrum visible check
if(!ParticleIsVisible(part)) return;
float fCosSize = CosLookup(part->m_fAngle)*fSize;
float fSinSize = SinLookup(part->m_fAngle)*fSize;
// calculate the four corners of the sprite
VectorMA (origin, fSinSize, up, point1);
VectorMA (point1, -fCosSize, right, point1);
VectorMA (origin, fCosSize, up, point2);
VectorMA (point2, fSinSize, right, point2);
VectorMA (origin, -fSinSize, up, point3);
VectorMA (point3, fCosSize, right, point3);
VectorMA (origin, -fCosSize, up, point4);
VectorMA (point4, -fSinSize, right, point4);
struct model_s * pModel;
int iContents = 0;
for (particle *pDraw = part; pDraw; pDraw = pDraw->m_pOverlay)
{
if (pDraw->pType->m_hSprite == 0)
continue;
if (pDraw->pType->m_iDrawCond)
{
if (iContents == 0)
iContents = gEngfuncs.PM_PointContents(origin, NULL);
if (iContents != pDraw->pType->m_iDrawCond)
continue;
}
pModel = (struct model_s *)gEngfuncs.GetSpritePointer( pDraw->pType->m_hSprite );
//Msg("UpdParticle %d: age %f, life %f, R:%f G:%f, B, %f \n", pDraw->pType->m_hSprite, part->age, part->age_death, pDraw->m_fRed, pDraw->m_fGreen, pDraw->m_fBlue);
// if we've reached the end of the sprite's frames, loop back
while (pDraw->frame > pModel->numframes) pDraw->frame -= pModel->numframes;
while (pDraw->frame < 0) pDraw->frame += pModel->numframes;
if ( !gEngfuncs.pTriAPI->SpriteTexture( pModel, int(pDraw->frame) ))continue;
gEngfuncs.pTriAPI->RenderMode(pDraw->pType->m_iRenderMode);
gEngfuncs.pTriAPI->Color4f( pDraw->m_fRed, pDraw->m_fGreen, pDraw->m_fBlue, pDraw->m_fAlpha );
gEngfuncs.pTriAPI->Begin( TRI_QUADS );
gEngfuncs.pTriAPI->TexCoord2f (0, 0);
gEngfuncs.pTriAPI->Vertex3fv(point1);
gEngfuncs.pTriAPI->TexCoord2f (1, 0);
gEngfuncs.pTriAPI->Vertex3fv (point2);
gEngfuncs.pTriAPI->TexCoord2f (1, 1);
gEngfuncs.pTriAPI->Vertex3fv (point3);
gEngfuncs.pTriAPI->TexCoord2f (0, 1);
gEngfuncs.pTriAPI->Vertex3fv (point4);
gEngfuncs.pTriAPI->End();
}
}

208
client/r_particle.h Normal file
View File

@ -0,0 +1,208 @@
// 02/08/02 November235: Particle System
#pragma once
#include "r_main.h"
class ParticleType;
class ParticleSystem;
void CreateAurora( int idx, char *file ); //make new partsystem
#define COLLISION_NONE 0
#define COLLISION_DIE 1
#define COLLISION_BOUNCE 2
struct particle
{
particle* nextpart;
particle* m_pOverlay; // for making multi-layered particles
ParticleType *pType;
vec3_t origin;
vec3_t velocity;
vec3_t accel;
vec3_t m_vecWind;
int m_iEntIndex; // if non-zero, this particle is tied to the given entity
float m_fRed;
float m_fGreen;
float m_fBlue;
float m_fRedStep;
float m_fGreenStep;
float m_fBlueStep;
float m_fAlpha;
float m_fAlphaStep;
float frame;
float m_fFrameStep;
float m_fAngle;
float m_fAngleStep;
float m_fSize;
float m_fSizeStep;
float m_fDrag;
float age;
float age_death;
float age_spray;
};
class RandomRange
{
public:
RandomRange() { m_fMin = m_fMax = 0; m_bDefined = false; }
RandomRange(float fValue) { m_fMin = m_fMax = fValue; m_bDefined = true; }
RandomRange(float fMin, float fMax) { m_fMin = fMin; m_fMax = fMax; m_bDefined = true; }
RandomRange( char *szToken );
float m_fMax;
float m_fMin;
bool m_bDefined;
float GetInstance()
{ return gEngfuncs.pfnRandomFloat(m_fMin, m_fMax); }
float GetOffset(float fBasis)
{ return GetInstance()-fBasis; }
bool IsDefined()
{ return m_bDefined; } //(m_fMin == 0 && m_fMax == 0); }
};
#define MAX_TYPENAME 30
class ParticleType
{
public:
ParticleType( ParticleType *pNext = NULL );
ParticleType(char *szFilename);
bool m_bIsDefined; // is this ParticleType just a placeholder?
int m_iRenderMode;
int m_iDrawCond;
int m_iCollision;
RandomRange m_Bounce;
RandomRange m_BounceFriction;
bool m_bBouncing;
RandomRange m_Life;
RandomRange m_StartAlpha;
RandomRange m_EndAlpha;
RandomRange m_StartRed;
RandomRange m_EndRed;
RandomRange m_StartGreen;
RandomRange m_EndGreen;
RandomRange m_StartBlue;
RandomRange m_EndBlue;
RandomRange m_StartSize;
RandomRange m_SizeDelta;
RandomRange m_EndSize;
RandomRange m_StartFrame;
RandomRange m_EndFrame;
RandomRange m_FrameRate; // incompatible with EndFrame
bool m_bEndFrame;
RandomRange m_StartAngle;
RandomRange m_AngleDelta;
RandomRange m_SprayRate;
RandomRange m_SprayForce;
RandomRange m_SprayPitch;
RandomRange m_SprayYaw;
RandomRange m_SprayRoll;
ParticleType *m_pSprayType;
RandomRange m_Gravity;
RandomRange m_WindStrength;
RandomRange m_WindYaw;
HSPRITE m_hSprite;
ParticleType *m_pOverlayType;
RandomRange m_Drag;
ParticleType *m_pNext;
char m_szName[MAX_TYPENAME];
// here is a particle system. Add a (set of) particles according to this type, and initialise their values.
particle* CreateParticle(ParticleSystem *pSys);//particle *pPart);
// initialise this particle. Does not define velocity or age.
void InitParticle(particle *pPart, ParticleSystem *pSys);
};
class ParticleSystem
{
public:
ParticleSystem( int entindex, char *szFilename );
~ParticleSystem( void );
void AllocateParticles( int iParticles );
void CalculateDistance();
ParticleType *GetType( const char *szName );
ParticleType *AddPlaceholderType( const char *szName );
ParticleType *ParseType( char *&szFile );
cl_entity_t *GetEntity() { return UTIL_GetClientEntityWithServerIndex(m_iEntIndex); }
static float c_fCosTable[360 + 90];
static bool c_bCosTableInit;
// General functions
bool UpdateSystem( float frametime ); //If this function returns false, the manager deletes the system
void DrawSystem();
particle *ActivateParticle(); // adds one of the free particles to the active list, and returns it for initialisation.
static float CosLookup(int angle) { return angle < 0? c_fCosTable[angle+360]: c_fCosTable[angle]; }
static float SinLookup(int angle) { return angle < -90? c_fCosTable[angle+450]: c_fCosTable[angle+90]; }
// returns false if the particle has died
bool UpdateParticle( particle *part, float frametime );
void DrawParticle( particle* part, vec3_t &right, vec3_t &up );
// Utility functions that have to be public
bool ParticleIsVisible( particle* part );
// Pointer to next system for linked list structure
ParticleSystem* m_pNextSystem;
particle* m_pActiveParticle;
float m_fViewerDist;
int m_iEntIndex;
int m_iEntAttachment;
int m_iKillCondition;
int enable;
private:
// the block of allocated particles
particle* m_pAllParticles;
// First particles in the linked list for the active particles and the dead particles
particle* m_pFreeParticle;
particle* m_pMainParticle; // the "source" particle.
ParticleType *m_pFirstType;
ParticleType *m_pMainType;
};
class ParticleSystemManager
{
public:
ParticleSystemManager( void );
~ParticleSystemManager( void );
ParticleSystem *FindSystem( cl_entity_t* pEntity );
void AddSystem( ParticleSystem* );
void UpdateSystems( void );
void ClearSystems( void );
ParticleSystem* m_pFirstSystem;
};
extern ParticleSystemManager* g_pParticleSystems;

818
client/r_util.cpp Normal file
View File

@ -0,0 +1,818 @@
/***
*
* Copyright (c) 2001-2006, Chain Studios. All rights reserved.
*
* This product contains software technology that is a part of Half-Life FX (R)
* from Chain Studios ("HLFX Technology"). 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 Chain Studios.
*
****/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "hud.h"
#include "r_main.h"
#include "r_util.h"
#include <string.h>
vec3_t vec3_origin( 0, 0, 0 );
double sqrt(double x);
HSPRITE m_hHudFont;
int *GetRect( void )
{
RECT wrect;
static int extent[4];
if(GetWindowRect( GetActiveWindow(), &wrect ))
{
if(!wrect.left)
{
extent[0] = wrect.left; //+4
extent[1] = wrect.top; //+30
extent[2] = wrect.right; //-4
extent[3] = wrect.bottom; //-4
}
else
{
extent[0] = wrect.left + 4; //+4
extent[1] = wrect.top + 30; //+30
extent[2] = wrect.right - 4; //-4
extent[3] = wrect.bottom - 4; //-4
}
}
return extent;
}
void ScaleColors( int &r, int &g, int &b, int a )
{
float x = (float)a / 255;
r = (int)(r * x);
g = (int)(g * x);
b = (int)(b * x);
}
unsigned int nextpow2(unsigned int x)
{
unsigned int y = 1;
while (x > y) y = y << 1;
return y;
}
float Length(const float *v)
{
int i;
float length;
length = 0;
for (i=0 ; i< 3 ; i++)
length += v[i]*v[i];
length = sqrt (length); // FIXME
return length;
}
float Distance(const vec3_t v1, const vec3_t v2)
{
vec3_t d;
VectorSubtract(v2,v1,d);
return Length(d);
}
void VectorAngles( const float *forward, float *angles )
{
float tmp, yaw, pitch;
if (forward[1] == 0 && forward[0] == 0)
{
yaw = 0;
if (forward[2] > 0)
pitch = 90;
else
pitch = 270;
}
else
{
yaw = (atan2(forward[1], forward[0]) * 180 / M_PI);
if (yaw < 0)
yaw += 360;
tmp = sqrt (forward[0]*forward[0] + forward[1]*forward[1]);
pitch = (atan2(forward[2], tmp) * 180 / M_PI);
if (pitch < 0)
pitch += 360;
}
angles[0] = pitch;
angles[1] = yaw;
angles[2] = 0;
}
float VectorNormalize (float *v)
{
float length, ilength;
length = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
length = sqrt (length); // FIXME
if (length)
{
ilength = 1/length;
v[0] *= ilength;
v[1] *= ilength;
v[2] *= ilength;
}
return length;
}
void VectorInverse ( float *v )
{
v[0] = -v[0];
v[1] = -v[1];
v[2] = -v[2];
}
void VectorScale (const float *in, float scale, float *out)
{
out[0] = in[0]*scale;
out[1] = in[1]*scale;
out[2] = in[2]*scale;
}
void VectorMA (const float *veca, float scale, const float *vecb, float *vecc)
{
vecc[0] = veca[0] + scale*vecb[0];
vecc[1] = veca[1] + scale*vecb[1];
vecc[2] = veca[2] + scale*vecb[2];
}
HSPRITE LoadSprite(const char *pszName)
{
int i;
char sz[256];
if (ScreenWidth < 640)
i = 320;
else
i = 640;
sprintf(sz, pszName, i);
return SPR_Load(sz);
}
float TransformColor ( float color )
{
float trns_clr;
if(color >= 0 ) trns_clr = color / 255.0f;
else trns_clr = 1.0;//default value
return trns_clr;
}
#define NOISE_SIZE 64
inline float fade ( float t )
{
return t * t * t * (t * (t * 6 - 15) + 10);
}
inline float lerp ( float t, float a, float b )
{
return a + t * (b - a);
}
inline float grad ( int hash, float x, float y, float z )
{
int h = hash & 15; // CONVERT LO 4 BITS OF HASH CODE
float u = h<8 ? x : y, // INTO 12 GRADIENT DIRECTIONS.
v = h<4 ? y : h==12 || h==14 ? x : z;
return ((h&1) == 0 ? u : -u) + ((h&2) == 0 ? v : -v);
}
int permutation [256] =
{
151,160,137,91,90,15,
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
};
int p[512];
void InitImprovedNoise()
{
for ( int i = 0; i < 256; i++ )
p [ 256 + i ] = p [ i ] = permutation [i];
}
float noise( float vx, float vy, float vz )
{
float fx = floor ( vx ); // get floor values of coords
float fy = floor ( vy );
float fz = floor ( vz );
int X = ((int) fx) & 255, // FIND UNIT CUBE THAT
Y = ((int) fy) & 255, // CONTAINS POINT.
Z = ((int) fz) & 255;
float x = vx - fx; // FIND RELATIVE X,Y,Z
float y = vy - fy; // OF POINT IN CUBE.
float z = vz - fz;
float u = fade ( x ), // COMPUTE FADE CURVES
v = fade ( y ), // FOR EACH OF X,Y,Z.
w = fade ( z );
int a = p [X ]+Y, aa = p [a]+Z, ab = p [a+1]+Z, // HASH COORDINATES OF
b = p [X+1]+Y, ba = p [b]+Z, bb = p [b+1]+Z; // THE 8 CUBE CORNERS,
return lerp(w, lerp(v, lerp(u, grad(p[aa ], x , y , z ), // AND ADD
grad(p[ba ], x-1, y , z )), // BLENDED
lerp(u, grad(p[ab ], x , y-1, z ), // RESULTS
grad(p[bb ], x-1, y-1, z ))),// FROM 8
lerp(v, lerp(u, grad(p[aa+1], x , y , z-1 ), // CORNERS
grad(p[ba+1], x-1, y , z-1 )), // OF CUBE
lerp(u, grad(p[ab+1], x , y-1, z-1 ),
grad(p[bb+1], x-1, y-1, z-1 ))));
}
void CreateNoiseTexture3D (float scale)
{
int i;
GLfloat *img = new GLfloat[NOISE_SIZE * NOISE_SIZE * NOISE_SIZE * 3];
GLfloat *p = img;
InitImprovedNoise();
for (i=0; i < NOISE_SIZE*NOISE_SIZE*NOISE_SIZE*3 ; i++)
{
float x = i + sin(i);
float y = i + cos(i);
*p++ = noise(x, y, i);
}
g_uiNoiseTex = pTexId++;
glBindTexture(GL_TEXTURE_3D, g_uiNoiseTex);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage3DEXT(GL_TEXTURE_3D, 0, 3, NOISE_SIZE, NOISE_SIZE, NOISE_SIZE, 0,
GL_RGB, GL_FLOAT, img);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_REPEAT );
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_REPEAT );
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_REPEAT );
delete [] img;
}
//
// R_SphereInFrustum
//
// Purpose: sphere visibility test
//
extern float frustumPlanes[6][4];
float R_SphereInFrustum( vec3_t o, float radius )
{
int p;
float d;
for( p = 0; p < 6; p++ )
{
d = frustumPlanes[p][0] * o[0] + frustumPlanes[p][1] * o[1] + frustumPlanes[p][2] * o[2] + frustumPlanes[p][3];
if( d <= -radius )
return 0;
}
return d + radius;
}
//
// UTIL_GetClientEntityWithServerIndex
//
// Purpose: searches for the server entity on client
// Assumes: colormap has server entity index
//
cl_entity_t *UTIL_GetClientEntityWithServerIndex( int sv_index )
{
cl_entity_t *e;
for (int ic=1;ic<MAX_EDICTS;ic++)
{
e = gEngfuncs.GetEntityByIndex( ic );
if (!e)
break;
if (!e->model)
continue;
if (e->curstate.colormap == sv_index)
return e;
}
return NULL;
}
/*
====================
AngleMatrix
====================
*/
void AngleMatrix (const float *angles, float (*matrix)[4] )
{
float angle;
float sr, sp, sy, cr, cp, cy;
angle = angles[YAW] * (M_PI*2 / 360);
sy = sin(angle);
cy = cos(angle);
angle = angles[PITCH] * (M_PI*2 / 360);
sp = sin(angle);
cp = cos(angle);
angle = angles[ROLL] * (M_PI*2 / 360);
sr = sin(angle);
cr = cos(angle);
// matrix = (YAW * PITCH) * ROLL
matrix[0][0] = cp*cy;
matrix[1][0] = cp*sy;
matrix[2][0] = -sp;
matrix[0][1] = sr*sp*cy+cr*-sy;
matrix[1][1] = sr*sp*sy+cr*cy;
matrix[2][1] = sr*cp;
matrix[0][2] = (cr*sp*cy+-sr*-sy);
matrix[1][2] = (cr*sp*sy+-sr*cy);
matrix[2][2] = cr*cp;
matrix[0][3] = 0.0;
matrix[1][3] = 0.0;
matrix[2][3] = 0.0;
}
/*
====================
VectorCompare
====================
*/
int VectorCompare (const float *v1, const float *v2)
{
int i;
for (i=0 ; i<3 ; i++)
if (v1[i] != v2[i]) return 0;
return 1;
}
/*
====================
CrossProduct
====================
*/
void CrossProduct (const float *v1, const float *v2, float *cross)
{
cross[0] = v1[1]*v2[2] - v1[2]*v2[1];
cross[1] = v1[2]*v2[0] - v1[0]*v2[2];
cross[2] = v1[0]*v2[1] - v1[1]*v2[0];
}
/*
====================
VectorTransform
====================
*/
void VectorTransform (const float *in1, float in2[3][4], float *out)
{
out[0] = DotProduct(in1, in2[0]) + in2[0][3];
out[1] = DotProduct(in1, in2[1]) + in2[1][3];
out[2] = DotProduct(in1, in2[2]) + in2[2][3];
}
/*
================
ConcatTransforms
================
*/
void ConcatTransforms (float in1[3][4], float in2[3][4], float out[3][4])
{
out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] +
in1[0][2] * in2[2][0];
out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] +
in1[0][2] * in2[2][1];
out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] +
in1[0][2] * in2[2][2];
out[0][3] = in1[0][0] * in2[0][3] + in1[0][1] * in2[1][3] +
in1[0][2] * in2[2][3] + in1[0][3];
out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] +
in1[1][2] * in2[2][0];
out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] +
in1[1][2] * in2[2][1];
out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] +
in1[1][2] * in2[2][2];
out[1][3] = in1[1][0] * in2[0][3] + in1[1][1] * in2[1][3] +
in1[1][2] * in2[2][3] + in1[1][3];
out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] +
in1[2][2] * in2[2][0];
out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] +
in1[2][2] * in2[2][1];
out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] +
in1[2][2] * in2[2][2];
out[2][3] = in1[2][0] * in2[0][3] + in1[2][1] * in2[1][3] +
in1[2][2] * in2[2][3] + in1[2][3];
}
// angles index are not the same as ROLL, PITCH, YAW
/*
====================
AngleQuaternion
====================
*/
void AngleQuaternion( float *angles, vec4_t quaternion )
{
float angle;
float sr, sp, sy, cr, cp, cy;
// FIXME: rescale the inputs to 1/2 angle
angle = angles[2] * 0.5;
sy = sin(angle);
cy = cos(angle);
angle = angles[1] * 0.5;
sp = sin(angle);
cp = cos(angle);
angle = angles[0] * 0.5;
sr = sin(angle);
cr = cos(angle);
quaternion[0] = sr*cp*cy-cr*sp*sy; // X
quaternion[1] = cr*sp*cy+sr*cp*sy; // Y
quaternion[2] = cr*cp*sy-sr*sp*cy; // Z
quaternion[3] = cr*cp*cy+sr*sp*sy; // W
}
/*
====================
QuaternionSlerp
====================
*/
void QuaternionSlerp( vec4_t p, vec4_t q, float t, vec4_t qt )
{
int i;
float omega, cosom, sinom, sclp, sclq;
// decide if one of the quaternions is backwards
float a = 0;
float b = 0;
for (i = 0; i < 4; i++)
{
a += (p[i]-q[i])*(p[i]-q[i]);
b += (p[i]+q[i])*(p[i]+q[i]);
}
if (a > b)
{
for (i = 0; i < 4; i++)
{
q[i] = -q[i];
}
}
cosom = p[0]*q[0] + p[1]*q[1] + p[2]*q[2] + p[3]*q[3];
if ((1.0 + cosom) > 0.000001)
{
if ((1.0 - cosom) > 0.000001)
{
omega = acos( cosom );
sinom = sin( omega );
sclp = sin( (1.0 - t)*omega) / sinom;
sclq = sin( t*omega ) / sinom;
}
else
{
sclp = 1.0 - t;
sclq = t;
}
for (i = 0; i < 4; i++) {
qt[i] = sclp * p[i] + sclq * q[i];
}
}
else
{
qt[0] = -q[1];
qt[1] = q[0];
qt[2] = -q[3];
qt[3] = q[2];
sclp = sin( (1.0 - t) * (0.5 * M_PI));
sclq = sin( t * (0.5 * M_PI));
for (i = 0; i < 3; i++)
{
qt[i] = sclp * p[i] + sclq * qt[i];
}
}
}
/*
====================
QuaternionMatrix
====================
*/
void QuaternionMatrix( vec4_t quaternion, float (*matrix)[4] )
{
matrix[0][0] = 1.0 - 2.0 * quaternion[1] * quaternion[1] - 2.0 * quaternion[2] * quaternion[2];
matrix[1][0] = 2.0 * quaternion[0] * quaternion[1] + 2.0 * quaternion[3] * quaternion[2];
matrix[2][0] = 2.0 * quaternion[0] * quaternion[2] - 2.0 * quaternion[3] * quaternion[1];
matrix[0][1] = 2.0 * quaternion[0] * quaternion[1] - 2.0 * quaternion[3] * quaternion[2];
matrix[1][1] = 1.0 - 2.0 * quaternion[0] * quaternion[0] - 2.0 * quaternion[2] * quaternion[2];
matrix[2][1] = 2.0 * quaternion[1] * quaternion[2] + 2.0 * quaternion[3] * quaternion[0];
matrix[0][2] = 2.0 * quaternion[0] * quaternion[2] + 2.0 * quaternion[3] * quaternion[1];
matrix[1][2] = 2.0 * quaternion[1] * quaternion[2] - 2.0 * quaternion[3] * quaternion[0];
matrix[2][2] = 1.0 - 2.0 * quaternion[0] * quaternion[0] - 2.0 * quaternion[1] * quaternion[1];
}
/*
====================
MatrixCopy
====================
*/
void MatrixCopy( float in[3][4], float out[3][4] )
{
memcpy( out, in, sizeof( float ) * 3 * 4 );
}
/*
====================
VectorIRotate
====================
*/
void VectorIRotate (const vec3_t in1, const float in2[3][4], vec3_t out)
{
out[0] = in1[0]*in2[0][0] + in1[1]*in2[1][0] + in1[2]*in2[2][0];
out[1] = in1[0]*in2[0][1] + in1[1]*in2[1][1] + in1[2]*in2[2][1];
out[2] = in1[0]*in2[0][2] + in1[1]*in2[1][2] + in1[2]*in2[2][2];
}
/*
====================
VectorRotateByMatrix
VectorTransformByMatrix
====================
*/
void VectorRotateByMatrix (const vec3_t &in1, const float *in2, vec3_t &out)
{
out[0] = in1[0]*in2[0] + in1[1]*in2[4] + in1[2]*in2[8];
out[1] = in1[0]*in2[1] + in1[1]*in2[5] + in1[2]*in2[9];
out[2] = in1[0]*in2[2] + in1[1]*in2[6] + in1[2]*in2[10];
}
void VectorTransformByMatrix (const vec3_t &in1, const float *in2, vec3_t &out)
{
out[0] = in1[0]*in2[0] + in1[1]*in2[4] + in1[2]*in2[8] + in2[12];
out[1] = in1[0]*in2[1] + in1[1]*in2[5] + in1[2]*in2[9] + in2[13];
out[2] = in1[0]*in2[2] + in1[1]*in2[6] + in1[2]*in2[10] + in2[14];
}
void VectorRotateByMatrix (const float *in1, const float *in2, float *out)
{
out[0] = in1[0]*in2[0] + in1[1]*in2[4] + in1[2]*in2[8];
out[1] = in1[0]*in2[1] + in1[1]*in2[5] + in1[2]*in2[9];
out[2] = in1[0]*in2[2] + in1[1]*in2[6] + in1[2]*in2[10];
}
void VectorTransformByMatrix (const float *in1, const float *in2, float *out)
{
out[0] = in1[0]*in2[0] + in1[1]*in2[4] + in1[2]*in2[8] + in2[12];
out[1] = in1[0]*in2[1] + in1[1]*in2[5] + in1[2]*in2[9] + in2[13];
out[2] = in1[0]*in2[2] + in1[1]*in2[6] + in1[2]*in2[10] + in2[14];
}
/*
====================
ObjectToWorldMatrix
====================
*/
void ObjectToWorldMatrix(cl_entity_t *e, float *result)
{
glPushMatrix();
glLoadIdentity();
glTranslatef(e->origin[0], e->origin[1], e->origin[2]);
glRotatef ( e->angles[1], 0, 0, 1);
glRotatef ( e->angles[0], 0, 1, 0);
glRotatef ( e->angles[2], 1, 0, 0);
glGetFloatv (GL_MODELVIEW_MATRIX, result);
glPopMatrix();
}
/*
====================
SPRITE_GetList
====================
*/
char *ParseHudSprite( char *pfile, char *psz, client_sprite_t *result )
{
char token[256];
client_sprite_t *p = new client_sprite_t;
int x = 0, y = 0, width = 0, height = 0;
int section = 0;
memset( p, 0, sizeof(client_sprite_t) );
while( pfile )
{
pfile = gEngfuncs.COM_ParseFile( pfile, token );
if( !stricmp( token, psz ))
{
pfile = gEngfuncs.COM_ParseFile( pfile, token );
if( !stricmp( token, "{" )) section = 1;
}
if(section)//parse section
{
if( !stricmp( token, "}" )) break;//end section
if ( !stricmp( token, "file" ))
{
pfile = gEngfuncs.COM_ParseFile(pfile, token);
strcpy(p->szSprite, token );
if( !gEngfuncs.COM_LoadFile( p->szSprite, 5, NULL )) return pfile;
else
{
gEngfuncs.COM_FreeFile( p->szSprite);
//fill structure at default
HSPRITE m_hSprite = SPR_Load(p->szSprite);
x = y = 0;
width = SPR_Width( m_hSprite, 0 );
height = SPR_Height( m_hSprite, 0 );
}
}
else if ( !stricmp( token, "name" ))
{
pfile = gEngfuncs.COM_ParseFile(pfile, token);
strcpy(p->szName, token );
}
else if ( !stricmp( token, "x" ))
{
pfile = gEngfuncs.COM_ParseFile(pfile, token);
x = atoi(token);
}
else if ( !stricmp( token, "y" ))
{
pfile = gEngfuncs.COM_ParseFile(pfile, token);
y = atoi(token);
}
else if ( !stricmp( token, "width" ))
{
pfile = gEngfuncs.COM_ParseFile(pfile, token);
width = atoi(token);
}
else if ( !stricmp( token, "height" ))
{
pfile = gEngfuncs.COM_ParseFile(pfile, token);
height = atoi(token);
}
}
}
if(!section) return pfile;//data not found
//calculate sprite position
p->rc.left = x;
p->rc.right = x + width;
p->rc.top = y;
p->rc.bottom = y + height;
//write resolution for backward compatibility
if (ScreenWidth < 640) p->iRes = 320;
else p->iRes = 640;
memcpy( result, p, sizeof(client_sprite_t));
return pfile;
}
client_sprite_t *SPR_GetList( char *psz, int *piCount )
{
int iSprCount = 0;
char *pfile = (char *)gEngfuncs.COM_LoadFile( psz, 5, NULL);
if (!pfile)
{
*piCount = iSprCount;
return NULL;
}
char token[256];
char *plist = pfile;
while ( pfile ) //calculate count of sprites
{
pfile = gEngfuncs.COM_ParseFile(pfile, token);
if ( !stricmp( token, "hudsprite" )) iSprCount++;
}
client_sprite_t *phud = new client_sprite_t[iSprCount];
for(int i = 0; i < iSprCount; i++ ) //parse structures
{
plist = ParseHudSprite( plist, "hudsprite", &phud[i] );
}
if(!iSprCount)Msg("SPR_GetList: %s don't have sprites\n", psz );
gEngfuncs.COM_FreeFile( pfile );
*piCount = iSprCount;
return phud;
}
/*
====================
Sys LoadGameDLL
====================
*/
bool Sys_LoadLibrary (const char* dllname, dllhandle_t* handle, const dllfunction_t *fcts)
{
const dllfunction_t *gamefunc;
char dllpath[128];
dllhandle_t dllhandle = 0;
if (handle == NULL) return false;
// Initializations
for (gamefunc = fcts; gamefunc && gamefunc->name != NULL; gamefunc++)
*gamefunc->funcvariable = NULL;
// Try every possible name
sprintf(dllpath, "%s/cl_dlls/%s", gEngfuncs.pfnGetGameDirectory(), dllname);
dllhandle = LoadLibrary (dllpath);
// No DLL found
if (! dllhandle) return false;
// Get the function adresses
for (gamefunc = fcts; gamefunc && gamefunc->name != NULL; gamefunc++)
if (!(*gamefunc->funcvariable = (void *) Sys_GetProcAddress (dllhandle, gamefunc->name)))
{
Sys_UnloadLibrary (&dllhandle);
return false;
}
Msg("%s loaded succesfully!\n", dllname);
*handle = dllhandle;
return true;
}
void Sys_UnloadLibrary (dllhandle_t* handle)
{
if (handle == NULL || *handle == NULL)
return;
FreeLibrary (*handle);
*handle = NULL;
}
void* Sys_GetProcAddress (dllhandle_t handle, const char* name)
{
return (void *)GetProcAddress (handle, name);
}

1117
client/r_view.cpp Normal file

File diff suppressed because it is too large Load Diff

45
client/r_view.h Normal file
View File

@ -0,0 +1,45 @@
//========= Copyright © 1996-2002, Valve LLC, All rights reserved. ============
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================
#ifndef VIEW_H
#define VIEW_H
void V_StartPitchDrift( void );
void V_StopPitchDrift( void );
//camera flags
#define CAMERA_ON 1
#define DRAW_HUD 2
#define INVERSE_X 4
#define MONSTER_VIEW 8
typedef struct pitchdrift_s
{
float pitchvel;
int nodrift;
float driftmove;
double laststop;
}pitchdrift_t;
static pitchdrift_t pd;
#define ORIGIN_BACKUP 64
#define ORIGIN_MASK ( ORIGIN_BACKUP - 1 )
typedef struct
{
float Origins[ ORIGIN_BACKUP ][3];
float OriginTime[ ORIGIN_BACKUP ];
float Angles[ ORIGIN_BACKUP ][3];
float AngleTime[ ORIGIN_BACKUP ];
int CurrentOrigin;
int CurrentAngle;
}viewinterp_t;
#endif // VIEW_H

680
client/r_weather.cpp Normal file
View File

@ -0,0 +1,680 @@
//=======================================================================
// Copyright (C) Shambler Team 2005
// rain.cpp - TriAPI weather effects
// based on original code from BUzer
//=======================================================================
#include "hud.h"
#include "r_main.h"
#include "r_util.h"
#include "const.h"
#include "entity_types.h"
#include "cdll_int.h"
#include "pm_defs.h"
#include "event_api.h"
#include "triangleapi.h"
#include "r_weather.h"
void WaterLandingEffect(cl_drip *drip);
void ParseRainFile( void );
rain_properties Rain;
cl_drip FirstChainDrip;
cl_rainfx FirstChainFX;
double rain_curtime; // current time
double rain_oldtime; // last time we have updated drips
double rain_timedelta; // difference between old time and current time
double rain_nextspawntime; // when the next drip should be spawned
int dripcounter = 0;
int fxcounter = 0;
/*
=================================
ProcessRain
Must think every frame.
=================================
*/
void ProcessRain( void )
{
rain_oldtime = rain_curtime; // save old time
rain_curtime = gEngfuncs.GetClientTime();
rain_timedelta = rain_curtime - rain_oldtime;
// first frame
if (rain_oldtime == 0)
{
// fix first frame bug with nextspawntime
rain_nextspawntime = gEngfuncs.GetClientTime();
ParseRainFile();
return;
}
if (Rain.dripsPerSecond == 0 && FirstChainDrip.p_Next == NULL)
{
// keep nextspawntime correct
rain_nextspawntime = rain_curtime;
return;
}
if (rain_timedelta == 0)
return; // not in pause
double timeBetweenDrips = 1 / (double)Rain.dripsPerSecond;
cl_drip* curDrip = FirstChainDrip.p_Next;
cl_drip* nextDrip = NULL;
cl_entity_t *player = gEngfuncs.GetLocalPlayer();
// save debug info
float debug_lifetime = 0;
int debug_howmany = 0;
int debug_attempted = 0;
int debug_dropped = 0;
while (curDrip != NULL) // go through list
{
nextDrip = curDrip->p_Next; // save pointer to next drip
if (Rain.weatherMode == 0)
curDrip->origin.z -= rain_timedelta * DRIPSPEED; // rain
else
curDrip->origin.z -= rain_timedelta * SNOWSPEED; // snow
curDrip->origin.x += rain_timedelta * curDrip->xDelta;
curDrip->origin.y += rain_timedelta * curDrip->yDelta;
// remove drip if its origin lower than minHeight
if (curDrip->origin.z < curDrip->minHeight)
{
if (curDrip->landInWater/* && Rain.weatherMode == 0*/)
WaterLandingEffect(curDrip); // create water rings
if (r_debug->value > 1)
{
debug_lifetime += (rain_curtime - curDrip->birthTime);
debug_howmany++;
}
curDrip->p_Prev->p_Next = curDrip->p_Next; // link chain
if (nextDrip != NULL)
nextDrip->p_Prev = curDrip->p_Prev;
delete curDrip;
dripcounter--;
}
curDrip = nextDrip; // restore pointer, so we can continue moving through chain
}
int maxDelta; // maximum height randomize distance
float falltime;
if (Rain.weatherMode == 0)
{
maxDelta = DRIPSPEED * rain_timedelta; // for rain
falltime = (Rain.globalHeight + 4096) / DRIPSPEED;
}
else
{
maxDelta = SNOWSPEED * rain_timedelta; // for snow
falltime = (Rain.globalHeight + 4096) / SNOWSPEED;
}
while (rain_nextspawntime < rain_curtime)
{
rain_nextspawntime += timeBetweenDrips;
if (r_debug->value > 1) debug_attempted++;
if (dripcounter < MAXDRIPS) // check for overflow
{
float deathHeight;
vec3_t vecStart, vecEnd;
vecStart[0] = gEngfuncs.pfnRandomFloat(player->origin.x - Rain.distFromPlayer, player->origin.x + Rain.distFromPlayer);
vecStart[1] = gEngfuncs.pfnRandomFloat(player->origin.y - Rain.distFromPlayer, player->origin.y + Rain.distFromPlayer);
vecStart[2] = Rain.globalHeight;
float xDelta = Rain.windX + gEngfuncs.pfnRandomFloat(Rain.randX * -1, Rain.randX);
float yDelta = Rain.windY + gEngfuncs.pfnRandomFloat(Rain.randY * -1, Rain.randY);
// find a point at bottom of map
vecEnd[0] = falltime * xDelta;
vecEnd[1] = falltime * yDelta;
vecEnd[2] = -4096;
pmtrace_t pmtrace;
gEngfuncs.pEventAPI->EV_SetTraceHull( 2 );
gEngfuncs.pEventAPI->EV_PlayerTrace( vecStart, vecStart + vecEnd, PM_STUDIO_IGNORE, -1, &pmtrace );
if (pmtrace.startsolid || pmtrace.allsolid)
{
if (r_debug->value > 1) debug_dropped++;
continue; // drip cannot be placed
}
// falling to water?
int contents = gEngfuncs.PM_PointContents( pmtrace.endpos, NULL );
if (contents == CONTENTS_WATER)
{
int waterEntity = gEngfuncs.PM_WaterEntity( pmtrace.endpos );
if ( waterEntity > 0 )
{
cl_entity_t *pwater = UTIL_GetClientEntityWithServerIndex( waterEntity );
if ( pwater && ( pwater->model != NULL ) )
{
deathHeight = pwater->curstate.maxs[2];
}
else
{
Msg("Rain error: can't get water entity\n");
continue;
}
}
else
{
Msg("Rain error: water is not func_water entity\n");
continue;
}
}
else
{
deathHeight = pmtrace.endpos[2];
}
// just in case..
if (deathHeight > vecStart[2])
{
Msg("Rain error: can't create drip in water\n");
continue;
}
cl_drip *newClDrip = new cl_drip;
if (!newClDrip)
{
Rain.dripsPerSecond = 0; // disable rain
Msg( "Rain error: failed to allocate object!\n");
return;
}
vecStart[2] -= gEngfuncs.pfnRandomFloat(0, maxDelta); // randomize a bit
newClDrip->alpha = gEngfuncs.pfnRandomFloat(0.12, 0.2);
VectorCopy(vecStart, newClDrip->origin);
newClDrip->xDelta = xDelta;
newClDrip->yDelta = yDelta;
newClDrip->birthTime = rain_curtime; // store time when it was spawned
newClDrip->minHeight = deathHeight;
if (contents == CONTENTS_WATER)
newClDrip->landInWater = 1;
else
newClDrip->landInWater = 0;
// add to first place in chain
newClDrip->p_Next = FirstChainDrip.p_Next;
newClDrip->p_Prev = &FirstChainDrip;
if (newClDrip->p_Next != NULL)
newClDrip->p_Next->p_Prev = newClDrip;
FirstChainDrip.p_Next = newClDrip;
dripcounter++;
}
else
{
Msg( "Rain error: Drip limit overflow!\n" );
return;
}
}
if (r_debug->value > 1) // print debug info
{
Msg( "Rain info: Drips exist: %i\n", dripcounter );
Msg( "Rain info: FX's exist: %i\n", fxcounter );
Msg( "Rain info: Attempted/Dropped: %i, %i\n", debug_attempted, debug_dropped);
if (debug_howmany)
{
float ave = debug_lifetime / (float)debug_howmany;
Msg( "Rain info: Average drip life time: %f\n", ave);
}
else
Msg( "Rain info: Average drip life time: --\n");
}
return;
}
/*
=================================
WaterLandingEffect
=================================
*/
void WaterLandingEffect(cl_drip *drip)
{
if (fxcounter >= MAXFX)
{
Msg( "Rain error: FX limit overflow!\n" );
return;
}
cl_rainfx *newFX = new cl_rainfx;
if (!newFX)
{
Msg( "Rain error: failed to allocate FX object!\n");
return;
}
newFX->alpha = gEngfuncs.pfnRandomFloat(0.6, 0.9);
VectorCopy(drip->origin, newFX->origin);
newFX->origin[2] = drip->minHeight; // correct position
newFX->birthTime = gEngfuncs.GetClientTime();
newFX->life = gEngfuncs.pfnRandomFloat(0.7, 1);
// add to first place in chain
newFX->p_Next = FirstChainFX.p_Next;
newFX->p_Prev = &FirstChainFX;
if (newFX->p_Next != NULL)
newFX->p_Next->p_Prev = newFX;
FirstChainFX.p_Next = newFX;
fxcounter++;
}
/*
=================================
ProcessFXObjects
Remove all fx objects with out time to live
Call every frame before ProcessRain
=================================
*/
void ProcessFXObjects( void )
{
float curtime = gEngfuncs.GetClientTime();
cl_rainfx* curFX = FirstChainFX.p_Next;
cl_rainfx* nextFX = NULL;
while (curFX != NULL) // go through FX objects list
{
nextFX = curFX->p_Next; // save pointer to next
// delete current?
if ((curFX->birthTime + curFX->life) < curtime)
{
curFX->p_Prev->p_Next = curFX->p_Next; // link chain
if (nextFX != NULL)
nextFX->p_Prev = curFX->p_Prev;
delete curFX;
fxcounter--;
}
curFX = nextFX; // restore pointer
}
}
/*
=================================
ResetRain
clear memory, delete all objects
=================================
*/
void ResetRain( void )
{
// delete all drips
cl_drip* delDrip = FirstChainDrip.p_Next;
FirstChainDrip.p_Next = NULL;
while (delDrip != NULL)
{
cl_drip* nextDrip = delDrip->p_Next; // save pointer to next drip in chain
delete delDrip;
delDrip = nextDrip; // restore pointer
dripcounter--;
}
// delete all FX objects
cl_rainfx* delFX = FirstChainFX.p_Next;
FirstChainFX.p_Next = NULL;
while (delFX != NULL)
{
cl_rainfx* nextFX = delFX->p_Next;
delete delFX;
delFX = nextFX;
fxcounter--;
}
InitRain();
return;
}
/*
=================================
InitRain
initialze system
=================================
*/
void InitRain( void )
{
Rain.dripsPerSecond = 0;
Rain.distFromPlayer = 0;
Rain.windX = 0;
Rain.windY = 0;
Rain.randX = 0;
Rain.randY = 0;
Rain.weatherMode = 0;
Rain.globalHeight = 0;
FirstChainDrip.birthTime = 0;
FirstChainDrip.minHeight = 0;
FirstChainDrip.origin[0]=0;
FirstChainDrip.origin[1]=0;
FirstChainDrip.origin[2]=0;
FirstChainDrip.alpha = 0;
FirstChainDrip.xDelta = 0;
FirstChainDrip.yDelta = 0;
FirstChainDrip.landInWater = 0;
FirstChainDrip.p_Next = NULL;
FirstChainDrip.p_Prev = NULL;
FirstChainFX.alpha = 0;
FirstChainFX.birthTime = 0;
FirstChainFX.life = 0;
FirstChainFX.origin[0] = 0;
FirstChainFX.origin[1] = 0;
FirstChainFX.origin[2] = 0;
FirstChainFX.p_Next = NULL;
FirstChainFX.p_Prev = NULL;
rain_oldtime = 0;
rain_curtime = 0;
rain_nextspawntime = 0;
return;
}
/*
===========================
ParseRainFile
List of settings:
drips - max raindrips\snowflakes
distance - radius of rain\snow
windx - wind shift X
windy - wind shift Y
randx - random shift X
randy - random shift Y
mode - rain = 0\snow =1
height - max height to create raindrips\snowflakes
===========================
*/
void ParseRainFile( void )
{
if (Rain.distFromPlayer != 0 || Rain.dripsPerSecond != 0 || Rain.globalHeight != 0)
return;
char mapname[64];
char token[64];
char *pfile;
strcpy( mapname, gEngfuncs.pfnGetLevelName() );
if (strlen(mapname) == 0)
{
Msg( "Rain error: unable to read map name\n");
return;
}
mapname[strlen(mapname)-4] = 0;
sprintf(mapname, "%s.pcs", mapname);
pfile = (char *)gEngfuncs.COM_LoadFile( mapname, 5, NULL);
if (!pfile)
{
if (r_debug->value > 1)
Msg("Rain: couldn't open rain settings file %s\n", mapname);
return;
}
while (true)
{
pfile = gEngfuncs.COM_ParseFile(pfile, token);
if (!pfile)
break;
if (!stricmp(token, "drips")) // dripsPerSecond
{
pfile = gEngfuncs.COM_ParseFile(pfile, token);
Rain.dripsPerSecond = atoi(token);
}
else if (!stricmp(token, "distance")) // distFromPlayer
{
pfile = gEngfuncs.COM_ParseFile(pfile, token);
Rain.distFromPlayer = atof(token);
}
else if (!stricmp(token, "windx")) // windX
{
pfile = gEngfuncs.COM_ParseFile(pfile, token);
Rain.windX = atof(token);
}
else if (!stricmp(token, "windy")) // windY
{
pfile = gEngfuncs.COM_ParseFile(pfile, token);
Rain.windY = atof(token);
}
else if (!stricmp(token, "randx")) // randX
{
pfile = gEngfuncs.COM_ParseFile(pfile, token);
Rain.randX = atof(token);
}
else if (!stricmp(token, "randy")) // randY
{
pfile = gEngfuncs.COM_ParseFile(pfile, token);
Rain.randY = atof(token);
}
else if (!stricmp(token, "mode")) // weatherMode
{
pfile = gEngfuncs.COM_ParseFile(pfile, token);
Rain.weatherMode = atoi(token);
}
else if (!stricmp(token, "height")) // globalHeight
{
pfile = gEngfuncs.COM_ParseFile(pfile, token);
Rain.globalHeight = atof(token);
}
else
Msg("Rain error: unknown token %s in file %s\n", token, mapname);
}
gEngfuncs.COM_FreeFile( pfile );
}
//-----------------------------------------------------
void SetPoint( float x, float y, float z, float (*matrix)[4])
{
vec3_t point, result;
point[0] = x;
point[1] = y;
point[2] = z;
VectorTransform(point, matrix, result);
gEngfuncs.pTriAPI->Vertex3f(result[0], result[1], result[2]);
}
/*
=================================
DrawRain
draw raindrips and snowflakes
=================================
*/
void DrawRain( void )
{
if (FirstChainDrip.p_Next == NULL)
return; // no drips to draw
HSPRITE hsprTexture;
const model_s *pTexture;
float visibleHeight = Rain.globalHeight - SNOWFADEDIST;
if (Rain.weatherMode == 0)
hsprTexture = LoadSprite( "sprites/raindrop.spr" ); // load rain sprite
else hsprTexture = LoadSprite( "sprites/snowflake.spr" ); // load snow sprite
if(!hsprTexture) return;
// usual triapi stuff
pTexture = gEngfuncs.GetSpritePointer( hsprTexture );
gEngfuncs.pTriAPI->SpriteTexture( (struct model_s *)pTexture, 0 );
gEngfuncs.pTriAPI->RenderMode( kRenderTransAdd );
gEngfuncs.pTriAPI->CullFace( TRI_NONE );
// go through drips list
cl_drip* Drip = FirstChainDrip.p_Next;
cl_entity_t *player = gEngfuncs.GetLocalPlayer();
if ( Rain.weatherMode == 0 ) // draw rain
{
while (Drip != NULL)
{
cl_drip* nextdDrip = Drip->p_Next;
Vector2D toPlayer;
toPlayer.x = player->origin[0] - Drip->origin[0];
toPlayer.y = player->origin[1] - Drip->origin[1];
toPlayer = toPlayer.Normalize();
toPlayer.x *= DRIP_SPRITE_HALFWIDTH;
toPlayer.y *= DRIP_SPRITE_HALFWIDTH;
float shiftX = (Drip->xDelta / DRIPSPEED) * DRIP_SPRITE_HALFHEIGHT;
float shiftY = (Drip->yDelta / DRIPSPEED) * DRIP_SPRITE_HALFHEIGHT;
gEngfuncs.pTriAPI->Color4f( 1.0, 1.0, 1.0, Drip->alpha );
gEngfuncs.pTriAPI->Begin( TRI_TRIANGLES );
gEngfuncs.pTriAPI->TexCoord2f( 0, 0 );
gEngfuncs.pTriAPI->Vertex3f( Drip->origin[0]-toPlayer.y - shiftX, Drip->origin[1]+toPlayer.x - shiftY,Drip->origin[2] + DRIP_SPRITE_HALFHEIGHT );
gEngfuncs.pTriAPI->TexCoord2f( 0.5, 1 );
gEngfuncs.pTriAPI->Vertex3f( Drip->origin[0] + shiftX, Drip->origin[1] + shiftY, Drip->origin[2]-DRIP_SPRITE_HALFHEIGHT );
gEngfuncs.pTriAPI->TexCoord2f( 1, 0 );
gEngfuncs.pTriAPI->Vertex3f( Drip->origin[0]+toPlayer.y - shiftX, Drip->origin[1]-toPlayer.x - shiftY, Drip->origin[2]+DRIP_SPRITE_HALFHEIGHT);
gEngfuncs.pTriAPI->End();
Drip = nextdDrip;
}
}
else // draw snow
{
vec3_t normal;
gEngfuncs.GetViewAngles((float*)normal);
float matrix[3][4];
AngleMatrix (normal, matrix); // calc view matrix
while (Drip != NULL)
{
cl_drip* nextdDrip = Drip->p_Next;
matrix[0][3] = Drip->origin[0]; // write origin to matrix
matrix[1][3] = Drip->origin[1];
matrix[2][3] = Drip->origin[2];
// apply start fading effect
float alpha = (Drip->origin[2] <= visibleHeight) ? Drip->alpha : ((Rain.globalHeight - Drip->origin[2]) / (float)SNOWFADEDIST) * Drip->alpha;
gEngfuncs.pTriAPI->Color4f( 1.0, 1.0, 1.0, alpha );
gEngfuncs.pTriAPI->Begin( TRI_QUADS );
gEngfuncs.pTriAPI->TexCoord2f( 0, 0 );
SetPoint(0, SNOW_SPRITE_HALFSIZE ,SNOW_SPRITE_HALFSIZE, matrix);
gEngfuncs.pTriAPI->TexCoord2f( 0, 1 );
SetPoint(0, SNOW_SPRITE_HALFSIZE ,-SNOW_SPRITE_HALFSIZE, matrix);
gEngfuncs.pTriAPI->TexCoord2f( 1, 1 );
SetPoint(0, -SNOW_SPRITE_HALFSIZE ,-SNOW_SPRITE_HALFSIZE, matrix);
gEngfuncs.pTriAPI->TexCoord2f( 1, 0 );
SetPoint(0, -SNOW_SPRITE_HALFSIZE ,SNOW_SPRITE_HALFSIZE, matrix);
gEngfuncs.pTriAPI->End();
Drip = nextdDrip;
}
}
}
/*
=================================
DrawFXObjects
=================================
*/
void DrawFXObjects( void )
{
if (FirstChainFX.p_Next == NULL)
return; // no objects to draw
float curtime = gEngfuncs.GetClientTime();
// usual triapi stuff
HSPRITE hsprTexture;
const model_s *pTexture;
hsprTexture = LoadSprite( "sprites/waterring.spr" ); // load water ring sprite
if(!hsprTexture) return;
pTexture = gEngfuncs.GetSpritePointer( hsprTexture );
gEngfuncs.pTriAPI->SpriteTexture( (struct model_s *)pTexture, 0 );
gEngfuncs.pTriAPI->RenderMode( kRenderTransAdd );
gEngfuncs.pTriAPI->CullFace( TRI_NONE );
// go through objects list
cl_rainfx* curFX = FirstChainFX.p_Next;
while (curFX != NULL)
{
cl_rainfx* nextFX = curFX->p_Next;
// fadeout
float alpha = ((curFX->birthTime + curFX->life - curtime) / curFX->life) * curFX->alpha;
float size = (curtime - curFX->birthTime) * MAXRINGHALFSIZE;
gEngfuncs.pTriAPI->Color4f( 1.0, 1.0, 1.0, alpha );
gEngfuncs.pTriAPI->Begin( TRI_QUADS );
gEngfuncs.pTriAPI->TexCoord2f( 0, 0 );
gEngfuncs.pTriAPI->Vertex3f(curFX->origin[0] - size, curFX->origin[1] - size, curFX->origin[2]);
gEngfuncs.pTriAPI->TexCoord2f( 0, 1 );
gEngfuncs.pTriAPI->Vertex3f(curFX->origin[0] - size, curFX->origin[1] + size, curFX->origin[2]);
gEngfuncs.pTriAPI->TexCoord2f( 1, 1 );
gEngfuncs.pTriAPI->Vertex3f(curFX->origin[0] + size, curFX->origin[1] + size, curFX->origin[2]);
gEngfuncs.pTriAPI->TexCoord2f( 1, 0 );
gEngfuncs.pTriAPI->Vertex3f(curFX->origin[0] + size, curFX->origin[1] - size, curFX->origin[2]);
gEngfuncs.pTriAPI->End();
curFX = nextFX;
}
}
/*
=================================
DrawAll
=================================
*/
void R_DrawWeather( void )
{
ProcessFXObjects();
ProcessRain();
DrawRain();
DrawFXObjects();
}

66
client/r_weather.h Normal file
View File

@ -0,0 +1,66 @@
/***
*
* Copyright (c) 1996-2004, Shambler Team. 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
* Shambler Team. All other use, distribution, or modification is prohibited
* without written permission from Shambler Team.
*
****/
/*
====== rain.h ========================================================
*/
#ifndef __RAIN_H__
#define __RAIN_H__
#define DRIPSPEED 900 // speed of raindrips (pixel per secs)
#define SNOWSPEED 200 // speed of snowflakes
#define SNOWFADEDIST 80
#define MAXDRIPS 2000 // max raindrops
#define MAXFX 3000 // max effects
#define DRIP_SPRITE_HALFHEIGHT 46
#define DRIP_SPRITE_HALFWIDTH 8
#define SNOW_SPRITE_HALFSIZE 3
// radius water rings
#define MAXRINGHALFSIZE 25
typedef struct cl_drip
{
float birthTime;
float minHeight; // minimal height to kill raindrop
vec3_t origin;
float alpha;
float xDelta; // side speed
float yDelta;
int landInWater;
cl_drip* p_Next; // next drip in chain
cl_drip* p_Prev; // previous drip in chain
} cl_drip_t;
typedef struct cl_rainfx
{
float birthTime;
float life;
vec3_t origin;
float alpha;
cl_rainfx* p_Next; // next fx in chain
cl_rainfx* p_Prev; // previous fx in chain
} cl_rainfx_t;
void ProcessRain( void );
void ProcessFXObjects( void );
void ResetRain( void );
void InitRain( void );
#endif

107
client/readme.txt Normal file
View File

@ -0,0 +1,107 @@
client dll readme.txt
-------------------------
This file details the structure of the half-life client dll, and
how it communicates with the half-life game engine.
Engine callback functions:
Drawing functions:
HSPRITE SPR_Load( char *picname );
Loads a sprite into memory, and returns a handle to it.
int SPR_Frames( HSPRITE sprite );
Returns the number of frames stored in the specified sprite.
int SPR_Height( HSPRITE x, int frame )
Returns the height, in pixels, of a sprite at the specified frame.
Returns 0 is the frame number or the sprite handle is invalid.
int SPR_Width( HSPRITE x, int f )
Returns the width, in pixels, of a sprite at the specified frame.
Returns 0 is the frame number or the sprite handle is invalid.
int SPR_Set( HSPRITE sprite, int r, int g, int b );
Prepares a sprite about to be drawn. RBG color values are applied to the sprite at this time.
void SPR_Draw( int frame, int x, int y );
Precondition: SPR_Set has already been called for a sprite.
Draws the currently active sprite to the screen, at position (x,y), where (0,0) is
the top left-hand corner of the screen.
void SPR_DrawHoles( int frame, int x, int y );
Precondition: SPR_Set has already been called for a sprite.
Draws the currently active sprite to the screen. Color index #255 is treated as transparent.
void SPR_DrawAdditive( int frame, int x, int y );
Precondition: SPR_Set has already been called for a sprite.
Draws the currently active sprite to the screen, adding it's color values to the background.
void SPR_EnableScissor( int x, int y, int width, int height );
Creates a clipping rectangle. No pixels will be drawn outside the specified area. Will
stay in effect until either the next frame, or SPR_DisableScissor is called.
void SPR_DisableScissor( void );
Disables the effect of an SPR_EnableScissor call.
int IsHighRes( void );
returns 1 if the res mode is 640x480 or higher; 0 otherwise.
int ScreenWidth( void );
returns the screen width, in pixels.
int ScreenHeight( void );
returns the screen height, in pixels.
// Sound functions
void PlaySound( char *szSound, int volume )
plays the sound 'szSound' at the specified volume. Loads the sound if it hasn't been cached.
If it can't find the sound, it displays an error message and plays no sound.
void PlaySound( int iSound, int volume )
Precondition: iSound has been precached.
Plays the sound, from the precache list.
// Communication functions
void SendClientCmd( char *szCmdString );
sends a command to the server, just as if the client had typed the szCmdString at the console.
char *GetPlayerName( int entity_number );
returns a pointer to a string, that contains the name of the specified client.
Returns NULL if the entity_number is not a client.
DECLARE_MESSAGE(), HOOK_MESSAGE()
These two macros bind the message sending between the entity DLL and the client DLL to
the CHud object.
HOOK_MESSAGE( message_name )
This is used inside CHud::Init(). It calls into the engine to hook that message
from the incoming message stream.
Precondition: There must be a function of name UserMsg_message_name declared
for CHud. Eg, CHud::UserMsg_Health() must be declared if you want to
use HOOK_MESSAGE( Health );
DECLARE_MESSAGE( message_name )
For each HOOK_MESSAGE you must have an equivalent DECLARE_MESSAGE. This creates
a function which passes the hooked messages into the CHud object.
HOOK_COMMAND(), DECLARE_COMMAND()
These two functions declare and hook console commands into the client dll.
HOOK_COMMAND( char *command, command_name )
Whenever the user types the 'command' at the console, the function 'command_name'
will be called.
Precondition: There must be a function of the name UserCmd_command_name declared
for CHud. Eg, CHud::UserMsg_ShowScores() must be declared if you want to
use HOOK_COMMAND( "+showscores", ShowScores );
DECLARE_COMMAND( command_name )
For each HOOK_COMMAND you must have an equivelant DECLARE_COMMAND. This creates
a function which passes the hooked commands into the CHud object.

View File

@ -91,7 +91,7 @@ BSC32=bscmake.exe
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
# ADD LINK32 msvcrt.lib user32.lib /nologo /dll /debug /machine:I386 /nodefaultlib:"msvcrtd.lib" /pdbtype:sept
# ADD LINK32 msvcrtd.lib user32.lib /nologo /dll /debug /machine:I386 /nodefaultlib:"libc.lib" /pdbtype:sept
# SUBTRACT LINK32 /incremental:no /nodefaultlib
# Begin Custom Build
TargetDir=\Xash3D\src_main\temp\common\!debug

View File

@ -11,6 +11,9 @@ call vcvars32
%MSDEV% baserc/baserc.dsp %CONFIG%"baserc - Win32 Debug" %build_target%
if errorlevel 1 set BUILD_ERROR=1
%MSDEV% client/client.dsp %CONFIG%"client - Win32 Debug" %build_target%
if errorlevel 1 set BUILD_ERROR=1
%MSDEV% engine/engine.dsp %CONFIG%"engine - Win32 Debug" %build_target%
if errorlevel 1 set BUILD_ERROR=1
@ -26,7 +29,7 @@ if errorlevel 1 set BUILD_ERROR=1
%MSDEV% render/render.dsp %CONFIG%"render - Win32 Debug" %build_target%
if errorlevel 1 set BUILD_ERROR=1
%MSDEV% sv_dll/server.dsp %CONFIG%"server - Win32 Debug" %build_target%
%MSDEV% server/server.dsp %CONFIG%"server - Win32 Debug" %build_target%
if errorlevel 1 set BUILD_ERROR=1
%MSDEV% vprogs/vprogs.dsp %CONFIG%"vprogs - Win32 Debug" %build_target%
@ -59,6 +62,7 @@ if exist engine\engine.plg del /f /q engine\engine.plg
if exist launch\launch.plg del /f /q launch\launch.plg
if exist common\common.plg del /f /q common\common.plg
if exist physic\physic.plg del /f /q physic\physic.plg
if exist server\server.plg del /f /q server\server.plg
if exist render\render.plg del /f /q render\render.plg
if exist viewer\viewer.plg del /f /q viewer\viewer.plg
if exist vprogs\vprogs.plg del /f /q vprogs\vprogs.plg
@ -67,5 +71,5 @@ if exist vsound\vsound.plg del /f /q vsound\vsound.plg
echo Build succeeded!
echo Please wait. Xash is now loading
cd D:\Xash3D\
quake.exe -game tmpQuArK -log -debug -dev 3 +map qctest
quake.exe -game tmpQuArK -log -debug -dev 5 +map qctest
:done

View File

@ -34,7 +34,7 @@ void CL_WriteDemoHeader( const char *name )
{
char buf_data[MAX_MSGLEN];
entity_state_t *state, nullstate;
pr_edict_t *ent;
edict_t *ent;
sizebuf_t buf;
int i, len;
@ -69,7 +69,7 @@ void CL_WriteDemoHeader( const char *name )
if( buf.cursize + com.strlen(cl.configstrings[i]) + 32 > buf.maxsize )
{
// write it out
len = LittleLong (buf.cursize);
len = LittleLong( buf.cursize );
FS_Write( cls.demofile, &len, 4 );
FS_Write( cls.demofile, buf.data, buf.cursize );
buf.cursize = 0;
@ -81,16 +81,14 @@ void CL_WriteDemoHeader( const char *name )
}
CL_VM_Begin();
// baselines
memset(&nullstate, 0, sizeof(nullstate));
Mem_Set( &nullstate, 0, sizeof( nullstate ));
for( i = 0; i < prog->num_edicts; i++ )
for( i = 0; i < game.numEntities; i++ )
{
ent = PRVM_EDICT_NUM( i );
state = &ent->priv.cl->baseline;
if(!state->model.index) continue;
ent = EDICT_NUM( i );
state = &ent->pvEngineData->baseline;
if( !state->model.index ) continue;
if( buf.cursize + 64 > buf.maxsize )
{
@ -101,7 +99,7 @@ void CL_WriteDemoHeader( const char *name )
buf.cursize = 0;
}
MSG_WriteByte( &buf, svc_spawnbaseline );
MSG_WriteDeltaEntity (&nullstate, &ent->priv.cl->baseline, &buf, true, true );
MSG_WriteDeltaEntity( &nullstate, &ent->pvEngineData->baseline, &buf, true, true );
}
MSG_WriteByte( &buf, svc_stufftext );
@ -111,8 +109,6 @@ void CL_WriteDemoHeader( const char *name )
len = LittleLong( buf.cursize );
FS_Write( cls.demofile, &len, 4 );
FS_Write( cls.demofile, buf.data, buf.cursize );
CL_VM_End();
}
/*

View File

@ -1,79 +0,0 @@
//=======================================================================
// Copyright XashXT Group 2008 ©
// cl_edict.h - client prvm edict
//=======================================================================
#ifndef CL_EDICT_H
#define CL_EDICT_H
struct cl_globalvars_s
{
int pad[34];
int pev;
int world;
string_t mapname;
float realtime;
float frametime;
vec3_t vieworg;
vec3_t viewangles;
vec3_t v_forward;
vec3_t v_right;
vec3_t v_up;
float onground;
float playernum;
float waterlevel;
float clientflags;
vec3_t cl_viewangles;
vec3_t simvel;
vec3_t simorg;
float idealpitch;
vec3_t viewheight;
float health;
float max_entities;
float maxclients;
float lerpfrac;
float intermission;
float demoplayback;
float paused;
vec3_t punchangle;
vec3_t crosshairangle;
int viewentity;
vec3_t blend_color;
float blend_alpha;
float screen_x;
float screen_y;
float screen_w;
float screen_h;
func_t HUD_Init;
func_t HUD_StudioEvent;
func_t HUD_ParseMessage;
func_t HUD_Render;
func_t HUD_UpdateEntities;
func_t HUD_Shutdown;
func_t V_CalcRefdef;
};
struct cl_entvars_s
{
string_t classname;
string_t globalname;
float modelindex;
float soundindex;
int chain;
int owner;
string_t model;
vec3_t origin;
vec3_t angles;
vec3_t mins;
vec3_t maxs;
float solid;
float sequence;
float effects;
float frame;
float body;
float skin;
float flags;
};
#define PROG_CRC_CLIENT 3720
#endif//CL_EDICT_H

View File

@ -587,8 +587,8 @@ void CL_AddParticles( void )
if( !cl_particles->integer ) return;
if( PRVM_EDICT_NUM( cl.frame.ps.number )->priv.cl->current.gravity != 0 )
gravity = PRVM_EDICT_NUM( cl.frame.ps.number )->priv.cl->current.gravity / 800.0;
if( EDICT_NUM( cl.frame.ps.number )->pvEngineData->current.gravity != 0 )
gravity = EDICT_NUM( cl.frame.ps.number )->pvEngineData->current.gravity / 800.0; // FIXME: register CS_GRAVITY
else gravity = 1.0f;
for( p = cl_active_particles; p; p = next )
@ -682,7 +682,7 @@ void CL_AddParticles( void )
if( p->flags & PARTICLE_BOUNCE )
{
pr_edict_t *clent = PRVM_EDICT_NUM( cl.frame.ps.number );
edict_t *clent = EDICT_NUM( cl.frame.ps.number );
// bouncy particle
VectorSet(mins, -radius, -radius, -radius);

View File

@ -13,15 +13,15 @@ FRAME PARSING
=========================================================================
*/
void CL_UpdateEntityFileds( pr_edict_t *ent )
void CL_UpdateEntityFields( edict_t *ent )
{
// copy state to progs
ent->progs.cl->classname = cl.edict_classnames[ent->priv.cl->current.classname];
ent->progs.cl->modelindex = ent->priv.cl->current.model.index;
ent->progs.cl->soundindex = ent->priv.cl->current.soundindex;
ent->progs.cl->model = PRVM_SetEngineString( cl.configstrings[CS_MODELS+ent->priv.cl->current.model.index] );
VectorCopy( ent->priv.cl->current.origin, ent->progs.cl->origin );
VectorCopy( ent->priv.cl->current.angles, ent->progs.cl->angles );
ent->v.classname = cl.edict_classnames[ent->pvEngineData->current.classname];
ent->v.modelindex = ent->pvEngineData->current.model.index;
ent->v.ambient = ent->pvEngineData->current.soundindex;
ent->v.model = MAKE_STRING( cl.configstrings[CS_MODELS+ent->pvEngineData->current.model.index] );
VectorCopy( ent->pvEngineData->current.origin, ent->v.origin );
VectorCopy( ent->pvEngineData->current.angles, ent->v.angles );
}
/*
@ -34,10 +34,10 @@ to the current frame
*/
void CL_DeltaEntity( sizebuf_t *msg, frame_t *frame, int newnum, entity_state_t *old, bool unchanged )
{
pr_edict_t *ent;
edict_t *ent;
entity_state_t *state;
ent = PRVM_EDICT_NUM( newnum );
ent = EDICT_NUM( newnum );
state = &cl_parse_entities[cl.parse_entities & (MAX_PARSE_ENTITIES-1)];
if( unchanged ) *state = *old;
@ -49,28 +49,28 @@ void CL_DeltaEntity( sizebuf_t *msg, frame_t *frame, int newnum, entity_state_t
frame->num_entities++;
// some data changes will force no lerping
if( state->model.index != ent->priv.cl->current.model.index || state->pmodel.index != ent->priv.cl->current.pmodel.index || state->model.body != ent->priv.cl->current.model.body
|| state->model.sequence != ent->priv.cl->current.model.sequence || abs(state->origin[0] - ent->priv.cl->current.origin[0]) > 512
|| abs(state->origin[1] - ent->priv.cl->current.origin[1]) > 512 || abs(state->origin[2] - ent->priv.cl->current.origin[2]) > 512 )
if( state->model.index != ent->pvEngineData->current.model.index || state->pmodel.index != ent->pvEngineData->current.pmodel.index || state->model.body != ent->pvEngineData->current.model.body
|| state->model.sequence != ent->pvEngineData->current.model.sequence || abs(state->origin[0] - ent->pvEngineData->current.origin[0]) > 512
|| abs(state->origin[1] - ent->pvEngineData->current.origin[1]) > 512 || abs(state->origin[2] - ent->pvEngineData->current.origin[2]) > 512 )
{
ent->priv.cl->serverframe = -99;
ent->pvEngineData->serverframe = -99;
}
if( ent->priv.cl->serverframe != cl.frame.serverframe - 1 )
if( ent->pvEngineData->serverframe != cl.frame.serverframe - 1 )
{
// duplicate the current state so lerping doesn't hurt anything
ent->priv.cl->prev = *state;
ent->pvEngineData->prev = *state;
}
else
{ // shuffle the last state to previous
ent->priv.cl->prev = ent->priv.cl->current;
ent->pvEngineData->prev = ent->pvEngineData->current;
}
ent->priv.cl->serverframe = cl.frame.serverframe;
ent->priv.cl->current = *state;
ent->pvEngineData->serverframe = cl.frame.serverframe;
ent->pvEngineData->current = *state;
// update prvm fields
CL_UpdateEntityFileds( ent );
CL_UpdateEntityFields( ent );
}
/*
@ -154,8 +154,8 @@ void CL_ParsePacketEntities( sizebuf_t *msg, frame_t *oldframe, frame_t *newfram
if( oldnum > newnum )
{
// delta from baseline
pr_edict_t *ent = PRVM_EDICT_NUM( newnum );
CL_DeltaEntity( msg, newframe, newnum, &ent->priv.cl->baseline, false );
edict_t *ent = EDICT_NUM( newnum );
CL_DeltaEntity( msg, newframe, newnum, &ent->pvEngineData->baseline, false );
continue;
}
@ -188,7 +188,7 @@ CL_ParseFrame
void CL_ParseFrame( sizebuf_t *msg )
{
int cmd, len, idx;
pr_edict_t *clent;
edict_t *clent;
frame_t *old;
memset( &cl.frame, 0, sizeof(cl.frame));
@ -243,7 +243,7 @@ void CL_ParseFrame( sizebuf_t *msg )
cmd = MSG_ReadByte( msg );
if( cmd != svc_playerinfo ) Host_Error( "CL_ParseFrame: not clientindex\n" );
idx = MSG_ReadByte( msg );
clent = PRVM_EDICT_NUM( idx ); // get client
clent = EDICT_NUM( idx ); // get client
if((idx-1) != cl.playernum ) Host_Error("CL_ParseFrame: invalid playernum (%d should be %d)\n", idx-1, cl.playernum );
}
else
@ -263,8 +263,8 @@ void CL_ParseFrame( sizebuf_t *msg )
if( sv_newprotocol->integer )
{
// now we can reading delta player state
if( old ) cl.frame.ps = MSG_ParseDeltaPlayer( &old->ps, &clent->priv.cl->current );
else cl.frame.ps = MSG_ParseDeltaPlayer( NULL, &clent->priv.cl->current );
if( old ) cl.frame.ps = MSG_ParseDeltaPlayer( &old->ps, &clent->pvEngineData->current );
else cl.frame.ps = MSG_ParseDeltaPlayer( NULL, &clent->pvEngineData->current );
}
// FIXME
@ -304,19 +304,17 @@ CL_AddPacketEntities
void CL_AddPacketEntities( frame_t *frame )
{
entity_state_t *s1;
pr_edict_t *ent;
edict_t *ent;
int pnum;
for( pnum = 0; pnum < frame->num_entities; pnum++ )
{
s1 = &cl_parse_entities[(frame->parse_entities + pnum)&(MAX_PARSE_ENTITIES-1)];
ent = PRVM_EDICT_NUM( s1->number );
re->AddRefEntity( &ent->priv.cl->current, &ent->priv.cl->prev, cl.lerpfrac );
ent = EDICT_NUM( s1->number );
re->AddRefEntity( &ent->pvEngineData->current, &ent->pvEngineData->prev, cl.lerpfrac );
}
}
/*
==============
CL_AddViewWeapon
@ -324,7 +322,7 @@ CL_AddViewWeapon
*/
void CL_AddViewWeapon( entity_state_t *ps )
{
pr_edict_t *view; // view model
edict_t *view; // view model
// allow the gun to be completely removed
if( !cl_gun->value ) return;
@ -332,12 +330,12 @@ void CL_AddViewWeapon( entity_state_t *ps )
// don't draw gun if in wide angle view
if( ps->fov > 135 ) return;
view = PRVM_EDICT_NUM( ps->aiment );
VectorCopy( cl.refdef.vieworg, view->priv.cl->current.origin );
VectorCopy( cl.refdef.viewangles, view->priv.cl->current.angles );
VectorCopy( cl.refdef.vieworg, view->priv.cl->prev.origin );
VectorCopy( cl.refdef.viewangles, view->priv.cl->prev.angles );
re->AddRefEntity( &view->priv.cl->current, &view->priv.cl->prev, cl.lerpfrac );
view = EDICT_NUM( ps->aiment );
VectorCopy( cl.refdef.vieworg, view->pvEngineData->current.origin );
VectorCopy( cl.refdef.viewangles, view->pvEngineData->current.angles );
VectorCopy( cl.refdef.vieworg, view->pvEngineData->prev.origin );
VectorCopy( cl.refdef.viewangles, view->pvEngineData->prev.angles );
re->AddRefEntity( &view->pvEngineData->current, &view->pvEngineData->prev, cl.lerpfrac );
}
@ -459,7 +457,7 @@ void CL_AddEntities( void )
void CL_GetEntitySoundSpatialization( int entnum, vec3_t origin, vec3_t velocity )
{
pr_edict_t *ent;
edict_t *ent;
cmodel_t *cmodel;
vec3_t midPoint;
@ -471,21 +469,21 @@ void CL_GetEntitySoundSpatialization( int entnum, vec3_t origin, vec3_t velocity
return;
}
ent = PRVM_EDICT_NUM( entnum );
ent = EDICT_NUM( entnum );
// calculate origin
origin[0] = ent->priv.cl->prev.origin[0] + (ent->priv.cl->current.origin[0] - ent->priv.cl->prev.origin[0]) * cl.lerpfrac;
origin[1] = ent->priv.cl->prev.origin[1] + (ent->priv.cl->current.origin[1] - ent->priv.cl->prev.origin[1]) * cl.lerpfrac;
origin[2] = ent->priv.cl->prev.origin[2] + (ent->priv.cl->current.origin[2] - ent->priv.cl->prev.origin[2]) * cl.lerpfrac;
origin[0] = ent->pvEngineData->prev.origin[0] + (ent->pvEngineData->current.origin[0] - ent->pvEngineData->prev.origin[0]) * cl.lerpfrac;
origin[1] = ent->pvEngineData->prev.origin[1] + (ent->pvEngineData->current.origin[1] - ent->pvEngineData->prev.origin[1]) * cl.lerpfrac;
origin[2] = ent->pvEngineData->prev.origin[2] + (ent->pvEngineData->current.origin[2] - ent->pvEngineData->prev.origin[2]) * cl.lerpfrac;
// calculate velocity
VectorSubtract( ent->priv.cl->current.origin, ent->priv.cl->prev.origin, velocity);
VectorSubtract( ent->pvEngineData->current.origin, ent->pvEngineData->prev.origin, velocity);
VectorScale(velocity, 10, velocity);
// if a brush model, offset the origin
if( VectorIsNull( origin ))
{
cmodel = cl.models[ent->priv.cl->current.model.index];
cmodel = cl.models[ent->pvEngineData->current.model.index];
if( !cmodel ) return;
VectorAverage( cmodel->mins, cmodel->maxs, midPoint );
VectorAdd( origin, midPoint, origin );

924
engine/client/cl_game.c Normal file
View File

@ -0,0 +1,924 @@
//=======================================================================
// Copyright XashXT Group 2008 ©
// cl_game.c - client dlls interaction
//=======================================================================
#include "common.h"
#include "client.h"
#include "byteorder.h"
#include "matrix_lib.h"
#include "com_library.h"
#include "const.h"
/*
====================
CL_GetClientEntity
Render callback for studio models
====================
*/
entity_state_t *CL_GetEdictByIndex( int index )
{
return &EDICT_NUM( index )->pvEngineData->current;
}
/*
====================
CL_GetLocalPlayer
Render callback for studio models
====================
*/
entity_state_t *CL_GetLocalPlayer( void )
{
return &EDICT_NUM( cl.playernum + 1 )->pvEngineData->current;
}
/*
====================
CL_GetMaxlients
Render callback for studio models
====================
*/
int CL_GetMaxClients( void )
{
return com.atoi( cl.configstrings[CS_MAXCLIENTS] );
}
/*
================
CL_FadeColor
================
*/
float *CL_FadeColor( float starttime, float endtime )
{
static vec4_t color;
float time, fade_time;
if( starttime == 0 ) return NULL;
time = (cls.realtime * 0.001f) - starttime;
if( time >= endtime ) return NULL;
// fade time is 1/4 of endtime
fade_time = endtime / 4;
fade_time = bound( 0.3f, fade_time, 10.0f );
// fade out
if((endtime - time) < fade_time)
color[3] = (endtime - time) * 1.0f / fade_time;
else color[3] = 1.0;
color[0] = color[1] = color[2] = 1.0f;
return color;
}
void CL_DrawHUD( void )
{
}
void CL_CopyTraceResult( TraceResult *out, trace_t trace )
{
if( !out ) return;
out->fAllSolid = trace.allsolid;
out->fStartSolid = trace.startsolid;
out->fStartStuck = trace.startstuck;
out->flFraction = trace.fraction;
out->iStartContents = trace.startcontents;
out->iContents = trace.contents;
out->iHitgroup = trace.hitgroup;
out->flPlaneDist = trace.plane.dist;
VectorCopy( trace.endpos, out->vecEndPos );
VectorCopy( trace.plane.normal, out->vecPlaneNormal );
if( trace.surface )
out->pTexName = trace.surface->name;
else out->pTexName = NULL;
out->pHit = trace.ent;
}
void CL_PrepUserMessage( char *pszName, const int svc_num )
{
user_message_t *msg;
char *end;
if( !pszName || !*pszName ) return; // ignore blank names
if( game.numMessages == MAX_USER_MESSAGES )
{
MsgDev( D_ERROR, "CL_PrepUserMessage: user messages limit is out\n" );
return;
}
// clear existing or allocate new one
msg = game.msg[game.numMessages];
if( msg ) Mem_Set( msg, 0, sizeof( *msg ));
else msg = Mem_Alloc( cls.mempool, sizeof( *msg ));
end = com.strchr( pszName, '@' );
if( !end )
{
MsgDev( D_ERROR, "CL_PrepUserMessage: can't register message %s\n", pszName );
return;
}
msg->size = com.atoi( end + 1 );
pszName[end-pszName] = '\0'; // remove size description from MsgName
msg->name = pszName;
msg->number = svc_num;
game.numMessages++;
// debug
Msg("name %s [%i][svc_%i]\n", msg->name, msg->size, msg->number );
}
void CL_ParseUserMessage( sizebuf_t *net_buffer, int svc_num )
{
user_message_t *msg;
int iSize;
byte *pbuf;
// NOTE: any user message parse on engine, not in client.dll
if( svc_num >= game.numMessages || !game.msg[svc_num] )
{
// unregister message can't be parsed
Host_Error( "CL_ParseUserMessage: illegible server message %d\n", svc_num );
return;
}
if( svc_num != game.msg[svc_num]->number )
{
int i;
// search for right number
for( i = 0; i < game.numMessages; i++ )
{
msg = game.msg[i];
if( !msg || msg->number == svc_num )
break;
}
// throw warn
MsgDev( D_WARN, "CL_ParseUserMessage: wrong message num %i\n", svc_num );
if( i == game.numMessages || !msg )
{
// this never happens
Host_Error( "CL_ParseUserMessage: illegible server message %d\n", svc_num );
return;
}
}
else msg = game.msg[svc_num];
iSize = msg->size;
pbuf = NULL;
// message with variable sizes receive actual size as first byte
// FIXME: replace with short for support messages more than 255 bytes ?
if( iSize == -1 ) iSize = MSG_ReadByte( net_buffer );
if( iSize > 0 ) pbuf = Mem_Alloc( cls.private, iSize );
// parse user message into buffer
MSG_ReadData( net_buffer, pbuf, iSize );
if( msg->func ) msg->func( msg->name, iSize, pbuf );
else MsgDev( D_WARN, "CL_ParseUserMessage: message %s doesn't have execute function\n", msg->name );
if( pbuf ) Mem_Free( pbuf );
}
void CL_InitEdict( edict_t *pEdict )
{
Com_Assert( pEdict == NULL );
pEdict->v.pContainingEntity = pEdict; // make cross-links for consistency
pEdict->pvEngineData = (ed_priv_t *)Mem_Alloc( cls.mempool, sizeof( ed_priv_t ));
pEdict->serialnumber = NUM_FOR_EDICT( pEdict ); // merged on first update
pEdict->free = false;
}
void CL_FreeEdict( edict_t *pEdict )
{
Com_Assert( pEdict == NULL );
Com_Assert( pEdict->free );
// unlink from world
// CL_UnlinkEdict( pEdict );
if( pEdict->pvEngineData ) Mem_Free( pEdict->pvEngineData );
Mem_Set( &pEdict->v, 0, sizeof( entvars_t ));
pEdict->pvEngineData = NULL;
// mark edict as freed
pEdict->freetime = cl.time * 0.001f;
pEdict->serialnumber = 0;
pEdict->free = true;
}
edict_t *CL_AllocEdict( void )
{
edict_t *pEdict;
int i;
for( i = 0; i < game.numEntities; i++ )
{
pEdict = EDICT_NUM( i );
// the first couple seconds of server time can involve a lot of
// freeing and allocating, so relax the replacement policy
if( pEdict->free && ( pEdict->freetime < 2.0f || ((cl.time * 0.001f) - pEdict->freetime) > 0.5f ))
{
CL_InitEdict( pEdict );
return pEdict;
}
}
if( i == game.maxEntities )
Host_Error( "CL_AllocEdict: no free edicts\n" );
game.numEntities++;
pEdict = EDICT_NUM( i );
CL_InitEdict( pEdict );
return pEdict;
}
void CL_FreeEdicts( void )
{
int i;
edict_t *ent;
for( i = 0; game.numEntities; i++ )
{
ent = EDICT_NUM( i );
if( ent->free ) continue;
CL_FreeEdict( ent );
}
}
/*
===============================================================================
CGame Builtin Functions
===============================================================================
*/
/*
=========
pfnMemAlloc
=========
*/
static void *pfnMemAlloc( size_t cb, const char *filename, const int fileline )
{
return com.malloc( cls.private, cb, filename, fileline );
}
/*
=========
pfnMemFree
=========
*/
static void pfnMemFree( void *mem, const char *filename, const int fileline )
{
com.free( mem, filename, fileline );
}
/*
=============
pfnLoadShader
=============
*/
shader_t pfnLoadShader( const char *szShaderName )
{
if( !re ) return 0; // render not initialized
if( !szShaderName || !*szShaderName )
{
MsgDev( D_ERROR, "CL_LoadShader: invalid shadername\n" );
return -1;
}
return re->RegisterShader( szShaderName, SHADER_NOMIP );
}
/*
=============
pfnFillRGBA
=============
*/
void pfnFillRGBA( int x, int y, int width, int height, const float *color, float alpha )
{
SCR_FillRect( x, y, width, height, GetRGBA( color[0], color[1], color[2], alpha ));
if( re ) re->SetColor( NULL );
}
/*
=============
pfnDrawImage
=============
*/
void pfnDrawImage( shader_t shader, int x, int y, int width, int height, int frame )
{
if( shader == -1 )
{
MsgDev( D_ERROR, "CL_DrawImage: invalid shader handle\n" );
return;
}
SCR_DrawPic( x, y, width, height, shader );
if( re ) re->SetColor( NULL );
}
/*
=============
pfnSetColor
=============
*/
void pfnSetColor( float r, float g, float b, float a )
{
if( !re ) return; // render not initialized
re->SetColor( GetRGBA( r, g, b, a ));
}
/*
=============
pfnRegisterVariable
=============
*/
void pfnRegisterVariable( const char *szName, const char *szValue, int flags, const char *szDesc )
{
// FIXME: translate client.dll flags to real cvar flags
Cvar_Get( szName, szValue, flags, szDesc );
}
/*
=============
pfnCvarSetValue
=============
*/
void pfnCvarSetValue( const char *cvar, float value )
{
Cvar_SetValue( cvar, value );
}
/*
=============
pfnGetCvarFloat
=============
*/
float pfnGetCvarFloat( const char *szName )
{
return Cvar_VariableValue( szName );
}
/*
=============
pfnGetCvarString
=============
*/
char* pfnGetCvarString( const char *szName )
{
return Cvar_VariableString( szName );
}
/*
=============
pfnGetCvarString
=============
*/
void pfnAddCommand( const char *cmd_name, xcommand_t func, const char *cmd_desc )
{
// NOTE: if( func == NULL ) cmd will be forwarded to a server
Cmd_AddCommand( cmd_name, func, cmd_desc );
}
/*
=============
pfnHookUserMsg
=============
*/
void pfnHookUserMsg( char *szMsgName, pfnUserMsgHook pfn )
{
user_message_t *msg;
int i;
// ignore blank names
if( !szMsgName || !*szMsgName ) return;
for( i = 0; game.numMessages; i++ )
{
msg = game.msg[i];
if( !msg ) break;
if( !com.strcmp( szMsgName, msg->name ))
{
// msg registration is complete
msg->func = pfn;
return;
}
}
MsgDev( D_ERROR, "CL_HookMessage: can't hook message %s\n", szMsgName );
}
/*
=============
pfnServerCmd
=============
*/
void pfnServerCmd( const char *szCmdString )
{
// server command adding in cmds queue
Cbuf_AddText( va( "cmd %s", szCmdString ));
}
/*
=============
pfnClientCmd
=============
*/
void pfnClientCmd( const char *szCmdString )
{
// client command executes immediately
Cmd_ExecuteString( szCmdString );
}
/*
=============
pfnTextMessageGet
=============
*/
void pfnGetPlayerInfo( int player_num, hud_player_info_t *pinfo )
{
// FIXME: implement
static hud_player_info_t null_info;
Mem_Copy( pinfo, &null_info, sizeof( null_info ));
}
/*
=============
pfnGetPlayerInfo
=============
*/
client_textmessage_t *pfnTextMessageGet( const char *pName )
{
// FIXME: implement
static client_textmessage_t null_msg;
return &null_msg;
}
/*
=============
pfnCmdArgc
=============
*/
int pfnCmdArgc( void )
{
return Cmd_Argc();
}
/*
=============
pfnCmdArgv
=============
*/
char *pfnCmdArgv( int argc )
{
if( argc >= 0 && argc < Cmd_Argc())
return Cmd_Argv( argc );
return "";
}
/*
=============
pfnAlertMessage
=============
*/
static void pfnAlertMessage( ALERT_TYPE type, char *szFmt, ... )
{
char buffer[2048]; // must support > 1k messages
va_list args;
va_start( args, szFmt );
com.vsnprintf( buffer, 2048, szFmt, args );
va_end( args );
// FIXME: implement message filter
com.print( buffer );
}
/*
=============
pfnPlaySoundByName
=============
*/
void pfnPlaySoundByName( const char *szSound, float volume, const float *org )
{
S_StartLocalSound( szSound, volume, org );
}
/*
=============
pfnPlaySoundByIndex
=============
*/
void pfnPlaySoundByIndex( int iSound, float volume, const float *org )
{
// make sure what we in-bounds
iSound = bound( 0, iSound, MAX_SOUNDS );
if( cl.sound_precache[iSound] == 0 )
{
MsgDev( D_ERROR, "CL_PlaySoundByIndex: invalid sound handle %i\n", iSound );
return;
}
S_StartSound( org, cl.playernum + 1, CHAN_AUTO, cl.sound_precache[iSound], volume, ATTN_NORM, PITCH_NORM );
}
/*
=============
pfnDrawCenterPrint
called each frame
=============
*/
void pfnDrawCenterPrint( void )
{
char *start;
int l, x, y, w;
float *color;
if( !cl.centerPrintTime ) return;
color = CL_FadeColor( cl.centerPrintTime * 0.001f, scr_centertime->value );
if( !color )
{
cl.centerPrintTime = 0;
return;
}
re->SetColor( color );
start = cl.centerPrint;
y = cl.centerPrintY - cl.centerPrintLines * BIGCHAR_HEIGHT / 2;
while( 1 )
{
char linebuffer[1024];
for ( l = 0; l < 50; l++ )
{
if ( !start[l] || start[l] == '\n' )
break;
linebuffer[l] = start[l];
}
linebuffer[l] = 0;
w = cl.centerPrintCharWidth * com.cstrlen( linebuffer );
x = ( SCREEN_WIDTH - w )>>1;
SCR_DrawStringExt( x, y, cl.centerPrintCharWidth, BIGCHAR_HEIGHT, linebuffer, color, false );
y += cl.centerPrintCharWidth * 1.5;
while( *start && ( *start != '\n' )) start++;
if( !*start ) break;
start++;
}
if( re ) re->SetColor( NULL );
}
/*
=============
pfnCenterPrint
called once from message
=============
*/
void pfnCenterPrint( const char *text, int y, int charWidth )
{
char *s;
com.strncpy( cl.centerPrint, text, sizeof( cl.centerPrint ));
cl.centerPrintTime = cls.realtime;
cl.centerPrintY = y;
cl.centerPrintCharWidth = charWidth;
// count the number of lines for centering
cl.centerPrintLines = 1;
s = cl.centerPrint;
while( *s )
{
if( *s == '\n' )
cl.centerPrintLines++;
s++;
}
}
/*
=============
pfnDrawCharacter
=============
*/
int pfnDrawCharacter( int x, int y, int width, int height, int number )
{
if( number < 32 || number > 255 )
{
MsgDev( D_WARN, "SCR_DrawChar: passed non-printable character %c\n", (char )number );
return false;
}
SCR_DrawChar( x, y, width, height, number );
if( re ) re->SetColor( NULL );
return true;
}
/*
=============
pfnDrawString
=============
*/
void pfnDrawString( int x, int y, int width, int height, const char *text )
{
if( !text || !*text )
{
MsgDev( D_ERROR, "SCR_DrawStringExt: passed null string!\n" );
return;
}
SCR_DrawStringExt( x, y, width, height, text, g_color_table[7], false );
if( re ) re->SetColor( NULL );
}
void pfnGetImageSize( int *w, int *h, shader_t shader )
{
if( re ) re->DrawGetPicSize( w, h, shader );
else
{
if( w ) *w = -1;
if( h ) *h = -1;
}
}
/*
=============
pfnGetViewAngles
return interpolated angles from previous frame
=============
*/
void pfnGetViewAngles( float *angles )
{
if( angles == NULL ) return;
VectorCopy( cl.refdef.viewangles, angles );
}
/*
=============
pfnGetEntityByIndex
=============
*/
edict_t* pfnGetEntityByIndex( int idx )
{
if( idx < 0 || idx > game.numEntities )
{
MsgDev( D_ERROR, "CL_GetEntityByIndex: invalid entindex %i\n", idx );
return EDICT_NUM( 0 );
}
return EDICT_NUM( idx );
}
/*
=============
pfnGetLocalPlayer
=============
*/
edict_t* pfnGetLocalPlayer( void )
{
return EDICT_NUM( cl.playernum + 1 );
}
/*
=============
pfnIsSpectateOnly
=============
*/
int pfnIsSpectateOnly( void )
{
// FIXME: implement
return 0;
}
/*
=============
pfnGetClientTime
=============
*/
float pfnGetClientTime( void )
{
return cl.time * 0.001f;
}
/*
=============
pfnGetMaxClients
=============
*/
int pfnGetMaxClients( void )
{
return com.atoi( cl.configstrings[CS_MAXCLIENTS] );
}
/*
=============
pfnGetViewModel
can return NULL
=============
*/
edict_t* pfnGetViewModel( void )
{
return EDICT_NUM( cl.playernum + 1 )->v.aiment;
}
/*
=============
pfnPointContents
=============
*/
static int pfnPointContents( const float *rgflVector )
{
return CL_PointContents( rgflVector );
}
/*
=============
pfnTraceLine
=============
*/
static void pfnTraceLine( const float *v1, const float *v2, int fNoMonsters, edict_t *pentToSkip, TraceResult *ptr )
{
trace_t trace;
int move;
move = (fNoMonsters) ? MOVE_NOMONSTERS : MOVE_NORMAL;
if( IS_NAN(v1[0]) || IS_NAN(v1[1]) || IS_NAN(v1[2]) || IS_NAN(v2[0]) || IS_NAN(v1[2]) || IS_NAN(v2[2] ))
Host_Error( "CL_Trace: NAN errors detected ('%f %f %f', '%f %f %f'\n", v1[0], v1[1], v1[2], v2[0], v2[1], v2[2] );
trace = CL_Trace( v1, vec3_origin, vec3_origin, v2, move, pentToSkip, CL_ContentsMask( pentToSkip ));
CL_CopyTraceResult( ptr, trace );
}
static triapi_t gTriApi =
{
sizeof( triapi_t ),
};
// engine callbacks
static cl_enginefuncs_t gEngfuncs =
{
sizeof( cl_enginefuncs_t ),
pfnMemAlloc,
pfnMemFree,
pfnLoadShader,
pfnFillRGBA,
pfnDrawImage,
pfnSetColor,
pfnRegisterVariable,
pfnCvarSetValue,
pfnGetCvarFloat,
pfnGetCvarString,
pfnAddCommand,
pfnHookUserMsg,
pfnServerCmd,
pfnClientCmd,
pfnGetPlayerInfo,
pfnTextMessageGet,
pfnCmdArgc,
pfnCmdArgv,
pfnAlertMessage,
pfnPlaySoundByName,
pfnPlaySoundByIndex,
AngleVectors,
pfnDrawCenterPrint,
pfnCenterPrint,
pfnDrawCharacter,
pfnDrawString,
pfnGetImageSize,
pfnGetViewAngles,
pfnGetEntityByIndex,
pfnGetLocalPlayer,
pfnIsSpectateOnly,
pfnGetClientTime,
pfnGetMaxClients,
pfnGetViewModel,
pfnPointContents,
pfnTraceLine,
pfnRandomLong,
pfnRandomFloat,
pfnLoadFile,
pfnFreeFile,
pfnGetGameDir,
Host_Error,
&gTriApi
};
/*
====================
StudioEvent
Event callback for studio models
====================
*/
void CL_StudioEvent( dstudioevent_t *event, entity_state_t *ent )
{
// do upcast
edict_t *pEdict = EDICT_NUM( ent->number );
cls.dllFuncs.pfnStudioEvent( event, pEdict );
}
void CL_UnloadProgs( void )
{
// initialize game
cls.dllFuncs.pfnShutdown();
StringTable_Delete( game.hStringTable );
Com_FreeLibrary( cls.game );
Mem_FreePool( &cls.mempool );
Mem_FreePool( &cls.private );
}
bool CL_LoadProgs( const char *name )
{
static CLIENTAPI GetClientAPI;
string libname;
edict_t *e;
int i;
if( cls.game ) CL_UnloadProgs();
// fill it in
com.snprintf( libname, MAX_STRING, "bin/%s.dll", name );
cls.mempool = Mem_AllocPool( "Client Edicts Zone" );
cls.private = Mem_AllocPool( "Client Private Zone" );
cls.game = Com_LoadLibrary( libname );
if( !cls.game ) return false;
GetClientAPI = (CLIENTAPI)Com_GetProcAddress( cls.game, "CreateAPI" );
if( !GetClientAPI )
{
MsgDev( D_ERROR, "CL_LoadProgs: failed to get address of CreateAPI proc\n" );
return false;
}
if( !GetClientAPI( &cls.dllFuncs, &gEngfuncs, INTERFACE_VERSION ))
{
MsgDev( D_ERROR, "CL_LoadProgs: can't init client API\n" );
return false;
}
// 65535 unique strings should be enough ...
game.hStringTable = StringTable_Create( "Client Strings", 0x10000 );
StringTable_SetString( game.hStringTable, "" ); // make NULL string
game.maxEntities = host.max_edicts; // FIXME: must come from CS_MAXENTITIES
game.maxClients = Host_MaxClients();
game.edicts = Mem_Alloc( cls.mempool, sizeof( edict_t ) * game.maxEntities );
game.numMessages = 1; // message with index 0 it's svc_bad
for( i = 0, e = game.edicts; i < game.maxEntities; i++, e++ )
e->free = true; // mark all edicts as freed
// initialize game
cls.dllFuncs.pfnInit();
return true;
}

View File

@ -869,8 +869,6 @@ void CL_PacketEvent( netadr_t from, sizebuf_t *msg )
if( host.type == HOST_DEDICATED || cls.demoplayback )
return;
CL_VM_Begin();
if( msg->cursize >= 4 && *(int *)msg->data == -1 )
{
cls.netchan.last_received = cls.realtime;
@ -906,7 +904,6 @@ void CL_PacketEvent( netadr_t from, sizebuf_t *msg )
if( cls.demorecording && !cls.demowaiting )
CL_WriteDemoMessage( msg, headerBytes );
}
CL_VM_End();
}
void CL_ReadPackets( void )
@ -1203,9 +1200,6 @@ void CL_Frame( dword time )
// if in the debugger last frame, don't timeout
if( time > 5000 ) cls.netchan.last_received = Sys_Milliseconds();
// setup the VM frame
CL_VM_Begin();
// fetch results from server
CL_ReadPackets();
@ -1235,9 +1229,6 @@ void CL_Frame( dword time )
SCR_RunCinematic();
Con_RunConsole();
// end the client VM frame
CL_VM_End();
cls.framecount++;
}
@ -1259,7 +1250,10 @@ void CL_Init( void )
Con_Init();
VID_Init();
CL_InitClientProgs();
if( !CL_LoadProgs( "client" ))
{
Host_Error( "CL_InitGame: can't initialize client.dll\n" );
}
UI_Init();
SCR_Init();
CL_InitLocal();
@ -1283,7 +1277,7 @@ void CL_Shutdown( void )
if( !cls.initialized ) return;
CL_WriteConfiguration();
CL_FreeClientProgs();
CL_UnloadProgs();
UI_Shutdown();
S_Shutdown();
SCR_Shutdown();

View File

@ -275,17 +275,16 @@ void CL_ParseBaseline( sizebuf_t *msg )
{
int newnum;
entity_state_t nullstate;
pr_edict_t *ent;
edict_t *ent;
CL_VM_Begin();
memset( &nullstate, 0, sizeof(nullstate));
Mem_Set( &nullstate, 0, sizeof( nullstate ));
newnum = MSG_ReadBits( msg, NET_WORD );
// increase edicts
while( newnum >= prog->num_edicts ) PRVM_ED_Alloc();
ent = PRVM_EDICT_NUM( newnum );
while( newnum >= game.numEntities ) CL_AllocEdict();
ent = EDICT_NUM( newnum );
MSG_ReadDeltaEntity( msg, &nullstate, &ent->priv.cl->baseline, newnum );
MSG_ReadDeltaEntity( msg, &nullstate, &ent->pvEngineData->baseline, newnum );
}
/*
@ -301,7 +300,6 @@ void CL_ParseConfigString( sizebuf_t *msg )
if( i < 0 || i >= MAX_CONFIGSTRINGS )
Host_Error("configstring > MAX_CONFIGSTRINGS\n");
com.strcpy( cl.configstrings[i], MSG_ReadString( msg ));
CL_VM_Begin();
// do something apropriate
if( i == CS_SKYNAME && cl.video_prepped )
@ -327,13 +325,12 @@ void CL_ParseConfigString( sizebuf_t *msg )
}
else if( i >= CS_USER_MESSAGES && i < CS_USER_MESSAGES+MAX_USER_MESSAGES )
{
// FIXME: register user message here
// Msg("PrepUserMessage: %s[svc_%i]\n", cl.configstrings[i], i - CS_USER_MESSAGES );
CL_PrepUserMessage( cl.configstrings[i], i - CS_USER_MESSAGES );
}
else if( i >= CS_CLASSNAMES && i < CS_CLASSNAMES+MAX_CLASSNAMES )
{
// prvm classnames for search by classname on client vm
cl.edict_classnames[i-CS_CLASSNAMES] = PRVM_SetEngineString( cl.configstrings[i] );
// edicts classnames for search by classname on client
cl.edict_classnames[i-CS_CLASSNAMES] = MAKE_STRING( cl.configstrings[i] );
}
else if( i >= CS_LIGHTSTYLES && i < CS_LIGHTSTYLES+MAX_LIGHTSTYLES )
{
@ -372,9 +369,6 @@ void CL_ParseServerMessage( sizebuf_t *msg )
char *s;
int cmd;
// client progs can recivied messages too
cls.multicast = msg;
// parse the message
while( 1 )
{
@ -442,9 +436,7 @@ void CL_ParseServerMessage( sizebuf_t *msg )
Host_Error( "CL_ParseServerMessage: svc_bad\n" );
break;
default:
// parse user messages
if(!CL_ParseUserMessage( cmd ))
Host_Error("CL_ParseServerMessage: illegible server message %d\n", cmd );
CL_ParseUserMessage( msg, cmd );
break;
}
}

View File

@ -1,22 +1,7 @@
/*
Copyright (C) 1997-2001 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
//=======================================================================
// Copyright XashXT Group 2008 ©
// cl_physics.c - client physic and prediction
//=======================================================================
#include "common.h"
#include "client.h"
@ -62,18 +47,39 @@ void CL_CheckPredictionError (void)
}
}
/*
===============================================================================
LINE TESTING IN HULLS
===============================================================================
*/
int CL_ContentsMask( const edict_t *passedict )
{
if( passedict )
{
if( passedict->v.flags & FL_MONSTER )
return MASK_MONSTERSOLID;
else if( passedict->v.flags & FL_CLIENT )
return MASK_PLAYERSOLID;
else if( passedict->v.solid == SOLID_TRIGGER )
return CONTENTS_SOLID|CONTENTS_BODY;
return MASK_SOLID;
}
return MASK_SOLID;
}
/*
==================
CL_Trace
==================
*/
trace_t CL_Trace( const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, pr_edict_t *passedict, int contentsmask )
trace_t CL_Trace( const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, edict_t *passedict, int contentsmask )
{
vec3_t hullmins, hullmaxs;
int i, bodycontents;
int passedictprog;
bool pointtrace;
pr_edict_t *traceowner, *touch;
edict_t *traceowner, *touch;
trace_t trace;
vec3_t clipboxmins, clipboxmaxs; // bounding box of entire move area
vec3_t clipmins, clipmaxs; // size of the moving object
@ -83,7 +89,7 @@ trace_t CL_Trace( const vec3_t start, const vec3_t mins, const vec3_t maxs, cons
matrix4x4 matrix, imatrix; // matrices to transform into/out of other entity's space
cmodel_t *model; // model of other entity
int numtouchedicts = 0; // list of entities to test for collisions
pr_edict_t *touchedicts[MAX_EDICTS];
edict_t *touchedicts[MAX_EDICTS];
VectorCopy( start, clipstart );
VectorCopy( end, clipend );
@ -96,7 +102,7 @@ trace_t CL_Trace( const vec3_t start, const vec3_t mins, const vec3_t maxs, cons
pe->ClipToWorld( &cliptrace, cl.worldmodel, clipstart, clipmins, clipmaxs, clipend, contentsmask );
cliptrace.startstuck = cliptrace.startsolid;
if( cliptrace.startsolid || cliptrace.fraction < 1 )
cliptrace.ent = prog ? (edict_t *)prog->edicts : NULL;
cliptrace.ent = (edict_t *)EDICT_NUM( 0 );
if( type == MOVE_WORLDONLY ) return cliptrace;
if( type == MOVE_MISSILE )
@ -120,21 +126,18 @@ trace_t CL_Trace( const vec3_t start, const vec3_t mins, const vec3_t maxs, cons
}
// if the passedict is world, make it NULL (to avoid two checks each time)
// this checks prog because this function is often called without a CSQC
// VM context
if( prog == NULL || passedict == prog->edicts ) passedict = NULL;
// precalculate prog value for passedict for comparisons
passedictprog = prog != NULL ? PRVM_EDICT_TO_PROG(passedict) : 0;
if( passedict == EDICT_NUM( 0 )) passedict = NULL;
// figure out whether this is a point trace for comparisons
pointtrace = VectorCompare( clipmins, clipmaxs );
// precalculate passedict's owner edict pointer for comparisons
traceowner = passedict ? PRVM_PROG_TO_EDICT( passedict->progs.cl->owner ) : NULL;
if( passedict && passedict->v.owner )
traceowner = passedict->v.owner;
else traceowner = NULL;
// clip to entities
// because this uses World_EntitiestoBox, we know all entity boxes overlap
// the clip region, so we can skip culling checks in the loop below
// note: if prog is NULL then there won't be any linked entities
numtouchedicts = 0;//CL_AreaEdicts( clipboxmins, clipboxmaxs, touchedicts, host.max_edicts );
numtouchedicts = 0;// FIXME: CL_AreaEdicts( clipboxmins, clipboxmaxs, touchedicts, host.max_edicts );
if( numtouchedicts > host.max_edicts )
{
// this never happens
@ -144,9 +147,8 @@ trace_t CL_Trace( const vec3_t start, const vec3_t mins, const vec3_t maxs, cons
for( i = 0; i < numtouchedicts; i++ )
{
touch = touchedicts[i];
if( touch->progs.cl->solid < SOLID_BBOX ) continue;
if( type == MOVE_NOMONSTERS && touch->progs.cl->solid != SOLID_BSP )
if( touch->v.solid < SOLID_BBOX ) continue;
if( type == MOVE_NOMONSTERS && touch->v.solid != SOLID_BSP )
continue;
if( passedict )
@ -156,9 +158,9 @@ trace_t CL_Trace( const vec3_t start, const vec3_t mins, const vec3_t maxs, cons
// don't clip owned entities against owner
if( traceowner == touch ) continue;
// don't clip owner against owned entities
if( passedictprog == touch->progs.cl->owner ) continue;
if( passedict == touch->v.owner ) continue;
// don't clip points against points (they can't collide)
if( pointtrace && VectorCompare( touch->progs.cl->mins, touch->progs.cl->maxs) && (type != MOVE_MISSILE || !((int)touch->progs.cl->flags & FL_MONSTER)))
if( pointtrace && VectorCompare( touch->v.mins, touch->v.maxs ) && (type != MOVE_MISSILE || !(touch->v.flags & FL_MONSTER)))
continue;
}
@ -166,20 +168,20 @@ trace_t CL_Trace( const vec3_t start, const vec3_t mins, const vec3_t maxs, cons
// might interact, so do an exact clip
model = NULL;
if((int)touch->progs.cl->solid == SOLID_BSP || type == MOVE_HITMODEL )
if( touch->v.solid == SOLID_BSP || type == MOVE_HITMODEL )
{
uint modelindex = (uint)touch->progs.cl->modelindex;
uint modelindex = (uint)touch->v.modelindex;
// if the modelindex is 0, it shouldn't be SOLID_BSP!
if( modelindex > 0 && modelindex < MAX_MODELS )
model = cl.models[(int)touch->progs.cl->modelindex];
model = cl.models[touch->v.modelindex];
}
if( model ) Matrix4x4_CreateFromEntity( matrix, touch->progs.cl->origin[0], touch->progs.cl->origin[1], touch->progs.cl->origin[2], touch->progs.cl->angles[0], touch->progs.cl->angles[1], touch->progs.cl->angles[2], 1 );
else Matrix4x4_CreateTranslate( matrix, touch->progs.cl->origin[0], touch->progs.cl->origin[1], touch->progs.cl->origin[2] );
if( model ) Matrix4x4_CreateFromEntity( matrix, touch->v.origin[0], touch->v.origin[1], touch->v.origin[2], touch->v.angles[0], touch->v.angles[1], touch->v.angles[2], 1 );
else Matrix4x4_CreateTranslate( matrix, touch->v.origin[0], touch->v.origin[1], touch->v.origin[2] );
Matrix4x4_Invert_Simple( imatrix, matrix );
if((int)touch->progs.cl->flags & FL_MONSTER)
pe->ClipToGenericEntity(&trace, model, touch->progs.cl->mins, touch->progs.cl->maxs, bodycontents, matrix, imatrix, clipstart, clipmins2, clipmaxs2, clipend, contentsmask );
else pe->ClipToGenericEntity(&trace, model, touch->progs.cl->mins, touch->progs.cl->maxs, bodycontents, matrix, imatrix, clipstart, clipmins, clipmaxs, clipend, contentsmask );
pe->CombineTraces( &cliptrace, &trace, (edict_t *)touch, touch->progs.cl->solid == SOLID_BSP );
if( touch->v.flags & FL_MONSTER )
pe->ClipToGenericEntity(&trace, model, touch->v.mins, touch->v.maxs, bodycontents, matrix, imatrix, clipstart, clipmins2, clipmaxs2, clipend, contentsmask );
else pe->ClipToGenericEntity(&trace, model, touch->v.mins, touch->v.maxs, bodycontents, matrix, imatrix, clipstart, clipmins, clipmaxs, clipend, contentsmask );
pe->CombineTraces( &cliptrace, &trace, (edict_t *)touch, touch->v.solid == SOLID_BSP );
}
return cliptrace;
}

View File

@ -6,222 +6,6 @@
#include "common.h"
#include "client.h"
/*
================
CL_FadeColor
================
*/
float *CL_FadeColor( float starttime, float endtime )
{
static vec4_t color;
float time, fade_time;
if( starttime == 0 ) return NULL;
time = (cls.realtime * 0.001f) - starttime;
if( time >= endtime ) return NULL;
// fade time is 1/4 of endtime
fade_time = endtime / 4;
fade_time = bound( 0.3f, fade_time, 10.0f );
// fade out
if((endtime - time) < fade_time)
color[3] = (endtime - time) * 1.0f / fade_time;
else color[3] = 1.0;
color[0] = color[1] = color[2] = 1.0f;
return color;
}
void CL_DrawHUD( void )
{
CL_VM_Begin();
// setup pparms
prog->globals.cl->health = cl.frame.ps.health;
prog->globals.cl->maxclients = com.atoi(cl.configstrings[CS_MAXCLIENTS]);
prog->globals.cl->max_entities = host.max_edicts;
prog->globals.cl->realtime = cls.realtime * 0.001f;
prog->globals.cl->paused = cl_paused->integer;
// setup args
PRVM_G_FLOAT(OFS_PARM0) = (float)cls.state;
PRVM_ExecuteProgram (prog->globals.cl->HUD_Render, "HUD_Render");
}
bool CL_ParseUserMessage( int svc_number )
{
bool msg_parsed = false;
// setup pparms
prog->globals.cl->health = cl.frame.ps.health;
prog->globals.cl->maxclients = com.atoi(cl.configstrings[CS_MAXCLIENTS]);
prog->globals.cl->realtime = cls.realtime * 0.001f;
prog->globals.cl->paused = cl_paused->integer;
// setup args
PRVM_G_FLOAT(OFS_PARM0) = (float)svc_number;
PRVM_ExecuteProgram (prog->globals.cl->HUD_ParseMessage, "HUD_ParseMessage");
msg_parsed = (bool)PRVM_G_FLOAT(OFS_RETURN);
return msg_parsed;
}
/*
====================
StudioEvent
Event callback for studio models
====================
*/
void CL_StudioEvent ( dstudioevent_t *event, entity_state_t *ent )
{
// setup args
PRVM_G_FLOAT(OFS_PARM0) = (float)event->event;
PRVM_G_INT(OFS_PARM1) = PRVM_SetEngineString( event->options );
VectorCopy( ent->origin, PRVM_G_VECTOR(OFS_PARM2));
VectorCopy( ent->angles, PRVM_G_VECTOR(OFS_PARM3));
PRVM_ExecuteProgram( prog->globals.cl->HUD_StudioEvent, "HUD_StudioEvent");
}
/*
====================
GetClientEntity
Render callback for studio models
====================
*/
entity_state_t *CL_GetEdictByIndex( int index )
{
return &PRVM_EDICT_NUM( index )->priv.cl->current;
}
entity_state_t *CL_GetLocalPlayer( void )
{
return &PRVM_EDICT_NUM( cl.playernum + 1 )->priv.cl->current;
}
int CL_GetMaxClients( void )
{
return com.atoi(cl.configstrings[CS_MAXCLIENTS]);
}
/*
===============================================================================
Client Builtin Functions
mathlib, debugger, and various misc helpers
===============================================================================
*/
void CL_BeginIncreaseEdicts( void )
{
int i;
pr_edict_t *ent;
// links don't survive the transition, so unlink everything
for (i = 0, ent = prog->edicts; i < prog->max_edicts; i++, ent++)
{
}
}
void CL_EndIncreaseEdicts( void )
{
int i;
pr_edict_t *ent;
for (i = 0, ent = prog->edicts; i < prog->max_edicts; i++, ent++)
{
}
}
void CL_InitEdict( pr_edict_t *e )
{
e->priv.cl->serialnumber = PRVM_NUM_FOR_EDICT(e);
e->priv.cl->free = false;
}
void CL_FreeEdict( pr_edict_t *ed )
{
ed->priv.cl->freetime = cl.time * 0.001f;
ed->priv.cl->free = true;
ed->progs.cl->model = 0;
ed->progs.cl->modelindex = 0;
ed->progs.cl->soundindex = 0;
ed->progs.cl->skin = 0;
ed->progs.cl->frame = 0;
VectorClear(ed->progs.cl->origin);
VectorClear(ed->progs.cl->angles);
}
void CL_FreeEdicts( void )
{
int i;
pr_edict_t *ent;
CL_VM_Begin();
for( i = 1; prog && i < prog->num_edicts; i++ )
{
ent = PRVM_EDICT_NUM(i);
CL_FreeEdict( ent );
}
CL_VM_End();
}
void CL_CountEdicts( void )
{
pr_edict_t *ent;
int i, active = 0, models = 0;
for (i = 0; i < prog->num_edicts; i++)
{
ent = PRVM_EDICT_NUM(i);
if (ent->priv.cl->free) continue;
active++;
if (ent->progs.cl->model) models++;
}
Msg("num_edicts:%3i\n", prog->num_edicts);
Msg("active :%3i\n", active);
Msg("view :%3i\n", models);
}
void CL_VM_Begin( void )
{
PRVM_Begin;
PRVM_SetProg( PRVM_CLIENTPROG );
if( prog ) *prog->time = cl.time * 0.001f;
}
void CL_VM_End( void )
{
PRVM_End;
}
bool CL_LoadEdict( pr_edict_t *ent )
{
return true;
}
static void PF_BeginRead( void )
{
}
static void PF_EndRead( void )
{
}
static void PF_ReadChar (void){ PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadChar( cls.multicast ); }
static void PF_ReadByte (void){ PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadByte( cls.multicast ); }
static void PF_ReadShort (void){ PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadShort( cls.multicast ); }
static void PF_ReadLong (void){ PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadLong( cls.multicast ); }
static void PF_ReadFloat (void){ PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadFloat( cls.multicast ); }
static void PF_ReadAngle (void){ PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadBits( cls.multicast, NET_FLOAT ); }
static void PF_ReadCoord (void){ PRVM_G_FLOAT(OFS_RETURN) = MSG_ReadBits( cls.multicast, NET_FLOAT ); }
static void PF_ReadString (void){ PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString( MSG_ReadString( cls.multicast) ); }
static void PF_ReadEntity (void){ VM_RETURN_EDICT( PRVM_PROG_TO_EDICT( MSG_ReadShort( cls.multicast ))); } // entindex
/*
=========
PF_drawnet
@ -294,96 +78,6 @@ static void PF_drawfps( void )
SCR_DrawBigStringColor(pos[0], pos[1], fpsstring, color );
}
/*
=========
PF_drawcenterprint
void DrawCenterPrint( void )
=========
*/
static void PF_drawcenterprint( void )
{
char *start;
int l, x, y, w;
float *color;
if(!cl.centerPrintTime ) return;
if(!VM_ValidateArgs( "DrawCenterPrint", 0 ))
return;
color = CL_FadeColor( cl.centerPrintTime * 0.001f, scr_centertime->value );
if( !color )
{
cl.centerPrintTime = 0;
return;
}
re->SetColor( color );
start = cl.centerPrint;
y = cl.centerPrintY - cl.centerPrintLines * BIGCHAR_HEIGHT/2;
while( 1 )
{
char linebuffer[1024];
for ( l = 0; l < 50; l++ )
{
if ( !start[l] || start[l] == '\n' )
break;
linebuffer[l] = start[l];
}
linebuffer[l] = 0;
w = cl.centerPrintCharWidth * com.cstrlen( linebuffer );
x = ( SCREEN_WIDTH - w )>>1;
SCR_DrawStringExt( x, y, cl.centerPrintCharWidth, BIGCHAR_HEIGHT, linebuffer, color, false );
y += cl.centerPrintCharWidth * 1.5;
while ( *start && ( *start != '\n' )) start++;
if( !*start ) break;
start++;
}
re->SetColor( NULL );
}
/*
=========
PF_centerprint
void HUD_CenterPrint( string text, float y, float charwidth )
=========
*/
static void PF_centerprint( void )
{
float y, width;
const char *text;
char *s;
if(!VM_ValidateArgs( "HUD_CenterPrint", 3 ))
return;
text = PRVM_G_STRING(OFS_PARM0);
y = PRVM_G_FLOAT(OFS_PARM1);
width = PRVM_G_FLOAT(OFS_PARM2);
VM_ValidateString( text );
com.strncpy( cl.centerPrint, text, sizeof(cl.centerPrint));
cl.centerPrintTime = cls.realtime;
cl.centerPrintY = y;
cl.centerPrintCharWidth = width;
// count the number of lines for centering
cl.centerPrintLines = 1;
s = cl.centerPrint;
while( *s )
{
if (*s == '\n') cl.centerPrintLines++;
s++;
}
}
/*
=========
PF_levelshot
@ -408,96 +102,6 @@ static void PF_levelshot( void )
}
}
/*
=========
PF_setcolor
void HUD_SetColor( vector rgb, float alpha )
=========
*/
static void PF_setcolor( void )
{
float *rgb, alpha;
if(!VM_ValidateArgs( "HUD_SetColor", 2 ))
return;
rgb = PRVM_G_VECTOR(OFS_PARM0);
alpha = PRVM_G_FLOAT(OFS_PARM1);
re->SetColor( GetRGBA( rgb[0], rgb[1], rgb[2], alpha ));
}
/*
=========
PF_startsound
void CL_StartSound( vector pos, entity e, float chan, float sfx, float vol, float attn, float pitch, float localsound )
=========
*/
static void PF_startsound( void )
{
float volume;
int channel;
sound_t sound_num;
int attenuation;
float *pos = NULL;
bool client_sound;
int ent = 0;
float pitch;
if( !VM_ValidateArgs( "CL_StartSound", 8 ))
return;
pos = PRVM_G_VECTOR(OFS_PARM0);
ent = PRVM_G_EDICTNUM(OFS_PARM1);
channel = (int)PRVM_G_FLOAT(OFS_PARM2);
sound_num = (sound_t)PRVM_G_FLOAT(OFS_PARM3);
volume = PRVM_G_FLOAT(OFS_PARM4);
attenuation = (int)PRVM_G_FLOAT(OFS_PARM5);
pitch = (int)PRVM_G_FLOAT(OFS_PARM6);
client_sound = (bool)PRVM_G_FLOAT(OFS_PARM7);
if( client_sound )
{
S_StartSound( pos, ent, channel, sound_num, volume, attenuation, pitch );
}
else if( cl.sound_precache[sound_num] )
{
S_StartSound( pos, ent, channel, cl.sound_precache[sound_num], volume, attenuation, pitch );
}
else VM_Warning( "CL_StartSound: can't play sound with index %i\n", sound_num );
}
/*
=========
PF_pointcontents
float CL_PointContents( vector point )
=========
*/
static void PF_pointcontents( void )
{
if( !VM_ValidateArgs( "CL_PointContents", 1 ))
return;
PRVM_G_FLOAT(OFS_RETURN) = CL_PointContents(PRVM_G_VECTOR(OFS_PARM0));
}
/*
=========
PF_startsound
sound_t CL_PrecacheSound( string samp )
=========
*/
static void PF_precachesound( void )
{
if( !VM_ValidateArgs( "CL_PrecacheSound", 1 ))
return;
VM_ValidateString(PRVM_G_STRING(OFS_PARM0));
PRVM_G_FLOAT(OFS_RETURN) = S_RegisterSound(PRVM_G_STRING(OFS_PARM0));
}
/*
=================
PF_findexplosionplane
@ -536,198 +140,4 @@ static void PF_findexplosionplane( void )
}
}
VectorCopy( dir, PRVM_G_VECTOR( OFS_RETURN ));
}
// NOTE: intervals between various "interfaces" was leave for future expansions
prvm_builtin_t vm_cl_builtins[] =
{
NULL, // #0 (leave blank as default, but can include easter egg )
// system events
VM_ConPrintf, // #1 void Con_Printf( ... )
VM_ConDPrintf, // #2 void Con_DPrintf( float level, ... )
VM_HostError, // #3 void Com_Error( ... )
VM_SysExit, // #4 void Sys_Exit( void )
VM_CmdArgv, // #5 string Cmd_Argv( float arg )
VM_CmdArgc, // #6 float Cmd_Argc( void )
NULL, // #7 -- reserved --
NULL, // #8 -- reserved --
NULL, // #9 -- reserved --
NULL, // #10 -- reserved --
// common tools
VM_ComTrace, // #11 void Com_Trace( float enable )
VM_ComFileExists, // #12 float Com_FileExists( string filename )
VM_ComFileSize, // #13 float Com_FileSize( string filename )
VM_ComFileTime, // #14 float Com_FileTime( string filename )
VM_ComLoadScript, // #15 float Com_LoadScript( string filename )
VM_ComResetScript, // #16 void Com_ResetScript( void )
VM_ComReadToken, // #17 string Com_ReadToken( float newline )
VM_Random, // #18 float Random( void )
VM_ComSearchFiles, // #19 float Com_Search( string mask, float casecmp )
VM_ComSearchNames, // #20 string Com_SearchFilename( float num )
VM_RandomLong, // #21 float RandomLong( float min, float max )
VM_RandomFloat, // #22 float RandomFloat( float min, float max )
VM_RandomVector, // #23 vector RandomVector( vector min, vector max )
VM_CvarRegister, // #24 void Cvar_Register( string name, string value, float flags )
VM_CvarSetValue, // #25 void Cvar_SetValue( string name, float value )
VM_CvarGetValue, // #26 float Cvar_GetValue( string name )
VM_CvarSetString, // #27 void Cvar_SetString( string name, string value )
VM_CvarGetString, // #28 void VM_CvarGetString( void )
VM_ComVA, // #29 string va( ... )
VM_ComStrlen, // #30 float strlen( string text )
VM_TimeStamp, // #31 string Com_TimeStamp( float format )
VM_LocalCmd, // #32 void LocalCmd( ... )
VM_SubString, // #33 string substring( string s, float start, float length )
VM_AddCommand, // #34 void Add_Command( string s )
VM_atof, // #35 float atof( string s )
VM_atoi, // #36 float atoi( string s )
VM_atov, // #37 vector atov( string s )
NULL, // #38 -- reserved --
NULL, // #39 -- reserved --
NULL, // #40 -- reserved --
// quakec intrinsics ( compiler will be lookup this functions, don't remove or rename )
VM_SpawnEdict, // #41 entity spawn( void )
VM_RemoveEdict, // #42 void remove( entity ent )
VM_NextEdict, // #43 entity nextent( entity ent )
VM_CopyEdict, // #44 void copyentity( entity src, entity dst )
NULL, // #45 -- reserved --
NULL, // #46 -- reserved --
NULL, // #47 -- reserved --
NULL, // #48 -- reserved --
NULL, // #49 -- reserved --
NULL, // #50 -- reserved --
// filesystem
VM_FS_Open, // #51 float fopen( string filename, float mode )
VM_FS_Close, // #52 void fclose( float handle )
VM_FS_Gets, // #53 string fgets( float handle )
VM_FS_Puts, // #54 void fputs( float handle, string s )
NULL, // #55 -- reserved --
NULL, // #56 -- reserved --
NULL, // #57 -- reserved --
NULL, // #58 -- reserved --
NULL, // #59 -- reserved --
NULL, // #60 -- reserved --
// mathlib
VM_min, // #61 float min(float a, float b )
VM_max, // #62 float max(float a, float b )
VM_bound, // #63 float bound(float min, float val, float max)
VM_pow, // #64 float pow(float x, float y)
VM_sin, // #65 float sin(float f)
VM_cos, // #66 float cos(float f)
VM_tan, // #67 float tan(float f)
VM_asin, // #68 float asin(float f)
VM_acos, // #69 float acos(float f)
VM_atan, // #70 float atan(float f)
VM_sqrt, // #71 float sqrt(float f)
VM_rint, // #72 float rint (float v)
VM_floor, // #73 float floor(float v)
VM_ceil, // #74 float ceil (float v)
VM_fabs, // #75 float fabs (float f)
VM_abs, // #76 float abs (float f)
NULL, // #77 -- reserved --
NULL, // #78 -- reserved --
VM_VectorNormalize, // #79 vector VectorNormalize( vector v )
VM_VectorLength, // #80 float VectorLength( vector v )
e10, e10, // #81 - #100 are reserved for future expansions
// network messaging
PF_BeginRead, // #101 void MsgBegin( void )
PF_ReadByte, // #102 float ReadByte( void )
PF_ReadChar, // #103 float ReadChar( void )
PF_ReadShort, // #104 float ReadShort( void )
PF_ReadLong, // #105 float ReadLong( void )
PF_ReadFloat, // #106 float ReadFloat( void )
PF_ReadAngle, // #107 float ReadAngle( void )
PF_ReadCoord, // #108 float ReadCoord( void )
PF_ReadString, // #109 string ReadString( void )
PF_ReadEntity, // #110 entity ReadEntity( void )
PF_EndRead, // #111 void MsgEnd( void )
// clientfuncs_t
VM_precache_pic, // #112 float precache_pic( string pic )
VM_drawcharacter, // #113 float DrawChar( vector pos, float char, vector scale, vector rgb, float alpha )
VM_drawstring, // #114 float DrawString( vector pos, string text, vector scale, vector rgb, float alpha )
VM_drawpic, // #115 float DrawPic( vector pos, string pic, vector size, vector rgb, float alpha )
VM_drawfill, // #116 void DrawFill( vector pos, vector size, vector rgb, float alpha )
VM_drawmodel, // #117 void DrawModel( vector pos, vector size, string mod, vector org, vector ang, float seq )
NULL, // #118 -- reserved --
VM_getimagesize, // #119 vector getimagesize( string pic )
PF_drawnet, // #120 void DrawNet( vector pos, string image )
PF_drawfps, // #121 void DrawFPS( vector pos )
PF_drawcenterprint, // #122 void DrawCenterPrint( void )
PF_centerprint, // #123 void HUD_CenterPrint( string text, float y, float charwidth )
PF_levelshot, // #124 float HUD_MakeLevelShot( void )
PF_setcolor, // #125 void HUD_SetColor( vector rgb, float alpha )
VM_localsound, // #126 void HUD_PlaySound( string sample )
PF_startsound, // #127 void CL_StartSound( vector, entity, float, float, float, float, float, float )
PF_addparticle, // #128 float AddParticle(vector, vector, vector, vector, vector, vector, vector, string, float)
PF_pointcontents, // #129 float CL_PointContents( vector point )
PF_precachesound, // #130 sound_t CL_PrecacheSound( string samp )
PF_adddecal, // #131 void AddDecal( vector, vector, vector, float, float, float, float, string, float )
PF_addlight, // #132 void AddLight( vector pos, vector col, float rad, float decay, float time, float key )
NULL, // #133
NULL, // #134
NULL, // #135
NULL, // #136
NULL, // #137
PF_findexplosionplane, // #138 vector CL_FindExplosionPlane( vector org, float radius )
};
const int vm_cl_numbuiltins = sizeof(vm_cl_builtins) / sizeof(prvm_builtin_t); //num of builtins
void CL_InitClientProgs( void )
{
Msg("\n");
PRVM_Begin;
PRVM_InitProg( PRVM_CLIENTPROG );
if( !prog->loaded )
{
prog->progs_mempool = Mem_AllocPool( "Client Progs" );
prog->name = "client";
prog->builtins = vm_cl_builtins;
prog->numbuiltins = vm_cl_numbuiltins;
prog->edictprivate_size = sizeof(cl_edict_t);
prog->max_edicts = host.max_edicts<<2;
prog->limit_edicts = host.max_edicts;
prog->begin_increase_edicts = CL_BeginIncreaseEdicts;
prog->end_increase_edicts = CL_EndIncreaseEdicts;
prog->init_edict = CL_InitEdict;
prog->free_edict = CL_FreeEdict;
prog->count_edicts = CL_CountEdicts;
prog->load_edict = CL_LoadEdict;
prog->filecrc = PROG_CRC_CLIENT;
// using default builtins
prog->init_cmd = VM_Cmd_Init;
prog->reset_cmd = VM_Cmd_Reset;
prog->error_cmd = VM_Error;
PRVM_LoadProgs( va("%s/client.dat", GI->vprogs_dir ));
}
// init some globals
prog->globals.cl->realtime = cls.realtime * 0.001f;
prog->globals.cl->pev = 0;
prog->globals.cl->mapname = PRVM_SetEngineString( cls.servername );
prog->globals.cl->playernum = cl.playernum;
// call the prog init
PRVM_ExecuteProgram( prog->globals.cl->HUD_Init, "HUD_Init" );
PRVM_End;
}
void CL_FreeClientProgs( void )
{
CL_VM_Begin();
prog->globals.cl->realtime = cls.realtime * 0.001f;
prog->globals.cl->pev = 0;
PRVM_ExecuteProgram(prog->globals.cl->HUD_Shutdown, "HUD_Shutdown");
PRVM_ResetProg();
CL_VM_End();
}

View File

@ -177,9 +177,9 @@ void SCR_DrawStringExt( int x, int y, float w, float h, const char *string, floa
re->SetColor( setColor );
while ( *s )
{
if(IsColorString( s ))
if( IsColorString( s ))
{
if ( !forceColor )
if( !forceColor )
{
Mem_Copy( color, g_color_table[ColorIndex(*(s+1))], sizeof( color ));
color[3] = setColor[3];
@ -343,8 +343,6 @@ SCR_Init
*/
void SCR_Init( void )
{
cls.mempool = Mem_AllocPool( "Client Static" );
scr_showpause = Cvar_Get("scr_showpause", "1", 0, "show pause picture" );
scr_centertime = Cvar_Get("scr_centertime", "2.5", 0, "centerprint hold time" );
scr_printspeed = Cvar_Get("scr_printspeed", "8", 0, "centerprint speed of print" );
@ -368,5 +366,4 @@ void SCR_Init( void )
void SCR_Shutdown( void )
{
Mem_FreePool( &cls.mempool );
}

View File

@ -157,7 +157,6 @@ void V_RenderView( void )
// build a refresh entity list and calc cl.sim*
// this also calls CL_CalcViewValues which loads
// v_forward, etc.
CL_VM_Begin();
CL_AddEntities ();
if( cl_testentities->value ) V_TestEntities();

View File

@ -23,7 +23,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define CLIENT_H
#include "mathlib.h"
#include "cl_edict.h"
#include "entity_def.h"
#include "clgame_api.h"
#include "render_api.h"
#define MAX_EDIT_LINE 256
@ -31,6 +32,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define MAX_SERVERS 64
#define ColorIndex(c) (((c) - '0') & 7)
#define NUM_FOR_EDICT(e) ((int)((edict_t *)(e) - game.edicts))
#define EDICT_NUM( num ) _EDICT_NUM( num, __FILE__, __LINE__ )
//=============================================================================
typedef struct frame_s
{
@ -163,15 +167,11 @@ typedef enum
dl_generic,
} dltype_t; // download type
struct cl_edict_s
// cl_private_edict_t
struct ed_priv_s
{
// generic_edict_t (don't move these fields!)
bool free;
float freetime; // cl.time when the object was freed
int serverframe; // if not current, this ent isn't in the frame
int serialnumber; // client serialnumber
// cl_private_edict_t
entity_state_t baseline; // delta from this if not from a previous frame
entity_state_t current;
entity_state_t prev; // will always be valid, but might just be a copy of current
@ -195,7 +195,32 @@ typedef struct serverinfo_s
} serverinfo_t;
typedef enum {key_game, key_console, key_message, key_menu} keydest_t;
typedef enum { key_game, key_console, key_message, key_menu } keydest_t;
typedef struct
{
const char *name;
int number; // svc_ number
int size; // if size == -1, size come from first byte after svcnum
pfnUserMsgHook func; // user-defined function
} user_message_t;
typedef struct
{
int maxClients;
int numEntities;
int maxEntities;
user_message_t *msg[MAX_USER_MESSAGES];
int numMessages; // actual count of user messages
union
{
edict_t *edicts; // acess by edict number
void *vp; // acess by offset in bytes
};
int hStringTable; // stringtable handle
} clgame_static_t;
typedef struct
{
@ -203,7 +228,11 @@ typedef struct
bool initialized;
keydest_t key_dest;
byte *mempool;
void *game; // pointer to client.dll
HUD_FUNCTIONS dllFuncs; // dll exported funcs
byte *mempool; // edicts pool
byte *private; // client.dll private pool
int framecount;
dword realtime; // always increasing, no clamping, etc
@ -214,7 +243,6 @@ typedef struct
float connect_time; // for connection retransmits
netchan_t netchan;
sizebuf_t *multicast; // ptr for current message buffer (net or demo flow)
int serverProtocol; // in case we are doing some kind of version hack
int challenge; // from the server to use for connecting
@ -245,6 +273,7 @@ typedef struct
} client_static_t;
extern client_static_t cls;
extern clgame_static_t game;
/*
==============================================================
@ -253,20 +282,6 @@ SCREEN CONSTS
==============================================================
*/
// all drawing is done to a 640*480 virtual screen size
// and will be automatically scaled to the real resolution
#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 480
#define TINYCHAR_WIDTH (SMALLCHAR_WIDTH)
#define TINYCHAR_HEIGHT (SMALLCHAR_HEIGHT/2)
#define SMALLCHAR_WIDTH 8
#define SMALLCHAR_HEIGHT 16
#define BIGCHAR_WIDTH 16
#define BIGCHAR_HEIGHT 24
#define GIANTCHAR_WIDTH 32
#define GIANTCHAR_HEIGHT 48
extern vrect_t scr_vrect; // position of render window
extern vec4_t g_color_table[8];
@ -446,12 +461,34 @@ void CL_InitClientProgs( void );
void CL_FreeClientProgs( void );
int CL_GetMaxClients( void );
void CL_DrawHUD( void );
pr_edict_t *CL_GetEdict( int entnum );
edict_t *CL_GetEdict( int entnum );
float *CL_FadeColor( float starttime, float endtime );
bool CL_ParseUserMessage( int svc_number );
void CL_FreeEdicts( void );
void CL_VM_Begin( void );
void CL_VM_End( void );
//
// cl_game.c
//
void CL_UnloadProgs( void );
bool CL_LoadProgs( const char *name );
void CL_ParseUserMessage( sizebuf_t *msg, int svc_num );
void CL_PrepUserMessage( char *pszName, const int svc_num );
edict_t *CL_AllocEdict( void );
void CL_FreeEdict( edict_t *pEdict );
string_t pfnAllocString( const char *szValue );
const char *pfnGetString( string_t iString );
void pfnGetGameDir( char *szGetGameDir );
long pfnRandomLong( long lLow, long lHigh );
float pfnRandomFloat( float flLow, float flHigh );
byte* pfnLoadFile( const char *filename, int *pLength );
void pfnFreeFile( void *buffer );
_inline edict_t *_EDICT_NUM( int n, const char *file, const int line )
{
if((n >= 0) && (n < game.maxEntities))
return game.edicts + n;
Host_Error( "EDICT_NUM: bad number %i (called at %s:%i)\n", n, file, line );
return NULL;
}
//
// cl_sound.c
@ -461,7 +498,7 @@ void CL_VM_End( void );
// if origin is NULL, the sound will be dynamically sourced from the entity
#define S_StartStreaming if( se ) se->StartStreaming
#define S_StartSound( a,b,c,d,e,f,g ) if( se ) se->StartSound( a, b, c, d, e, f, g, true );
#define S_StartLocalSound( name ) ( se && se->StartLocalSound( name ))
#define S_StartLocalSound( a, b, c ) if( se ) se->StartLocalSound( a, b, c )
#define S_StartBackgroundTrack if( se ) se->StartBackgroundTrack
#define S_StopBackgroundTrack if( se ) se->StopBackgroundTrack
#define S_RawSamples if( se ) se->StreamRawSamples
@ -525,14 +562,15 @@ void V_RenderSplash( void );
float V_CalcFov( float fov_x, float width, float height );
//
// cl_pred.c
// cl_phys.c
//
void CL_InitPrediction (void);
void CL_PredictMove (void);
void CL_CheckPredictionError (void);
int CL_PointContents( const vec3_t point );
int CL_ContentsMask( const edict_t *passedict );
bool CL_AmbientLevel( const vec3_t point, float *volumes );
trace_t CL_Trace(const vec3_t s1, const vec3_t m1, const vec3_t m2, const vec3_t s2, int type, pr_edict_t *e, int mask);
trace_t CL_Trace( const vec3_t s1, const vec3_t m1, const vec3_t m2, const vec3_t s2, int type, edict_t *e, int mask );
//
// cl_ents.c
@ -590,8 +628,6 @@ extern field_t chatField;
// cl_menu.c
//
extern bool ui_active;
extern const int vm_ui_numbuiltins;
extern prvm_builtin_t vm_ui_builtins[];
void UI_Init( void );
void UI_DrawCredits( void );

View File

@ -145,6 +145,13 @@ PRVM INTERACTIONS
*/
char *ED_NewString( const char *string, byte *mempool );
#define NUM_FOR_EDICT(e) ((int)((edict_t *)(e) - game.edicts))
#define EDICT_NUM( num ) _EDICT_NUM( num, __FILE__, __LINE__ )
// for constant strings
#define STRING( offset ) pfnGetString( offset )
#define MAKE_STRING(str) pfnAllocString( str )
#define prog vm->prog // global callback to vprogs.dll
#define PRVM_EDICT_NUM( num ) _PRVM_EDICT_NUM( num, __FILE__, __LINE__ )
@ -317,13 +324,6 @@ enum e_trace
MOVE_HITMODEL,
};
// client printf level
enum e_clprint
{
PRINT_CONSOLE = 0, // normal messages
PRINT_CENTER, // centerprint
PRINT_CHAT, // chat messages (with sound)
};
extern byte *zonepool;
#define Z_Malloc(size) Mem_Alloc( zonepool, size )

View File

@ -171,7 +171,7 @@ Con_CheckResize
If the line width has changed, reformat the buffer.
================
*/
void Con_CheckResize (void)
void Con_CheckResize( void )
{
int i, j, width, oldwidth, oldtotallines, numlines, numchars;
short tbuf[CON_TEXTSIZE];

View File

@ -9,6 +9,84 @@
#include "const.h"
#include "client.h"
/*
=============
pfnAllocString
=============
*/
string_t pfnAllocString( const char *szValue )
{
return StringTable_SetString( game.hStringTable, szValue );
}
/*
=============
pfnGetString
=============
*/
const char *pfnGetString( string_t iString )
{
return StringTable_GetString( game.hStringTable, iString );
}
/*
=============
pfnLoadFile
=============
*/
byte* pfnLoadFile( const char *filename, int *pLength )
{
return FS_LoadFile( filename, pLength );
}
/*
=============
pfnFreeFile
=============
*/
void pfnFreeFile( void *buffer )
{
if( buffer ) Mem_Free( buffer );
}
/*
=============
pfnRandomLong
=============
*/
long pfnRandomLong( long lLow, long lHigh )
{
return Com_RandomLong( lLow, lHigh );
}
/*
=============
pfnRandomFloat
=============
*/
float pfnRandomFloat( float flLow, float flHigh )
{
return Com_RandomFloat( flLow, flHigh );
}
/*
=============
pfnGetGameDir
=============
*/
void pfnGetGameDir( char *szGetGameDir )
{
// FIXME: potentially crashpoint
com.strcpy( szGetGameDir, FS_Gamedir );
}
/*
=======================================================================
@ -804,7 +882,8 @@ void VM_localsound( void )
return;
s = PRVM_G_STRING( OFS_PARM0 );
if(!S_StartLocalSound( s ))
S_StartLocalSound( s, 1.0f, NULL );
else
{
VM_Warning( "localsound: can't play %s!\n", s );
PRVM_G_FLOAT(OFS_RETURN) = 0;

View File

@ -102,8 +102,6 @@ void UI_KeyEvent( int key )
prog->globals.ui->time = cls.realtime * 0.001f;
PRVM_G_INT(OFS_PARM1) = PRVM_SetEngineString(ascii);
PRVM_ExecuteProgram (prog->globals.ui->m_keydown, "m_keydown");
CL_VM_Begin(); // restore clvm state
}
void UI_Draw( void )
@ -115,8 +113,6 @@ void UI_Draw( void )
prog->globals.ui->time = cls.realtime * 0.001f;
PRVM_ExecuteProgram (prog->globals.ui->m_draw, "m_draw");
UI_DrawCredits(); // display game credits
CL_VM_Begin(); // restore clvm state
}
void UI_DrawCredits( void )
@ -160,7 +156,6 @@ void UI_ShowMenu( void )
ui_active = true;
prog->globals.ui->time = cls.realtime * 0.001f;
PRVM_ExecuteProgram (prog->globals.ui->m_show, "m_show");
CL_VM_Begin(); // restore clvm state
}
void UI_HideMenu( void )
@ -173,7 +168,6 @@ void UI_HideMenu( void )
ui_active = false;
prog->globals.ui->time = cls.realtime * 0.001f;
PRVM_ExecuteProgram (prog->globals.ui->m_hide, "m_hide");
CL_VM_Begin(); // restore clvm state
}
void UI_Shutdown( void )

View File

@ -134,6 +134,10 @@ SOURCE=.\client\cl_frame.c
# End Source File
# Begin Source File
SOURCE=.\client\cl_game.c
# End Source File
# Begin Source File
SOURCE=.\client\cl_input.c
# End Source File
# Begin Source File
@ -146,7 +150,7 @@ SOURCE=.\client\cl_parse.c
# End Source File
# Begin Source File
SOURCE=.\client\cl_pred.c
SOURCE=.\client\cl_phys.c
# End Source File
# Begin Source File
@ -262,10 +266,6 @@ SOURCE=.\server\sv_world.c
# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd"
# Begin Source File
SOURCE=.\client\cl_edict.h
# End Source File
# Begin Source File
SOURCE=.\client\client.h
# End Source File
# Begin Source File

View File

@ -529,7 +529,7 @@ void Host_Error( const char *error, ... )
if( host.framecount < 3 || host.state == HOST_SHUTDOWN )
{
Msg( "Host_InitError:" );
Msg( "Host_InitError: " );
com.error( hosterror1 );
}
else if( host.framecount == host.errorframe )

View File

@ -22,13 +22,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define SERVER_H
#include "mathlib.h"
#include "svprog_def.h"
#include "entity_def.h"
#include "svgame_api.h"
//=============================================================================
#define NUM_FOR_EDICT(e) ((int)((edict_t *)(e) - game.edicts))
#define EDICT_NUM( num ) _EDICT_NUM( num, __FILE__, __LINE__ )
#define AREA_SOLID 1
#define AREA_TRIGGERS 2
@ -215,7 +212,7 @@ typedef struct
dword funcBase; // base offset
int hStringTable; // stringtable handle
} game_static_t;
} svgame_static_t;
typedef struct
{
@ -254,7 +251,7 @@ typedef struct
extern netadr_t master_adr[MAX_MASTERS]; // address of the master server
extern const char *ed_name[];
extern server_static_t svs; // persistant server info
extern game_static_t game; // persistant game info
extern svgame_static_t game; // persistant game info
extern server_t sv; // local server
extern cvar_t *sv_paused;
@ -384,6 +381,11 @@ float SV_AngleMod( float ideal, float current, float speed );
void SV_SpawnEntities( const char *mapname, script_t *entities );
string_t pfnAllocString( const char *szValue );
const char *pfnGetString( string_t iString );
void pfnGetGameDir( char *szGetGameDir );
long pfnRandomLong( long lLow, long lHigh );
float pfnRandomFloat( float flLow, float flHigh );
byte* pfnLoadFile( const char *filename, int *pLength );
void pfnFreeFile( void *buffer );
_inline edict_t *_EDICT_NUM( int n, const char * file, const int line )
{
@ -393,10 +395,6 @@ _inline edict_t *_EDICT_NUM( int n, const char * file, const int line )
return NULL;
}
// for constant strings
#define STRING( offset ) pfnGetString( offset )
#define MAKE_STRING(str) pfnAllocString( str )
//
// sv_studio.c
//

View File

@ -302,7 +302,7 @@ void SV_FlushRedirect( netadr_t adr, int dest, char *buf )
case RD_CLIENT:
if( !sv_client ) return; // client not set
MSG_WriteByte( &sv_client->netchan.message, svc_print );
MSG_WriteByte( &sv_client->netchan.message, PRINT_CONSOLE );
MSG_WriteByte( &sv_client->netchan.message, HUD_PRINTCONSOLE );
MSG_WriteString( &sv_client->netchan.message, buf );
break;
case RD_NONE:

View File

@ -370,9 +370,9 @@ void SV_Kick_f( void )
}
if(!SV_SetPlayer()) return;
SV_BroadcastPrintf( PRINT_CONSOLE, "%s was kicked\n", sv_client->name );
SV_ClientPrintf(sv_client, PRINT_CONSOLE, "You were kicked from the game\n");
SV_DropClient(sv_client);
SV_BroadcastPrintf( HUD_PRINTCONSOLE, "%s was kicked\n", sv_client->name );
SV_ClientPrintf( sv_client, HUD_PRINTCONSOLE, "You were kicked from the game\n" );
SV_DropClient( sv_client );
sv_client->lastmessage = svs.realtime; // min case there is a funny zombie
}
@ -454,7 +454,7 @@ void SV_ConSay_f( void )
for (i = 0, client = svs.clients; i < Host_MaxClients(); i++, client++)
{
if( client->state != cs_spawned ) continue;
SV_ClientPrintf( client, PRINT_CHAT, "%s\n", text );
SV_ClientPrintf( client, HUD_PRINTTALK, "%s\n", text );
}
}

View File

@ -626,7 +626,7 @@ void SV_SendClientMessages( void )
{
MSG_Clear( &cl->netchan.message );
MSG_Clear( &cl->datagram );
SV_BroadcastPrintf( PRINT_CONSOLE, "%s overflowed\n", cl->name );
SV_BroadcastPrintf( HUD_PRINTCONSOLE, "%s overflowed\n", cl->name );
SV_DropClient( cl );
}

View File

@ -994,12 +994,24 @@ bool SV_ReadComment( char *comment, int savenum )
===============================================================================
*/
void *pfnMemAlloc( size_t cb, const char *filename, const int fileline )
/*
=========
pfnMemAlloc
=========
*/
static void *pfnMemAlloc( size_t cb, const char *filename, const int fileline )
{
return com.malloc( svs.mempool, cb, filename, fileline );
return com.malloc( svs.private, cb, filename, fileline );
}
void pfnMemFree( void *mem, const char *filename, const int fileline )
/*
=========
pfnMemFree
=========
*/
static void pfnMemFree( void *mem, const char *filename, const int fileline )
{
com.free( mem, filename, fileline );
}
@ -1482,18 +1494,6 @@ void pfnMakeVectors( const float *rgflVector )
AngleVectors( rgflVector, svs.globals->v_forward, svs.globals->v_right, svs.globals->v_up );
}
/*
==============
pfnAngleVectors
can receive NULL output args
==============
*/
void pfnAngleVectors( const float *rgflVector, float *forward, float *right, float *up )
{
AngleVectors( rgflVector, forward, right, up );
}
/*
==============
pfnCreateEntity
@ -1835,7 +1835,7 @@ pfnTraceLine
=================
*/
void pfnTraceLine( const float *v1, const float *v2, int fNoMonsters, edict_t *pentToSkip, TraceResult *ptr )
static void pfnTraceLine( const float *v1, const float *v2, int fNoMonsters, edict_t *pentToSkip, TraceResult *ptr )
{
trace_t trace;
int move;
@ -2100,7 +2100,7 @@ pfnPointContents
=============
*/
int pfnPointContents( const float *rgflVector )
static int pfnPointContents( const float *rgflVector )
{
return SV_PointContents( rgflVector );
}
@ -2225,6 +2225,18 @@ void pfnWriteCoord( float flValue )
if( game.msg_leftsize != 0xFFFF ) game.msg_leftsize -= 4;
}
/*
=============
pfnWriteFloat
=============
*/
void pfnWriteFloat( float flValue )
{
MSG_WriteFloat( &sv.multicast, flValue );
if( game.msg_leftsize != 0xFFFF ) game.msg_leftsize -= 4;
}
/*
=============
pfnWriteString
@ -2320,7 +2332,7 @@ pfnAlertMessage
=============
*/
void pfnAlertMessage( ALERT_TYPE level, char *szFmt, ... )
static void pfnAlertMessage( ALERT_TYPE level, char *szFmt, ... )
{
char buffer[2048]; // must support > 1k messages
va_list args;
@ -2362,28 +2374,6 @@ void pfnFreeEntPrivateData( edict_t *pEdict )
pEdict->pvServerData = NULL; // freed
}
/*
=============
pfnAllocString
=============
*/
string_t pfnAllocString( const char *szValue )
{
return StringTable_SetString( game.hStringTable, szValue );
}
/*
=============
pfnGetString
=============
*/
const char *pfnGetString( string_t iString )
{
return StringTable_GetString( game.hStringTable, iString );
}
/*
=============
pfnPEntityOfEntOffset
@ -2481,8 +2471,18 @@ int pfnRegUserMsg( const char *pszName, int iSize )
{
// register message first
int msg_index;
string msg_name;
msg_index = SV_UserMessageIndex( pszName );
// scan name for reserved symbol
if( com.strchr( pszName, '@' ))
{
MsgDev( D_ERROR, "SV_RegisterUserMessage: invalid name %s\n", pszName );
return svc_bad; // force error
}
// build message name, fmt: MsgName@size
com.snprintf( msg_name, MAX_STRING, "%s@%i", pszName, iSize );
msg_index = SV_UserMessageIndex( msg_name );
if( iSize == -1 )
game.msg_sizes[msg_index] = 0xFFFF;
else game.msg_sizes[msg_index] = iSize;
@ -2578,17 +2578,16 @@ void pfnClientPrintf( edict_t* pEdict, int ptype, const char *szMsg )
switch( ptype )
{
case PRINT_CONSOLE:
SV_ClientPrintf (client, PRINT_CONSOLE, (char *)szMsg );
case HUD_PRINTTALK:
case HUD_PRINTNOTIFY: // don't leave message in console history
case HUD_PRINTCONSOLE:
SV_ClientPrintf( client, ptype, (char *)szMsg );
break;
case PRINT_CENTER:
case HUD_PRINTCENTER:
MSG_Begin( svc_centerprint );
MSG_WriteString( &sv.multicast, szMsg );
MSG_Send( MSG_ONE_R, NULL, pEdict );
break;
case PRINT_CHAT:
SV_ClientPrintf( client, PRINT_CHAT, (char *)szMsg );
break;
default:
MsgDev( D_ERROR, "SV_ClientPrintf: invalid destination\n" );
break;
@ -2608,7 +2607,7 @@ void pfnServerPrint( const char *szMsg )
// while loading in-progress we can sending message
com.print( szMsg ); // only for local client
}
else SV_BroadcastPrintf( PRINT_CONSOLE, (char *)szMsg );
else SV_BroadcastPrintf( HUD_PRINTCONSOLE, (char *)szMsg );
}
/*
@ -2710,28 +2709,6 @@ word pfnCRC_Final( word pulCRC )
return pulCRC ^ 0x0000;
}
/*
=============
pfnRandomLong
=============
*/
long pfnRandomLong( long lLow, long lHigh )
{
return Com_RandomLong( lLow, lHigh );
}
/*
=============
pfnRandomFloat
=============
*/
float pfnRandomFloat( float flLow, float flHigh )
{
return Com_RandomFloat( flLow, flHigh );
}
/*
=============
pfnSetView
@ -2754,28 +2731,6 @@ void pfnCrosshairAngle( const edict_t *pClient, float pitch, float yaw )
// FIXME: implement
}
/*
=============
pfnLoadFile
=============
*/
byte* pfnLoadFile( const char *filename, int *pLength )
{
return FS_LoadFile( filename, pLength );
}
/*
=============
pfnFreeFile
=============
*/
void pfnFreeFile( void *buffer )
{
if( buffer ) Mem_Free( buffer );
}
/*
=============
pfnCompareFileTime
@ -2800,19 +2755,7 @@ int pfnCompareFileTime( const char *filename1, const char *filename2, int *iComp
/*
=============
pfnGetGameDir
=============
*/
void pfnGetGameDir( char *szGetGameDir )
{
// FIXME: potentially crashpoint
com.strcpy( szGetGameDir, FS_Gamedir );
}
/*
=============
pfnGetGameDir
pfnStaticDecal
=============
*/
@ -3001,7 +2944,7 @@ static enginefuncs_t gEngfuncs =
pfnEntitiesInPVS,
pfnEntitiesInPHS,
pfnMakeVectors,
pfnAngleVectors,
AngleVectors,
pfnCreateEntity,
pfnRemoveEntity,
pfnCreateNamedEntity,
@ -3034,6 +2977,7 @@ static enginefuncs_t gEngfuncs =
pfnWriteLong,
pfnWriteAngle,
pfnWriteCoord,
pfnWriteFloat,
pfnWriteString,
pfnWriteEntity,
pfnCVarRegister,

View File

@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "server.h"
server_static_t svs; // persistant server info
game_static_t game; // persistant game info
svgame_static_t game; // persistant game info
server_t sv; // local server
/*

View File

@ -164,7 +164,7 @@ void SV_CheckTimeouts( void )
}
if(( cl->state == cs_connected || cl->state == cs_spawned) && cl->lastmessage < droppoint )
{
SV_BroadcastPrintf( PRINT_CONSOLE, "%s timed out\n", cl->name );
SV_BroadcastPrintf( HUD_PRINTCONSOLE, "%s timed out\n", cl->name );
SV_DropClient( cl );
cl->state = cs_free; // don't bother with zombie state
}
@ -394,9 +394,9 @@ void SV_FinalMessage( char *message, bool reconnect )
sizebuf_t msg;
int i;
MSG_Init( &msg, msg_buf, sizeof(msg_buf));
MSG_Init( &msg, msg_buf, sizeof( msg_buf ));
MSG_WriteByte( &msg, svc_print );
MSG_WriteByte( &msg, PRINT_CONSOLE );
MSG_WriteByte( &msg, HUD_PRINTCONSOLE );
MSG_WriteString( &msg, message );
if( reconnect )

View File

@ -154,7 +154,7 @@ trace_t SV_Trace( const vec3_t start, const vec3_t mins, const vec3_t maxs, cons
uint modelindex = (uint)touch->v.modelindex;
// if the modelindex is 0, it shouldn't be SOLID_BSP!
if( modelindex > 0 && modelindex < MAX_MODELS )
model = sv.models[(int)touch->v.modelindex];
model = sv.models[touch->v.modelindex];
}
if( model ) Matrix4x4_CreateFromEntity( matrix, touch->v.origin[0], touch->v.origin[1], touch->v.origin[2], touch->v.angles[0], touch->v.angles[1], touch->v.angles[2], 1 );
else Matrix4x4_CreateTranslate( matrix, touch->v.origin[0], touch->v.origin[1], touch->v.origin[2] );

View File

@ -40,13 +40,13 @@ static long Cvar_GetHashValue( const char *fname )
Cvar_InfoValidate
============
*/
static bool Cvar_ValidateString(const char *s, bool isvalue )
static bool Cvar_ValidateString( const char *s, bool isvalue )
{
if ( !s ) return false;
if (strstr(s, "\\") && !isvalue)
if( !s ) return false;
if( com.strstr( s, "\\" ) && !isvalue )
return false;
if (strstr(s, "\"")) return false;
if (strstr(s, ";")) return false;
if( com.strstr( s, "\"" )) return false;
if( com.strstr( s, ";" )) return false;
return true;
}

235
public/clgame_api.h Normal file
View File

@ -0,0 +1,235 @@
//=======================================================================
// Copyright XashXT Group 2008 ©
// clgame_api.h - entity interface between engine and clgame
//=======================================================================
#ifndef CLGAME_API_H
#define CLGAME_API_H
typedef int HSPRITE; // handle to a graphic
typedef int (*pfnUserMsgHook)( const char *pszName, int iSize, void *pbuf ); // user message handle
typedef enum
{
TRI_FRONT = 0,
TRI_BACK,
TRI_NONE,
} TRI_CULL;
typedef enum
{
TRI_TRIANGLES = 0,
TRI_TRIANGLE_FAN,
TRI_TRIANGLE_STRIP,
TRI_POLYGON,
TRI_QUADS,
TRI_LINES,
} TRI_DRAW;
typedef struct triapi_s
{
size_t api_size; // must match with sizeof( triapi_t );
void (*Bind)( HSPRITE shader ); // use handle that return pfnLoadShader
void (*Begin)( TRI_DRAW mode );
void (*End)( void );
void (*Vertex2f)( float x, float y );
void (*Vertex3f)( float x, float y, float z );
void (*Vertex2fv)( const float *v );
void (*Vertex3fv)( const float *v );
void (*Color3f)( float r, float g, float b );
void (*Color4f)( float r, float g, float b, float a );
void (*Color4ub)( byte r, byte g, byte b, byte a );
void (*TexCoord2f)( float u, float v );
void (*TexCoord2fv)( const float *v );
void (*CullFace)( TRI_CULL mode );
} triapi_t;
// FIXME: get rid of this
typedef struct
{
char *name;
short ping;
byte thisplayer; // TRUE if this is the calling player
// stuff that's unused at the moment, but should be done
byte spectator;
byte packetloss;
char *model;
short topcolor;
short bottomcolor;
} hud_player_info_t;
// FIXME: get rid of this
typedef struct client_textmessage_s
{
int effect;
byte r1, g1, b1, a1; // 2 colors for effects
byte r2, g2, b2, a2;
float x;
float y;
float fadein;
float fadeout;
float holdtime;
float fxtime;
const char *pName;
const char *pMessage;
} client_textmessage_t;
// NOTE: engine trace struct not matched with clgame trace
typedef struct
{
BOOL fAllSolid; // if true, plane is not valid
BOOL fStartSolid; // if true, the initial point was in a solid area
BOOL fStartStuck; // if true, trace started from solid entity
float flFraction; // time completed, 1.0 = didn't hit anything
vec3_t vecEndPos; // final position
int iStartContents; // start pos conetnts
int iContents; // final pos contents
int iHitgroup; // 0 == generic, non zero is specific body part
float flPlaneDist; // planes distance
vec3_t vecPlaneNormal; // surface normal at impact
const char *pTexName; // texture name that we hitting (brushes and studiomodels)
edict_t *pHit; // entity the surface is on
} TraceResult;
typedef struct ref_params_s
{
// output
vec3_t vieworg;
vec3_t viewangles;
float fov;
vec3_t forward;
vec3_t right;
vec3_t up;
float frametime; // client frametime
float lerpfrac; // interp value
float time; // client time
// misc
BOOL intermission;
BOOL demoplayback;
BOOL demorecord;
BOOL spectator;
BOOL paused;
qword iWeaponBits; // pev->weapon
dword iKeyBits; // pev->button
edict_t *onground; // pointer to onground entity
int waterlevel;
// input
vec3_t velocity;
vec3_t angles; // input viewangles
vec3_t origin; // origin + viewheight = vieworg
vec3_t viewheight;
float idealpitch;
float v_idlescale; // used for concussion effect
float mouse_sensitivity;
int health;
vec3_t crosshairangle; // pfnCrosshairAngle values from server
vec3_t punchangle; // recivied from server
edict_t *viewentity;
int clientnum;
int num_entities;
int max_entities;
int max_clients;
} ref_params_t;
typedef struct cl_enginefuncs_s
{
// interface validator
size_t api_size; // must matched with sizeof(cl_enginefuncs_t)
// engine memory manager
void* (*pfnMemAlloc)( size_t cb, const char *filename, const int fileline );
void (*pfnMemFree)( void *mem, const char *filename, const int fileline );
// screen handlers
HSPRITE (*pfnLoadShader)( const char *szShaderName );
void (*pfnFillRGBA)( int x, int y, int width, int height, const float *color, float alpha );
void (*pfnDrawImage)( HSPRITE shader, int x, int y, int width, int height, int frame );
void (*pfnSetColor)( float r, float g, float b, float a );
// cvar handlers
void (*pfnRegisterVariable)( const char *szName, const char *szValue, int flags, const char *szDesc );
void (*pfnCvarSetValue)( const char *cvar, float value );
float (*pfnGetCvarFloat)( const char *szName );
char* (*pfnGetCvarString)( const char *szName );
// command handlers
void (*pfnAddCommand)( const char *cmd_name, void (*function)(void), const char *cmd_desc );
void (*pfnHookUserMsg)( char *szMsgName, pfnUserMsgHook pfn );
void (*pfnServerCmd)( const char *szCmdString );
void (*pfnClientCmd)( const char *szCmdString );
void (*pfnGetPlayerInfo)( int player_num, hud_player_info_t *pinfo );
client_textmessage_t *(*pfnTextMessageGet)( const char *pName );
int (*pfnCmdArgc)( void );
char *(*pfnCmdArgv)( int argc );
void (*pfnAlertMessage)( ALERT_TYPE, char *szFmt, ... );
// sound handlers (NULL origin == play at current client origin)
void (*pfnPlaySoundByName)( const char *szSound, float volume, const float *org );
void (*pfnPlaySoundByIndex)( int iSound, float volume, const float *org );
// vector helpers
void (*pfnAngleVectors)( const float *rgflVector, float *forward, float *right, float *up );
void (*pfnDrawCenterPrint)( void );
void (*pfnCenterPrint)( const char *text, int y, int charWidth );
int (*pfnDrawCharacter)( int x, int y, int width, int height, int number );
void (*pfnDrawString)( int x, int y, int width, int height, const char *text );
void (*pfnGetImageSize)( int *w, int *h, shader_t shader );
// local client handlers
void (*pfnGetViewAngles)( float *angles );
edict_t* (*pfnGetEntityByIndex)( int idx ); // matched with entity serialnumber
edict_t* (*pfnGetLocalPlayer)( void );
int (*pfnIsSpectateOnly)( void ); // returns 1 if the client is a spectator only
float (*pfnGetClientTime)( void );
int (*pfnGetMaxClients)( void );
edict_t* (*pfnGetViewModel)( void );
int (*pfnPointContents)( const float *rgflVector );
void (*pfnTraceLine)( const float *v1, const float *v2, int fNoMonsters, edict_t *pentToSkip, TraceResult *ptr );
long (*pfnRandomLong)( long lLow, long lHigh );
float (*pfnRandomFloat)( float flLow, float flHigh );
byte* (*pfnLoadFile)( const char *filename, int *pLength );
void (*pfnFreeFile)( void *buffer );
void (*pfnGetGameDir)( char *szGetGameDir );
void (*pfnHostError)( const char *szFmt, ... ); // invoke host error
triapi_t *pTriAPI;
} cl_enginefuncs_t;
typedef struct
{
// interface validator
size_t api_size; // must matched with sizeof(HUD_FUNCTIONS)
int (*pfnVidInit)( void );
void (*pfnInit)( void );
int (*pfnRedraw)( float flTime, int intermission );
int (*pfnUpdateClientData)( ref_params_t *parms, float flTime );
void (*pfnReset)( void );
void (*pfnFrame)( double time );
void (*pfnShutdown)( void );
void (*pfnDrawNormalTriangles)( void );
void (*pfnDrawTransparentTriangles)( void );
void (*pfnCreateEntities)( void );
void (*pfnStudioEvent)( const dstudioevent_t *event, edict_t *entity );
void (*pfnCalcRefdef)( ref_params_t *parms );
} HUD_FUNCTIONS;
typedef int (*CLIENTAPI)( HUD_FUNCTIONS *pFunctionTable, cl_enginefuncs_t* pEngfuncsFromEngine, int interfaceVersion );
#endif//CLGAME_API_H

View File

@ -5,6 +5,14 @@
#ifndef CONST_H
#define CONST_H
// shared typedefs
typedef unsigned __int64 qword;
typedef unsigned long dword;
typedef unsigned int uint;
typedef unsigned short word;
typedef unsigned char byte;
typedef int shader_t;
// euler angle order
#define PITCH 0
#define YAW 1
@ -102,6 +110,16 @@
#define DAMAGE_YES 1
#define DAMAGE_AIM 2
typedef enum
{
at_console = 1, // format: [msg]
at_warning, // format: Warning: [msg]
at_error, // format: Error: [msg]
at_loading, // print messages during loading
at_aiconsole, // same as at_console, but only shown if developer level is 5!
at_logged // server print to console ( only in multiplayer games ).
} ALERT_TYPE;
// engine edict types
typedef enum
{
@ -210,4 +228,53 @@ typedef enum
kRenderFxNoReflect, // don't reflecting in mirrors
} kRenderFx_t;
// all drawing is done to a 640*480 virtual screen size
// and will be automatically scaled to the real resolution
#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 480
#define TINYCHAR_WIDTH (SMALLCHAR_WIDTH)
#define TINYCHAR_HEIGHT (SMALLCHAR_HEIGHT/2)
#define SMALLCHAR_WIDTH 8
#define SMALLCHAR_HEIGHT 16
#define BIGCHAR_WIDTH 16
#define BIGCHAR_HEIGHT 24
#define GIANTCHAR_WIDTH 32
#define GIANTCHAR_HEIGHT 48
#define HUD_PRINTNOTIFY 1
#define HUD_PRINTCONSOLE 2
#define HUD_PRINTTALK 3
#define HUD_PRINTCENTER 4
#define INTERFACE_VERSION 1 // both the client and server iface version
//=======================================================================
//
// server.dll - client.dll definitions only
//
//=======================================================================
#define MAX_WEAPONS 32
#define MAX_AMMO_SLOTS 32
#define HIDEHUD_WEAPONS BIT( 0 )
#define HIDEHUD_FLASHLIGHT BIT( 1 )
#define HIDEHUD_ALL BIT( 2 )
#define HIDEHUD_HEALTH BIT( 3 )
#define ITEM_SUIT BIT( 4 )
enum ShakeCommand_t
{
SHAKE_START = 0, // Starts the screen shake for all players within the radius.
SHAKE_STOP, // Stops the screen shake for all players within the radius.
SHAKE_AMPLITUDE, // Modifies the amplitude of an active screen shake for all players within the radius.
SHAKE_FREQUENCY, // Modifies the frequency of an active screen shake for all players within the radius.
};
#define FFADE_IN 0x0000 // Just here so we don't pass 0 into the function
#define FFADE_OUT 0x0001 // Fade out (not in)
#define FFADE_MODULATE 0x0002 // Modulate (don't blend)
#define FFADE_STAYOUT 0x0004 // ignores the duration, stays faded out until new ScreenFade message received
#define FFADE_CUSTOMVIEW 0x0008 // fading only at custom viewing (don't sending this to engine )
#endif//CONST_H

View File

@ -1,57 +1,15 @@
//=======================================================================
// Copyright XashXT Group 2008 ©
// svprog_def.h - engine <-> svgame communications
// entity_def.h - generic engine edict
//=======================================================================
#ifndef SVDEFS_API_H
#define SVDEFS_API_H
#ifndef ENTITY_DEF_H
#define ENTITY_DEF_H
typedef struct ed_priv_s ed_priv_t; // engine private data
typedef struct edict_s edict_t;
typedef struct globalvars_s
{
float time;
float frametime;
string_t mapname;
string_t startspot;
vec3_t spotOffset; // landmark offset
BOOL deathmatch;
BOOL coop;
BOOL teamplay;
int serverflags;
int maxClients;
int numClients; // actual clients count
int maxEntities;
int numEntities; // actual ents count
vec3_t v_forward;
vec3_t v_right;
vec3_t v_up;
BOOL trace_allsolid;
BOOL trace_startsolid;
BOOL trace_startstuck;
float trace_fraction;
vec3_t trace_endpos;
vec3_t trace_plane_normal;
float trace_plane_dist;
int trace_start_contents;
int trace_contents;
int trace_hitgroup;
const char *trace_texture; // texture name that we hitting (brushes and studiomodels)
edict_t *trace_ent;
int total_secrets;
int found_secrets; // number of secrets found
int total_monsters;
int killed_monsters; // number of monsters killed
void *pSaveData; // savedata base offset
} globalvars_t;
// NOT FINALIZED!
// TODO: move to CBaseEntity all fields which doesn't existing on client side
// TODO: generic edict must have all fields as valid on client side too
typedef struct entvars_s
{
string_t classname;
@ -119,7 +77,7 @@ typedef struct entvars_s
float health;
float frags;
int weapons; // bit mask for available weapons
qword weapons; // bit mask for available weapons
float takedamage;
int deadflag;
@ -197,4 +155,4 @@ struct edict_s
// other fields from progs come immediately after
};
#endif//SVDEFS_API_H
#endif//ENTITY_DEF_H

View File

@ -5,26 +5,6 @@
#ifndef SVGAME_API_H
#define SVGAME_API_H
#define INTERFACE_VERSION 138
typedef enum
{
at_console = 1, // format: [msg]
at_warning, // format: Warning: [msg]
at_error, // format: Error: [msg]
at_loading, // print messages during loading
at_aiconsole, // same as at_console, but only shown if developer level is 5!
at_logged // server print to console ( only in multiplayer games ).
} ALERT_TYPE;
// Client_Printf modes
enum
{
print_console = 0,
print_center,
print_chat
};
enum
{
WALKMOVE_NORMAL = 0,
@ -54,6 +34,49 @@ typedef struct
edict_t *pHit; // entity the surface is on
} TraceResult;
typedef struct globalvars_s
{
float time;
float frametime;
string_t mapname;
string_t startspot;
vec3_t spotOffset; // landmark offset
BOOL deathmatch;
BOOL coop;
BOOL teamplay;
int serverflags;
int maxClients;
int numClients; // actual clients count
int maxEntities;
int numEntities; // actual ents count
vec3_t v_forward;
vec3_t v_right;
vec3_t v_up;
BOOL trace_allsolid;
BOOL trace_startsolid;
BOOL trace_startstuck;
float trace_fraction;
vec3_t trace_endpos;
vec3_t trace_plane_normal;
float trace_plane_dist;
int trace_start_contents;
int trace_contents;
int trace_hitgroup;
const char *trace_texture; // texture name that we hitting (brushes and studiomodels)
edict_t *trace_ent;
int total_secrets;
int found_secrets; // number of secrets found
int total_monsters;
int killed_monsters; // number of monsters killed
void *pSaveData; // savedata base offset
} globalvars_t;
// engine hands this to DLLs for functionality callbacks
typedef struct enginefuncs_s
{
@ -115,6 +138,7 @@ typedef struct enginefuncs_s
void (*pfnWriteLong)( int iValue );
void (*pfnWriteAngle)( float flValue );
void (*pfnWriteCoord)( float flValue );
void (*pfnWriteFloat)( float flValue );
void (*pfnWriteString)( const char *sz );
void (*pfnWriteEntity)( int iValue );
void (*pfnCVarRegister)( const char *name, const char *value, int flags, const char *desc );
@ -148,7 +172,7 @@ typedef struct enginefuncs_s
void (*pfnCRC_Init)( word *pulCRC );
void (*pfnCRC_ProcessBuffer)( word *pulCRC, void *p, int len );
word (*pfnCRC_Final)( word pulCRC );
long (*pfnRandomLong)( long lLow, long lHigh );
long (*pfnRandomLong)( long lLow, long lHigh );
float (*pfnRandomFloat)( float flLow, float flHigh );
void (*pfnSetView)( const edict_t *pClient, const edict_t *pViewent );
void (*pfnCrosshairAngle)( const edict_t *pClient, float pitch, float yaw );
@ -317,8 +341,6 @@ typedef struct
// returns string describing current .dll. E.g., TeamFotrress 2, Half-Life
const char *(*pfnGetGameDescription)( void );
// notify game .dll that engine is going to shut down. Allows mod authors to set a breakpoint.
void (*pfnHostError)( const char *error_string );
} DLL_FUNCTIONS;
// TODO: create single func

View File

@ -31,7 +31,7 @@ typedef struct vsound_exp_s
void (*StartSound)( const vec3_t pos, int ent, int chan, sound_t sfx, float vol, float attn, float pitch, bool loop );
void (*StreamRawSamples)( int samples, int rate, int width, int channels, const byte *data );
bool (*AddLoopingSound)( int entnum, sound_t handle, float volume, float attn );
bool (*StartLocalSound)( const char *name );
bool (*StartLocalSound)( const char *name, float volume, const float *origin );
void (*StartBackgroundTrack)( const char *introTrack, const char *loopTrack );
void (*StopBackgroundTrack)( void );

View File

@ -11,6 +11,9 @@ call vcvars32
%MSDEV% baserc/baserc.dsp %CONFIG%"baserc - Win32 Release" %build_target%
if errorlevel 1 set BUILD_ERROR=1
%MSDEV% client/client.dsp %CONFIG%"client - Win32 Release" %build_target%
if errorlevel 1 set BUILD_ERROR=1
%MSDEV% engine/engine.dsp %CONFIG%"engine - Win32 Release" %build_target%
if errorlevel 1 set BUILD_ERROR=1
@ -26,7 +29,7 @@ if errorlevel 1 set BUILD_ERROR=1
%MSDEV% render/render.dsp %CONFIG%"render - Win32 Release" %build_target%
if errorlevel 1 set BUILD_ERROR=1
%MSDEV% sv_dll/server.dsp %CONFIG%"server - Win32 Release" %build_target%
%MSDEV% server/server.dsp %CONFIG%"server - Win32 Release" %build_target%
if errorlevel 1 set BUILD_ERROR=1
%MSDEV% vprogs/vprogs.dsp %CONFIG%"vprogs - Win32 Release" %build_target%
@ -60,7 +63,7 @@ if exist launch\launch.plg del /f /q launch\launch.plg
if exist common\common.plg del /f /q common\common.plg
if exist physic\physic.plg del /f /q physic\physic.plg
if exist render\render.plg del /f /q render\render.plg
if exist sv_dll\server.plg del /f /q sv_dll\server.plg
if exist server\server.plg del /f /q server\server.plg
if exist vprogs\vprogs.plg del /f /q vprogs\vprogs.plg
if exist vsound\vsound.plg del /f /q vsound\vsound.plg

106
server/ents/basebeams.h Normal file
View File

@ -0,0 +1,106 @@
//=======================================================================
// Copyright (C) XashXT Group 2006
//=======================================================================
#ifndef BASEBEAMS_H
#define BASEBEAMS_H
#define SF_BEAM_TRIPPED 0x80000 //bit who indicated "trip" action
class CBeam : public CBaseLogic
{
public:
void Spawn( void );
void Precache( void );
int ObjectCaps( void ) { return (CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION); }
void Touch( CBaseEntity *pOther );
// These functions are here to show the way beams are encoded as entities.
// Encoding beams as entities simplifies their management in the client/server architecture
inline void SetType( int type ) { pev->rendermode = (pev->rendermode & 0xF0) | (type&0x0F); }
inline void SetFlags( int flags ) { pev->rendermode = (pev->rendermode & 0x0F) | (flags&0xF0); }
inline void SetStartPos( const Vector& pos ) { pev->origin = pos; }
inline void SetEndPos( const Vector& pos ) { pev->angles = pos; }
void SetStartEntity( int entityIndex );
void SetEndEntity( int entityIndex );
inline void SetStartAttachment( int attachment ) { pev->sequence = (pev->sequence & 0x0FFF) | ((attachment&0xF)<<12); }
inline void SetEndAttachment( int attachment ) { pev->skin = (pev->skin & 0x0FFF) | ((attachment&0xF)<<12); }
inline void SetTexture( int spriteIndex ) { pev->modelindex = spriteIndex; }
inline void SetWidth( int width ) { pev->scale = width; }
inline void SetNoise( int amplitude ) { pev->body = amplitude; }
inline void SetColor( int r, int g, int b ) { pev->rendercolor.x = r; pev->rendercolor.y = g; pev->rendercolor.z = b; }
inline void SetBrightness( int brightness ) { pev->renderamt = brightness; }
inline void SetFrame( float frame ) { pev->frame = frame; }
inline void SetScrollRate( int speed ) { pev->animtime = speed; }
inline int GetType( void ) { return pev->rendermode & 0x0F; }
inline int GetFlags( void ) { return pev->rendermode & 0xF0; }
inline int GetStartEntity( void ) { return pev->sequence & 0xFFF; }
inline int GetEndEntity( void ) { return pev->skin & 0xFFF; }
const Vector &GetStartPos( void );
const Vector &GetEndPos( void );
Vector Center( void ) { return (GetStartPos() + GetEndPos()) * 0.5; }; // center point of beam
inline int GetTexture( void ) { return pev->modelindex; }
inline int GetWidth( void ) { return pev->scale; }
inline int GetNoise( void ) { return pev->body; }
inline Vector GetColor( void ) { return Vector(pev->rendercolor.x, pev->rendercolor.y, pev->rendercolor.z ); }
inline int GetBrightness( void ) { return pev->renderamt; }
inline int GetFrame( void ) { return pev->frame; }
inline int GetScrollRate( void ) { return pev->animtime; }
CBaseEntity* GetTripEntity( TraceResult *ptr );
// Call after you change start/end positions
void RelinkBeam( void );
void SetObjectCollisionBox( void );
void DoSparks( const Vector &start, const Vector &end );
CBaseEntity *RandomTargetname( const char *szName );
void BeamDamage( TraceResult *ptr );
// Init after BeamCreate()
void BeamInit( const char *pSpriteName, int width );
void PointsInit( const Vector &start, const Vector &end );
void PointEntInit( const Vector &start, int endIndex );
void EntsInit( int startIndex, int endIndex );
void HoseInit( const Vector &start, const Vector &direction );
static CBeam *BeamCreate( const char *pSpriteName, int width );
inline void LiveForTime( float time ) { SetThink( Remove ); SetNextThink( time ); }
inline void BeamDamageInstant( TraceResult *ptr, float damage )
{
pev->dmg = damage;
pev->dmgtime = gpGlobals->time - 1;
BeamDamage(ptr);
}
};
class CLaser : public CBeam
{
public:
void Spawn( void );
void PostSpawn( void );
void Precache( void );
void KeyValue( KeyValueData *pkvd );
void Activate( void );
void SetObjectCollisionBox( void );
void TurnOn( void );
void TurnOff( void );
void FireAtPoint( Vector startpos, TraceResult &point );
void Think( void );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
CSprite *m_pStartSprite;
CSprite *m_pEndSprite;
};
#endif //BASEBEAMS_H

846
server/ents/basebrush.cpp Normal file
View File

@ -0,0 +1,846 @@
//=======================================================================
// Copyright (C) Shambler Team 2004
// basebreak.cpp - base for all brush
// entities.
//=======================================================================
#include "extdll.h"
#include "utils.h"
#include "sfx.h"
#include "cbase.h"
#include "decals.h"
#include "client.h"
#include "soundent.h"
#include "saverestore.h"
#include "gamerules.h"
#include "basebrush.h"
#include "defaults.h"
#include "player.h"
//=======================================================================
// STATIC BREACABLE BRUSHES
//=======================================================================
#define DAMAGE_SOUND(x, y, z)EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, x[RANDOM_LONG(0, ARRAYSIZE(x)-1)], y, ATTN_NORM, 0, z)
//spawn objects
const char *CBaseBrush::pSpawnObjects[] =
{
NULL, // 0
"item_battery", // 1
"item_healthkit", // 2
"weapon_9mmhandgun", // 3
"ammo_9mmclip", // 4
"weapon_9mmAR", // 5
"ammo_9mmAR", // 6
"ammo_m203", // 7
"weapon_shotgun", // 8
"ammo_buckshot", // 9
"weapon_crossbow", // 10
"ammo_crossbow", // 11
"weapon_357", // 12
"ammo_357", // 13
"weapon_rpg", // 14
"ammo_rpgclip", // 15
"ammo_gaussclip", // 16
"weapon_handgrenade", // 17
"weapon_gauss", // 18
};
//material sounds
const char *CBaseBrush::pSoundsWood[] =
{
"materials/wood/wood1.wav",
"materials/wood/wood2.wav",
"materials/wood/wood3.wav",
"materials/wood/wood4.wav",
};
const char *CBaseBrush::pSoundsFlesh[] =
{
"materials/flesh/flesh1.wav",
"materials/flesh/flesh2.wav",
"materials/flesh/flesh3.wav",
"materials/flesh/flesh4.wav",
"materials/flesh/flesh5.wav",
"materials/flesh/flesh6.wav",
"materials/flesh/flesh7.wav",
"materials/flesh/flesh8.wav",
};
const char *CBaseBrush::pSoundsMetal[] =
{
"materials/metal/metal1.wav",
"materials/metal/metal2.wav",
"materials/metal/metal3.wav",
"materials/metal/metal4.wav",
"materials/metal/metal5.wav",
};
const char *CBaseBrush::pSoundsCrete[] =
{
"materials/crete/concrete1.wav",
"materials/crete/concrete2.wav",
"materials/crete/concrete3.wav",
"materials/crete/concrete4.wav",
"materials/crete/concrete5.wav",
"materials/crete/concrete6.wav",
"materials/crete/concrete7.wav",
"materials/crete/concrete8.wav",
};
const char *CBaseBrush::pSoundsGlass[] =
{
"materials/glass/glass1.wav",
"materials/glass/glass2.wav",
"materials/glass/glass3.wav",
"materials/glass/glass4.wav",
"materials/glass/glass5.wav",
"materials/glass/glass6.wav",
"materials/glass/glass7.wav",
"materials/glass/glass8.wav",
};
const char *CBaseBrush::pSoundsCeil[] =
{
"materials/ceil/ceiling1.wav",
"materials/ceil/ceiling2.wav",
"materials/ceil/ceiling3.wav",
"materials/ceil/ceiling4.wav",
};
const char **CBaseBrush::MaterialSoundList( Materials precacheMaterial, int &soundCount )
{
const char **pSoundList = NULL;
switch ( precacheMaterial )
{
case None:
soundCount = 0;
break;
case Bones:
case Flesh:
pSoundList = pSoundsFlesh;
soundCount = ARRAYSIZE(pSoundsFlesh);
break;
case CinderBlock:
case Concrete:
case Rocks:
pSoundList = pSoundsCrete;
soundCount = ARRAYSIZE(pSoundsCrete);
break;
case Glass:
case Computer:
case UnbreakableGlass:
pSoundList = pSoundsGlass;
soundCount = ARRAYSIZE(pSoundsGlass);
break;
case MetalPlate:
case AirDuct:
case Metal:
pSoundList = pSoundsMetal;
soundCount = ARRAYSIZE(pSoundsMetal);
break;
case CeilingTile:
pSoundList = pSoundsCeil;
soundCount = ARRAYSIZE(pSoundsCeil);
break;
case Wood:
pSoundList = pSoundsWood;
soundCount = ARRAYSIZE(pSoundsWood);
break;
default:
soundCount = 0;
break;
}
return pSoundList;
}
void CBaseBrush::MaterialSoundPrecache( Materials precacheMaterial )
{
const char **pSoundList;
int i, soundCount = 0;
pSoundList = MaterialSoundList( precacheMaterial, soundCount );
for ( i = 0; i < soundCount; i++ ) UTIL_PrecacheSound( (char *)pSoundList[i] );
}
void CBaseBrush::PlayRandomSound( edict_t *pEdict, Materials soundMaterial, float volume )
{
const char **pSoundList;
int soundCount = 0;
pSoundList = MaterialSoundList( soundMaterial, soundCount );
if ( soundCount ) EMIT_SOUND( pEdict, CHAN_BODY, pSoundList[ RANDOM_LONG(0, soundCount-1) ], volume, 1.0 );
}
void CBaseBrush :: AxisDir( void )
{
//make backward compatibility
if ( pev->movedir != g_vecZero) return;
//Don't change this!
if ( FBitSet(pev->spawnflags, SF_BRUSH_ROTATE_Z_AXIS))
pev->movedir = Vector ( 0, 0, 1 ); // around z-axis
else if ( FBitSet(pev->spawnflags, SF_BRUSH_ROTATE_X_AXIS))
pev->movedir = Vector ( 1, 0, 0 ); // around x-axis
else pev->movedir = Vector ( 0, 1, 0 ); // around y-axis
}
void CBaseBrush::KeyValue( KeyValueData* pkvd )
{
//as default all brushes can select material
//and set strength (0 = unbreakable)
if (FStrEq(pkvd->szKeyName, "material"))
{
int i = atoi( pkvd->szValue);
if ((i < 0) || (i >= LastMaterial))
m_Material = Wood;
else m_Material = (Materials)i;
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "strength") )
{
pev->health = atof(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "spawnobject") )
{
int namelen = strlen(pkvd->szValue) - 1;
int obj = atoi( pkvd->szValue );
//custom spawn object
if(namelen > 2) m_iSpawnObject = ALLOC_STRING( pkvd->szValue );
else if ( obj > 0 && obj < ARRAYSIZE(pSpawnObjects))
m_iSpawnObject = MAKE_STRING( pSpawnObjects[obj] );
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "gibmodel"))
{
m_iGibModel = ALLOC_STRING(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "volume"))
{
//0.0 - silence, 1.0 - loudest( obsolete )
m_flVolume = atof(pkvd->szValue);
if (m_flVolume > 1.0) m_flVolume = 1.0;
if (m_flVolume < 0.0) m_flVolume = 0.0;
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "movesound") || FStrEq(pkvd->szKeyName, "movesnd"))
{
m_iMoveSound = ALLOC_STRING(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "stopsound") || FStrEq(pkvd->szKeyName, "stopsnd"))
{
m_iStopSound = ALLOC_STRING(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "contents"))
{
pev->skin = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else CBaseLogic::KeyValue( pkvd );
}
//Base functions
TYPEDESCRIPTION CBaseBrush::m_SaveData[] =
{
DEFINE_FIELD( CBaseBrush, m_flVolume, FIELD_FLOAT ), //volume of sounds
DEFINE_FIELD( CBaseBrush, m_pitch, FIELD_FLOAT ), //pitch of sound
DEFINE_FIELD( CBaseBrush, m_Material, FIELD_INTEGER ), //brush material
DEFINE_FIELD( CBaseBrush, m_iMagnitude, FIELD_INTEGER ), //explosion magnitude
DEFINE_FIELD( CBaseBrush, m_iMoveSound, FIELD_STRING ), //sound scheme like Quake
DEFINE_FIELD( CBaseBrush, m_iStartSound, FIELD_STRING ), //sound scheme like Quake
DEFINE_FIELD( CBaseBrush, m_iStopSound, FIELD_STRING ), //sound scheme like Quake
DEFINE_FIELD( CBaseBrush, m_iSpawnObject, FIELD_STRING ), //spawnobject index
DEFINE_FIELD( CBaseBrush, m_iGibModel, FIELD_STRING ), //custom gibname
DEFINE_FIELD( CBaseBrush, m_vecPlayerPos, FIELD_VECTOR ), //for controllable entity like tank
DEFINE_FIELD( CBaseBrush, m_pController, FIELD_CLASSPTR ), //for controllable entity like tank
};
IMPLEMENT_SAVERESTORE( CBaseBrush, CBaseLogic );
void CBaseBrush::Spawn( void )
{
Precache();
if (!m_flVolume)m_flVolume = 1.0;//just enable full volume
//breacable brush (if mapmaker just set material - just play material sound)
if(IsBreakable())
pev->takedamage = DAMAGE_YES;
else pev->takedamage = DAMAGE_NO;
pev->solid = SOLID_BSP;
pev->movetype = MOVETYPE_PUSH;
if (!m_pParent)pev->flags |= FL_WORLDBRUSH;
}
void CBaseBrush::Precache( void )
{
const char *pGibName = "";
switch (m_Material)
{
case Bones:
pGibName = "models/gibs/bones.mdl";
break;
case Flesh:
pGibName = "models/gibs/flesh.mdl";
UTIL_PrecacheSound("materials/flesh/bustflesh1.wav");
UTIL_PrecacheSound("materials/flesh/bustflesh2.wav");
break;
case Concrete:
pGibName = "models/gibs/concrete.mdl";
UTIL_PrecacheSound("materials/crete/bustcrete1.wav");
UTIL_PrecacheSound("materials/crete/bustcrete2.wav");
break;
case Rocks:
pGibName = "models/gibs/rock.mdl";
UTIL_PrecacheSound("materials/crete/bustcrete1.wav");
UTIL_PrecacheSound("materials/crete/bustcrete2.wav");
break;
case CinderBlock:
pGibName = "models/gibs/cinder.mdl";
UTIL_PrecacheSound("materials/crete/bustcrete1.wav");
UTIL_PrecacheSound("materials/crete/bustcrete2.wav");
break;
case Computer:
pGibName = "models/gibs/computer.mdl";
UTIL_PrecacheSound("materials/metal/bustmetal1.wav");
UTIL_PrecacheSound("materials/metal/bustmetal2.wav");
break;
case Glass:
case UnbreakableGlass:
pGibName = "models/gibs/glass.mdl";
UTIL_PrecacheSound("materials/glass/bustglass1.wav");
UTIL_PrecacheSound("materials/glass/bustglass2.wav");
break;
case MetalPlate:
pGibName = "models/gibs/metalplate.mdl";
UTIL_PrecacheSound("materials/metal/bustmetal1.wav");
UTIL_PrecacheSound("materials/metal/bustmetal2.wav");
break;
case Metal:
pGibName = "models/gibs/metal.mdl";
UTIL_PrecacheSound("materials/metal/bustmetal1.wav");
UTIL_PrecacheSound("materials/metal/bustmetal2.wav");
break;
case AirDuct:
pGibName = "models/gibs/vent.mdl";
UTIL_PrecacheSound("materials/metal/bustmetal1.wav");
UTIL_PrecacheSound("materials/metal/bustmetal2.wav");
break;
case CeilingTile:
pGibName = "models/gibs/ceiling.mdl";
UTIL_PrecacheSound("materials/ceil/bustceiling1.wav");
UTIL_PrecacheSound("materials/ceil/bustceiling2.wav");
break;
case Wood:
pGibName = "models/gibs/wood.mdl";
UTIL_PrecacheSound("materials/wood/bustcrate1.wav");
UTIL_PrecacheSound("materials/wood/bustcrate2.wav");
break;
case None:
default:
if(pev->health > 0)//mapmaker forget set material ?
{
DevMsg("\n======/Xash SmartFiled System/======\n\n");
DevMsg("Please set material for %s,\n", STRING(pev->classname));
DevMsg("if we want make this breakable\n");
}
break;
}
MaterialSoundPrecache( m_Material );
if(IsBreakable())
{
m_idShard = UTIL_PrecacheModel( m_iGibModel, (char*)pGibName );//precache model
if(m_iSpawnObject)UTIL_PrecacheEntity( m_iSpawnObject );
}
UTIL_PrecacheModel( pev->model );//can use *.mdl for any brush
}
void CBaseBrush :: DamageSound( void )
{
float fvol;
int pitch;
if (RANDOM_LONG(0,2)) pitch = PITCH_NORM;
else pitch = 95 + RANDOM_LONG(0,34);
fvol = RANDOM_FLOAT(m_flVolume/1.5, m_flVolume);
switch (m_Material)
{
case None: break;
case Bones:
case Flesh:
DAMAGE_SOUND(pSoundsFlesh, fvol, pitch );
break;
case CinderBlock:
case Concrete:
case Rocks:
DAMAGE_SOUND(pSoundsCrete, fvol, pitch );
break;
case Computer:
case Glass:
case UnbreakableGlass:
DAMAGE_SOUND(pSoundsGlass, fvol, pitch );
break;
case MetalPlate:
case AirDuct:
case Metal:
DAMAGE_SOUND(pSoundsMetal, fvol, pitch );
break;
case CeilingTile:
DAMAGE_SOUND(pSoundsCeil, fvol, pitch );
break;
case Wood:
DAMAGE_SOUND(pSoundsWood, fvol, pitch );
break;
default: break;
}
}
void CBaseBrush::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType )
{
switch( m_Material )//apply effects for some materials (may be extended in future)
{
case Computer:
{
if(RANDOM_LONG(0,1))UTIL_Sparks( ptr->vecEndPos );
float flVolume = RANDOM_FLOAT ( 0.7 , 1.0 );//random volume range
switch ( RANDOM_LONG(0,1) )
{
case 0: EMIT_SOUND(ENT(pev), CHAN_VOICE, "materials/spark5.wav", flVolume, ATTN_NORM); break;
case 1: EMIT_SOUND(ENT(pev), CHAN_VOICE, "materials/spark6.wav", flVolume, ATTN_NORM); break;
}
}
break;
case Glass:
{
if(pev->health == 0)//unbreakable glass
{
if( RANDOM_LONG(0,1))UTIL_Ricochet( ptr->vecEndPos, RANDOM_FLOAT(0.5,1.5) );
UTIL_DecalTrace(ptr, DECAL_BPROOF1);
}
else UTIL_DecalTrace(ptr, DECAL_GLASSBREAK1 + RANDOM_LONG(0, 2));//breakable glass
}
break;
case UnbreakableGlass:
UTIL_Ricochet( ptr->vecEndPos, RANDOM_FLOAT(0.5,1.5) );
break;
}
CBaseLogic::TraceAttack( pevAttacker, flDamage, vecDir, ptr, bitsDamageType );
}
int CBaseBrush :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType )
{
Vector vecTemp;
if ( pevAttacker == pevInflictor )
{
vecTemp = pevInflictor->origin - ( pev->absmin + ( pev->size * 0.5 ) );
if((pevAttacker->flags & FL_CLIENT) && (pev->spawnflags & SF_BREAK_CROWBAR) && (bitsDamageType & DMG_CLUB))
flDamage = pev->health; //old valve flag
}
else vecTemp = pevInflictor->origin - ( pev->absmin + ( pev->size * 0.5 ) );
if (!IsBreakable())
{
DamageSound();
return 0;
}
// Breakables take double damage from the crowbar
if ( bitsDamageType & DMG_CLUB ) flDamage *= 1.2;
else if( bitsDamageType & DMG_CRUSH ) flDamage *= 2;
else if( bitsDamageType & DMG_BLAST ) flDamage *= 3;
else if( bitsDamageType & DMG_BULLET && m_Material == Glass )flDamage *= 0.5;
else if( bitsDamageType & DMG_BULLET && m_Material == Wood )flDamage *= 0.7;
else flDamage = 0; //don't give any other damage
DmgType = bitsDamageType;//member damage type
g_vecAttackDir = vecTemp.Normalize();
pev->health -= flDamage;
if (pev->health <= 0)
{
Killed( pevAttacker, GIB_NORMAL );
Die();
return 0;
}
DamageSound();
return 1;
}
void CBaseBrush :: Blocked( CBaseEntity *pOther )
{
if(m_pParent && m_pParent->edict() && pFlags & PF_PARENTMOVE)//tell parent
m_pParent->Blocked( pOther );
if(!pOther->IsPlayer() && !pOther->IsMonster())//crash breakable
{
if(IsBreakable())TakeDamage( pev, pev, 5, DMG_CRUSH );
}
}
BOOL CBaseBrush :: IsBreakable( void )
{
return (pev->health > 0 && m_Material != UnbreakableGlass);
}
void CBaseBrush::Die( void )
{
Vector vecSpot, vecVelocity;
char cFlag = 0;
int pitch, soundbits = NULL;
float fvol;
pitch = 95 + RANDOM_LONG(0, 29);
if(pitch > 97 && pitch < 103)pitch = 100;
fvol = RANDOM_FLOAT(0.85, 1.0) + (abs(pev->health) / 100.0);
if(fvol > 1.0)fvol = 1.0;
switch (m_Material)
{
case None: break; //just in case
case Bones:
case Flesh:
if(RANDOM_LONG(0,1) )
EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "materials/flesh/bustflesh1.wav", fvol, ATTN_NORM, 0, pitch);
else EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "materials/flesh/bustflesh2.wav", fvol, ATTN_NORM, 0, pitch);
cFlag = BREAK_FLESH;
soundbits = bits_SOUND_MEAT;
break;
case CinderBlock:
case Concrete:
case Rocks:
if(RANDOM_LONG(0,1) )
EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "materials/crete/bustcrete1.wav", fvol, ATTN_NORM, 0, pitch);
else EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "materials/crete/bustcrete2.wav", fvol, ATTN_NORM, 0, pitch);
cFlag = BREAK_CONCRETE;
soundbits = bits_SOUND_WORLD;
break;
case Computer:
case Metal:
case AirDuct:
case MetalPlate:
if(RANDOM_LONG(0,1) )
EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "materials/metal/bustmetal1.wav", fvol, ATTN_NORM, 0, pitch);
else EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "materials/metal/bustmetal2.wav", fvol, ATTN_NORM, 0, pitch);
cFlag = BREAK_METAL;
soundbits = bits_SOUND_WORLD;
break;
case Glass:
if( RANDOM_LONG(0,1) )
EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "materials/glass/bustglass1.wav", fvol, ATTN_NORM, 0, pitch);
else EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "materials/glass/bustglass2.wav", fvol, ATTN_NORM, 0, pitch);
cFlag = BREAK_GLASS;
soundbits = bits_SOUND_WORLD;
break;
case Wood:
if( RANDOM_LONG(0,1) )
EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "materials/wood/bustcrate1.wav", fvol, ATTN_NORM, 0, pitch);
else EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "materials/wood/bustcrate2.wav", fvol, ATTN_NORM, 0, pitch);
cFlag = BREAK_WOOD;
soundbits = bits_SOUND_WORLD;
break;
case CeilingTile:
if( RANDOM_LONG(0,1) )
EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "materials/ceil/bustceiling1.wav",fvol, ATTN_NORM, 0, pitch);
else EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "materials/ceil/bustceiling1.wav",fvol, ATTN_NORM, 0, pitch);
cFlag = BREAK_CONCRETE;
soundbits = bits_SOUND_GARBAGE;
break;
}
if (DmgType & DMG_CLUB)//direction from crowbar
vecVelocity = g_vecAttackDir * clamp(pev->dmg * -10, -1024, 1024);
else vecVelocity = UTIL_RandomVector() * clamp(pev->dmg * 10, 1, 240);
vecSpot = pev->origin + (pev->mins + pev->maxs) * 0.5;
float time = RANDOM_FLOAT(25, 100);
if(soundbits) CSoundEnt::InsertSound(soundbits, pev->origin, 128, 0.5);//make noise for ai
SFX_MakeGibs( m_idShard, vecSpot, pev->size, vecVelocity, time, cFlag);
//what the hell does this ?
UTIL_FindBreakable( this );
// If I'm getting removed, don't fire something that could fire myself
pev->targetname = 0;
pev->solid = SOLID_NOT;
UTIL_FireTargets( pev->target, NULL, NULL, USE_TOGGLE );
pev->target = 0;
Msg("Fire!\n");
SetThink( Remove );
SetNextThink( 0.1 );
//pev->effects |= EF_NODRAW;
//pev->takedamage = DAMAGE_NO;
//make explosion
if(m_iMagnitude) UTIL_Explode( Center(), edict(), m_iMagnitude );
if(m_iSpawnObject) CBaseEntity::Create( (char *)STRING(m_iSpawnObject), Center(), pev->angles, edict() );
// Fire targets on break
//UTIL_Remove( this );
}
int CBaseBrush :: DamageDecal( int bitsDamageType )
{
if ( m_Material == Glass )
return DECAL_GLASSBREAK1 + RANDOM_LONG(0,2);
if ( m_Material == Glass && pev->health <= 0)
return DECAL_BPROOF1;
if ( m_Material == UnbreakableGlass )
return DECAL_BPROOF1;
if ( m_Material == Wood )
return DECAL_GUNSHOT1 + RANDOM_LONG(0,4);
if ( m_Material == Computer )
return DECAL_BIGSHOT1 + RANDOM_LONG(0,4);
return CBaseEntity::DamageDecal( bitsDamageType );
}
//=======================================================================
// moving breakable brushes
//=======================================================================
//material moving sounds
const char *CPushable::pPushWood[] =
{
"materials/wood/pushwood1.wav",
"materials/wood/pushwood2.wav",
"materials/wood/pushwood3.wav",
};
const char *CPushable::pPushFlesh[] =
{
"materials/flesh/pushflesh1.wav",
"materials/flesh/pushflesh2.wav",
"materials/flesh/pushflesh3.wav",
};
const char *CPushable::pPushMetal[] =
{
"materials/metal/pushmetal1.wav",
"materials/metal/pushmetal2.wav",
"materials/metal/pushmetal3.wav",
};
const char *CPushable::pPushCrete[] =
{
"materials/crete/pushstone1.wav",
"materials/crete/pushstone2.wav",
"materials/crete/pushstone3.wav",
};
const char *CPushable::pPushGlass[] =
{
"materials/glass/pushglass1.wav",
"materials/glass/pushglass2.wav",
"materials/glass/pushglass3.wav",
};
const char *CPushable::pPushCeil[] =
{
"materials/ceil/ceiling1.wav",
"materials/ceil/ceiling2.wav",
"materials/ceil/ceiling3.wav",
};
void CPushable :: Spawn( void )
{
Precache();
pev->movetype = MOVETYPE_PUSHSTEP;
pev->solid = SOLID_BBOX;
UTIL_SetModel( ENT(pev), pev->model );
UTIL_SetSize( pev, pev->absmin, pev->absmax );
if (!m_flVolume)m_flVolume = 1.0;//just enable full volume
//breacable brush (if mapmaker just set material - just play material sound)
if(m_Material != None)
pev->takedamage = DAMAGE_YES;
else pev->takedamage = DAMAGE_NO;
pev->speed = MatFrictionTable( m_Material );
SetBits( pev->flags, FL_FLOAT );
pev->origin.z += 1; // Pick up off of the floor
UTIL_SetOrigin( this, pev->origin );
// Multiply by area of the box's cross-section (assume 1000 units^3 standard volume)
pev->skin = MatByoancyTable( pev, m_Material );
pev->frags = 0;
}
void CPushable :: Precache( void )
{
CBaseBrush::Precache();
PushSoundPrecache( m_Material );
}
const char **CPushable::PushSoundList( Materials precacheMaterial, int &soundCount )
{
const char **pSoundList = NULL;
switch ( precacheMaterial )
{
case None:
soundCount = 0;
break;
case Bones:
case Flesh:
pSoundList = pPushFlesh;
soundCount = ARRAYSIZE(pPushFlesh);
break;
case CinderBlock:
case Concrete:
case Rocks:
pSoundList = pPushCrete;
soundCount = ARRAYSIZE(pPushCrete);
break;
case Computer:
case Glass:
pSoundList = pPushGlass;
soundCount = ARRAYSIZE(pPushGlass);
break;
case MetalPlate:
case AirDuct:
case Metal:
pSoundList = pPushMetal;
soundCount = ARRAYSIZE(pPushMetal);
break;
case CeilingTile:
pSoundList = pPushCeil;
soundCount = ARRAYSIZE(pPushCeil);
break;
case Wood:
pSoundList = pPushWood;
soundCount = ARRAYSIZE(pPushWood);
break;
default:
soundCount = 0;
break;
}
return pSoundList;
}
void CPushable::PushSoundPrecache( Materials precacheMaterial )
{
const char **pPushList;
int i, soundCount = 0;
pPushList = PushSoundList( precacheMaterial, soundCount );
for ( i = 0; i < soundCount; i++ ) UTIL_PrecacheSound( (char *)pPushList[i] );
}
int CPushable::PlayPushSound( edict_t *pEdict, Materials soundMaterial, float volume )
{
const char **pPushList;
int soundCount = 0;
int m_lastPushSnd = 0;
pPushList = PushSoundList( soundMaterial, soundCount );
if ( soundCount )
{
m_lastPushSnd = RANDOM_LONG(0, soundCount-1);
EMIT_SOUND( pEdict, CHAN_WEAPON, pPushList[ m_lastPushSnd ], volume, 1.0 );
}
return m_lastPushSnd;
}
void CPushable::StopPushSound( edict_t *pEdict, Materials soundMaterial, int m_lastPushSnd )
{
const char **pPushList;
int soundCount = 0;
pPushList = PushSoundList( soundMaterial, soundCount );
if ( soundCount ) STOP_SOUND( pEdict, CHAN_WEAPON, pPushList[ m_lastPushSnd ] );
}
void CPushable :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
if(useType == USE_SHOWINFO)
{
DEBUGHEAD;
}
else if ( pActivator && pActivator->IsPlayer() && pActivator->pev->velocity != g_vecZero )
Move( pActivator, 0 );
}
void CPushable :: Touch( CBaseEntity *pOther )
{
if ( pOther == g_pWorld ) return;
Move( pOther, 1 );
}
void CPushable :: Move( CBaseEntity *pOther, int push )
{
entvars_t* pevToucher = pOther->pev;
int playerTouch = 0;
// Is entity standing on this pushable ?
if ( FBitSet(pevToucher->flags, FL_ONGROUND) && pevToucher->groundentity && VARS(pevToucher->groundentity) == pev )
{
// Only push if floating
if ( pev->waterlevel > 0 && pev->watertype & MASK_WATER )
pev->velocity.z += pevToucher->velocity.z * 0.1;
return;
}
if ( pOther->IsPlayer() )
{
// Don't push unless the player is pushing forward and NOT use (pull)
if ( push && !(pevToucher->button & (IN_FORWARD|IN_USE)) ) return;
playerTouch = 1;
}
float factor;
if ( playerTouch )
{
// Don't push away from jumping/falling players unless in water
if( !(pevToucher->flags & FL_ONGROUND) )
{
if( pev->waterlevel < 1 || !(pev->watertype & MASK_WATER) )
return;
else factor = 0.1;
}
else factor = 1;
}
else factor = 0.25;
if (!push) factor = factor*0.5;
pev->velocity.x += pevToucher->velocity.x * factor;
pev->velocity.y += pevToucher->velocity.y * factor;
float length = sqrt( pev->velocity.x * pev->velocity.x + pev->velocity.y * pev->velocity.y );
if ( push && (length > MaxSpeed()) )
{
pev->velocity.x = (pev->velocity.x * MaxSpeed() / length );
pev->velocity.y = (pev->velocity.y * MaxSpeed() / length );
}
if ( playerTouch )
{
pevToucher->velocity.x = pev->velocity.x;
pevToucher->velocity.y = pev->velocity.y;
if ( (gpGlobals->time - pev->frags) > 0.7 )
{
pev->frags = gpGlobals->time;
if ( length > 0 && FBitSet(pev->flags, FL_ONGROUND) )
m_lastSound = PlayPushSound( edict(), m_Material, m_flVolume );
else StopPushSound( edict(), m_Material, m_lastSound );
}
}
}
LINK_ENTITY_TO_CLASS( func_pushable, CPushable );

207
server/ents/basebrush.h Normal file
View File

@ -0,0 +1,207 @@
//=======================================================================
// Copyright (C) Shambler Team 2004
// basebrush.h - base for all brush
// entities.
//=======================================================================
#ifndef BASEBRUSH_H
#define BASEBRUSH_H
//ANY BRUSH MAY BE SET MATERIAL
typedef enum {
Glass = 0, //glass.mdl
Wood, //wood.mdl
Metal, //metal.mdl
Flesh, //flesh.mdl
CinderBlock, //cinder.mdl
CeilingTile, //ceiling.mdl
Computer, //computer.mdl
UnbreakableGlass, //galss.mdl
Rocks, //rock.mdl
Bones, //bones.mdl
Concrete, //concrete.mdl
MetalPlate, //metalplate.mdl
AirDuct, //vent.mdl
None, //very strange pos
LastMaterial, //just in case
}Materials;
//gibs physics
typedef enum {
Bounce = 0, // bounce at collision
Noclip, // no collisions
Sticky, // sticky physic
Fly, // fly
Toss, // toss
WalkStep, // monsters walk
} Physics;
static float MatFrictionTable( Materials mat)
{
float friction = 0;
switch(mat)
{
case CinderBlock:
case Concrete:
case Rocks: friction = 300; break;
case Glass: friction = 200; break;
case MetalPlate:
case Metal: friction = 60; break;
case Wood: friction = 250; break;
case None:
default: friction = 100; break;
}
return friction;
}
static float MatByoancyTable( entvars_t *pev, Materials mat)
{
float byoancy = 0;
switch(mat)
{
case CinderBlock:
case Concrete:
case Rocks: byoancy = 0; break;
case Glass: byoancy = 5; break;
case MetalPlate:
case Metal: byoancy = 1; break;
case Wood: byoancy = 30; break;
case None:
default: byoancy = 100; break;
}
return ( byoancy * (pev->maxs.x - pev->mins.x) * (pev->maxs.y - pev->mins.y) ) * 0.0005;
}
static float MatMassTable( entvars_t *pev, Materials mat)
{
float mass = 0;
switch(mat)
{
case CinderBlock:
case Concrete:
case Rocks: mass = 2; break;
case Glass: mass = 1.2; break;
case MetalPlate:
case Metal: mass = 1.5; break;
case Wood: mass = 0.8; break;
case None:
default: mass = 1; break;
}
return (pev->size.Length() * mass);
}
static float MatVolume( Materials mat )
{
float volume = 0;
switch(mat)
{
case None: volume = 0.9; //unbreakable
case Bones: //bones.mdl
case Flesh: volume = 0.2; break; //flesh.mdl
case CinderBlock: //cinder.mdl
case Concrete: //concrete.mdl
case Rocks: volume = 0.25; break; //rock.mdl
case Computer: //computer.mdl
case Glass: volume = 0.3; break; //glass.mdl
case MetalPlate: //metalplate.mdl
case Metal: //metal.mdl
case AirDuct: volume = 0.1; break; //vent.mdl
case CeilingTile: //ceiling.mdl
case Wood: volume = 0.15; break; //wood.mdl
default: volume = 0.2; break;
}
return volume;
}
extern DLL_GLOBAL Vector g_vecAttackDir;
#define SetMoveDone( a ) m_pfnCallWhenMoveDone = static_cast <void (CBaseMover::*)(void)> (a)
class CBaseBrush : public CBaseLogic
{
public:
void Spawn( void );
void Precache( void );
void KeyValue( KeyValueData* pkvd);
void Blocked( CBaseEntity *pOther );
virtual void AxisDir( void );
virtual int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType );
void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType );
int DamageDecal( int bitsDamageType );
BOOL IsBreakable( void );
void EXPORT Die( void );
virtual CBaseBrush *MyBrushPointer( void ) { return this; }
virtual int ObjectCaps( void ) { return (CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION); }
static TYPEDESCRIPTION m_SaveData[];
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static void MaterialSoundPrecache( Materials precacheMaterial );
static void PlayRandomSound( edict_t *pEdict, Materials soundMaterial, float volume );
static const char **MaterialSoundList( Materials precacheMaterial, int &soundCount );
static const char *pSoundsWood[];
static const char *pSoundsFlesh[];
static const char *pSoundsMetal[];
static const char *pSoundsCrete[];
static const char *pSoundsGlass[];
static const char *pSoundsCeil[];
//spawnobject name
static const char *pSpawnObjects[];
void DamageSound( void );
Materials m_Material;
CBasePlayer* m_pController; //player pointer
Vector m_vecPlayerPos; //player position
int m_iMoveSound; //move sound or preset
int m_iStartSound; //start sound or preset
int m_iStopSound; //stop sound or preset
int m_idShard; //index of gibs
int m_iMagnitude; //explosion magnitude
int m_iSpawnObject; //spawnobject name
int m_iGibModel; //custom gib model
int DmgType; //temp container for right calculate damage
float m_flVolume; //moving brushes has volume of move sound
float m_flBlockedTime; //don't save this
int m_pitch; //sound pitch
};
class CPushable : public CBaseBrush
{
public:
void Spawn ( void );
void Precache( void );
void Touch ( CBaseEntity *pOther );
void Move( CBaseEntity *pMover, int push );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
virtual int ObjectCaps( void ) { return (CBaseBrush :: ObjectCaps() | FCAP_CONTINUOUS_USE); }
virtual BOOL IsPushable( void ) { return TRUE; }
inline float MaxSpeed( void ) { return pev->speed; }
static const char *pPushWood[];
static const char *pPushFlesh[];
static const char *pPushMetal[];
static const char *pPushCrete[];
static const char *pPushGlass[];
static const char *pPushCeil[]; //fixme
static void PushSoundPrecache( Materials precacheMaterial );
static int PlayPushSound( edict_t *pEdict, Materials soundMaterial, float volume );
static void StopPushSound( edict_t *pEdict, Materials soundMaterial, int m_lastPushSnd );
static const char **PushSoundList( Materials precacheMaterial, int &soundCount );
int m_lastSound; // no need to save/restore, just keeps the same sound from playing twice in a row
};
#include "basemover.h"
#endif // BASEBRUSH_H

959
server/ents/baseentity.cpp Normal file
View File

@ -0,0 +1,959 @@
//=======================================================================
// Copyright (C) Shambler Team 2005
// baseentity.cpp - base class
//=======================================================================
#include "extdll.h"
#include "utils.h"
#include "cbase.h"
#include "saverestore.h"
#include "client.h"
#include "nodes.h"
#include "decals.h"
#include "gamerules.h"
#include "game.h"
#include "damage.h"
#include "defaults.h"
#include "bullets.h"
extern Vector VecBModelOrigin( entvars_t* pevBModel );
extern DLL_GLOBAL Vector g_vecAttackDir;
extern void SetObjectCollisionBox( entvars_t *pev );
extern BOOL NewLevel;
extern CGraph WorldGraph;
//=======================================================================
// decent mechanisms
//=======================================================================
void CBaseEntity::DontThink( void )
{
m_fNextThink = 0;
if (m_pParent == NULL && m_pChild == NULL)
{
pev->nextthink = 0;
m_fPevNextThink = 0;
}
}
void CBaseEntity :: SetEternalThink( void )
{
if (pev->movetype == MOVETYPE_PUSH)
{
pev->nextthink = pev->ltime + 1E6;
m_fPevNextThink = pev->nextthink;
}
CBaseEntity *pChild;
for (pChild = m_pChild; pChild != NULL; pChild = pChild->m_pNextChild)
pChild->SetEternalThink( );
}
void CBaseEntity :: SetNextThink( float delay, BOOL correctSpeed )
{
if (m_pParent || m_pChild )
{
if (pev->movetype == MOVETYPE_PUSH)
m_fNextThink = pev->ltime + delay;
else m_fNextThink = gpGlobals->time + delay;
SetEternalThink( );
UTIL_MarkChild( this, correctSpeed, FALSE );
}
else
{
// set nextthink as normal.
if (pev->movetype == MOVETYPE_PUSH)pev->nextthink = pev->ltime + delay;
else pev->nextthink = gpGlobals->time + delay;
m_fPevNextThink = m_fNextThink = pev->nextthink;
}
}
void CBaseEntity :: AbsoluteNextThink( float time, BOOL correctSpeed )
{
if (m_pParent || m_pChild)
{
m_fNextThink = time;
SetEternalThink( );
UTIL_MarkChild( this, correctSpeed, FALSE );
}
else
{
// set nextthink as normal.
pev->nextthink = time;
m_fPevNextThink = m_fNextThink = pev->nextthink;
}
}
void CBaseEntity :: ThinkCorrection( void )
{
if (pev->nextthink != m_fPevNextThink)
{
m_fNextThink += pev->nextthink - m_fPevNextThink;
m_fPevNextThink = pev->nextthink;
}
}
//=======================================================================
// set parent (void ) dinamically link parents
//=======================================================================
void CBaseEntity :: SetParent( int m_iNewParent, int m_iAttachment )
{
if(!m_iNewParent) //unlink entity from chain
{
ResetParent();
return;//disable
}
CBaseEntity* pParent;
if(!m_iAttachment) //try to extract aiment from name
{
char *name = (char*)STRING(m_iNewParent);
for (char *c = name; *c; c++)
{
if (*c == '.')
{
m_iAttachment = atoi(c+1);
name[strlen(name)-2] = 0;
pParent = UTIL_FindEntityByTargetname( NULL, name);
SetParent( pParent, m_iAttachment);
return;
}
}
}
pParent = UTIL_FindEntityByTargetname( NULL, STRING(m_iNewParent));
SetParent( pParent, m_iAttachment );
}
//=======================================================================
// set parent main function
//=======================================================================
void CBaseEntity :: SetParent( CBaseEntity* pParent, int m_iAttachment )
{
m_pParent = pParent;
if(!m_pParent)
{
Msg("=========/Xash Parent System Info:/=========\n");
if(pev->targetname) Msg("Warning! Not found parent for %s with name %s\n", STRING(pev->classname), STRING(pev->targetname) );
else Msg("Warning! Not found parent for %s\n", STRING(pev->classname) );
SHIFT;
ResetParent();//lose parent or not found parent
return;
}
//check for himself parent
if(m_pParent == this)
{
ALERT(at_console, "=========/Xash Parent System Info:/=========\n");
if(pev->targetname) Msg( "ERROR! %s with name %s has illegal parent\n", STRING(pev->classname), STRING(pev->targetname) );
else Msg( "ERROR! %s has illegal parent\n", STRING(pev->classname) );
SHIFT;
ResetParent();//clear parent
return;
}
CBaseEntity *pCurChild = m_pParent->m_pChild;
while (pCurChild) //check that this entity isn't already in the list of children
{
if (pCurChild == this) break;
pCurChild = pCurChild->m_pNextChild;
}
if(!pCurChild)
{
m_pNextChild = m_pParent->m_pChild; // may be null: that's fine by me.
m_pParent->m_pChild = this;
if(m_iAttachment)
{
if(pev->flags & FL_POINTENTITY || pev->flags & FL_MONSTER)
{
pev->skin = ENTINDEX(m_pParent->edict());
pev->body = m_iAttachment;
pev->aiment = m_pParent->edict();
pev->movetype = MOVETYPE_FOLLOW;
}
else //error
{
ALERT(at_console, "=========/Xash Parent System Info:/=========\n");
if(pev->targetname) Msg("ERROR! %s with name %s not following with aiment %d!(yet)\n", STRING(pev->classname), STRING(pev->targetname), m_iAttachment );
else Msg("ERROR! %s not following with aiment %d!(yet)\n", STRING(pev->classname), m_iAttachment );
SHIFT;
}
return;
}
else//appllayed to origin
{
if (pev->movetype == MOVETYPE_NONE)
{
if (pev->solid == SOLID_BSP)
pev->movetype = MOVETYPE_PUSH;
else pev->movetype = MOVETYPE_NOCLIP;
SetBits (pFlags, PF_MOVENONE);//member movetype
}
if(m_pParent->pev->movetype == MOVETYPE_WALK)//parent is walking monster?
{
SetBits (pFlags, PF_POSTORG);//copy pos from parent every frame
pev->solid = SOLID_NOT;//set non solid
}
pParentOrigin = m_pParent->pev->origin;
pParentAngles = m_pParent->pev->angles;
}
if (m_pParent->m_vecSpawnOffset != g_vecZero)
{
UTIL_AssignOrigin(this, pev->origin + m_pParent->m_vecSpawnOffset);
m_vecSpawnOffset = m_vecSpawnOffset + m_pParent->m_vecSpawnOffset;
}
OffsetOrigin = pev->origin - pParentOrigin;
OffsetAngles = pev->angles - pParentAngles;
if((m_pParent->pFlags & PF_ANGULAR && OffsetOrigin != g_vecZero) || m_pParent->pev->flags & FL_POINTENTITY)
{
SetBits (pFlags, PF_POSTORG);//magic stuff
//GetPInfo( this );
}
if(g_serveractive)//maybe parent is moving ?
{
pev->velocity += m_pParent->pev->velocity;
pev->avelocity += m_pParent->pev->avelocity;
}
}
}
//=======================================================================
// reset parent (void ) dinamically unlink parents
//=======================================================================
void CBaseEntity :: ResetParent( void )
{
if(pFlags & PF_MOVENONE)//this entity was static e.g. func_wall
{
ClearBits (pFlags, PF_MOVENONE);
pev->movetype = MOVETYPE_NONE;
}
if ( !g_pWorld )return; //???
CBaseEntity* pTemp;
for (pTemp = g_pWorld; pTemp->m_pLinkList != NULL; pTemp = pTemp->m_pLinkList)
{
if (this == pTemp->m_pLinkList)
{
pTemp->m_pLinkList = this->m_pLinkList;//save pointer
this->m_pLinkList = NULL;
break;
}
}
if (m_pParent)
{
pTemp = m_pParent->m_pChild;
if (pTemp == this)m_pParent->m_pChild = this->m_pNextChild;
else
{
while (pTemp->m_pNextChild)
{
if (pTemp->m_pNextChild == this)
{
pTemp->m_pNextChild = this->m_pNextChild;
break;
}
pTemp = pTemp->m_pNextChild;
}
}
}
if (m_pNextChild)
{
CBaseEntity* pCur = m_pChild;
CBaseEntity* pNext;
while (pCur != NULL)
{
pNext = pCur->m_pNextChild;
//bring children to a stop
UTIL_SetChildVelocity (pCur, g_vecZero, MAX_CHILDS);
UTIL_SetChildAvelocity(pCur, g_vecZero, MAX_CHILDS);
pCur->m_pParent = NULL;
pCur->m_pNextChild = NULL;
pCur = pNext;
}
}
}
//=======================================================================
// setup physics (execute once at spawn)
//=======================================================================
void CBaseEntity :: SetupPhysics( void )
{
//rebuild all parents
if(pFlags & PF_LINKCHILD) LinkChild( this );
if(m_physinit) return;
SetParent(); //set all parents
m_physinit = true;
PostSpawn();//post spawn
}
void CBaseEntity :: RestorePhysics( void )
{
if(m_iParent) SetParent();
}
void CBaseEntity :: ClearPointers( void )
{
m_pChild = NULL;
m_pNextChild = NULL;
m_pLinkList = NULL;
}
//=========================================================
// FVisible - returns true if a line can be traced from
// the caller's eyes to the target vector
//=========================================================
BOOL CBaseEntity :: FVisible ( const Vector &vecOrigin )
{
TraceResult tr;
Vector vecLookerOrigin;
vecLookerOrigin = EyePosition();//look through the caller's 'eyes'
UTIL_TraceLine(vecLookerOrigin, vecOrigin, ignore_monsters, ignore_glass, ENT(pev)/*pentIgnore*/, &tr);
if (tr.flFraction != 1.0)
return FALSE;
return TRUE;// line of sight is valid.
}
//=========================================================
// FVisible - returns true if a line can be traced from
// the caller's eyes to the target
//=========================================================
BOOL CBaseEntity :: FVisible ( CBaseEntity *pEntity )
{
TraceResult tr;
Vector vecLookerOrigin;
Vector vecTargetOrigin;
if( FBitSet( pEntity->pev->flags, FL_NOTARGET ))
return FALSE;
// don't look through water
if ((pev->waterlevel != 3 && pEntity->pev->waterlevel == 3) || (pev->waterlevel == 3 && pEntity->pev->waterlevel == 0))
return FALSE;
vecLookerOrigin = pev->origin + pev->view_ofs;//look through the caller's 'eyes'
vecTargetOrigin = pEntity->EyePosition();
UTIL_TraceLine(vecLookerOrigin, vecTargetOrigin, ignore_monsters, ignore_glass, ENT(pev)/*pentIgnore*/, &tr);
if (tr.flFraction != 1.0 && tr.pHit != ENT(pEntity->pev))
return FALSE;
return TRUE;
}
//=======================================================================
// fire bullets
//=======================================================================
void CBaseEntity::FireBullets(ULONG cShots, Vector vecSrc, Vector vecDirShooting, Vector vecSpread, float flDistance, int iBulletType, int iTracerFreq, int iDamage, entvars_t *pevAttacker )
{
static int tracerCount;
int tracer;
TraceResult tr;
Vector vecRight = gpGlobals->v_right;
Vector vecUp = gpGlobals->v_up;
if ( pevAttacker == NULL )
pevAttacker = pev; // the default attacker is ourselves
ClearMultiDamage();
gMultiDamage.type = DMG_BULLET | DMG_NEVERGIB;
for (ULONG iShot = 1; iShot <= cShots; iShot++)
{
// get circular gaussian spread
float x, y, z;
do {
x = RANDOM_FLOAT(-0.5,0.5) + RANDOM_FLOAT(-0.5,0.5);
y = RANDOM_FLOAT(-0.5,0.5) + RANDOM_FLOAT(-0.5,0.5);
z = x*x+y*y;
} while (z > 1);
Vector vecDir = vecDirShooting +
x * vecSpread.x * vecRight +
y * vecSpread.y * vecUp;
Vector vecEnd;
vecEnd = vecSrc + vecDir * flDistance;
UTIL_TraceLine(vecSrc, vecEnd, dont_ignore_monsters, ENT(pev)/*pentIgnore*/, &tr);
tracer = 0;
if (iTracerFreq != 0 && (tracerCount++ % iTracerFreq) == 0)
{
Vector vecTracerSrc;
if ( IsPlayer() )
{// adjust tracer position for player
vecTracerSrc = vecSrc + Vector ( 0 , 0 , -4 ) + gpGlobals->v_right * 2 + gpGlobals->v_forward * 16;
}
else
{
vecTracerSrc = vecSrc;
}
if ( iTracerFreq != 1 ) // guns that always trace also always decal
tracer = 1;
switch( iBulletType )
{
case BULLET_MP5:
case BULLET_9MM:
case BULLET_12MM:
case BULLET_357:
case BULLET_556:
case BULLET_762:
case BULLET_BUCKSHOT:
default:
MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, vecTracerSrc );
WRITE_BYTE( TE_TRACER );
WRITE_COORD( vecTracerSrc.x );
WRITE_COORD( vecTracerSrc.y );
WRITE_COORD( vecTracerSrc.z );
WRITE_COORD( tr.vecEndPos.x );
WRITE_COORD( tr.vecEndPos.y );
WRITE_COORD( tr.vecEndPos.z );
MESSAGE_END();
break;
}
}
// do damage, paint decals
if (tr.flFraction != 1.0)
{
CBaseEntity *pEntity = CBaseEntity::Instance(tr.pHit);
if ( iDamage )
{
pEntity->TraceAttack(pevAttacker, iDamage, vecDir, &tr, DMG_BULLET | ((iDamage > 16) ? DMG_ALWAYSGIB : DMG_NEVERGIB) );
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
}
else switch(iBulletType)
{
default:
case BULLET_9MM:
pEntity->TraceAttack(pevAttacker, _9MM_DMG, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
break;
case BULLET_MP5:
pEntity->TraceAttack(pevAttacker, _MP5_DMG, vecDir, &tr, DMG_BULLET);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
break;
case BULLET_12MM:
pEntity->TraceAttack(pevAttacker, _12MM_DMG, vecDir, &tr, DMG_BULLET);
if ( !tracer )
{
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
}
break;
case BULLET_556:
pEntity->TraceAttack(pevAttacker, _556_DMG, vecDir, &tr, DMG_BULLET);
if ( !tracer )
{
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
}
break;
case BULLET_762:
pEntity->TraceAttack(pevAttacker, _762_DMG, vecDir, &tr, DMG_BULLET);
if ( !tracer )
{
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
}
break;
case BULLET_BUCKSHOT:
pEntity->TraceAttack(pevAttacker, BUCKSHOT_DMG, vecDir, &tr, DMG_BULLET);
if ( !tracer )
{
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
}
break;
case BULLET_357:
pEntity->TraceAttack(pevAttacker, _357_DMG, vecDir, &tr, DMG_BULLET);
if ( !tracer )
{
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
DecalGunshot( &tr, iBulletType );
}
break;
case BULLET_NONE: // FIX
pEntity->TraceAttack(pevAttacker, 50, vecDir, &tr, DMG_CLUB);
TEXTURETYPE_PlaySound(&tr, vecSrc, vecEnd, iBulletType);
// only decal glass
if ( !FNullEnt(tr.pHit) && VARS(tr.pHit)->rendermode != 0)
{
UTIL_DecalTrace( &tr, DECAL_GLASSBREAK1 + RANDOM_LONG(0,2) );
}
break;
}
}
// make bullet trails
UTIL_BubbleTrail( vecSrc, tr.vecEndPos, (flDistance * tr.flFraction) / 64.0 );
}
ApplyMultiDamage(pev, pevAttacker);
}
//=======================================================================
// traceing operations
//=======================================================================
void CBaseEntity :: TraceBleed( float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType )
{
if (BloodColor() == DONT_BLEED)
return;
if (flDamage == 0)
return;
if (!(bitsDamageType & (DMG_CRUSH | DMG_BULLET | DMG_SLASH | DMG_BLAST | DMG_CLUB | DMG_MORTAR)))
return;
// make blood decal on the wall!
TraceResult Bloodtr;
Vector vecTraceDir;
float flNoise;
int cCount;
int i;
if (flDamage < 10)
{
flNoise = 0.1;
cCount = 1;
}
else if (flDamage < 25)
{
flNoise = 0.2;
cCount = 2;
}
else
{
flNoise = 0.3;
cCount = 4;
}
for ( i = 0 ; i < cCount ; i++ )
{
vecTraceDir = vecDir * -1;// trace in the opposite direction the shot came from (the direction the shot is going)
vecTraceDir.x += RANDOM_FLOAT( -flNoise, flNoise );
vecTraceDir.y += RANDOM_FLOAT( -flNoise, flNoise );
vecTraceDir.z += RANDOM_FLOAT( -flNoise, flNoise );
UTIL_TraceLine( ptr->vecEndPos, ptr->vecEndPos + vecTraceDir * -172, ignore_monsters, ENT(pev), &Bloodtr);
if ( Bloodtr.flFraction != 1.0 )
{
UTIL_BloodDecalTrace( &Bloodtr, BloodColor() );
}
}
}
void CBaseEntity::TraceAttack(entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType)
{
Vector vecOrigin = ptr->vecEndPos - vecDir * 4;
if ( pev->takedamage )
{
AddMultiDamage( pevAttacker, this, flDamage, bitsDamageType );
int blood = BloodColor();
if ( blood != DONT_BLEED )
{
SpawnBlood(vecOrigin, blood, flDamage);// a little surface blood.
TraceBleed( flDamage, vecDir, ptr, bitsDamageType );
}
}
}
//=======================================================================
// take damage\health
//=======================================================================
int CBaseEntity :: TakeHealth( float flHealth, int bitsDamageType )
{
if(!pev->takedamage) return 0;
if ( pev->health >= pev->max_health ) return 0;
pev->health += flHealth;
pev->health = min(pev->health, pev->max_health);
return 1;
}
int CBaseEntity :: TakeArmor( float flArmor, int suit )
{
if(!pev->takedamage) return 0;
if (pev->armorvalue >= MAX_NORMAL_BATTERY) return 0;
pev->armorvalue += flArmor;
pev->armorvalue = min(pev->armorvalue, MAX_NORMAL_BATTERY);
return 1;
}
int CBaseEntity :: TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType )
{
Vector vecTemp;
if (!pev->takedamage) return 0;
// if Attacker == Inflictor, the attack was a melee or other instant-hit attack.
// (that is, no actual entity projectile was involved in the attack so use the shooter's origin).
if ( pevAttacker == pevInflictor )
{
vecTemp = pevInflictor->origin - ( VecBModelOrigin(pev) );
}
else // an actual missile was involved.
{
vecTemp = pevInflictor->origin - ( VecBModelOrigin(pev) );
}
// this global is still used for glass and other non-monster killables, along with decals.
g_vecAttackDir = vecTemp.Normalize();
// save damage based on the target's armor level
// figure momentum add (don't let hurt brushes or other triggers move player)
if ((!FNullEnt(pevInflictor)) && (pev->movetype == MOVETYPE_WALK || pev->movetype == MOVETYPE_STEP) && (pevAttacker->solid != SOLID_TRIGGER) )
{
Vector vecDir = pev->origin - (pevInflictor->absmin + pevInflictor->absmax) * 0.5;
vecDir = vecDir.Normalize();
float flForce = flDamage * ((32 * 32 * 72.0) / (pev->size.x * pev->size.y * pev->size.z)) * 5;
if (flForce > 1000.0) flForce = 1000.0;
pev->velocity = pev->velocity + vecDir * flForce;
}
// do the damage
pev->health -= flDamage;
if (pev->health <= 0)
{
Killed( pevAttacker, GIB_NORMAL );
return 0;
}
return 1;
}
int CBaseEntity :: DamageDecal( int bitsDamageType )
{
if ( pev->rendermode == kRenderTransAlpha )
return -1;
if ( pev->rendermode != kRenderNormal )
return DECAL_BPROOF1;
return DECAL_GUNSHOT1 + RANDOM_LONG(0, 4);
}
//=======================================================================
// killed/create operations
//=======================================================================
CBaseEntity * CBaseEntity::Create( char *szName, const Vector &vecOrigin, const Vector &vecAngles, edict_t *pentOwner )
{
edict_t *pent;
int istr = ALLOC_STRING(szName);
CBaseEntity *pEntity;
// check for virtual entities
if( FUNCTION_FROM_NAME( szName ) != 0 )
{
pent = CREATE_NAMED_ENTITY( istr );
if ( FNullEnt( pent )) return NULL;
}
else if(!strncmp( szName, "weapon_", 7))
{
//may be this a weapon_generic entity?
pent = CREATE_NAMED_ENTITY(MAKE_STRING("weapon_generic"));
if ( FNullEnt( pent )) return NULL; // this never gonna called anymore. just in case
pent->v.netname = istr;
}
else if(!strncmp( szName, "item_", 5))
{
//may be this a weapon_generic entity?
pent = CREATE_NAMED_ENTITY(MAKE_STRING("item_generic"));
if ( FNullEnt( pent )) return NULL; // this never gonna called anymore. just in case
pent->v.netname = istr;
}
else if(!strncmp( szName, "ammo_", 5))
{
//may be this a weapon_generic entity?
pent = CREATE_NAMED_ENTITY(MAKE_STRING("item_generic"));
if ( FNullEnt( pent )) return NULL; // this never gonna called anymore. just in case
pent->v.netname = istr;
}
else //unknown error
{
Msg("can't create %s\n", szName );
return NULL;
}
pEntity = Instance( pent );
pEntity->pev->owner = pentOwner;
pEntity->pev->origin = vecOrigin;
pEntity->pev->angles = vecAngles;
DispatchSpawn( pEntity->edict() );
return pEntity;
}
CBaseEntity * CBaseEntity::CreateGib( char *szName, char *szModel )
{
edict_t *pent;
CBaseEntity *pEntity;
string_t model = MAKE_STRING( szModel );
int istr = ALLOC_STRING(szName);
//check for virtual entities
if(GetProcAddress( GetModuleHandle("server"), szName ))
{
pent = CREATE_NAMED_ENTITY(istr);
if ( FNullEnt( pent )) return NULL;
}
else if(!strncmp( szName, "weapon_", 7))
{
//may be this a weapon_generic entity?
pent = CREATE_NAMED_ENTITY(MAKE_STRING("weapon_generic"));
if ( FNullEnt( pent )) return NULL; //this never gonna called anymore. just in case
pent->v.netname = istr;
}
else if(!strncmp( szName, "item_", 5))
{
//may be this a weapon_generic entity?
pent = CREATE_NAMED_ENTITY(MAKE_STRING("item_generic"));
if ( FNullEnt( pent )) return NULL;//this never gonna called anymore. just in case
pent->v.netname = istr;
}
else if(!strncmp( szName, "ammo_", 5))
{
//may be this a weapon_generic entity?
pent = CREATE_NAMED_ENTITY(MAKE_STRING("item_generic"));
if ( FNullEnt( pent )) return NULL;//this never gonna called anymore. just in case
pent->v.netname = istr;
}
else //unknown error
{
Msg("can't create %s\n", szName );
return NULL;
}
pEntity = Instance( pent );
DispatchSpawn( pEntity->edict() );
if(!FStringNull( model ))
{
UTIL_SetModel( pEntity->edict(), szModel );
Msg("szModel %s\n", szModel );
}
return pEntity;
}
void CBaseEntity :: Killed( entvars_t *pevAttacker, int iGib )
{
pev->takedamage = DAMAGE_NO;
pev->deadflag = DEAD_DEAD;
UTIL_Remove( this );
}
void CBaseEntity::UpdateOnRemove( void )
{
ResetParent();
if ( FBitSet( pev->flags, FL_GRAPHED ) )
{
for (int i = 0 ; i < WorldGraph.m_cLinks ; i++ )
{
if ( WorldGraph.m_pLinkPool [ i ].m_pLinkEnt == pev )
WorldGraph.m_pLinkPool [ i ].m_pLinkEnt = NULL;
}
}
if ( pev->globalname ) gGlobalState.EntitySetState( pev->globalname, GLOBAL_DEAD );
}
//=======================================================================
// three methods of remove entity
//=======================================================================
void CBaseEntity :: Remove( void )
{
UpdateOnRemove();
if (pev->health > 0)pev->health = 0;
REMOVE_ENTITY(ENT(pev));
}
void CBaseEntity :: PVSRemove( void )
{
if ( FNullEnt( FIND_CLIENT_IN_PVS( edict()))) SetThink( Remove );
SetNextThink( 0.1 );
}
void CBaseEntity :: Fadeout( void )
{
if (pev->rendermode == kRenderNormal)
{
pev->renderamt = 255;
pev->rendermode = kRenderTransTexture;
}
pev->solid = SOLID_NOT;
pev->avelocity = g_vecZero;
if ( pev->renderamt > 7 )
{
pev->renderamt -= 7;
SetNextThink( 0.1 );
}
else
{
pev->renderamt = 0;
SetNextThink( 0.2 );
SetThink( Remove );
}
}
//=======================================================================
// global save\restore data
//=======================================================================
TYPEDESCRIPTION CBaseEntity::m_SaveData[] =
{
DEFINE_FIELD( CBaseEntity, m_pGoalEnt, FIELD_CLASSPTR ),
//parent system saves
DEFINE_FIELD( CBaseEntity, m_iParent, FIELD_STRING ),
DEFINE_FIELD( CBaseEntity, m_pParent, FIELD_CLASSPTR ),
DEFINE_FIELD( CBaseEntity, m_pChild, FIELD_CLASSPTR ),
DEFINE_FIELD( CBaseEntity, m_pNextChild, FIELD_CLASSPTR ),
DEFINE_FIELD( CBaseEntity, OffsetOrigin, FIELD_VECTOR ),
DEFINE_FIELD( CBaseEntity, OffsetAngles, FIELD_VECTOR ),
DEFINE_FIELD( CBaseEntity, pFlags, FIELD_INTEGER ),
DEFINE_FIELD( CBaseEntity, m_physinit, FIELD_BOOLEAN ),
//local child coordinates
DEFINE_FIELD( CBaseEntity, PostOrigin, FIELD_VECTOR ),
DEFINE_FIELD( CBaseEntity, PostAngles, FIELD_VECTOR ),
DEFINE_FIELD( CBaseEntity, PostVelocity, FIELD_VECTOR ),
DEFINE_FIELD( CBaseEntity, PostAvelocity, FIELD_VECTOR ),
//think time
DEFINE_FIELD( CBaseEntity, m_fNextThink, FIELD_TIME ),
DEFINE_FIELD( CBaseEntity, m_fPevNextThink, FIELD_TIME ),
DEFINE_FIELD( CBaseEntity, m_iStyle, FIELD_INTEGER ),
DEFINE_FIELD( CBaseEntity, m_iAcessLevel, FIELD_INTEGER ),
DEFINE_FIELD( CBaseEntity, m_pfnThink, FIELD_FUNCTION ),
DEFINE_FIELD( CBaseEntity, m_pfnTouch, FIELD_FUNCTION ),
DEFINE_FIELD( CBaseEntity, m_pfnUse, FIELD_FUNCTION ),
DEFINE_FIELD( CBaseEntity, m_pfnBlocked, FIELD_FUNCTION ),
};
int CBaseEntity::Save( CSave &save )
{
ThinkCorrection();
if ( save.WriteEntVars( "ENTVARS", pev ) )
{
if (pev->targetname)
return save.WriteFields( STRING(pev->targetname), "BASE", this, m_SaveData, ARRAYSIZE(m_SaveData) );
else return save.WriteFields( STRING(pev->classname ), "BASE", this, m_SaveData, ARRAYSIZE(m_SaveData) );
}
return 0;
}
int CBaseEntity::Restore( CRestore &restore )
{
int status;
status = restore.ReadEntVars( "ENTVARS", pev );
if ( status ) status = restore.ReadFields( "BASE", this, m_SaveData, ARRAYSIZE(m_SaveData) );
if ( pev->modelindex != 0 && !FStringNull(pev->model) )
{
Vector mins = pev->mins, maxs = pev->maxs; // Set model is about to destroy these
UTIL_PrecacheModel( pev->model );
UTIL_SetModel(ENT(pev), pev->model);
UTIL_SetSize(pev, mins, maxs);
}
return status;
}
//=======================================================================
// collisoin boxes
//=======================================================================
void CBaseEntity::SetObjectCollisionBox( void )
{
::SetObjectCollisionBox( pev );
}
int CBaseEntity :: Intersects( CBaseEntity *pOther )
{
if ( pOther->pev->absmin.x > pev->absmax.x ||
pOther->pev->absmin.y > pev->absmax.y ||
pOther->pev->absmin.z > pev->absmax.z ||
pOther->pev->absmax.x < pev->absmin.x ||
pOther->pev->absmax.y < pev->absmin.y ||
pOther->pev->absmax.z < pev->absmin.z )
return FALSE;
return TRUE;
}
//=======================================================================
// Dormant operations
//=======================================================================
void CBaseEntity :: MakeDormant( void )
{
SetBits( pev->flags, FL_DORMANT );
pev->solid = SOLID_NOT;
pev->movetype = MOVETYPE_NONE;
SetBits( pev->effects, EF_NODRAW );
DontThink();
UTIL_SetOrigin( this, pev->origin );
}
int CBaseEntity :: IsDormant( void )
{
return FBitSet( pev->flags, FL_DORMANT );
}
BOOL CBaseEntity :: IsInWorld( void )
{
if (pev->origin.x >= MAP_HALFSIZE) return FALSE;
if (pev->origin.y >= MAP_HALFSIZE) return FALSE;
if (pev->origin.z >= MAP_HALFSIZE) return FALSE;
if (pev->origin.x <= -MAP_HALFSIZE) return FALSE;
if (pev->origin.y <= -MAP_HALFSIZE) return FALSE;
if (pev->origin.z <= -MAP_HALFSIZE) return FALSE;
if (pev->velocity.x >= MAX_VELOCITY) return FALSE;
if (pev->velocity.y >= MAX_VELOCITY) return FALSE;
if (pev->velocity.z >= MAX_VELOCITY) return FALSE;
if (pev->velocity.x <= -MAX_VELOCITY) return FALSE;
if (pev->velocity.y <= -MAX_VELOCITY) return FALSE;
if (pev->velocity.z <= -MAX_VELOCITY) return FALSE;
return TRUE;
}

324
server/ents/baseentity.h Normal file
View File

@ -0,0 +1,324 @@
//=======================================================================
// Copyright (C) XashXT Group 2006
//=======================================================================
#ifndef BASEENTITY_H
#define BASEENTITY_H
class CBaseEntity
{
public:
// Constructor. Set engine to use C/C++ callback functions
// pointers to engine data
entvars_t *pev; // Don't need to save/restore this pointer, the engine resets it
// path corners
CBaseEntity *m_pGoalEnt;// path corner we are heading towards
CBaseEntity *m_pLink;// used for temporary link-list operations.
float m_fNextThink;
float m_fPevNextThink;
float flTravelTime; //time to moving brushes
int m_iStyle;
int m_iAcessLevel;//acess level for retinal sacners
//===================================================================================================
// Xash BaseEntity
//===================================================================================================
// Gets the interface to the collideable representation of the entity
virtual void SetModelIndex( int index );
virtual int GetModelIndex( void ) const;
// Returns a CBaseAnimating if the entity is derived from CBaseAnimating.
virtual CBaseAnimating* GetBaseAnimating() { return NULL; }
void SetName( string_t newTarget );
void SetParent( string_t newParent, CBaseEntity *pActivator );
virtual void ChangeCamera( string_t newcamera ) {}
public:
unsigned char m_iParentAttachment; // 0 if we're relative to the parent's absorigin and absangles.
EHANDLE m_hParent;
EHANDLE m_hMoveParent;
EHANDLE m_hMoveChild;
EHANDLE m_hMovePeer;
//===================================================================================================
// Xash Parent System 0.2 beta
//===================================================================================================
Vector PostOrigin; //child postorigin
Vector PostAngles; //child postangles
Vector PostVelocity; //child postvelocity
Vector PostAvelocity; //child postavelocity
Vector OffsetOrigin; //spawn offset origin
Vector OffsetAngles; //spawn offset angles
Vector pParentAngles; //temp container
Vector pParentOrigin; //temp container
Vector m_vecSpawnOffset; //temp container
int pFlags; //xash flags
BOOL m_physinit; //physics initializator
virtual void SetParent ( void ) { SetParent(m_iParent); }
void SetParent ( int m_iNewParent, int m_iAttachment = 0 );
void SetParent ( CBaseEntity* pParent, int m_iAttachment = 0 );
void ResetParent( void );
virtual void SetupPhysics( void );//setup parent system and physics
CBaseEntity *m_pParent; //pointer to parent entity
CBaseEntity *m_pChild; //pointer to children(may be this)
CBaseEntity *m_pNextChild; //link to next chlidren
CBaseEntity *m_pLinkList; //list of linked childrens
string_t m_iParent;//name of parent
virtual void SetNextThink( float delay, BOOL correctSpeed = FALSE );
virtual void AbsoluteNextThink( float time, BOOL correctSpeed = FALSE );
void SetEternalThink( void );
void DontThink( void );
virtual void ThinkCorrection( void );
//phys metods
virtual void SetAngularImpulse( float impulse ){}
virtual void SetLinearImpulse( float impulse ) {}
// initialization functions
virtual void Spawn( void ) { return; }
virtual void Precache( void ) { return; }
virtual void KeyValue( KeyValueData* pkvd)
{
if (FStrEq(pkvd->szKeyName, "parent"))
{
m_iParent = ALLOC_STRING(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "style"))
{
m_iStyle = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else pkvd->fHandled = FALSE;
}
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
virtual int ObjectCaps( void ) { return m_pParent?m_pParent->ObjectCaps()&FCAP_ACROSS_TRANSITION:FCAP_ACROSS_TRANSITION; }
virtual void Activate( void ) {}
virtual void PostActivate( void ) {}
virtual void PostSpawn( void ) {}
virtual void DesiredAction( void ) {}
virtual void StartMessage( CBasePlayer *pPlayer ) {}
// Setup the object->object collision box (pev->mins / pev->maxs is the object->world collision box)
virtual void SetObjectCollisionBox( void );
void UTIL_AutoSetSize( void )//automatically set collision box
{
dstudiohdr_t *pstudiohdr;
pstudiohdr = (dstudiohdr_t*)GET_MODEL_PTR( ENT(pev) );
if (pstudiohdr == NULL)
{
ALERT(at_console,"Unable to fetch model pointer!\n");
return;
}
dstudioseqdesc_t *pseqdesc;
pseqdesc = (dstudioseqdesc_t *)((byte *)pstudiohdr + pstudiohdr->seqindex);
UTIL_SetSize(pev,pseqdesc[ pev->sequence ].bbmin,pseqdesc[ pev->sequence ].bbmax);
}
// Classify - returns the type of group (e.g., "alien monster", or "human military" so that monsters
// on the same side won't attack each other, even if they have different classnames.
virtual int Classify ( void ) { return CLASS_NONE; };
virtual void DeathNotice ( entvars_t *pevChild ) {}// monster maker children use this to tell the monster maker that they have died.
// global concept of "entities with states", so that state_watchers and
// mastership (mastery? masterhood?) can work universally.
virtual STATE GetState ( void ) { return STATE_OFF; };
// For team-specific doors in multiplayer, etc: a master's state depends on who wants to know.
virtual STATE GetState ( CBaseEntity* pEnt ) { return GetState(); };
static TYPEDESCRIPTION m_SaveData[];
virtual void ClearPointers( void );
virtual void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType);
virtual int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType );
virtual int TakeHealth( float flHealth, int bitsDamageType );
virtual int TakeArmor( float flArmor, int suit = 0 );
virtual int TakeItem( int iItem ) { return 0; }
virtual void Killed( entvars_t *pevAttacker, int iGib );
virtual int BloodColor( void ) { return DONT_BLEED; }
virtual void TraceBleed( float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType );
virtual CBaseMonster *MyMonsterPointer( void ) { return NULL;}
virtual CSquadMonster *MySquadMonsterPointer( void ) { return NULL;}
virtual CBaseBrush *MyBrushPointer( void ) { return NULL; }
virtual void AddPoints( int score, BOOL bAllowNegativeScore ) {}
virtual void AddPointsToTeam( int score, BOOL bAllowNegativeScore ) {}
virtual BOOL AddPlayerItem( CBasePlayerWeapon *pItem ) { return 0; }
virtual BOOL RemovePlayerItem( CBasePlayerWeapon *pItem ) { return 0; }
virtual int GiveAmmo( int iAmount, char *szName, int iMax ) { return -1; };
virtual float GetDelay( void ) { return 0; }
virtual int IsMoving( void ) { return pev->velocity != g_vecZero; }
virtual void RestorePhysics( void );
virtual void OverrideReset( void ) {}
virtual int DamageDecal( int bitsDamageType );
// This is ONLY used by the node graph to test movement through a door
virtual void SetToggleState( int state ) {}
virtual void StartSneaking( void ) {}
virtual void StopSneaking( void ) {}
virtual BOOL OnControls( entvars_t *pev ) { return FALSE; }
virtual BOOL IsSneaking( void ) { return FALSE; }
virtual BOOL IsAlive( void ) { return (pev->deadflag == DEAD_NO) && pev->health > 0; }
virtual BOOL IsBSPModel( void ) { return pev->solid == SOLID_BSP || pev->movetype == MOVETYPE_PUSHSTEP; }
virtual BOOL ReflectGauss( void ) { return ( IsBSPModel() && !pev->takedamage ); }
virtual BOOL HasTarget( string_t targetname ) { return FStrEq(STRING(targetname), STRING(pev->targetname) ); }
virtual BOOL IsInWorld( void );
virtual BOOL IsPlayer( void ) { return FALSE; }
virtual BOOL IsPushable( void ) { return FALSE; }
virtual BOOL IsMonster( void ) { return (pev->flags & FL_MONSTER ? TRUE : FALSE); }
virtual BOOL IsNetClient( void ) { return FALSE; }
virtual BOOL IsFuncScreen( void ) { return FALSE; }
virtual const char *TeamID( void ) { return ""; }
virtual CBaseEntity *GetNext( void ) { return NULL; }
virtual CBaseEntity *GetPrev( void ) { return NULL; }
// fundamental callbacks
void (CBaseEntity ::*m_pfnThink)(void);
void (CBaseEntity ::*m_pfnTouch)( CBaseEntity *pOther );
void (CBaseEntity ::*m_pfnUse)( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
void (CBaseEntity ::*m_pfnBlocked)( CBaseEntity *pOther );
virtual void Think( void ) { if (m_pfnThink) (this->*m_pfnThink)(); };
virtual void Touch( CBaseEntity *pOther ) { if (m_pfnTouch) (this->*m_pfnTouch)( pOther ); };
virtual void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
if (m_pfnUse) (this->*m_pfnUse)( pActivator, pCaller, useType, value );
}
virtual void Blocked( CBaseEntity *pOther ) { if (m_pfnBlocked) (this->*m_pfnBlocked)( pOther ); };
// allow engine to allocate instance data
void *operator new( size_t stAllocateBlock, entvars_t *pev )
{
return (void *)ALLOC_PRIVATE(ENT(pev), stAllocateBlock);
};
// don't use this.
#if _MSC_VER >= 1200 // only build this code if MSVC++ 6.0 or higher
void operator delete(void *pMem, entvars_t *pev)
{
pev->flags |= FL_KILLME;
};
#endif
void UpdateOnRemove( void );
// common member functions
void EXPORT Remove( void );
void EXPORT Fadeout( void );
void EXPORT PVSRemove( void );
void EXPORT SUB_CallUseToggle( void ) { this->Use( this, this, USE_TOGGLE, 0 ); }
void FireBullets( ULONG cShots, Vector vecSrc, Vector vecDirShooting, Vector vecSpread, float flDistance, int iBulletType, int iTracerFreq = 4, int iDamage = 0, entvars_t *pevAttacker = NULL );
virtual CBaseEntity *Respawn( void ) { return NULL; }
// Do the bounding boxes of these two intersect?
int Intersects( CBaseEntity *pOther );
void MakeDormant( void );
int IsDormant( void );
BOOL IsLockedByMaster( void ) { return FALSE; }
static CBaseEntity *Instance( edict_t *pent )
{
if ( !pent ) pent = ENT(0);
CBaseEntity *pEnt = (CBaseEntity *)GET_PRIVATE(pent);
return pEnt;
}
static CBaseEntity *Instance( entvars_t *pev ) { return Instance( ENT( pev ) ); }
static CBaseEntity *Instance( int eoffset) { return Instance( ENT( eoffset) ); }
CBaseMonster *GetMonsterPointer( entvars_t *pevMonster )
{
CBaseEntity *pEntity = Instance( pevMonster );
if ( pEntity ) return pEntity->MyMonsterPointer();
return NULL;
}
CBaseMonster *GetMonsterPointer( edict_t *pentMonster )
{
CBaseEntity *pEntity = Instance( pentMonster );
if ( pEntity ) return pEntity->MyMonsterPointer();
return NULL;
}
// Ugly code to lookup all functions to make sure they are exported when set.
#ifdef _DEBUG
void FunctionCheck( void *pFunction, char *name )
{
if (pFunction && !NAME_FOR_FUNCTION((unsigned long)(pFunction)) )
ALERT( at_error, "No EXPORT: %s:%s (%08lx)\n", STRING(pev->classname), name, (unsigned long)pFunction );
}
BASEPTR ThinkSet( BASEPTR func, char *name )
{
m_pfnThink = func;
FunctionCheck( (void *)*((int *)((char *)this + ( offsetof(CBaseEntity,m_pfnThink)))), name );
return func;
}
ENTITYFUNCPTR TouchSet( ENTITYFUNCPTR func, char *name )
{
m_pfnTouch = func;
FunctionCheck( (void *)*((int *)((char *)this + ( offsetof(CBaseEntity,m_pfnTouch)))), name );
return func;
}
USEPTR UseSet( USEPTR func, char *name )
{
m_pfnUse = func;
FunctionCheck( (void *)*((int *)((char *)this + ( offsetof(CBaseEntity,m_pfnUse)))), name );
return func;
}
ENTITYFUNCPTR BlockedSet( ENTITYFUNCPTR func, char *name )
{
m_pfnBlocked = func;
FunctionCheck( (void *)*((int *)((char *)this + ( offsetof(CBaseEntity,m_pfnBlocked)))), name );
return func;
}
#endif
// used by monsters that are created by the MonsterMaker
virtual void UpdateOwner( void ) { return; };
static CBaseEntity *Create( char *szName, const Vector &vecOrigin, const Vector &vecAngles, edict_t *pentOwner = NULL );
static CBaseEntity *CBaseEntity::CreateGib( char *szName, char *szModel );
virtual BOOL FBecomeProne( void ) {return FALSE;};
edict_t *edict() { return ENT( pev ); };
EOFFSET eoffset( ) { return OFFSET( pev ); };
int entindex( ) { return ENTINDEX( edict() ); };
virtual Vector Center( ) { return (pev->absmax + pev->absmin) * 0.5; }; // center point of entity
virtual Vector EyePosition( ) { return pev->origin + pev->view_ofs; }; // position of eyes
virtual Vector EarPosition( ) { return pev->origin + pev->view_ofs; }; // position of ears
virtual Vector BodyTarget( const Vector &posSrc ) { return Center( ); }; // position to shoot at
virtual int Illumination( ) { return GETENTITYILLUM( ENT( pev ) ); };
virtual BOOL FVisible ( CBaseEntity *pEntity );
virtual BOOL FVisible ( const Vector &vecOrigin );
};
inline void CBaseEntity::SetModelIndex( int index )
{
pev->modelindex = index;
}
inline int CBaseEntity::GetModelIndex( void ) const
{
return pev->modelindex;
}
#endif //BASEENTITY_H

2361
server/ents/basefunc.cpp Normal file

File diff suppressed because it is too large Load Diff

2900
server/ents/basefx.cpp Normal file

File diff suppressed because it is too large Load Diff

229
server/ents/baseinfo.cpp Normal file
View File

@ -0,0 +1,229 @@
//=======================================================================
// Copyright (C) Shambler Team 2004
// baseinfo.cpp - point info entities.
// e.g. info_target
//=======================================================================
#include "extdll.h"
#include "utils.h"
#include "cbase.h"
#include "player.h"
//=======================================================================
// info_target (target entity)
//=======================================================================
class CInfoTarget : public CPointEntity
{
public:
void Spawn( void )
{
pev->solid = SOLID_NOT;
UTIL_SetModel(ENT(pev),"models/common/null.mdl");
UTIL_SetSize(pev, g_vecZero, g_vecZero);
SetBits( pev->flags, FL_POINTENTITY );
}
};
void CBaseDMStart::KeyValue( KeyValueData *pkvd )
{
if (FStrEq(pkvd->szKeyName, "master"))
{
pev->netname = ALLOC_STRING(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else
CPointEntity::KeyValue( pkvd );
}
STATE CBaseDMStart::GetState( CBaseEntity *pEntity )
{
if (UTIL_IsMasterTriggered( pev->netname, pEntity ))
return STATE_ON;
else return STATE_OFF;
}
//=========================================================
// static infodecal
//=========================================================
class CDecal : public CBaseEntity
{
public:
void KeyValue( KeyValueData *pkvd )
{
if (FStrEq(pkvd->szKeyName, "texture"))
{
pev->skin = DECAL_INDEX( pkvd->szValue );
if ( pev->skin >= 0 ) return;
Msg( "Can't find decal %s\n", pkvd->szValue );
}
}
void PostSpawn( void ) { if(FStringNull(pev->targetname))MakeDecal(); }
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { MakeDecal(); }
void MakeDecal( void )
{
if ( pev->skin < 0 ) { REMOVE_ENTITY(ENT(pev)); return; }
TraceResult trace;
int entityIndex, modelIndex;
UTIL_TraceLine( pev->origin - Vector(5,5,5), pev->origin + Vector(5,5,5), ignore_monsters, ENT(pev), &trace );
entityIndex = (short)ENTINDEX(trace.pHit);
if ( entityIndex ) modelIndex = (int)VARS(trace.pHit)->modelindex;
else modelIndex = 0;
if(FStringNull(pev->targetname)) g_engfuncs.pfnStaticDecal( pev->origin, (int)pev->skin, entityIndex, modelIndex );
else
{
MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY);
WRITE_BYTE( TE_BSPDECAL );
WRITE_COORD( pev->origin.x );
WRITE_COORD( pev->origin.y );
WRITE_COORD( pev->origin.z );
WRITE_SHORT( (int)pev->skin );
WRITE_SHORT( entityIndex );
if(entityIndex) WRITE_SHORT( modelIndex );
MESSAGE_END();
}
SetThink( Remove );
SetNextThink( 0.3 );
}
};
//=========================================================
// Multiplayer intermission spots.
//=========================================================
class CInfoIntermission:public CPointEntity
{
void Spawn( void );
void Think( void );
void PostActivate( void );
CBaseEntity *pTarget;
};
void CInfoIntermission::Spawn( void )
{
pev->solid = SOLID_NOT;
pev->movetype = MOVETYPE_NOCLIP;
UTIL_SetOrigin( this, pev->origin );
UTIL_SetModel( ENT( pev ), "sprites/null.spr" );
SetNextThink( 1 );// let targets spawn!
}
void CInfoIntermission::PostActivate( void )
{
pTarget = UTIL_FindEntityByTargetname( NULL, STRING( pev->target ));
if( !pev->speed ) pev->speed = 100;
}
void CInfoIntermission::Think ( void )
{
if( pTarget )
{
UTIL_WatchTarget( this, pTarget );
SetNextThink( 0 );
}
}
//====================================================================
// multisource
//====================================================================
TYPEDESCRIPTION CMultiSource::m_SaveData[] =
{
DEFINE_ARRAY( CMultiSource, m_rgEntities, FIELD_EHANDLE, MAX_MULTI_TARGETS ),
DEFINE_ARRAY( CMultiSource, m_rgTriggered, FIELD_INTEGER, MAX_MULTI_TARGETS ),
DEFINE_FIELD( CMultiSource, m_iTotal, FIELD_INTEGER ),
}; IMPLEMENT_SAVERESTORE( CMultiSource, CBaseLogic );
LINK_ENTITY_TO_CLASS( multisource, CMultiSource );
void CMultiSource::Spawn()
{
pev->solid = SOLID_NOT;
pev->movetype = MOVETYPE_NONE;
SetNextThink( 0.1 );
pev->spawnflags |= SF_START_ON;
SetThink( Register );
}
void CMultiSource::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
int i = 0;
// Find the entity in our list
while (i < m_iTotal) if ( m_rgEntities[i++] == pCaller ) break;
// if we didn't find it, report error and leave
if (i > m_iTotal) return;
STATE s = GetState();
m_rgTriggered[i-1] ^= 1;
if ( s == GetState()) return;
if ( s == STATE_OFF )
{
USE_TYPE useType = USE_TOGGLE;
if ( m_globalstate ) useType = USE_ON;
UTIL_FireTargets( pev->target, NULL, this, useType, value );
UTIL_FireTargets( m_iszKillTarget, NULL, this, USE_REMOVE );
}
}
STATE CMultiSource::GetState( void )
{
// Is everything triggered?
int i = 0;
// Still initializing?
if ( pev->spawnflags & SF_START_ON ) return STATE_OFF;
while (i < m_iTotal)
{
if (m_rgTriggered[i] == 0) break;
i++;
}
if (i == m_iTotal)
{
if ( !m_globalstate || gGlobalState.EntityGetState( m_globalstate ) == GLOBAL_ON )
return STATE_ON;
}
return STATE_OFF;
}
void CMultiSource::Register(void)
{
m_iTotal = 0;
memset( m_rgEntities, 0, MAX_MULTI_TARGETS * sizeof(EHANDLE) );
SetThink(NULL);
// search for all entities which target this multisource (pev->targetname)
CBaseEntity *pTarget = UTIL_FindEntityByTarget( NULL, STRING(pev->targetname) );
while (pTarget && (m_iTotal < MAX_MULTI_TARGETS))
{
m_rgEntities[m_iTotal++] = pTarget;
pTarget = UTIL_FindEntityByTarget( pTarget, STRING(pev->targetname));
}
pTarget = UTIL_FindEntityByClassname(NULL, "multi_manager");
while (pTarget && (m_iTotal < MAX_MULTI_TARGETS))
{
if ( pTarget->HasTarget(pev->targetname) ) m_rgEntities[m_iTotal++] = pTarget;
pTarget = UTIL_FindEntityByClassname( pTarget, "multi_manager" );
}
pev->spawnflags &= ~SF_START_ON;
}
LINK_ENTITY_TO_CLASS( infodecal, CDecal );
LINK_ENTITY_TO_CLASS( info_target, CInfoTarget );
LINK_ENTITY_TO_CLASS( info_teleport_destination, CPointEntity );
LINK_ENTITY_TO_CLASS( info_null, CNullEntity);
LINK_ENTITY_TO_CLASS( info_texlights, CNullEntity);
LINK_ENTITY_TO_CLASS( info_compile_parameters, CNullEntity);
LINK_ENTITY_TO_CLASS( info_intermission, CInfoIntermission );
LINK_ENTITY_TO_CLASS( info_player_deathmatch, CBaseDMStart);
LINK_ENTITY_TO_CLASS( info_player_start, CPointEntity);
LINK_ENTITY_TO_CLASS( info_landmark, CPointEntity);

44
server/ents/baseinfo.h Normal file
View File

@ -0,0 +1,44 @@
//=======================================================================
// Copyright (C) XashXT Group 2006
//=======================================================================
#ifndef BASEINFO_H
#define BASEINFO_H
class CPointEntity : public CBaseEntity
{
public:
void Spawn( void ){ pev->solid = SOLID_NOT; }
virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
};
class CNullEntity : public CBaseEntity
{
public:
void Spawn( void ){ REMOVE_ENTITY(ENT(pev)); }
};
class CBaseDMStart : public CPointEntity
{
public:
void KeyValue( KeyValueData *pkvd );
STATE GetState( CBaseEntity *pEntity );
private:
};
class CLaserSpot : public CBaseEntity//laser spot for different weapons
{
void Spawn( void );
void Precache( void );
int ObjectCaps( void ) { return FCAP_DONT_SAVE; }
public:
void Suspend( float flSuspendTime );
void Update( CBasePlayer *m_pPlayer );
void EXPORT Revive( void );
void Killed( void ){ UTIL_Remove( this ); }
static CLaserSpot *CreateSpot( void );
};
#endif //BASEINFO_H

628
server/ents/baseitem.cpp Normal file
View File

@ -0,0 +1,628 @@
//=======================================================================
// Copyright (C) Shambler Team 2004
// item_.cpp - items entities: suit,
// helmet, lighter, etc
//=======================================================================
#include "extdll.h"
#include "utils.h"
#include "cbase.h"
#include "saverestore.h"
#include "baseweapon.h"
#include "player.h"
#include "gamerules.h"
#include "defaults.h"
extern int gEvilImpulse101;
extern int gmsgItemPickup;
//***********************************************************
// main functions ()
//***********************************************************
void CItem::Spawn( void )
{
Precache();
pev->movetype = MOVETYPE_TOSS;
pev->solid = SOLID_BBOX;
UTIL_SetOrigin( this, pev->origin );
UTIL_SetSize(pev, g_vecZero, g_vecZero );
SetTouch( ItemTouch );
SetThink( ItemFall );
UTIL_SetModel(ENT(pev), pev->model, (char *)Model() );
SetNextThink( 0.1 );
}
void CItem::Precache( void )
{
UTIL_PrecacheModel( pev->model, (char *)Model() );
UTIL_PrecacheSound((char *)PickSound());
UTIL_PrecacheSound((char *)FallSound());
}
void CItem::ItemTouch( CBaseEntity *pOther )
{
//removed this limitation for monsters
if ( !pOther->IsPlayer() ) return;
CBasePlayer *pPlayer = (CBasePlayer *)pOther;
if (!UTIL_IsMasterTriggered(m_sMaster, pPlayer)) return;
if (pPlayer->pev->deadflag != DEAD_NO) return;
if (AddItem( pPlayer ))
{
UTIL_FireTargets( pev->target, pOther, this, USE_TOGGLE );
SetTouch( NULL );
if (IsItem() && gmsgItemPickup)
{
//show icon for item
MESSAGE_BEGIN( MSG_ONE, gmsgItemPickup, NULL, pPlayer->pev );
WRITE_STRING( STRING(pev->classname) );
MESSAGE_END();
}
//play pickup sound
EMIT_SOUND( pPlayer->edict(), CHAN_ITEM, (char *)PickSound(), 1, ATTN_NORM );
// tell the owner item was taken
if (!FNullEnt( pev->owner )) pev->owner->v.impulse = 0;
//enable respawn in multiplayer
if ( g_pGameRules->IsMultiplayer()) Respawn();
else UTIL_Remove( this );
}
else if (gEvilImpulse101) UTIL_Remove( this );
}
void CItem::ItemFall ( void )
{
SetNextThink( 0.1 );
if ( pev->flags & FL_ONGROUND )
{
// clatter if we have an owner (i.e., dropped by someone)
// don't clatter if the item is waiting to respawn (if it's waiting, it is invisible!)
if ( !FNullEnt( pev->owner ))
{
EMIT_SOUND(ENT(pev), CHAN_ITEM, (char *)FallSound(), 1, ATTN_NORM);
ItemOnGround(); //do somewhat if needed
}
// lie flat
pev->angles.x = 0;
pev->angles.z = 0;
pev->solid = SOLID_TRIGGER;
if (IsAmmo()) UTIL_SetSize(pev, Vector(-8, -8, -8), Vector(8, 8, 8));
if (IsItem()) UTIL_SetSize ( pev, Vector ( -8, -8, 0 ), Vector ( 8, 8, 8 ) );
else UTIL_AutoSetSize();
UTIL_SetOrigin( this, pev->origin );// link into world.
SetTouch( ItemTouch );
SetThink (NULL);
}
}
CBaseEntity* CItem::Respawn( void )
{
SetTouch( NULL );
pev->effects |= EF_NODRAW;
SetThink( Materialize );
AbsoluteNextThink( ItemRespawnTime( this ) );
return this;
}
float CItem::ItemRespawnTime( CItem *pItem )
{
//makes different time to respawn for weapons and items
float flRespawnTime;
if (IsAmmo()) flRespawnTime = RESPAWN_TIME_30SEC;
if (IsItem()) flRespawnTime = RESPAWN_TIME_120SEC;
return gpGlobals->time + flRespawnTime;
}
void CItem::Materialize( void )
{
if ( pev->effects & EF_NODRAW )
{
// changing from invisible state to visible.
pev->effects &= ~EF_NODRAW;
pev->renderfx = kRenderFxGlowShell;
pev->renderamt = 40;
pev->frags = 0;
pev->rendercolor.x = RANDOM_LONG(25, 255);
pev->rendercolor.y = RANDOM_LONG(25, 255);
pev->rendercolor.z = RANDOM_LONG(25, 255);
pev->scale = 0.01;
SetNextThink (0.001);
}
if( pev->scale > 1.2 ) pev->frags = 1;
if ( pev->frags == 1 )
{ //set effects for respawn item
if(pev->scale > 1.0) pev->scale -= 0.05;
else
{
pev->renderfx = kRenderFxNone;
pev->frags = 0;
SetTouch( ItemTouch );
EMIT_SOUND_DYN( ENT(pev), CHAN_WEAPON, "items/respawn.wav", 1, ATTN_NORM, 0, 150 );
SetThink( NULL );
DontThink();
}
}
else pev->scale += 0.05;
SetNextThink (0.001);
}
//***********************************************************
// generic item
//***********************************************************
class CGenericItem : public CItem
{
const char *Model( void ){ return "models/w_adrenaline.mdl"; }
BOOL AddItem( CBaseEntity *pOther )
{
CBasePlayer *pPlayer = (CBasePlayer *)pOther;
if (pPlayer->pev->deadflag != DEAD_NO) return FALSE;
if (pOther->GiveAmmo( AMMO_GLOCKCLIP_GIVE, "9mm", _9MM_MAX_CARRY ) != -1)
{
EMIT_SOUND(ENT(pev), CHAN_ITEM, "weapons/glock/clip_in.wav", 1, ATTN_NORM);
return TRUE;
}
MESSAGE_BEGIN( MSG_ONE, gmsgItemPickup, NULL, pPlayer->pev );
WRITE_STRING( STRING(pev->classname) );
MESSAGE_END();
return FALSE;
}
};
LINK_ENTITY_TO_CLASS( item_generic, CGenericItem );
//***********************************************************
// items
//***********************************************************
class CItemSuit : public CItem
{
const char *Model( void ){ return "models/w_suit.mdl"; }
BOOL AddItem( CBaseEntity *pOther )
{
CBasePlayer *pPlayer = (CBasePlayer *)pOther;
if ( pPlayer->pev->deadflag != DEAD_NO ) return FALSE;
if ( pPlayer->m_iHideHUD & ITEM_SUIT )
return FALSE;
if ( pev->spawnflags & 1 )//SF_SUIT_SHORTLOGON
EMIT_SOUND_SUIT(pPlayer->edict(), "!HEV_A0"); // short version of suit logon,
else EMIT_SOUND_SUIT(pPlayer->edict(), "!HEV_AAx"); // long version of suit logon
pPlayer->m_iHideHUD |= ITEM_SUIT;
return TRUE;
}
};
LINK_ENTITY_TO_CLASS(item_suit, CItemSuit);
class CItemLongJump : public CItem
{
const char *Model( void ){ return "models/w_longjump.mdl"; }
const char *PickSound( void ){ return "buttons/bell1.wav"; }
BOOL AddItem( CBaseEntity *pOther )
{
CBasePlayer *pPlayer = (CBasePlayer *)pOther;
if ( pPlayer->m_iHideHUD & ITEM_SUIT && !pPlayer->m_fLongJump)
{
pPlayer->m_fLongJump = TRUE;// player now has longjump module
EMIT_SOUND_SUIT( pPlayer->edict(), "!HEV_A1" );
return TRUE;
}
return FALSE;
}
};
LINK_ENTITY_TO_CLASS( item_longjump, CItemLongJump );
class CItemBattery : public CItem
{
const char *Model( void ){ return "models/w_battery.mdl"; }
const char *PickSound( void ){ return "items/gunpickup2.wav"; }
BOOL AddItem( CBaseEntity *pOther ) { return pOther->TakeArmor( BATTERY_CHARGE, TRUE ); }
};
LINK_ENTITY_TO_CLASS(item_battery, CItemBattery);
class CHealthKit : public CItem
{
const char *Model( void ){ return "models/items/w_medkit.mdl"; }
const char *PickSound( void ){ return "items/smallmedkit1.wav"; }
BOOL AddItem( CBaseEntity *pOther ) { return pOther->TakeHealth( MEDKIT_CAP, DMG_GENERIC ); }
};
LINK_ENTITY_TO_CLASS( item_healthkit, CHealthKit );
class CItemSoda : public CItem
{
public:
const char *Model( void ){ return "models/can.mdl"; }
const char *FallSound( void ){ return "weapons/g_bounce3.wav"; }
BOOL AddItem( CBaseEntity *pOther ) { pOther->TakeHealth( pev->skin * 1, DMG_GENERIC ); return 1; }
};
LINK_ENTITY_TO_CLASS( item_sodacan, CItemSoda );
class CItemSecurity : public CItem
{
const char *Model( void ){ return "models/w_security.mdl"; }
const char *PickSound( void ){ return "items/gunpickup2.wav"; }
BOOL AddItem( CBaseEntity *pOther ) { return TRUE; }
};
LINK_ENTITY_TO_CLASS(item_security, CItemSecurity);
class CItemArmorVest : public CItem
{
const char *Model( void ){ return "models/w_vest.mdl"; }
const char *PickSound( void ){ return "items/gunpickup2.wav"; }
BOOL AddItem( CBaseEntity *pOther ) { return pOther->TakeArmor( 60 ); }
};
LINK_ENTITY_TO_CLASS(item_armorvest, CItemArmorVest);
class CItemHelmet : public CItem
{
const char *Model( void ){ return "models/w_helmet.mdl"; }
const char *PickSound( void ){ return "items/gunpickup2.wav"; }
BOOL AddItem( CBaseEntity *pOther ) { return pOther->TakeArmor( 40 ); }
};
LINK_ENTITY_TO_CLASS(item_helmet, CItemHelmet);
//***********************************************************
// ammo
//***********************************************************
class CGlockAmmo : public CItem
{
const char *Model( void ){ return "models/w_9mmclip.mdl"; }
const char *PickSound( void ){ return "weapons/glock/clip_in.wav"; }
BOOL AddItem( CBaseEntity *pOther ) { return pOther->GiveAmmo( AMMO_GLOCKCLIP_GIVE, "9mm", 250 ); }
};
LINK_ENTITY_TO_CLASS( ammo_9mmclip, CGlockAmmo );
LINK_ENTITY_TO_CLASS( ammo_glockclip, CGlockAmmo );
class CPythonAmmo : public CItem
{
const char *Model( void ){ return "models/w_357ammobox.mdl"; }
const char *PickSound( void ){ return "weapons/glock/clip_in.wav"; }
BOOL AddItem( CBaseEntity *pOther ) { return pOther->GiveAmmo( AMMO_357BOX_GIVE, "357", 21 ); }
};
LINK_ENTITY_TO_CLASS( ammo_357, CPythonAmmo );
class CSniperAmmo : public CItem
{
const char *Model( void ){ return "models/w_m40a1clip.mdl"; }
const char *PickSound( void ){ return "weapons/glock/clip_in.wav"; }
BOOL AddItem( CBaseEntity *pOther ) { return pOther->GiveAmmo( AMMO_357BOX_GIVE, "762", 21 ); }
};
LINK_ENTITY_TO_CLASS( ammo_762, CSniperAmmo );
class CRpgAmmo : public CItem
{
const char *Model( void ){ return "models/w_rpgammo.mdl"; }
const char *PickSound( void ){ return "weapons/glock/clip_in.wav"; }
BOOL AddItem( CBaseEntity *pOther ) { return pOther->GiveAmmo( AMMO_RPGCLIP_GIVE, "rockets", 3 ); }
};
LINK_ENTITY_TO_CLASS( ammo_rpgclip, CRpgAmmo );
class CMP5AmmoClip : public CItem
{
const char *Model( void ){ return "models/w_9mmARclip.mdl"; }
const char *PickSound( void ){ return "weapons/glock/clip_in.wav"; }
BOOL AddItem( CBaseEntity *pOther ) { return pOther->GiveAmmo( AMMO_MP5CLIP_GIVE, "9mm", 250); }
};
LINK_ENTITY_TO_CLASS( ammo_9mmAR, CMP5AmmoClip );
LINK_ENTITY_TO_CLASS( ammo_mp5clip, CMP5AmmoClip );
class CSawAmmo : public CItem
{
const char *Model( void ){ return "models/w_saw_clip.mdl"; }
const char *PickSound( void ){ return "weapons/glock/clip_in.wav"; }
BOOL AddItem( CBaseEntity *pOther ) { return pOther->GiveAmmo( AMMO_CHAINBOX_GIVE, "556", SAW_MAX_CLIP); }
};
LINK_ENTITY_TO_CLASS( ammo_556, CSawAmmo );
class CMP5AmmoGrenade : public CItem
{
const char *Model( void ){ return "models/w_ARgrenade.mdl"; }
const char *PickSound( void ){ return "weapons/glock/clip_in.wav"; }
BOOL AddItem( CBaseEntity *pOther ) { return pOther->GiveAmmo( AMMO_M203BOX_GIVE, "m203", 10 ); }
};
LINK_ENTITY_TO_CLASS( ammo_m203, CMP5AmmoGrenade );
class CGaussAmmo : public CItem
{
const char *Model( void ){ return "models/w_gaussammo.mdl"; }
const char *PickSound( void ){ return "weapons/glock/clip_in.wav"; }
BOOL AddItem( CBaseEntity *pOther ) { return pOther->GiveAmmo( AMMO_URANIUMBOX_GIVE, "uranium", 100 ); }
};
LINK_ENTITY_TO_CLASS( ammo_gaussclip, CGaussAmmo );
class CShotgunAmmoBox : public CItem
{
const char *Model( void ){ return "models/w_shotbox.mdl"; }
const char *PickSound( void ){ return "weapons/glock/clip_in.wav"; }
BOOL AddItem( CBaseEntity *pOther ) { return pOther->GiveAmmo( 12, "buckshot", 125 ); }
};
LINK_ENTITY_TO_CLASS( ammo_buckshot, CShotgunAmmoBox );
class CCrossbowAmmo : public CItem
{
const char *Model( void ){ return "models/w_crossbow_clip.mdl"; }
const char *PickSound( void ){ return "weapons/glock/clip_in.wav"; }
BOOL AddItem( CBaseEntity *pOther ) { return pOther->GiveAmmo( 5, "bolts", 50 ); }
};
LINK_ENTITY_TO_CLASS( ammo_crossbow, CCrossbowAmmo );
class CShotgunAmmoShell : public CItem
{
const char *Model( void ){ return "models/shellBuck.mdl"; }
const char *PickSound( void ){ return "weapons/glock/clip_in.wav"; }
BOOL AddItem( CBaseEntity *pOther ) { return pOther->GiveAmmo( 1, "buckshot", 125 ); }
};
LINK_ENTITY_TO_CLASS( ammo_buckshell, CShotgunAmmoShell );
//***********************************************************
// item_weaponbox - a single entity that can store weapons
// and ammo.
//***********************************************************
LINK_ENTITY_TO_CLASS( item_weaponbox, CWeaponBox );
TYPEDESCRIPTION CWeaponBox::m_SaveData[] =
{
DEFINE_ARRAY( CWeaponBox, m_rgAmmo, FIELD_INTEGER, MAX_AMMO_SLOTS ),
DEFINE_ARRAY( CWeaponBox, m_rgiszAmmo, FIELD_STRING, MAX_AMMO_SLOTS ),
DEFINE_ARRAY( CWeaponBox, m_rgpPlayerItems, FIELD_CLASSPTR, MAX_ITEM_TYPES ),
DEFINE_FIELD( CWeaponBox, m_cAmmoTypes, FIELD_INTEGER ),
}; IMPLEMENT_SAVERESTORE( CWeaponBox, CBaseEntity );
void CWeaponBox::Precache( void )
{
UTIL_PrecacheModel("models/w_weaponbox.mdl");
}
void CWeaponBox :: KeyValue( KeyValueData *pkvd )
{
if ( m_cAmmoTypes < MAX_AMMO_SLOTS )
{
PackAmmo( ALLOC_STRING(pkvd->szKeyName), atoi(pkvd->szValue) );
m_cAmmoTypes++;// count this new ammo type.
pkvd->fHandled = TRUE;
}
else Msg( "WeaponBox too full! only %d ammotypes allowed\n", MAX_AMMO_SLOTS );
}
void CWeaponBox::Spawn( void )
{
Precache( );
pev->movetype = MOVETYPE_TOSS;
pev->solid = SOLID_TRIGGER;
UTIL_SetSize( pev, g_vecZero, g_vecZero );
UTIL_SetModel( ENT(pev), "models/w_weaponbox.mdl");
}
void CWeaponBox::Kill( void )
{
CBasePlayerWeapon *pWeapon;
int i;
// destroy the weapons
for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ )
{
pWeapon = m_rgpPlayerItems[ i ];
while ( pWeapon )
{
pWeapon->SetThink( Remove );
pWeapon->SetNextThink( 0.1 );
pWeapon = pWeapon->m_pNext;
}
}
// remove the box
UTIL_Remove( this );
}
void CWeaponBox::Touch( CBaseEntity *pOther )
{
if ( !(pev->flags & FL_ONGROUND ) ) return;
if ( !pOther->IsPlayer() ) return;
if ( !pOther->IsAlive() ) return;
CBasePlayer *pPlayer = (CBasePlayer *)pOther;
int i;
// dole out ammo
for ( i = 0 ; i < MAX_AMMO_SLOTS ; i++ )
{
if ( !FStringNull( m_rgiszAmmo[ i ] ) )
{
// there's some ammo of this type.
pPlayer->GiveAmmo( m_rgAmmo[ i ], (char *)STRING( m_rgiszAmmo[ i ] ), MaxAmmoCarry( m_rgiszAmmo[ i ] ) );
// now empty the ammo from the weaponbox since we just gave it to the player
m_rgiszAmmo[ i ] = iStringNull;
m_rgAmmo[ i ] = 0;
}
}
// go through my weapons and try to give the usable ones to the player.
// it's important the the player be given ammo first, so the weapons code doesn't refuse
// to deploy a better weapon that the player may pick up because he has no ammo for it.
for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ )
{
if ( m_rgpPlayerItems[ i ] )
{
CBasePlayerWeapon *pItem;
// have at least one weapon in this slot
while ( m_rgpPlayerItems[ i ] )
{
//ALERT ( at_console, "trying to give %s\n", STRING( m_rgpPlayerItems[ i ]->pev->classname ) );
pItem = m_rgpPlayerItems[ i ];
m_rgpPlayerItems[ i ] = m_rgpPlayerItems[ i ]->m_pNext;// unlink this weapon from the box
if ( pPlayer->AddPlayerItem( pItem ) ) pItem->AttachToPlayer( pPlayer );
}
}
}
EMIT_SOUND( pOther->edict(), CHAN_ITEM, "items/gunpickup2.wav", 1, ATTN_NORM );
SetTouch(NULL);
UTIL_Remove(this);
}
int CWeaponBox::MaxAmmoCarry( int iszName )
{
for ( int i = 0; i < MAX_WEAPONS; i++ )
{
if ( CBasePlayerWeapon::ItemInfoArray[i].pszAmmo1 && !strcmp( STRING(iszName), CBasePlayerWeapon::ItemInfoArray[i].pszAmmo1 ) )
return CBasePlayerWeapon::ItemInfoArray[i].iMaxAmmo1;
if ( CBasePlayerWeapon::ItemInfoArray[i].pszAmmo2 && !strcmp( STRING(iszName), CBasePlayerWeapon::ItemInfoArray[i].pszAmmo2 ) )
return CBasePlayerWeapon::ItemInfoArray[i].iMaxAmmo2;
}
Msg( "MaxAmmoCarry() doesn't recognize '%s'!\n", STRING( iszName ) );
return -1;
}
BOOL CWeaponBox::PackWeapon( CBasePlayerWeapon *pWeapon )
{
// is one of these weapons already packed in this box?
if ( HasWeapon( pWeapon ) ) return FALSE;// box can only hold one of each weapon type
if ( pWeapon->m_pPlayer )
{
// failed to unhook the weapon from the player!
if ( !pWeapon->m_pPlayer->RemovePlayerItem( pWeapon ) ) return FALSE;
}
int iWeaponSlot = pWeapon->iItemSlot();
if ( m_rgpPlayerItems[ iWeaponSlot ] )
{
// there's already one weapon in this slot, so link this into the slot's column
pWeapon->m_pNext = m_rgpPlayerItems[ iWeaponSlot ];
m_rgpPlayerItems[ iWeaponSlot ] = pWeapon;
}
else
{
// first weapon we have for this slot
m_rgpPlayerItems[ iWeaponSlot ] = pWeapon;
pWeapon->m_pNext = NULL;
}
pWeapon->pev->spawnflags |= SF_NORESPAWN;// never respawn
pWeapon->pev->movetype = MOVETYPE_NONE;
pWeapon->pev->solid = SOLID_NOT;
pWeapon->pev->effects = EF_NODRAW;
pWeapon->pev->modelindex = 0;
pWeapon->pev->model = iStringNull;
pWeapon->pev->owner = edict();
pWeapon->SetThink( NULL );// crowbar may be trying to swing again, etc.
pWeapon->SetTouch( NULL );
pWeapon->m_pPlayer = NULL;
return TRUE;
}
BOOL CWeaponBox::PackAmmo( int iszName, int iCount )
{
int iMaxCarry;
if ( FStringNull( iszName ) )
{
// error here
Msg( "NULL String in PackAmmo!\n" );
return FALSE;
}
iMaxCarry = MaxAmmoCarry( iszName );
if ( iMaxCarry != -1 && iCount > 0 )
{
GiveAmmo( iCount, (char *)STRING( iszName ), iMaxCarry );
return TRUE;
}
return FALSE;
}
int CWeaponBox::GiveAmmo( int iCount, char *szName, int iMax, int *pIndex/* = NULL*/ )
{
int i;
for (i = 1; i < MAX_AMMO_SLOTS && !FStringNull( m_rgiszAmmo[i] ); i++)
{
if (stricmp( szName, STRING( m_rgiszAmmo[i])) == 0)
{
if (pIndex) *pIndex = i;
int iAdd = min( iCount, iMax - m_rgAmmo[i]);
if (iCount == 0 || iAdd > 0)
{
m_rgAmmo[i] += iAdd;
return i;
}
return -1;
}
}
if (i < MAX_AMMO_SLOTS)
{
if (pIndex) *pIndex = i;
m_rgiszAmmo[i] = MAKE_STRING( szName );
m_rgAmmo[i] = iCount;
return i;
}
Msg("out of named ammo slots\n");
return i;
}
BOOL CWeaponBox::HasWeapon( CBasePlayerWeapon *pCheckItem )
{
CBasePlayerWeapon *pItem = m_rgpPlayerItems[pCheckItem->iItemSlot()];
while (pItem)
{
if (FClassnameIs( pItem->pev, STRING( pCheckItem->pev->classname) )) return TRUE;
pItem = pItem->m_pNext;
}
return FALSE;
}
BOOL CWeaponBox::IsEmpty( void )
{
int i;
for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ )
{
if ( m_rgpPlayerItems[ i ] ) return FALSE;
}
for ( i = 0 ; i < MAX_AMMO_SLOTS ; i++ )
{
if ( !FStringNull( m_rgiszAmmo[ i ] )) return FALSE; // still have a bit of this type of ammo
}
return TRUE;
}
void CWeaponBox::SetObjectCollisionBox( void )
{
pev->absmin = pev->origin + Vector(-16, -16, 0);
pev->absmax = pev->origin + Vector(16, 16, 16);
}

68
server/ents/baseitem.h Normal file
View File

@ -0,0 +1,68 @@
/***
*
* 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.
*
****/
#ifndef ITEMS_H
#define ITEMS_H
class CItem : public CBaseLogic
{
public:
void Spawn( void );
void Precache( void );
CBaseEntity* Respawn( void );
void EXPORT ItemTouch( CBaseEntity *pOther );
void EXPORT Materialize( void );
void EXPORT ItemFall( void );
virtual BOOL AddItem( CBaseEntity *pOther ) { return TRUE; };
virtual void ItemOnGround( void ) {};
float ItemRespawnTime( CItem *pItem );
BOOL IsItem( void ) { return !strncmp( STRING(pev->classname), "item_", 5 ); }
BOOL IsAmmo( void ) { return !strncmp( STRING(pev->classname), "ammo_", 5 ); }
//item_generic
virtual const char *Model( void ){ return NULL; }
virtual const char *PickSound( void ){ return "common/null.wav"; }
virtual const char *FallSound( void ){ return "common/null.wav"; }
};
class CWeaponBox : public CBaseEntity
{
void Precache( void );
void Spawn( void );
void Touch( CBaseEntity *pOther );
void KeyValue( KeyValueData *pkvd );
BOOL IsEmpty( void );
int GiveAmmo( int iCount, char *szName, int iMax, int *pIndex = NULL );
void SetObjectCollisionBox( void );
public:
void EXPORT Kill ( void );
int Save( CSave &save );
int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
BOOL HasWeapon( CBasePlayerWeapon *pCheckItem );
BOOL PackWeapon( CBasePlayerWeapon *pWeapon );
BOOL PackAmmo( int iszName, int iCount );
CBasePlayerWeapon *m_rgpPlayerItems[MAX_ITEM_TYPES];// one slot for each
int MaxAmmoCarry( int iszName );
int m_rgiszAmmo[MAX_AMMO_SLOTS];// ammo names
int m_rgAmmo[MAX_AMMO_SLOTS];// ammo quantities
int m_cAmmoTypes;// how many ammo types packed into this box (if packed by a level designer)
};
#endif // ITEMS_H

533
server/ents/baselogic.cpp Normal file
View File

@ -0,0 +1,533 @@
//=======================================================================
// Copyright (C) Shambler Team 2004
// logicentity.cpp - all entities with prefix "logic_"
// additional entities for smart system
//=======================================================================
#include "extdll.h"
#include "utils.h"
#include "cbase.h"
#include "defaults.h"
// Use CBaseDelay as main function (renamed as CBaseLogic, see declaration in cbase.h).
//=======================================================================
// main functions ()
//=======================================================================
void CBaseLogic :: KeyValue( KeyValueData *pkvd )
{
if (FStrEq(pkvd->szKeyName, "delay") || FStrEq(pkvd->szKeyName, "MaxDelay"))
{
m_flDelay = atof( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "wait") || FStrEq(pkvd->szKeyName, "MaxWait"))
{
m_flWait = atof(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "master"))
{
m_sMaster = ALLOC_STRING( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "killtarget"))
{
m_iszKillTarget = ALLOC_STRING( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "globalstate"))
{
m_globalstate = ALLOC_STRING( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else CBaseEntity::KeyValue( pkvd );
}
void CBaseLogic :: FireTargets( USE_TYPE useType, float value )
{
//fire our targets
UTIL_FireTargets( pev->target, m_hActivator, this, useType, value );
UTIL_FireTargets( m_iszKillTarget, m_hActivator, this, USE_REMOVE );
}
BOOL CBaseLogic :: IsLockedByMaster( void )
{
if (UTIL_IsMasterTriggered(m_sMaster, m_hActivator))
return FALSE;
return TRUE;
}
BOOL CBaseLogic :: IsLockedByMaster( CBaseEntity *pActivator )
{
if (UTIL_IsMasterTriggered(m_sMaster, pActivator))
return FALSE;
return TRUE;
}
BOOL CBaseLogic :: IsLockedByMaster( USE_TYPE useType )
{
if (UTIL_IsMasterTriggered(m_sMaster, m_hActivator))
return FALSE;
else if (useType == USE_SHOWINFO) return FALSE;//pass to debug info
return TRUE;
}
TYPEDESCRIPTION CBaseLogic::m_SaveData[] =
{
DEFINE_FIELD( CBaseLogic, m_flDelay, FIELD_FLOAT ),
DEFINE_FIELD( CBaseLogic, m_hActivator, FIELD_EHANDLE ),
DEFINE_FIELD( CBaseLogic, m_hTarget, FIELD_EHANDLE ),
DEFINE_FIELD( CBaseLogic, m_iState, FIELD_INTEGER ),
DEFINE_FIELD( CBaseLogic, m_sMaster, FIELD_STRING),
DEFINE_FIELD( CBaseLogic, m_iszKillTarget, FIELD_STRING ),
DEFINE_FIELD( CBaseLogic, m_sSet, FIELD_STRING),
DEFINE_FIELD( CBaseLogic, m_sReset, FIELD_STRING),
DEFINE_FIELD( CBaseLogic, m_flWait, FIELD_FLOAT ),
DEFINE_FIELD( CBaseLogic, m_globalstate, FIELD_STRING ),
};IMPLEMENT_SAVERESTORE( CBaseLogic, CBaseEntity );
//=======================================================================
// Logic_generator
//=======================================================================
#define NORMAL_MODE 0
#define RANDOM_MODE 1
#define RANDOM_MODE_WITH_DEC 2
class CGenerator : public CBaseLogic
{
public:
void KeyValue( KeyValueData *pkvd );
void Spawn( void );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
void Think (void);
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
private:
int m_iFireCount;
int m_iMaxFireCount;
};
LINK_ENTITY_TO_CLASS( logic_generator, CGenerator );
void CGenerator :: Spawn()
{
if(pev->button == RANDOM_MODE || pev->button == RANDOM_MODE_WITH_DEC)
{ //set delay with decceleration
if (!m_flDelay || pev->button == RANDOM_MODE_WITH_DEC) m_flDelay = 0.005;
//generate max count automaticallly, if not set on map
if (!m_iMaxFireCount) m_iMaxFireCount = RANDOM_LONG(100, 200);
}
else
{
//Smart Field System ©
if (!m_iMaxFireCount) m_iMaxFireCount = -1;//disable counting for normal mode
}
if(pev->spawnflags & SF_START_ON)
{
m_iState = STATE_ON;//initialy off in random mode
SetNextThink(m_flDelay);
}
}
TYPEDESCRIPTION CGenerator :: m_SaveData[] =
{
DEFINE_FIELD( CGenerator, m_iMaxFireCount, FIELD_INTEGER ),
DEFINE_FIELD( CGenerator, m_iFireCount, FIELD_INTEGER ),
};
IMPLEMENT_SAVERESTORE( CGenerator, CBaseLogic );
void CGenerator :: KeyValue( KeyValueData *pkvd )
{
if (FStrEq(pkvd->szKeyName, "maxcount"))
{
m_iMaxFireCount = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
if (FStrEq(pkvd->szKeyName, "mode"))
{
pev->button = atof(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else CBaseLogic::KeyValue( pkvd );
}
void CGenerator :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
if (useType == USE_TOGGLE)
{
if(m_iState) useType = USE_OFF;
else useType = USE_ON;
}
if (useType == USE_ON)
{
if(pev->button == RANDOM_MODE || pev->button == RANDOM_MODE_WITH_DEC)
{
if(pev->button == RANDOM_MODE_WITH_DEC) m_flDelay = 0.005;
m_iFireCount = RANDOM_LONG(0, m_iMaxFireCount/2);
}
m_iState = STATE_ON;
SetNextThink( 0 );//immediately start firing targets
}
else if (useType == USE_OFF)
{
m_iState = STATE_OFF;
DontThink();
}
else if (useType == USE_SET) //set max count of impulses
m_iMaxFireCount = value;
else if (useType == USE_RESET) //immediately reset
m_iFireCount = 0;
else if (useType == USE_SHOWINFO)
{
ALERT(at_console, "======/Xash Debug System/======\n");
ALERT(at_console, "classname: %s\n", STRING(pev->classname));
ALERT(at_console, "State: %s, Delay time %f\n", GetStringForState( GetState()), m_flDelay);
ALERT(at_console, "FireCount: %d, Max FireCount: %d\n", m_iFireCount, m_iMaxFireCount);
}
}
void CGenerator :: Think( void )
{
if(m_iFireCount != -1)//if counter enabled
{
if(m_iFireCount == m_iMaxFireCount)
{
m_iFireCount = NULL;
DontThink();
m_iState = STATE_OFF;
return;
}
else m_iFireCount++;
}
if(pev->button == RANDOM_MODE_WITH_DEC)
{ //deceleration for random mode
if(m_iMaxFireCount - m_iFireCount < 40) m_flDelay += 0.005;
}
UTIL_FireTargets( pev->target, this, this, USE_TOGGLE );
SetNextThink(m_flDelay);
}
//=======================================================================
// Logic_switcher
//=======================================================================
#define MODE_INCREMENT 0
#define MODE_DECREMENT 1
#define MODE_RANDOM_VALUE 2
class CSwitcher : public CBaseLogic
{
public:
void KeyValue( KeyValueData *pkvd );
void Spawn ( void );
void Think ( void );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
int m_cTargets;// the total number of targets in this manager's fire list.
int m_index; // Current target
int m_iTargetName [ MAX_MULTI_TARGETS ];// list if indexes into global string array
};
LINK_ENTITY_TO_CLASS( logic_switcher, CSwitcher );
// Global Savedata for switcher
TYPEDESCRIPTION CSwitcher::m_SaveData[] =
{ DEFINE_FIELD( CSwitcher, m_cTargets, FIELD_INTEGER ),
DEFINE_FIELD( CSwitcher, m_index, FIELD_INTEGER ),
DEFINE_ARRAY( CSwitcher, m_iTargetName, FIELD_STRING, MAX_MULTI_TARGETS ),
};IMPLEMENT_SAVERESTORE(CSwitcher, CBaseLogic);
void CSwitcher :: KeyValue( KeyValueData *pkvd )
{
if (FStrEq(pkvd->szKeyName, "mode"))
{
pev->button = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if ( m_cTargets < MAX_MULTI_TARGETS )
{
// add this field to the target list
// this assumes that additional fields are targetnames and their values are delay values.
char tmp[128];
UTIL_StripToken( pkvd->szKeyName, tmp );
m_iTargetName [ m_cTargets ] = ALLOC_STRING( tmp );
m_cTargets++;
pkvd->fHandled = TRUE;
}
}
void CSwitcher :: Spawn( void )
{
int r_index = 0;
int w_index = m_cTargets - 1;
while(r_index < w_index)
{
//we store target with right index in tempname
int name = m_iTargetName [r_index];
//target with right name is free, record new value from wrong name
m_iTargetName [r_index] = m_iTargetName [w_index];
//ok, we can swap targets
m_iTargetName [w_index] = name;
r_index++;
w_index--;
}
m_iState = STATE_OFF;
m_index = 0;
if(pev->spawnflags & SF_START_ON)
{
m_iState = STATE_ON;
SetNextThink (m_flDelay);
}
}
void CSwitcher :: Think ( void )
{
if(pev->button == MODE_INCREMENT)//increase target number
{
m_index++;
if(m_index >= m_cTargets) m_index = 0;
}
else if(pev->button == MODE_DECREMENT)
{
m_index--;
if(m_index < 0) m_index = m_cTargets - 1;
}
else if(pev->button == MODE_RANDOM_VALUE)
{
m_index = RANDOM_LONG (0, m_cTargets - 1);
}
SetNextThink (m_flDelay);
}
void CSwitcher :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
m_hActivator = pActivator;
if(IsLockedByMaster( useType )) return;
if (useType == USE_SET)//set new target for activate (direct choose or increment\decrement)
{
if(pev->spawnflags & SF_START_ON)
{
m_iState = STATE_ON;
SetNextThink (m_flDelay);
return;
}
//set maximum priority for direct choose
if(value)
{
m_index = (value - 1);
if(m_index >= m_cTargets) m_index = -1;
return;
}
if(pev->button == MODE_INCREMENT)
{
m_index++;
if(m_index >= m_cTargets) m_index = 0;
}
else if(pev->button == MODE_DECREMENT)//increase target number
{
m_index--;
if(m_index < 0) m_index = m_cTargets - 1;
}
else if(pev->button == MODE_RANDOM_VALUE)
{
m_index = RANDOM_LONG (0, m_cTargets - 1);
}
}
else if (useType == USE_RESET)
{
//reset switcher
m_iState = STATE_OFF;
DontThink();
m_index = 0;
return;
}
else if (useType == USE_SHOWINFO)
{
ALERT(at_console, "======/Xash Debug System/======\n");
ALERT(at_console, "classname: %s\n", STRING(pev->classname));
ALERT(at_console, "State: %s, number of targets %d\n", GetStringForState( GetState()), m_cTargets - 1);
ALERT(at_console, "Current target %s, target number %d\n", STRING(m_iTargetName[ m_index ]), m_index );
}
else if(m_index != -1)//fire any other USE_TYPE and right index
{
UTIL_FireTargets( m_iTargetName[ m_index ], m_hActivator, this, useType, value );
}
}
//=======================================================================
// Logic_counter
//=======================================================================
class CLogicCounter : public CBaseLogic
{
public:
void Spawn( void );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
void KeyValue( KeyValueData *pkvd );
};
LINK_ENTITY_TO_CLASS( logic_counter, CLogicCounter );
void CLogicCounter :: KeyValue( KeyValueData *pkvd )
{
if (FStrEq(pkvd->szKeyName, "count"))
{
pev->frags = atof(pkvd->szValue);
pkvd->fHandled = TRUE;
}
if (FStrEq(pkvd->szKeyName, "maxcount"))
{
pev->body = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else CBaseLogic::KeyValue( pkvd );
}
void CLogicCounter :: Spawn( void )
{
//Smart Field System ©
if (pev->frags == 0) pev->frags = 2;
if (pev->body == 0) pev->body = 100;
if (pev->frags == -1) pev->frags = RANDOM_LONG (1, pev->body);
//save number of impulses
pev->impulse = pev->frags;
m_iState = STATE_OFF;//always disable
}
void CLogicCounter :: Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
if (useType == USE_SET)
{
if(value) pev->impulse = pev->frags = value; //write new value
else pev->impulse = pev->frags = RANDOM_LONG (1, pev->body);//write random value
}
else if (useType == USE_RESET) pev->frags = pev->impulse; //restore counter to default
else if (useType == USE_SHOWINFO)
{
ALERT(at_console, "======/Xash Debug System/======\n");
ALERT(at_console, "classname: %s\n", STRING(pev->classname));
ALERT(at_console, "start count %d, current count %.f\n",pev->impulse , pev->impulse - pev->frags );
ALERT(at_console, "left activates: %.f\n", pev->frags);
}
else //any other useType
{
pev->frags--;
if(pev->frags > 0) return;
pev->frags = 0; //don't reset counter in negative value
UTIL_FireTargets( pev->target, pActivator, this, useType, value ); //activate target
}
}
//=======================================================================
// Logic_usetype - sorting different usetypes
//=======================================================================
class CLogicUseType : public CBaseLogic
{
public:
void KeyValue( KeyValueData *pkvd );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
};
LINK_ENTITY_TO_CLASS( logic_usetype, CLogicUseType );
void CLogicUseType::KeyValue( KeyValueData *pkvd )
{
if (FStrEq(pkvd->szKeyName, "toggle"))
{
pev->target = ALLOC_STRING( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "enable"))
{
pev->message = ALLOC_STRING( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "disable"))
{
pev->netname = ALLOC_STRING( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "set"))
{
m_sSet = ALLOC_STRING( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if (FStrEq(pkvd->szKeyName, "reset"))
{
m_sReset = ALLOC_STRING( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else CBaseLogic::KeyValue( pkvd );
}
void CLogicUseType::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
if(IsLockedByMaster( useType )) return;
if(useType == USE_TOGGLE) UTIL_FireTargets(pev->target, pActivator, this, useType, value );
else if (useType == USE_ON) UTIL_FireTargets(pev->message, pActivator, this, useType, value );
else if (useType == USE_OFF) UTIL_FireTargets(pev->netname, pActivator, this, useType, value );
else if (useType == USE_SET) UTIL_FireTargets(m_sSet, pActivator, this, useType, value );
else if (useType == USE_RESET)UTIL_FireTargets(m_sReset, pActivator, this, useType, value );
else if (useType == USE_SHOWINFO)
{
ALERT(at_console, "======/Xash Debug System/======\n");
ALERT(at_console, "classname: %s\n", STRING(pev->classname));
ALERT(at_console, "This entity don't displayed settings!\n" );
SHIFT;
}
}
//=======================================================================
// Logic_scale - apply scale for value
//=======================================================================
class CLogicScale : public CBaseLogic
{
public:
void KeyValue( KeyValueData *pkvd )
{
if (FStrEq(pkvd->szKeyName, "mode"))
{
pev->impulse = atoi( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else CBaseLogic::KeyValue( pkvd );
}
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
if (useType == USE_SHOWINFO)
{
DEBUGHEAD;
ALERT(at_console, "Mode %s. Scale %.3f.\n", pev->impulse ? "Bool" : "Float", pev->scale);
SHIFT;
}
else
{
if(pev->impulse == 0)//bool logic
{
if(value >= 0.99)UTIL_FireTargets(pev->target, pActivator, this, USE_ON, 1 );
if(value <= 0.01)UTIL_FireTargets(pev->target, pActivator, this, USE_OFF,0 );
}
if(pev->impulse == 1)//direct scale
UTIL_FireTargets(pev->target, pActivator, this, USE_SET, value * pev->scale );
if(pev->impulse == 2)//inverse sacle
UTIL_FireTargets(pev->target, pActivator, this, USE_SET, pev->scale * (1-value));
}
}
};
LINK_ENTITY_TO_CLASS( logic_scale, CLogicScale);

145
server/ents/baselogic.h Normal file
View File

@ -0,0 +1,145 @@
//=======================================================================
// Copyright (C) XashXT Group 2006
//=======================================================================
#ifndef BASELOGIC_H
#define BASELOGIC_H
#define MAX_MULTI_TARGETS 32 // maximum number of targets that can be added in a list
class CBaseLogic : public CBaseEntity
{
public:
BOOL IsLockedByMaster( void );
BOOL IsLockedByMaster( USE_TYPE useType );
BOOL IsLockedByMaster( CBaseEntity *pActivator );
void FireTargets( USE_TYPE useType = USE_TOGGLE, float value = 0 );
virtual int ObjectCaps( void ) { return CBaseEntity :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
virtual void KeyValue( KeyValueData* pkvd);
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
virtual STATE GetState( void ) { return m_iState; };
float m_flDelay;
float m_flWait;
EHANDLE m_hActivator;
EHANDLE m_hTarget;
STATE m_iState;
string_t m_sMaster;
string_t m_iszKillTarget; //evil stuff. agrhh
string_t m_sSet;//used for logic_usetype
string_t m_sReset;//used for logic_usetype
string_t m_globalstate;
float m_flMin, m_flMax;
};
#define SF_CORNER_WAITTRIG 0x001
#define SF_CORNER_TELEPORT 0x002
#define SF_CORNER_FIREONCE 0x004
class CPathCorner : public CBaseLogic
{
public:
void Spawn( );
void KeyValue( KeyValueData* pkvd );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
float GetDelay( void ){ return pev->spawnflags & SF_CORNER_WAITTRIG ? -1 : m_flWait; }
void GetSpeed( float *speed ) { if(pev->speed != 0) *speed = pev->speed; }
void UpdateTargets( void );
void Link( void );
void PostActivate( void ){ Link(); }
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
CPathCorner *Instance( edict_t *pent )
{
if ( FClassnameIs( pent, "path_corner" ) )
return (CPathCorner *)GET_PRIVATE(pent);
return NULL;
}
CPathCorner *m_pNextPath1;
CPathCorner *m_pNextPath2;
CPathCorner *m_pPrevPath;
CBaseEntity *ValidPath( CBaseEntity *m_pPath );
CBaseEntity *GetNext( void );
CBaseEntity *GetPrev( void ){ return ValidPath( m_pPrevPath ); }
void SetPrev( CPathCorner *pPrev ) { m_pPrevPath = (CPathCorner *)ValidPath((CPathCorner *)pPrev); }
};
class CPathTrack : public CBaseLogic
{
public:
void Spawn( void );
void Activate( void );
void KeyValue( KeyValueData* pkvd);
void SetPrevious( CPathTrack *pprevious );
void Link( void );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
CBaseEntity *ValidPath( CBaseEntity *ppath, int testFlag ); // Returns ppath if enabled, NULL otherwise
void Project( CBaseEntity *pstart, CBaseEntity *pend, Vector *origin, float dist );
static CPathTrack *Instance( edict_t *pent );
CBaseEntity *LookAhead( Vector *origin, float dist, int move );
CBaseEntity *Nearest( Vector origin ); //notused
CBaseEntity *GetNext( void );
CBaseEntity *GetPrev( void );
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
#if PATH_SPARKLE_DEBUG
void EXPORT Sparkle(void);
#endif
float m_length;
string_t m_altName;
CPathTrack *m_pnext;
CPathTrack *m_pprevious;
CPathTrack *m_paltpath;
};
class CMultiSource : public CBaseLogic
{
public:
void Spawn( );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
STATE GetState( void );
void EXPORT Register( void );
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
EHANDLE m_rgEntities[MAX_MULTI_TARGETS];
int m_rgTriggered[MAX_MULTI_TARGETS];
int m_iTotal;
};
#include "baseinfo.h"
class CUtilRainModify : public CPointEntity
{
public:
void Spawn( void );
void KeyValue( KeyValueData *pkvd );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
int drips;
RandomRange windXY;
RandomRange randXY;
float fadeTime;
};
#endif //BASELOGIC_H

1834
server/ents/basemover.cpp Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

252
server/ents/basemover.h Normal file
View File

@ -0,0 +1,252 @@
//=======================================================================
// Copyright (C) XashXT Group 2006
//=======================================================================
#ifndef BASEMOVER_H
#define BASEMOVER_H
//rotating brush flags
#define SF_BRUSH_ROTATE_Z_AXIS 4
#define SF_BRUSH_ROTATE_X_AXIS 8
//door flags
#define SF_DOOR_START_OPEN 1
#define SF_DOOR_ROTATE_BACKWARDS 2
#define SF_DOOR_PASSABLE 8
#define SF_DOOR_ONEWAY 16
#define SF_DOOR_NO_AUTO_RETURN 32
#define SF_DOOR_ROTATE_Z 64
#define SF_DOOR_ROTATE_X 128
#define SF_DOOR_USE_ONLY 256
#define SF_DOOR_NOMONSTERS 512
// Tracktrain spawn flags
#define SF_TRACKTRAIN_NOPITCH 0x0001
#define SF_TRACKTRAIN_NOCONTROL 0x0002
#define SF_TRACKTRAIN_FORWARDONLY 0x0004
#define SF_TRACKTRAIN_PASSABLE 0x0008
#define SF_TRACKTRAIN_NOYAW 0x0010 //LRC
#define SF_TRACKTRAIN_AVELOCITY 0x800000 //LRC - avelocity has been set manually, don't turn.
#define SF_TRACKTRAIN_AVEL_GEARS 0x400000 //LRC - avelocity should be scaled up/down when the train changes gear.
// Spawnflag for CPathTrack
#define SF_PATH_DISABLED 0x00000001
#define SF_PATH_FIREONCE 0x00000002
#define SF_PATH_ALTREVERSE 0x00000004
#define SF_PATH_DISABLE_TRAIN 0x00000008
#define SF_PATH_ALTERNATE 0x00008000
#define SF_PATH_AVELOCITY 0x00080000 //LRC
//LRC - values in 'armortype'
#define PATHSPEED_SET 0
#define PATHSPEED_ACCEL 1
#define PATHSPEED_TIME 2
#define PATHSPEED_SET_MASTER 3
class CBaseMover : public CBaseBrush
{
public:
void KeyValue( KeyValueData *pkvd );
virtual void AxisDir( void );
void (CBaseMover::*m_pfnCallWhenMoveDone)(void); //custom movedone function
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
float m_flMoveDistance;//rotating distance
float m_flBlockedTime; //set blocked refresh time
int m_iMode;//style of working
float m_flValue;//value to send
float m_flHeight;
float m_flLip;
Vector m_vecPosition1; //startpos
Vector m_vecPosition2; //endpos
Vector m_vecAngle1; //startangle
Vector m_vecAngle2; //endangle
Vector m_vecFinalDest; //basemover finalpos
Vector m_vecFinalAngle; //basemover finalangle
Vector m_vecFloor; //basemover dest floor
float m_flLinearMoveSpeed;//member linear speed
float m_flAngularMoveSpeed;//member angular speed
// common member functions
void LinearMove ( Vector vecInput, float flSpeed );
void AngularMove( Vector vecDestAngle, float flSpeed );
void ComplexMove( Vector vecInput, Vector vecDestAngle, float flSpeed );
void EXPORT LinearMoveNow( void );
void EXPORT AngularMoveNow( void );
void EXPORT ComplexMoveNow( void );
void EXPORT LinearMoveDone( void );
void EXPORT AngularMoveDone( void );
void EXPORT ComplexMoveDone( void );
void EXPORT LinearMoveDoneNow( void );
void EXPORT AngularMoveDoneNow( void );
void EXPORT ComplexMoveDoneNow( void );
};
class CBaseDoor : public CBaseMover
{
public:
void Spawn( void );
void Precache( void );
virtual void PostSpawn( void );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
BOOL IsWater( void ){ return pev->skin != 0; };
void EXPORT DoorTouch( CBaseEntity *pOther );
virtual void Blocked( CBaseEntity *pOther );
virtual int ObjectCaps( void )
{
if ( FBitSet ( pev->spawnflags, SF_DOOR_USE_ONLY ))//door without name player can direct using
return (CBaseMover::ObjectCaps() & ~FCAP_ACROSS_TRANSITION | FCAP_IMPULSE_USE );
else return (CBaseMover::ObjectCaps() & ~FCAP_ACROSS_TRANSITION);
};
// local functions
void EXPORT DoorGoUp( void );
void EXPORT DoorGoDown( void );
void EXPORT DoorHitTop( void );
void EXPORT DoorHitBottom( void );
virtual BOOL IsRotatingDoor( void ){ return FALSE; };
};
class CRotDoor : public CBaseDoor
{
public:
virtual void PostSpawn( void ) {}
virtual BOOL IsRotatingDoor( void ){ return TRUE; };
};
class CMomentaryDoor : public CBaseMover
{
public:
void Spawn( void );
void Precache( void );
void PostSpawn( void );
void EXPORT MomentaryMoveDone( void );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
virtual int ObjectCaps( void ) { return CBaseMover :: ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
};
class CBasePlatform : public CBaseMover
{
public:
void Precache( void );
void Spawn( void );
void PostSpawn( void );
void Setup( void );
void PostActivate( void );
virtual void Blocked( CBaseEntity *pOther );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
float CalcFloor( void )
{
float curfloor = ((pev->origin.z - m_vecPosition1.z) / step()) + 1;
if (curfloor - floor(curfloor) > 0.5) return ceil(curfloor);
else return floor(curfloor);
}
float step( void ) { return pev->size.z + m_flHeight - 2; }
float ftime( void ) { return step() / pev->speed; } //time to moving between floors
void EXPORT GoUp( void );
void EXPORT GoDown( void );
void GoToFloor( float floor );
void EXPORT HitTop( void );
void EXPORT HitBottom( void );
void EXPORT HitFloor( void );
virtual BOOL IsMovingPlatform( void ) { return m_flHeight != 0 && m_flMoveDistance == 0; }
virtual BOOL IsRotatingPlatform( void ){ return m_flHeight == 0 && m_flMoveDistance != 0; }
virtual BOOL IsComplexPlatform( void ) { return m_flHeight != 0 && m_flMoveDistance != 0; }
};
class CFuncTrain : public CBasePlatform
{
public:
void Spawn( void );
void PostSpawn( void );
void OverrideReset( void );
void PostActivate( void );
void ClearPointers( void );
BOOL Stop( float flWait = -1 );
BOOL Teleport( void );
void Blocked( CBaseEntity *pOther );
BOOL IsWater( void ){ return pev->skin != 0; };
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
Vector TrainOrg( void ) { return (pev->mins + pev->maxs) * 0.5; }
void EXPORT Wait( void );
void EXPORT Next( void );
//path operations
CBaseEntity *FindPath( void );
CBaseEntity *FindNextPath( void );
void Reverse( void );
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
CBaseEntity *pCurPath, *pNextPath;
};
class CFuncTrackTrain : public CBaseMover
{
public:
void Spawn( void );
void Precache( void );
void Blocked( CBaseEntity *pOther );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
void KeyValue( KeyValueData* pkvd );
void EXPORT DesiredAction( void ); //LRC - used to be called Next!
void PostActivate( void );
void ClearPointers( void );
// void EXPORT Next( void );
void EXPORT PostponeNext( void );
void EXPORT Find( void );
void EXPORT NearestPath( void );
void EXPORT DeadEnd( void );
void NextThink( float thinkTime, BOOL alwaysThink );
void SetTrack( CBaseEntity *track ) { pPath = ((CPathTrack *)track)->Nearest(pev->origin); }
void SetControls( entvars_t *pevControls );
BOOL OnControls( entvars_t *pev );
void StopSound ( void );
void UpdateSound ( void );
static CFuncTrackTrain *Instance( edict_t *pent )
{
if ( FClassnameIs( pent, "func_tracktrain" ) )
return (CFuncTrackTrain *)GET_PRIVATE(pent);
return NULL;
}
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
virtual int ObjectCaps( void ) { return CBaseMover :: ObjectCaps() | FCAP_DIRECTIONAL_USE; }
virtual void OverrideReset( void );
CBaseEntity *pPath;
float m_length;
float m_height;
// I get it... this records the train's max speed (as set by the level designer), whereas
// pev->speed records the current speed (as set by the player). --LRC
// m_speed is also stored, as an int, in pev->impulse.
float m_speed;
float m_dir;
float m_startSpeed;
Vector m_controlMins;
Vector m_controlMaxs;
int m_soundPlaying;
float m_flBank;
float m_oldSpeed;
Vector m_vecBaseAvel; // LRC - the underlying avelocity, superceded by normal turning behaviour where applicable
};
#endif //BASEMOVER_H

270
server/ents/baseother.cpp Normal file
View File

@ -0,0 +1,270 @@
//=======================================================================
// Copyright (C) XashXT Group 2007
// misc temporary entities
//=======================================================================
#include "extdll.h"
#include "utils.h"
#include "cbase.h"
#include "player.h"
//=======================================================================
// sparkleent - explode post sparks
//=======================================================================
class CEnvShower : public CBaseEntity
{
void Spawn( void );
void Think( void );
void Touch( CBaseEntity *pOther );
int ObjectCaps( void ) { return FCAP_DONT_SAVE; }
};
LINK_ENTITY_TO_CLASS( sparkleent, CEnvShower );
void CEnvShower::Spawn( void )
{
pev->velocity = RANDOM_FLOAT( 200, 300 ) * pev->angles;
pev->velocity.x += RANDOM_FLOAT(-100.f,100.f);
pev->velocity.y += RANDOM_FLOAT(-100.f,100.f);
if ( pev->velocity.z >= 0 ) pev->velocity.z += 200;
else pev->velocity.z -= 200;
pev->movetype = MOVETYPE_BOUNCE;
pev->gravity = 0.5;
SetNextThink( 0.1 );
pev->solid = SOLID_NOT;
UTIL_SetModel( edict(), "models/common/null.mdl");
UTIL_SetSize(pev, g_vecZero, g_vecZero );
pev->effects |= EF_NODRAW;
pev->speed = RANDOM_FLOAT( 0.5, 1.5 );
pev->angles = g_vecZero;
}
void CEnvShower::Think( void )
{
UTIL_Sparks( pev->origin );
pev->speed -= 0.1;
if ( pev->speed > 0 ) SetNextThink( 0.1 );
else UTIL_Remove( this );
pev->flags &= ~FL_ONGROUND;
}
void CEnvShower::Touch( CBaseEntity *pOther )
{
if ( pev->flags & FL_ONGROUND )
pev->velocity = pev->velocity * 0.1;
else pev->velocity = pev->velocity * 0.6;
if ( (pev->velocity.x*pev->velocity.x+pev->velocity.y*pev->velocity.y) < 10.0 ) pev->speed = 0;
}
//====================================================================
// ChangeLevelFire
//====================================================================
class ChangeLevelFire : public CBaseLogic
{
public:
void PostActivate( void ) { FireTargets(); UTIL_Remove( this ); }
int ObjectCaps( void ) { return CBaseEntity::ObjectCaps() | FCAP_FORCE_TRANSITION; }
};
LINK_ENTITY_TO_CLASS( fireent, ChangeLevelFire );
//=======================================================================
// faderent - rendering time effects
//=======================================================================
class CRenderFxFader : public CBaseLogic
{
public:
void Spawn ( void ) { m_hTarget = CBaseEntity::Instance( pev->owner ); }
void Think ( void )
{
if (((CBaseEntity*)m_hTarget) == NULL)
{
UTIL_Remove( this );
return;
}
float flDegree = (gpGlobals->time - pev->dmgtime)/pev->speed;
if (flDegree >= 1)
{
m_hTarget->pev->renderamt = pev->renderamt + pev->health;
m_hTarget->pev->rendercolor = pev->rendercolor + pev->movedir;
m_hTarget->pev->scale = pev->scale + pev->frags;
m_hTarget->pev->framerate = pev->framerate + pev->max_health;
UTIL_FireTargets( pev->target, m_hTarget, this, USE_TOGGLE );
m_hTarget = NULL;
SetNextThink( 0.1 );
SetThink(Remove);
}
else
{
m_hTarget->pev->renderamt = pev->renderamt + pev->health * flDegree;
m_hTarget->pev->rendercolor.x = pev->rendercolor.x + pev->movedir.x * flDegree;
m_hTarget->pev->rendercolor.y = pev->rendercolor.y + pev->movedir.y * flDegree;
m_hTarget->pev->rendercolor.z = pev->rendercolor.z + pev->movedir.z * flDegree;
m_hTarget->pev->scale = pev->scale + pev->frags * flDegree;
m_hTarget->pev->framerate = pev->framerate + pev->max_health * flDegree;
SetNextThink( 0.01 );//fader step
}
}
};
LINK_ENTITY_TO_CLASS( faderent, CRenderFxFader );
//=======================================================================
// smokeent - temporary smoke
//=======================================================================
class CSmokeEnt : public CBaseAnimating
{
void Spawn( void );
void Think( void );
};
LINK_ENTITY_TO_CLASS( smokeent, CSmokeEnt );
void CSmokeEnt::Spawn( void )
{
pev->solid = SOLID_BBOX;
pev->movetype = MOVETYPE_NONE;
pev->armorvalue = gpGlobals->time;
if(!pev->team) pev->team = 50;
if(pev->team > 250)pev->team = 250;//normalize and remember about random value 0 - 5
}
void CSmokeEnt::Think( void )
{
if (pev->dmgtime)
{
if (pev->dmgtime < gpGlobals->time)
{
UTIL_Remove( this );
return;
}
else if (RANDOM_FLOAT( 0, pev->dmgtime - pev->armorvalue ) > pev->dmgtime - gpGlobals->time)
{
return;
}
}
Vector VecSrc = UTIL_RandomVector( pev->absmin, pev->absmax );
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, VecSrc );
WRITE_BYTE( TE_SMOKE );
WRITE_COORD( VecSrc.x );
WRITE_COORD( VecSrc.y );
WRITE_COORD( VecSrc.z );
WRITE_SHORT( g_sModelIndexSmoke );
WRITE_BYTE( RANDOM_LONG(0,5) + pev->impulse ); // scale * 10
WRITE_BYTE( RANDOM_LONG(0, 3) + 8 ); // framerate
MESSAGE_END();
StudioFrameAdvance( );//animate model if present
SetNextThink( 0.2 );
}
//=========================================================
// func_platform floor indicator
//=========================================================
class CFloorEnt:public CPointEntity
{
void Think( void );
void PostActivate( void );
CBasePlatform *pElevator; //no need to save - PostActivate will be restore this
};
void CFloorEnt::PostActivate( void )
{
pElevator = (CBasePlatform*)CBasePlatform::Instance( pev->owner );
SetNextThink( 0 );
}
void CFloorEnt::Think ( void )
{
if(pElevator && (pElevator->GetState() == STATE_TURN_ON || pElevator->GetState() == STATE_TURN_OFF) )
{
UTIL_FireTargets( pev->target, pElevator, this, USE_SET, pElevator->CalcFloor());
SetNextThink( 0.01 );
}
else UTIL_Remove( this );
}
LINK_ENTITY_TO_CLASS( floorent, CFloorEnt );
//====================================================================
// Laser pointer target
//====================================================================
CLaserSpot *CLaserSpot::CreateSpot( void )
{
CLaserSpot *pSpot = GetClassPtr( (CLaserSpot *)NULL );
pSpot->Spawn();
pSpot->pev->classname = MAKE_STRING("laserspotent");
return pSpot;
}
void CLaserSpot::Precache( void )
{
UTIL_PrecacheModel("sprites/glow02.spr");
UTIL_PrecacheSound("weapons/spot_on.wav");
UTIL_PrecacheSound("weapons/spot_off.wav");
}
void CLaserSpot::Spawn( void )
{
Precache( );
//laser dot settings
pev->movetype = MOVETYPE_FLY;
pev->solid = SOLID_NOT;
pev->scale = 1.0;
pev->rendermode = kRenderGlow;
pev->renderfx = kRenderFxNoDissipation;
pev->renderamt = 255;
pev->rendercolor = Vector( 200, 12, 12 );
UTIL_SetModel(ENT(pev), "sprites/glow02.spr" );
UTIL_SetOrigin( this, pev->origin );
}
void CLaserSpot::Suspend( float flSuspendTime )
{
pev->effects |= EF_NODRAW;
// -1 means suspend indefinitely
if (flSuspendTime == -1) SetThink( NULL );
else
{
SetThink( Revive );
SetNextThink( flSuspendTime );
}
}
void CLaserSpot::Revive( void )
{
pev->effects &= ~EF_NODRAW;
SetThink( NULL );
}
void CLaserSpot::Update( CBasePlayer *m_pPlayer )
{
TraceResult tr;
UTIL_MakeVectors( m_pPlayer->pev->v_angle );
UTIL_TraceLine( m_pPlayer->GetGunPosition(), m_pPlayer->GetGunPosition() + gpGlobals->v_forward * 8192, dont_ignore_monsters, ENT(m_pPlayer->pev), &tr );
UTIL_SetOrigin( this, tr.vecEndPos );
if( UTIL_PointContents( tr.vecEndPos ) & CONTENTS_SKY && VARS(tr.pHit)->solid == SOLID_BSP )
{
pev->renderamt = 33;
pev->effects |= EF_NODRAW;
}
else
{
pev->effects &= ~EF_NODRAW;
float SpotDistance = (tr.vecEndPos - m_pPlayer->GetGunPosition()).Length();
int brightness = (1 / log(SpotDistance / 0.3)) * 1300;
pev->scale = SpotDistance / 2500 + RANDOM_FLOAT(0.01, SpotDistance/2750);
if(pev->renderamt >= 255) pev->renderamt = brightness + RANDOM_LONG(1, SpotDistance/400);
else pev->renderamt += 5;
}
}
LINK_ENTITY_TO_CLASS( laserspotent, CLaserSpot );

Some files were not shown because too many files have changed in this diff Show More