08 Jul 2011

This commit is contained in:
g-cont 2011-07-08 00:00:00 +04:00 committed by Alibek Omarov
parent 3296d44539
commit dc5dc9f653
89 changed files with 7466 additions and 1436 deletions

View File

@ -1,8 +1,35 @@
build ????
Fixed wrong sound angles whe client see in the camera
Fix crash on change gl_texturemode.
Change relationsip for GetLocalPlayer. Now it's always valid.
Client: fix drawing beams for 'solid' mode
Image: fix BMP loader for 4-bit color bmp's
Client: fix lightlevel calculating for local client (remove 'ambient' base from final value)
GameUI: first implementation of custom strings and support 'strings.lst' parsing
GameUI: replace unneeded button 'credits' with button 'previews'
Render: fix sprite interpolation
Render: fix angled sprites offset
Render: implement detail textures like in Steam Half-Life (thx n00b)
Client: rework env_funnel effect
build 1598
Client: fix crosshair drawing
Sound: change libmad mp3 decoder on a mpg123
Client: fix gibs randomization for TE_BREAKMODEL
Engine: fix modelloader bug (engine crash after not found map)
Video: add resolution 1366x768
GameUI: fix skill select
Network: change 'svc_setangle' message, include rollangle
Render: fix chrome rendering on a studiomodels
Render: added video memory economy mode - cvar 'gl_luminance_textures'
GameDLL: first implementation of extended engineinterface. Metamod is supported now
build 1557
Sound: fixed wrong sound angles when client see in the camera
Render: fix crash on change gl_texturemode and gl_anisotropy.
Client: change relationsip for GetLocalPlayer. Now it's always valid.
Server: rewrite SV_Multicast for correct work with custom user cameras
Server: rewrite FIND_CLIENT_IN_PVS like in QW
build 1540

16
cl_dll/cl_dll.plg Normal file
View File

@ -0,0 +1,16 @@
<html>
<body>
<pre>
<h1>Build Log</h1>
<h3>
--------------------Configuration: cl_dll - Win32 Release--------------------
</h3>
<h3>Command Lines</h3>
<h3>Results</h3>
client.dll - 0 error(s), 0 warning(s)
</pre>
</body>
</html>

View File

@ -137,7 +137,7 @@ char* READ_STRING( void )
if ( giRead+1 > giSize )
break; // no more characters
c = READ_CHAR();
c = READ_BYTE();
if (c == -1 || c == 0)
break;
string[l] = c;

View File

@ -75,7 +75,8 @@ typedef struct texture_s
struct texture_s *anim_next; // in the animation sequence
struct texture_s *alternate_anims; // bmodels in frmae 1 use these
int fb_texturenum; // auto-luma texturenum
unsigned int unused[3]; // reserved
int dt_texturenum; // detail-texture binding
unsigned int unused[2]; // reserved
} texture_t;
typedef struct

View File

@ -59,6 +59,7 @@ CBaseEntity
extern "C" EXPORT int GetEntityAPI( DLL_FUNCTIONS *pFunctionTable, int interfaceVersion );
extern "C" EXPORT int GetEntityAPI2( DLL_FUNCTIONS *pFunctionTable, int *interfaceVersion );
extern "C" EXPORT int Server_GetPhysicsInterface( int iVersion, server_physics_api_t *pfuncsFromEngine, physics_interface_t *pFunctionTable );
extern int DispatchSpawn( edict_t *pent );
extern void DispatchKeyValue( edict_t *pentKeyvalue, KeyValueData *pkvd );

View File

@ -28,7 +28,7 @@
// Holds engine functionality callbacks
enginefuncs_t g_engfuncs;
globalvars_t *gpGlobals;
server_physics_api_t g_physfuncs;
#ifdef _WIN32

141
dlls/hl.plg Normal file
View File

@ -0,0 +1,141 @@
<html>
<body>
<pre>
<h1>Build Log</h1>
<h3>
--------------------Configuration: hl - Win32 Release--------------------
</h3>
<h3>Command Lines</h3>
Creating temporary file "C:\DOCUME~1\ÌÈØÀ\LOCALS~1\Temp\RSP32FA.tmp" with contents
[
/nologo /G5 /MT /W3 /O2 /I "..\dlls" /I "..\engine" /I "..\common" /I "..\pm_shared" /I "..\game_shared" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "QUIVER" /D "VOXEL" /D "QUAKE2" /D "VALVE_DLL" /D "CLIENT_WEAPONS" /Fr"..\temp\dlls\!release/" /Fp"..\temp\dlls\!release/hl.pch" /YX /Fo"..\temp\dlls\!release/" /Fd"..\temp\dlls\!release/" /FD /c
"D:\Xash3D\src_main\dlls\client.cpp"
]
Creating command line "cl.exe @"C:\DOCUME~1\ÌÈØÀ\LOCALS~1\Temp\RSP32FA.tmp""
Creating temporary file "C:\DOCUME~1\ÌÈØÀ\LOCALS~1\Temp\RSP32FB.tmp" with contents
[
kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"..\temp\dlls\!release/hl.pdb" /debug /machine:I386 /def:".\hl.def" /out:"..\temp\dlls\!release/hl.dll" /implib:"..\temp\dlls\!release/hl.lib"
"\Xash3D\src_main\temp\dlls\!release\aflock.obj"
"\Xash3D\src_main\temp\dlls\!release\agrunt.obj"
"\Xash3D\src_main\temp\dlls\!release\airtank.obj"
"\Xash3D\src_main\temp\dlls\!release\animating.obj"
"\Xash3D\src_main\temp\dlls\!release\animation.obj"
"\Xash3D\src_main\temp\dlls\!release\apache.obj"
"\Xash3D\src_main\temp\dlls\!release\barnacle.obj"
"\Xash3D\src_main\temp\dlls\!release\barney.obj"
"\Xash3D\src_main\temp\dlls\!release\bigmomma.obj"
"\Xash3D\src_main\temp\dlls\!release\bloater.obj"
"\Xash3D\src_main\temp\dlls\!release\bmodels.obj"
"\Xash3D\src_main\temp\dlls\!release\bullsquid.obj"
"\Xash3D\src_main\temp\dlls\!release\buttons.obj"
"\Xash3D\src_main\temp\dlls\!release\cbase.obj"
"\Xash3D\src_main\temp\dlls\!release\client.obj"
"\Xash3D\src_main\temp\dlls\!release\combat.obj"
"\Xash3D\src_main\temp\dlls\!release\controller.obj"
"\Xash3D\src_main\temp\dlls\!release\crossbow.obj"
"\Xash3D\src_main\temp\dlls\!release\crowbar.obj"
"\Xash3D\src_main\temp\dlls\!release\defaultai.obj"
"\Xash3D\src_main\temp\dlls\!release\doors.obj"
"\Xash3D\src_main\temp\dlls\!release\effects.obj"
"\Xash3D\src_main\temp\dlls\!release\egon.obj"
"\Xash3D\src_main\temp\dlls\!release\explode.obj"
"\Xash3D\src_main\temp\dlls\!release\flyingmonster.obj"
"\Xash3D\src_main\temp\dlls\!release\func_break.obj"
"\Xash3D\src_main\temp\dlls\!release\func_tank.obj"
"\Xash3D\src_main\temp\dlls\!release\game.obj"
"\Xash3D\src_main\temp\dlls\!release\gamerules.obj"
"\Xash3D\src_main\temp\dlls\!release\gargantua.obj"
"\Xash3D\src_main\temp\dlls\!release\gauss.obj"
"\Xash3D\src_main\temp\dlls\!release\genericmonster.obj"
"\Xash3D\src_main\temp\dlls\!release\ggrenade.obj"
"\Xash3D\src_main\temp\dlls\!release\globals.obj"
"\Xash3D\src_main\temp\dlls\!release\gman.obj"
"\Xash3D\src_main\temp\dlls\!release\h_ai.obj"
"\Xash3D\src_main\temp\dlls\!release\h_battery.obj"
"\Xash3D\src_main\temp\dlls\!release\h_cine.obj"
"\Xash3D\src_main\temp\dlls\!release\h_cycler.obj"
"\Xash3D\src_main\temp\dlls\!release\h_export.obj"
"\Xash3D\src_main\temp\dlls\!release\handgrenade.obj"
"\Xash3D\src_main\temp\dlls\!release\hassassin.obj"
"\Xash3D\src_main\temp\dlls\!release\headcrab.obj"
"\Xash3D\src_main\temp\dlls\!release\healthkit.obj"
"\Xash3D\src_main\temp\dlls\!release\hgrunt.obj"
"\Xash3D\src_main\temp\dlls\!release\hl_wpn_glock.obj"
"\Xash3D\src_main\temp\dlls\!release\hornet.obj"
"\Xash3D\src_main\temp\dlls\!release\hornetgun.obj"
"\Xash3D\src_main\temp\dlls\!release\houndeye.obj"
"\Xash3D\src_main\temp\dlls\!release\ichthyosaur.obj"
"\Xash3D\src_main\temp\dlls\!release\islave.obj"
"\Xash3D\src_main\temp\dlls\!release\items.obj"
"\Xash3D\src_main\temp\dlls\!release\leech.obj"
"\Xash3D\src_main\temp\dlls\!release\lights.obj"
"\Xash3D\src_main\temp\dlls\!release\maprules.obj"
"\Xash3D\src_main\temp\dlls\!release\monstermaker.obj"
"\Xash3D\src_main\temp\dlls\!release\monsters.obj"
"\Xash3D\src_main\temp\dlls\!release\monsterstate.obj"
"\Xash3D\src_main\temp\dlls\!release\mortar.obj"
"\Xash3D\src_main\temp\dlls\!release\mp5.obj"
"\Xash3D\src_main\temp\dlls\!release\multiplay_gamerules.obj"
"\Xash3D\src_main\temp\dlls\!release\nihilanth.obj"
"\Xash3D\src_main\temp\dlls\!release\nodes.obj"
"\Xash3D\src_main\temp\dlls\!release\osprey.obj"
"\Xash3D\src_main\temp\dlls\!release\pathcorner.obj"
"\Xash3D\src_main\temp\dlls\!release\plane.obj"
"\Xash3D\src_main\temp\dlls\!release\plats.obj"
"\Xash3D\src_main\temp\dlls\!release\player.obj"
"\Xash3D\src_main\temp\dlls\!release\pm_debug.obj"
"\Xash3D\src_main\temp\dlls\!release\pm_math.obj"
"\Xash3D\src_main\temp\dlls\!release\pm_shared.obj"
"\Xash3D\src_main\temp\dlls\!release\python.obj"
"\Xash3D\src_main\temp\dlls\!release\rat.obj"
"\Xash3D\src_main\temp\dlls\!release\roach.obj"
"\Xash3D\src_main\temp\dlls\!release\rpg.obj"
"\Xash3D\src_main\temp\dlls\!release\satchel.obj"
"\Xash3D\src_main\temp\dlls\!release\schedule.obj"
"\Xash3D\src_main\temp\dlls\!release\scientist.obj"
"\Xash3D\src_main\temp\dlls\!release\scripted.obj"
"\Xash3D\src_main\temp\dlls\!release\shotgun.obj"
"\Xash3D\src_main\temp\dlls\!release\singleplay_gamerules.obj"
"\Xash3D\src_main\temp\dlls\!release\skill.obj"
"\Xash3D\src_main\temp\dlls\!release\sound.obj"
"\Xash3D\src_main\temp\dlls\!release\soundent.obj"
"\Xash3D\src_main\temp\dlls\!release\spectator.obj"
"\Xash3D\src_main\temp\dlls\!release\squadmonster.obj"
"\Xash3D\src_main\temp\dlls\!release\squeakgrenade.obj"
"\Xash3D\src_main\temp\dlls\!release\subs.obj"
"\Xash3D\src_main\temp\dlls\!release\talkmonster.obj"
"\Xash3D\src_main\temp\dlls\!release\teamplay_gamerules.obj"
"\Xash3D\src_main\temp\dlls\!release\tempmonster.obj"
"\Xash3D\src_main\temp\dlls\!release\tentacle.obj"
"\Xash3D\src_main\temp\dlls\!release\triggers.obj"
"\Xash3D\src_main\temp\dlls\!release\tripmine.obj"
"\Xash3D\src_main\temp\dlls\!release\turret.obj"
"\Xash3D\src_main\temp\dlls\!release\util.obj"
"\Xash3D\src_main\temp\dlls\!release\voice_gamemgr.obj"
"\Xash3D\src_main\temp\dlls\!release\weapons.obj"
"\Xash3D\src_main\temp\dlls\!release\world.obj"
"\Xash3D\src_main\temp\dlls\!release\xen.obj"
"\Xash3D\src_main\temp\dlls\!release\zombie.obj"
]
Creating command line "link.exe @"C:\DOCUME~1\ÌÈØÀ\LOCALS~1\Temp\RSP32FB.tmp""
Creating temporary file "C:\DOCUME~1\ÌÈØÀ\LOCALS~1\Temp\RSP32FC.bat" with contents
[
@echo off
copy \Xash3D\src_main\temp\dlls\!release\hl.dll "D:\Xash3D\valve\dlls\hl.dll"
]
Creating command line ""C:\DOCUME~1\ÌÈØÀ\LOCALS~1\Temp\RSP32FC.bat""
Compiling...
client.cpp
Linking...
Creating library ..\temp\dlls\!release/hl.lib and object ..\temp\dlls\!release/hl.exp
<h3>Output Window</h3>
Performing Custom Build Step on \Xash3D\src_main\temp\dlls\!release\hl.dll
‘ª®¯¨à®¢ ­® ä ©«®¢: 1.
<h3>Results</h3>
hl.dll - 0 error(s), 0 warning(s)
</pre>
</body>
</html>

28
dlls/physcallback.h Normal file
View File

@ -0,0 +1,28 @@
/***
*
* 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 PHYSCALLBACK_H
#define PHYSCALLBACK_H
#pragma once
#include "physint.h"
// Must be provided by user of this code
extern server_physics_api_t g_physfuncs;
// The actual physic callbacks
#define LINK_ENTITY (*g_physfuncs.pfnLinkEdict)
#define PHYSICS_TIME (*g_physfuncs.pfnGetServerTime)
#endif //PHYSCALLBACK_H

View File

@ -22,6 +22,11 @@
#ifndef ENGINECALLBACK_H
#include "enginecallback.h"
#endif
#ifndef PHYSCALLBACK_H
#include "physcallback.h"
#endif
inline void MESSAGE_BEGIN( int msg_dest, int msg_type, const float *pOrigin, entvars_t *ent ); // implementation later in this file
extern globalvars_t *gpGlobals;

View File

@ -767,6 +767,7 @@ void CBasePlayerItem::AttachToPlayer ( CBasePlayer *pPlayer )
pev->owner = pPlayer->edict();
pev->nextthink = gpGlobals->time + .1;
SetTouch( NULL );
SetThink( NULL );
}
// CALLED THROUGH the newly-touched weapon's instance. The existing player weapon is pOriginal

View File

@ -33,6 +33,7 @@
#include "weapons.h"
#include "gamerules.h"
#include "teamplay_gamerules.h"
#include "physcallback.h"
extern CGraph WorldGraph;
extern CSoundEnt *pSoundEnt;
@ -748,3 +749,112 @@ void CWorld :: KeyValue( KeyValueData *pkvd )
else
CBaseEntity::KeyValue( pkvd );
}
//
// Xash3D physics interface
//
typedef void (__cdecl *LINK_ENTITY_FN)( entvars_t *pev );
//
// attempt to create custom entity when default method is failed
// 0 - attempt to create, -1 - reject to create
//
int DispatchCreateEntity( edict_t *pent, const char *szName )
{
/*
#ifdef CREATE_ENTITY_TEST
// quake armor entities. we just replaced it with item_battery...
if( !strcmp( szName, "item_armor1" ) || !strcmp( szName, "item_armor2" ))
{
LINK_ENTITY_FN SpawnEdict;
// ugly method to get acess with himself exports
SpawnEdict = (LINK_ENTITY_FN)GetProcAddress( GetModuleHandle( "hl" ), "item_battery" );
if( SpawnEdict != NULL ) // found the valid spawn
{
// BUGBUG: old classname hanging in memory
pent->v.classname = ALLOC_STRING( "item_battery" );
// ALERT( at_console, "DispatchCreateEntity: replace %s with %s\n", szName, STRING( pent->v.classname ));
SpawnEdict( &pent->v );
return 0; // handled
}
}
#endif
*/
return -1;
}
//
// run custom physics for each entity
// return 0 to use built-in engine physic
//
int DispatchPhysicsEntity( edict_t *pEdict )
{
CBaseEntity *pEntity = (CBaseEntity *)GET_PRIVATE(pEdict);
if( !pEntity )
{
// ALERT( at_console, "skip %s [%i] without private data\n", STRING( pEdict->v.classname ), ENTINDEX( pEdict ));
return 0; // not initialized
}
// NOTE: at this point pEntity assume to be valid
/*
#ifdef CUSTOM_PHYSICS_TEST
// test alien controller without physics, thinking only
if( FClassnameIs( pEntity->pev, "monster_alien_controller" ))
{
float thinktime;
thinktime = pEntity->pev->nextthink;
if( thinktime <= 0.0f || thinktime > PHYSICS_TIME() + gpGlobals->frametime )
return 1;
if( thinktime < PHYSICS_TIME( ))
thinktime = PHYSICS_TIME(); // don't let things stay in the past.
// it is possible to start that way
// by a trigger with a local time.
pEntity->pev->nextthink = 0.0f;
gpGlobals->time = thinktime;
DispatchThink( pEdict );
#ifdef GRAVITY_TEST
// stupid fake gravity test
pEntity->pev->origin.z -= 1;
LINK_ENTITY( pEdict, true );
#endif
return 1; // handled
}
#endif
*/
return 0;
}
static physics_interface_t gPhysicsInterface =
{
SV_PHYSICS_INTERFACE_VERSION,
DispatchCreateEntity,
DispatchPhysicsEntity,
};
int Server_GetPhysicsInterface( int iVersion, server_physics_api_t *pfuncsFromEngine, physics_interface_t *pFunctionTable )
{
if ( !pFunctionTable || !pfuncsFromEngine || iVersion != SV_PHYSICS_INTERFACE_VERSION )
{
return FALSE;
}
// copy new physics interface
memcpy(&g_physfuncs, pfuncsFromEngine, sizeof(server_physics_api_t));
// fill engine callbacks
memcpy( pFunctionTable, &gPhysicsInterface, sizeof( physics_interface_t ) );
return TRUE;
}

View File

@ -250,6 +250,96 @@ typedef struct cl_enginefuncs_s
void (*pfnSetMousePos)( int x, int y );
void (*pfnSetMouseEnable)( qboolean fEnable );
// Returns pointer to start of registered cvar linked list. Added for VGUI console autocomplete?
void* (*pfnGetCvarList)( void );
// Returns pointer to start of registered command linked list. Added for VGUI console autocomplete?
void* (*pfnGetCmdList)( void );
// Not sure about this - convert cvar_t pointer to cvar name? Why?
char* (*pfnCvarNameFromPointer)( void *pointer );
// Not sure abput this - convert cmd_function_t pointer to command name? Why?
char* (*pfnCmdNameFromPointer)( void *pointer );
// Returns current time for this client? Why is this needed when GetClientTime() already exists?
float (*pfnGetCurrentTime)( void );
// Unsure - always seems to return 800.0f (no, it's not horizontal resolution)
float (*pfnGetGravityFactor)( void );
// Appears to be identical to function in IEngineStudio
void* (*pfnGetModelByIndex)( int index );
// Appears to modifies hidden cvar gl_texsort.
void (*pfnSetGL_TexSort)( int value );
// Colour scaling values for screen. Only works when gl_texsort is active.
void (*pfnSetGL_TexSort_Colour)( float red, float green, float blue );
// Final scaling factor for screen. Only works when gl_texsort is active.
void (*pfnSetGL_TexSort_Scale)( float scale );
// Seems to be a client entry point to the pfnSequenceGet function introduced for CS:CZ
void* (*pfnSequenceGet)( const char *fileName, const char *entryName );
// Draws a sprite on the screen - parameters are likely incorrect.
void (*pfnDrawSpriteGeneric)( int frame, int x, int y, const wrect_t *prc, int u1, int u2, int u3, int u4 );
// Seems to be a client entry point to the pfnSequencePickSentence function introduced for CS:CZ
void* (*pfnSequencePickSentence)( const char *groupName, int pickMethod, int *picked );
// Unknown (deals with wchar_t's)
void (*pfnUnknownFunction6)( void *u1, void *u2, void *u3, void *u4, void *u5, void *u6 );
// Unknown (deals with wchar_t's)
void (*pfnUnknownFunction7)( void *u1, void *u2 );
// Unknown (something to do with players infostring?)
char* (*pfnUnknownFunction8)( char *u1 );
// Completely unknown
void (*pfnUnknownFunction9)( void *u1, void *u2 );
// Completely unknown
void (*pfnUnknownFunction10)( void *u1, void *u2, void *u3, void *u4, void *u5 );
// Seems to be a client entry point to the pfnGetApproxWavePlayLen function introduced for CS:CZ
unsigned int (*pfnGetApproxWavePlayLen)( char *filename );
// Completely unknown
int (*pfnUnknownFunction11)( void );
// Sets cvar value - why is this needed when Cvar_SetValue already exists?
void (*Cvar_Set)( char *name, char *value );
// Seems to be a client entry point to the pfnIsCareerMatch function introduced for CS:CZ
int (*pfnIsCareerMatch)( void );
// Starts a local sound - why is this needed?
void (*pfnStartDynamicSound)( char *filename, float volume, float pitch );
// MP3 interface - unsure what int parameter is. Just queues the sound up - have to issue command "mp3 play" to start it. */
void (*pfnMP3_InitStream)( char *filename, int i1 );
// Returns unknown, constantly increasing float. Timing? (calls QueryPerformanceCounters)
float (*pfnUnknownFunction12)( void );
// Seems to be a client entry point to the pfnProcessTutorMessageDecayBuffer function introduced for CS:CZ
void (*pfnProcessTutorMessageDecayBuffer)( int *buffer, int buflen );
// Seems to be a client entry point to the pfnConstructTutorMessageDecayBuffer function introduced for CS:CZ
void (*pfnConstructTutorMessageDecayBuffer)( int *buffer, int buflen );
// Seems to be a client entry point to the pfnResetTutorMessageDecayData function introduced for CS:CZ
void (*pfnResetTutorMessageDecayData)( void );
// Seems to be an exact copy of the previous StartDynamicSound function???
void (*pfnStartDynamicSound2)( char *filename, float volume, float pitch );
// Seems to be an exact copy of the previous FillRGBA function???
void (*pfnFillRGBA2)( int x, int y, int width, int height, int r, int g, int b, int a );
} cl_enginefunc_t;
#define CLDLL_INTERFACE_VERSION 7

View File

@ -110,15 +110,9 @@ void CL_UpdateEntityFields( cl_entity_t *ent )
if( m_pGround && m_pGround->curstate.movetype == MOVETYPE_PUSH )
{
studiohdr_t *phdr;
mstudioseqdesc_t *pseqdesc;
qboolean applyVel, applyAvel;
qboolean applyVel, applyAvel;
d = 1.0f - cl.lerpFrac; // use backlerp to interpolate pusher position
phdr = (studiohdr_t *)Mod_Extradata( ent->model );
ASSERT( phdr != NULL );
pseqdesc = (mstudioseqdesc_t *)((byte *)phdr + phdr->seqindex) + ent->curstate.sequence;
d = d - 1.0f;
applyVel = !VectorCompare( m_pGround->curstate.origin, m_pGround->prevstate.origin );

View File

@ -867,7 +867,7 @@ void CL_DrawCrosshair( void )
clgame.ds.pSprite = clgame.ds.pCrosshair;
GL_SetRenderMode( kRenderTransAlpha );
GL_SetRenderMode( kRenderTransTexture );
*(int *)clgame.ds.spriteColor = *(int *)clgame.ds.rgbaCrosshair;
SPR_EnableScissor( x - 0.5f * width, y - 0.5f * height, width, height );
@ -3685,7 +3685,7 @@ void CL_UnloadProgs( void )
Q_memset( &clgame, 0, sizeof( clgame ));
Cvar_Unlink();
Cmd_Unlink();
Cmd_Unlink( CMD_CLIENTDLL );
}
qboolean CL_LoadProgs( const char *name )

View File

@ -259,7 +259,7 @@ usercmd_t CL_CreateCmd( void )
active = ( cls.state == ca_active && !cl.refdef.paused && !cl.refdef.intermission );
clgame.dllFuncs.CL_CreateMove( cl.time - cl.oldtime, &cmd, active );
R_LightForPoint( cl.frame.local.client.origin, &color, false, 128.0f );
R_LightForPoint( cl.frame.local.client.origin, &color, false, false, 128.0f );
cmd.lightlevel = (color.r + color.g + color.b) / 3;
// never let client.dll calc frametime for player
@ -818,6 +818,33 @@ void CL_LocalServers_f( void )
Netchan_OutOfBandPrint( NS_CLIENT, adr, "info %i", PROTOCOL_VERSION );
}
/*
=================
CL_InternetServers_f
=================
*/
void CL_InternetServers_f( void )
{
netadr_t adr;
char part1query[12];
char part2query[128];
string fullquery;
MsgDev( D_INFO, "Scanning for servers on the internet area...\n" );
NET_Config( true ); // allow remote
if( !NET_StringToAdr( MASTERSERVER_ADR, &adr ) )
MsgDev( D_INFO, "Can't resolve adr: %s\n", MASTERSERVER_ADR );
Q_snprintf( part1query, sizeof( part1query ), "%c%c0.0.0.0:0", 0x31, 0xFF );
Q_snprintf( part2query, sizeof( part2query ), "\\gamedir\\%s_xash\\nap\\%d", GI->gamedir, 70 );
Q_memcpy( fullquery, part1query, sizeof( part1query ));
Q_memcpy( fullquery + sizeof( part1query ), part2query, Q_strlen( part2query ));
NET_SendPacket( NS_CLIENT, sizeof( part1query ) + Q_strlen( part2query ) + 1, fullquery, adr );
}
/*
====================
CL_Packet_f
@ -1116,6 +1143,8 @@ void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg )
char *args;
char *c, buf[MAX_SYSPATH];
int len = sizeof( buf );
int dataoffset = 0;
netadr_t servadr;
BF_Clear( msg );
BF_ReadLong( msg ); // skip the -1
@ -1202,6 +1231,33 @@ void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg )
// user out of band message (must be handled in CL_ConnectionlessPacket)
if( len > 0 ) Netchan_OutOfBand( NS_SERVER, from, len, buf );
}
else if( msg->pData[0] == 0xFF && msg->pData[1] == 0xFF && msg->pData[2] == 0xFF && msg->pData[3] == 0xFF && msg->pData[4] == 0x66 && msg->pData[5] == 0x0A )
{
dataoffset = 6;
while( 1 )
{
servadr.type = NA_IP;
servadr.ip[0] = msg->pData[dataoffset + 0];
servadr.ip[1] = msg->pData[dataoffset + 1];
servadr.ip[2] = msg->pData[dataoffset + 2];
servadr.ip[3] = msg->pData[dataoffset + 3];
servadr.port = *(word *)&msg->pData[dataoffset + 4];
if( !servadr.port )
break;
MsgDev( D_INFO, "Found server: %s\n", NET_AdrToString( servadr ));
NET_Config( true ); // allow remote
Netchan_OutOfBandPrint( NS_CLIENT, servadr, "info %i", PROTOCOL_VERSION );
dataoffset += 6;
}
}
else MsgDev( D_ERROR, "bad connectionless packet from %s:\n%s\n", NET_AdrToString( from ), args );
}
@ -1277,8 +1333,8 @@ void CL_ReadNetMessage( void )
if( Netchan_CopyFileFragments( &cls.netchan, &net_message ))
{
// Remove from resource request stuff.
// CL_ProcessFile( true, cls.netchan.incomingfilename );
// remove from resource request stuff.
CL_ProcessFile( true, cls.netchan.incomingfilename );
}
}
@ -1315,6 +1371,28 @@ void CL_ReadPackets( void )
}
/*
====================
CL_ProcessFile
A file has been received via the fragmentation/reassembly layer, put it in the right spot and
see if we have finished downloading files.
====================
*/
void CL_ProcessFile( BOOL successfully_received, const char *filename )
{
MsgDev( D_INFO, "Received %s, but file processing is not hooked up!!!\n", filename );
if( cls.downloadfileid == cls.downloadcount - 1 )
{
MsgDev( D_INFO,"All Files downloaded\n" );
BF_WriteByte( &cls.netchan.message, clc_stringcmd );
BF_WriteString( &cls.netchan.message, "continueloading" );
}
cls.downloadfileid++;
}
//=============================================================================
/*
@ -1443,6 +1521,7 @@ void CL_InitLocal( void )
// register our commands
Cmd_AddCommand ("pause", NULL, "pause the game (if the server allows pausing)" );
Cmd_AddCommand ("localservers", CL_LocalServers_f, "collect info about local servers" );
Cmd_AddCommand ("internetservers", CL_InternetServers_f, "collect info about internet servers" );
Cmd_AddCommand ("cd", CL_PlayCDTrack_f, "Play cd-track (not real cd-player of course)" );
Cmd_AddCommand ("userinfo", CL_Userinfo_f, "print current client userinfo" );

View File

@ -38,7 +38,6 @@ void UI_UpdateMenu( float realtime )
if( !menu.hInstance ) return;
menu.dllFuncs.pfnRedraw( realtime );
UI_UpdateUserinfo();
Con_DrawVersion();
}
void UI_KeyEvent( int key, qboolean down )

View File

@ -72,9 +72,9 @@ const char *svc_strings[256] =
"svc_packetentities",
"svc_deltapacketentities",
"svc_chokecount",
"svc_unused43",
"svc_resourcelist",
"svc_deltamovevars",
"svc_unused45",
"svc_customization",
"svc_unused46",
"svc_crosshairangle",
"svc_soundfade",
@ -97,8 +97,9 @@ typedef struct
qboolean parsing;
} msg_debug_t;
static msg_debug_t cls_message_debug;
static int starting_count;
static msg_debug_t cls_message_debug;
static int starting_count;
static resourcelist_t reslist;
const char *CL_MsgInfo( int cmd )
{
@ -450,6 +451,11 @@ void CL_ParseEvent( sizebuf_t *msg )
CL_ParseReliableEvent( msg, 0 );
}
void CL_ParseCustomization( sizebuf_t *msg )
{
// FIXME: ???
}
/*
=====================================================================
@ -695,6 +701,7 @@ void CL_ParseSetAngle( sizebuf_t *msg )
{
cl.refdef.cl_viewangles[0] = BF_ReadBitAngle( msg, 16 );
cl.refdef.cl_viewangles[1] = BF_ReadBitAngle( msg, 16 );
cl.refdef.cl_viewangles[2] = BF_ReadBitAngle( msg, 16 );
}
/*
@ -893,6 +900,88 @@ void CL_ServerInfo( sizebuf_t *msg )
Info_SetValueForKey( cl.serverinfo, key, value );
}
/*
==============
CL_CheckingResFile
==============
*/
void CL_CheckingResFile( char *pResFileName )
{
sizebuf_t buf;
byte data[32];
if( FS_FileExists( pResFileName, false ))
return; // already existing
cls.downloadcount++;
Msg( "Starting downloads file: %s\n", pResFileName );
if( cls.state == ca_disconnected ) return;
BF_Init( &buf, "ClientPacket", data, sizeof( data ));
BF_WriteByte( &buf, clc_resourcelist );
BF_WriteString( &buf, pResFileName );
if( !cls.netchan.remote_address.type ) // download in singleplayer ???
cls.netchan.remote_address.type = NA_LOOPBACK;
// make sure message will be delivered
Netchan_Transmit( &cls.netchan, BF_GetNumBytesWritten( &buf ), BF_GetData( &buf ));
}
/*
==============
CL_CheckingSoundResFile
==============
*/
void CL_CheckingSoundResFile( char *pResFileName )
{
string filepath;
Q_snprintf( filepath, sizeof( filepath ), "sound/%s", pResFileName );
CL_CheckingResFile( filepath );
}
/*
==============
CL_ParseResourceList
==============
*/
void CL_ParseResourceList( sizebuf_t *msg )
{
int i = 0;
Q_memset( &reslist, 0, sizeof( resourcelist_t ));
reslist.rescount = BF_ReadWord( msg ) - 1;
for( i = 0; i < reslist.rescount; i++ )
{
reslist.restype[i] = BF_ReadWord( msg );
Q_strncpy( reslist.resnames[i], BF_ReadString( msg ), CS_SIZE );
}
cls.downloadcount = 0;
for( i = 0; i < reslist.rescount; i++ )
{
if( reslist.restype[i] == t_sound )
CL_CheckingSoundResFile( reslist.resnames[i] );
else CL_CheckingResFile( reslist.resnames[i] );
}
if( !cls.downloadcount )
{
BF_WriteByte( &cls.netchan.message, clc_stringcmd );
BF_WriteString( &cls.netchan.message, "continueloading" );
}
}
/*
==============
CL_ParseDirector
@ -1229,6 +1318,9 @@ void CL_ParseServerMessage( sizebuf_t *msg )
case svc_deltamovevars:
CL_ParseMovevars( msg );
break;
case svc_customization:
CL_ParseCustomization( msg );
break;
case svc_centerprint:
CL_CenterPrint( BF_ReadString( msg ), 0.35f );
break;
@ -1293,6 +1385,9 @@ void CL_ParseServerMessage( sizebuf_t *msg )
}
}
break;
case svc_resourcelist:
CL_ParseResourceList( msg );
break;
case svc_director:
CL_ParseDirector( msg );
break;

View File

@ -47,6 +47,7 @@ void SCR_DrawFPS( void )
static int framecount = 0;
double newtime;
char fpsstring[32];
int offset;
if( cls.state != ca_active ) return;
if( !cl_showfps->integer || cl.background ) return;
@ -75,7 +76,9 @@ void SCR_DrawFPS( void )
Q_snprintf( fpsstring, sizeof( fpsstring ), "%4i fps", (int)(calc + 0.5));
MakeRGBA( color, 255, 255, 255, 255 );
}
Con_DrawString( scr_width->integer - 68, 4, fpsstring, color );
Con_DrawStringLen( fpsstring, &offset, NULL );
Con_DrawString( scr_width->integer - offset - 2, 4, fpsstring, color );
}
/*
@ -446,7 +449,7 @@ void SCR_Init( void )
if( host.state != HOST_RESTART && !UI_LoadProgs( ))
{
Msg( "^1Error: ^7can't initialize MainUI.dll\n" ); // there is non fatal for us
Msg( "^1Error: ^7can't initialize menu.dll\n" ); // there is non fatal for us
if( !host.developer ) host.developer = 1; // we need console, because menu is missing
}

View File

@ -866,11 +866,12 @@ CL_BreakModel
Create a shards
==============
*/
void CL_BreakModel( const vec3_t pos, const vec3_t size, const vec3_t dir, float random, float life, int count, int modelIndex, char flags )
void CL_BreakModel( const vec3_t pos, const vec3_t size, const vec3_t direction, float random, float life, int count, int modelIndex, char flags )
{
int i, frameCount;
TEMPENTITY *pTemp;
char type;
vec3_t dir;
if( !modelIndex ) return;
type = flags & BREAK_TYPEMASK;
@ -886,13 +887,28 @@ void CL_BreakModel( const vec3_t pos, const vec3_t size, const vec3_t dir, float
count = (size[0] * size[1] + size[1] * size[2] + size[2] * size[0]) / (3 * SHARD_VOLUME * SHARD_VOLUME);
}
VectorCopy( direction, dir );
// limit to 100 pieces
if( count > 100 ) count = 100;
if( VectorIsNull( direction ))
random *= 10;
for( i = 0; i < count; i++ )
{
vec3_t vecSpot;
if( VectorIsNull( direction ))
{
// random direction for each piece
dir[0] = Com_RandomFloat( -1.0f, 1.0f );
dir[1] = Com_RandomFloat( -1.0f, 1.0f );
dir[2] = Com_RandomFloat( -1.0f, 1.0f );
VectorNormalize( dir );
}
// fill up the box with stuff
vecSpot[0] = pos[0] + Com_RandomFloat( -0.5f, 0.5f ) * size[0];
vecSpot[1] = pos[1] + Com_RandomFloat( -0.5f, 0.5f ) * size[1];
@ -1287,46 +1303,46 @@ void CL_FunnelSprite( const vec3_t pos, int spriteIndex, int flags )
{
for( j = -256; j <= 256; j += 32 )
{
if( pTemp || !spriteIndex )
if( flags & SF_FUNNEL_REVERSE )
{
pPart = CL_AllocParticle( NULL );
pTemp = NULL;
VectorCopy( pos, m_vecPos );
dest[0] = pos[0] + i;
dest[1] = pos[1] + j;
dest[2] = pos[2] + Com_RandomFloat( 100, 800 );
// send particle heading to dest at a random speed
VectorSubtract( dest, m_vecPos, dir );
// velocity based on how far particle has to travel away from org
vel = dest[2] / 8;
}
else
{
m_vecPos[0] = pos[0] + i;
m_vecPos[1] = pos[1] + j;
m_vecPos[2] = pos[2] + Com_RandomFloat( 100, 800 );
// send particle heading to org at a random speed
VectorSubtract( pos, m_vecPos, dir );
// velocity based on how far particle starts from org
vel = m_vecPos[2] / 8;
}
if( pPart && spriteIndex && CL_PointContents( m_vecPos ) == CONTENTS_EMPTY )
{
pTemp = CL_TempEntAlloc( pos, Mod_Handle( spriteIndex ));
pPart = NULL;
}
else
{
pPart = CL_AllocParticle( NULL );
pTemp = NULL;
}
if( pTemp || pPart )
{
if( flags & SF_FUNNEL_REVERSE )
{
VectorCopy( pos, m_vecPos );
dest[0] = pos[0] + i;
dest[1] = pos[1] + j;
dest[2] = pos[2] + Com_RandomFloat( 100, 800 );
// send particle heading to dest at a random speed
VectorSubtract( dest, m_vecPos, dir );
// velocity based on how far particle has to travel away from org
vel = dest[2] / 8;
}
else
{
m_vecPos[0] = pos[0] + i;
m_vecPos[1] = pos[1] + j;
m_vecPos[2] = pos[2] + Com_RandomFloat( 100, 800 );
// send particle heading to org at a random speed
VectorSubtract( pos, m_vecPos, dir );
// velocity based on how far particle starts from org
vel = m_vecPos[2] / 8;
}
flDist = VectorNormalizeLength( dir ); // save the distance
if( vel < 64 ) vel = 64;
@ -1339,9 +1355,10 @@ void CL_FunnelSprite( const vec3_t pos, int spriteIndex, int flags )
VectorScale( dir, vel, pTemp->entity.baseline.origin );
pTemp->entity.curstate.rendermode = kRenderTransAdd;
pTemp->flags |= FTENT_FADEOUT;
pTemp->fadeSpeed = 2.0f;
pTemp->die = cl.time + Com_RandomFloat( life * 0.5, life );
pTemp->fadeSpeed = 3.0f;
pTemp->die = cl.time + life - Com_RandomFloat( 0.5f, 0.6f );
pTemp->entity.curstate.renderamt = pTemp->entity.baseline.renderamt = 255;
pTemp->entity.curstate.scale = 0.75f;
}
if( pPart )
@ -1352,7 +1369,7 @@ void CL_FunnelSprite( const vec3_t pos, int spriteIndex, int flags )
VectorScale( dir, vel, pPart->vel );
// die right when you get there
pPart->die += Com_RandomFloat( life * 0.5f, life );
pPart->die += life;
}
}
}
@ -2394,7 +2411,7 @@ void CL_UpdateFlashlight( cl_entity_t *pEnt )
VectorClear( view_ofs );
if(( pEnt->index - 1 ) == cl.playernum )
VectorCopy( cl.predicted_viewofs, view_ofs );
VectorCopy( cl.refdef.viewheight, view_ofs );
VectorAdd( pEnt->origin, view_ofs, vecSrc );
VectorMA( vecSrc, FLASHLIGHT_DISTANCE, forward, vecEnd );

View File

@ -182,6 +182,7 @@ void V_PostRender( void )
CL_DrawHUD( CL_CHANGELEVEL );
Con_DrawConsole();
UI_UpdateMenu( host.realtime );
Con_DrawVersion();
Con_DrawDebug(); // must be last
S_ExtraUpdate();
}

View File

@ -162,7 +162,8 @@ typedef enum
{
key_console = 0,
key_game,
key_menu
key_menu,
key_message
} keydest_t;
typedef enum
@ -441,6 +442,10 @@ typedef struct
const float *envshot_vieworg; // envshot position
string shotname;
// download info
int downloadcount;
int downloadfileid;
// demo loop control
int demonum; // -1 = don't play demos
string demos[MAX_DEMOS]; // when not playing
@ -531,6 +536,7 @@ void SCR_TimeRefresh_f( void );
void CL_Init( void );
void CL_SendCommand( void );
void CL_Disconnect_f( void );
void CL_ProcessFile( qboolean successfully_received, const char *filename );
void CL_GetChallengePacket( void );
void CL_PingServers_f( void );
void CL_ClearState( void );
@ -690,6 +696,7 @@ int Con_DrawString( int x, int y, const char *string, rgba_t setColor );
void Con_DefaultColor( int r, int g, int b );
void Con_CharEvent( int key );
void Key_Console( int key );
void Key_Message( int key );
void Con_Close( void );
//

View File

@ -560,19 +560,25 @@ void R_ShowTextures( void )
R_Set2DMode( true );
}
if( gl_showtextures->integer == TEX_DETAIL )
pglClearColor( 1.0f, 0.0f, 0.5f, 1.0f );
pglClear( GL_COLOR_BUFFER_BIT );
pglFinish();
if( gl_showtextures->integer == TEX_LIGHTMAP || gl_showtextures->integer == TEX_VGUI )
switch( gl_showtextures->integer )
{
case TEX_LIGHTMAP:
case TEX_VGUI:
case TEX_DETAIL:
// draw lightmaps as big images
base_w = 5;
base_h = 4;
}
else
{
break;
default:
base_w = 16;
base_h = 12;
break;
}
for( i = j = 0; i < MAX_TEXTURES; i++ )

View File

@ -1412,7 +1412,7 @@ void CL_DrawBeam( BEAM *pbeam )
}
frame = ((int)( pbeam->frame + cl.time * pbeam->frameRate ) % pbeam->frameCount );
rendermode = ( pbeam->flags & FBEAM_SOLID ) ? kRenderNormal : kRenderTransAdd;
rendermode = ( pbeam->flags & FBEAM_SOLID ) ? kRenderTransColor : kRenderTransAdd;
// set color
VectorSet( srcColor, pbeam->r, pbeam->g, pbeam->b );

View File

@ -566,6 +566,8 @@ static void R_DecalCreate( decalinfo_t *decalinfo, msurface_t *surf, float x, fl
if( count < MAX_OVERLAP_DECALS ) pold = NULL;
pdecal = R_DecalAlloc( pold );
if( !pdecal ) return; // r_decals == 0 ???
pdecal->flags = decalinfo->m_Flags;
VectorCopy( decalinfo->m_Position, pdecal->position );

View File

@ -201,7 +201,9 @@ typedef float GLmatrix[16];
#define GL_LUMINANCE8_ALPHA8 0x8045
#define GL_LUMINANCE12_ALPHA4 0x8046
#define GL_LUMINANCE12_ALPHA12 0x8047
#define GL_LUMINANCE16_ALPHA16 0x8048
#define GL_LUMINANCE16_ALPHA16 0x8048
#define GL_LUMINANCE 0x1909
#define GL_LUMINANCE_ALPHA 0x190A
#define GL_DEPTH_COMPONENT 0x1902
#define GL_INTENSITY 0x8049
#define GL_INTENSITY4 0x804A

View File

@ -124,6 +124,19 @@ GL_TexFilter
*/
void GL_TexFilter( gltexture_t *tex, qboolean update )
{
qboolean allowNearest;
switch( tex->texType )
{
case TEX_NOMIP:
case TEX_LIGHTMAP:
allowNearest = false;
break;
default:
allowNearest = true;
break;
}
// set texture filter
if( tex->flags & TF_DEPTHMAP )
{
@ -142,8 +155,16 @@ void GL_TexFilter( gltexture_t *tex, qboolean update )
}
else
{
pglTexParameteri( tex->target, GL_TEXTURE_MIN_FILTER, r_textureMagFilter );
pglTexParameteri( tex->target, GL_TEXTURE_MAG_FILTER, r_textureMagFilter );
if( r_textureMagFilter == GL_NEAREST && allowNearest )
{
pglTexParameteri( tex->target, GL_TEXTURE_MIN_FILTER, r_textureMagFilter );
pglTexParameteri( tex->target, GL_TEXTURE_MAG_FILTER, r_textureMagFilter );
}
else
{
pglTexParameteri( tex->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
pglTexParameteri( tex->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
}
}
}
else
@ -345,9 +366,17 @@ void R_TextureList_f( void )
case GL_RGB5:
Msg( "RGB5 " );
break;
case GL_LUMINANCE4_ALPHA4:
Msg( "L4A4 " );
break;
case GL_LUMINANCE_ALPHA:
case GL_LUMINANCE8_ALPHA8:
Msg( "L8A8 " );
break;
case GL_LUMINANCE4:
Msg( "L4 " );
break;
case GL_LUMINANCE:
case GL_LUMINANCE8:
Msg( "L8 " );
break;
@ -469,7 +498,7 @@ void GL_RoundImageDimensions( word *width, word *height, texFlags_t flags, qbool
GL_TextureFormat
===============
*/
static GLenum GL_TextureFormat( gltexture_t *tex, int samples )
static GLenum GL_TextureFormat( gltexture_t *tex, int *samples )
{
qboolean compress;
GLenum format;
@ -487,7 +516,7 @@ static GLenum GL_TextureFormat( gltexture_t *tex, int samples )
}
else if( compress )
{
switch( samples )
switch( *samples )
{
case 1: format = GL_COMPRESSED_LUMINANCE_ARB; break;
case 2: format = GL_COMPRESSED_LUMINANCE_ALPHA_ARB; break;
@ -503,25 +532,51 @@ static GLenum GL_TextureFormat( gltexture_t *tex, int samples )
{
int bits = gl_texturebits->integer;
switch( samples )
switch( *samples )
{
case 1: format = GL_LUMINANCE8; break;
case 2: format = GL_LUMINANCE8_ALPHA8; break;
case 3:
switch( bits )
if( gl_luminance_textures->integer )
{
case 16: format = GL_RGB5; break;
case 32: format = GL_RGB8; break;
default: format = GL_RGB; break;
switch( bits )
{
case 16: format = GL_LUMINANCE4; break;
case 32: format = GL_LUMINANCE8; break;
default: format = GL_LUMINANCE; break;
}
*samples = 1; // merge for right calc statistics
}
else
{
switch( bits )
{
case 16: format = GL_RGB5; break;
case 32: format = GL_RGB8; break;
default: format = GL_RGB; break;
}
}
break;
case 4:
default:
switch( bits )
if( gl_luminance_textures->integer )
{
case 16: format = GL_RGBA4; break;
case 32: format = GL_RGBA8; break;
default: format = GL_RGBA; break;
switch( bits )
{
case 16: format = GL_LUMINANCE4_ALPHA4; break;
case 32: format = GL_LUMINANCE8_ALPHA8; break;
default: format = GL_LUMINANCE_ALPHA; break;
}
*samples = 2; // merge for right calc statistics
}
else
{
switch( bits )
{
case 16: format = GL_RGBA4; break;
case 32: format = GL_RGBA8; break;
default: format = GL_RGBA; break;
}
}
break;
}
@ -809,7 +864,7 @@ static void GL_UploadTexture( rgbdata_t *pic, gltexture_t *tex, qboolean subImag
// determine format
inFormat = PFDesc[pic->type].glFormat;
outFormat = GL_TextureFormat( tex, samples );
outFormat = GL_TextureFormat( tex, &samples );
tex->format = outFormat;
// determine target
@ -930,6 +985,9 @@ int GL_LoadTexture( const char *name, const byte *buf, size_t size, int flags )
pic = FS_LoadImage( name, buf, size );
if( !pic ) return 0; // couldn't loading image
// force upload texture as RGB or RGBA (detail textures requires this)
if( flags & TF_FORCE_COLOR ) pic->flags |= IMAGE_HAS_COLOR;
// find a free texture slot
if( r_numTextures == MAX_TEXTURES )
Host_Error( "GL_LoadTexture: MAX_TEXTURES limit exceeds\n" );
@ -1003,6 +1061,9 @@ int GL_LoadTextureInternal( const char *name, rgbdata_t *pic, texFlags_t flags,
Host_Error( "Couldn't find texture %s for update\n", name );
}
// force upload texture as RGB or RGBA (detail textures requires this)
if( flags & TF_FORCE_COLOR ) pic->flags |= IMAGE_HAS_COLOR;
// find a free texture slot
if( r_numTextures == MAX_TEXTURES )
Host_Error( "GL_LoadTexture: MAX_TEXTURES limit exceeds\n" );
@ -1345,17 +1406,18 @@ static void R_InitBuiltinTextures( void )
char *name;
int *texnum;
rgbdata_t *(*init)( texFlags_t *flags );
int texType;
}
textures[] =
{
{ "*default", &tr.defaultTexture, R_InitDefaultTexture },
{ "*white", &tr.whiteTexture, R_InitWhiteTexture },
{ "*black", &tr.blackTexture, R_InitBlackTexture },
{ "*particle", &tr.particleTexture, R_InitParticleTexture },
{ "*particle2", &tr.particleTexture2, R_InitParticleTexture2 },
{ "*cintexture", &tr.cinTexture, R_InitCinematicTexture },
{ "*dlight", &tr.dlightTexture, R_InitDlightTexture },
{ "*sky", &tr.skyTexture, R_InitSkyTexture },
{ "*default", &tr.defaultTexture, R_InitDefaultTexture, TEX_SYSTEM },
{ "*white", &tr.whiteTexture, R_InitWhiteTexture, TEX_SYSTEM },
{ "*black", &tr.blackTexture, R_InitBlackTexture, TEX_SYSTEM },
{ "*particle", &tr.particleTexture, R_InitParticleTexture, TEX_SYSTEM },
{ "*particle2", &tr.particleTexture2, R_InitParticleTexture2, TEX_SYSTEM },
{ "*cintexture", &tr.cinTexture, R_InitCinematicTexture, TEX_NOMIP }, // force linear filter
{ "*dlight", &tr.dlightTexture, R_InitDlightTexture, TEX_LIGHTMAP },
{ "*sky", &tr.skyTexture, R_InitSkyTexture, TEX_SYSTEM },
{ NULL, NULL, NULL }
};
size_t i, num_builtin_textures = sizeof( textures ) / sizeof( textures[0] ) - 1;
@ -1369,7 +1431,7 @@ static void R_InitBuiltinTextures( void )
if( pic == NULL ) continue;
*textures[i].texnum = GL_LoadTextureInternal( textures[i].name, pic, flags, false );
GL_SetTextureType( *textures[i].texnum, TEX_SYSTEM );
GL_SetTextureType( *textures[i].texnum, textures[i].texType );
}
}
@ -1428,7 +1490,9 @@ void R_ShutdownImages( void )
GL_SelectTexture( i );
pglBindTexture( GL_TEXTURE_2D, 0 );
pglBindTexture( GL_TEXTURE_CUBE_MAP_ARB, 0 );
if( GL_Support( GL_TEXTURECUBEMAP_EXT ))
pglBindTexture( GL_TEXTURE_CUBE_MAP_ARB, 0 );
}
for( i = 0, image = r_textures; i < r_numTextures; i++, image++ )

View File

@ -29,6 +29,7 @@ extern byte *r_temppool;
#define BLOCK_HEIGHT 128 // lightmap block height
#define MAX_TEXTURES 4096
#define MAX_DETAIL_TEXTURES 256
#define MAX_LIGHTMAPS 128
#define SUBDIVIDE_SIZE 64
@ -62,7 +63,8 @@ typedef enum
TEX_LIGHTMAP, // lightmap textures
TEX_DECAL, // decals
TEX_VGUI, // vgui fonts or images
TEX_CUBEMAP // cubemap textures (sky)
TEX_CUBEMAP, // cubemap textures (sky)
TEX_DETAIL // detail textures
} texType_t;
typedef enum
@ -82,6 +84,7 @@ typedef enum
TF_MAKELUMA = BIT(12), // create luma from quake texture
TF_NORMALMAP = BIT(13), // is a normalmap
TF_LIGHTMAP = BIT(14), // is a lightmap
TF_FORCE_COLOR = BIT(15), // force upload monochrome textures as RGB (detail textures)
} texFlags_t;
typedef struct gltexture_s
@ -103,6 +106,10 @@ typedef struct gltexture_s
byte texType; // used for gl_showtextures
size_t size; // upload size for debug targets
// detail textures stuff
float xscale;
float yscale;
struct gltexture_s *nextHash;
} gltexture_t;
@ -308,7 +315,7 @@ void R_PushDlights( void );
void R_AnimateLight( void );
void R_MarkLights( dlight_t *light, int bit, mnode_t *node );
void R_LightDir( const vec3_t origin, vec3_t lightDir, float radius );
void R_LightForPoint( const vec3_t point, color24 *ambientLight, qboolean invLight, float radius );
void R_LightForPoint( const vec3_t point, color24 *ambientLight, qboolean invLight, qboolean useAmbient, float radius );
//
// gl_rmain.c
@ -425,7 +432,6 @@ void GL_SetRenderMode( int mode );
void R_RunViewmodelEvents( void );
void R_DrawViewModel( void );
int R_GetSpriteTexture( const struct model_s *m_pSpriteModel, int frame );
void R_LightForPoint( const vec3_t point, color24 *ambientLight, qboolean invLight, float radius );
void R_DecalShoot( int textureIndex, int entityIndex, int modelIndex, vec3_t pos, int flags, vec3_t saxis );
void R_RemoveEfrags( struct cl_entity_s *ent );
void R_AddEfrags( struct cl_entity_s *ent );
@ -444,6 +450,7 @@ enum
{
GL_OPENGL_110 = 0, // base
GL_WGL_SWAPCONTROL,
GL_WGL_PROCADDRESS,
GL_HARDWARE_GAMMA_CONTROL,
GL_ARB_VERTEX_BUFFER_OBJECT_EXT,
GL_ENV_COMBINE_EXT,
@ -576,6 +583,7 @@ extern convar_t *gl_texturemode;
extern convar_t *gl_texture_lodbias;
extern convar_t *gl_showtextures;
extern convar_t *gl_compress_textures;
extern convar_t *gl_luminance_textures;
extern convar_t *gl_wireframe;
extern convar_t *gl_allow_static;
extern convar_t *gl_picmip;
@ -592,6 +600,7 @@ extern convar_t *r_norefresh;
extern convar_t *r_lighting_extended;
extern convar_t *r_lighting_modulate;
extern convar_t *r_lighting_ambient;
extern convar_t *r_detailtextures;
extern convar_t *r_faceplanecull;
extern convar_t *r_drawentities;
extern convar_t *r_adjust_fov;

View File

@ -125,11 +125,11 @@ void R_MarkLights( dlight_t *light, int bit, mnode_t *node )
}
// mark the polygons
surf = cl.worldmodel->surfaces + node->firstsurface;
surf = RI.currentmodel->surfaces + node->firstsurface;
for( i = 0; i < node->numsurfaces; i++, surf++ )
{
mextrasurf_t *info = SURF_INFO( surf, cl.worldmodel );
mextrasurf_t *info = SURF_INFO( surf, RI.currentmodel );
if( !BoundsAndSphereIntersect( info->mins, info->maxs, light->origin, light->radius ))
continue; // no intersection
@ -160,6 +160,9 @@ void R_PushDlights( void )
// advanced yet for this frame
l = cl_dlights;
RI.currententity = clgame.entities;
RI.currentmodel = RI.currententity->model;
for( i = 0; i < MAX_DLIGHTS; i++, l++ )
{
if( l->die < cl.time || !l->radius )
@ -168,7 +171,7 @@ void R_PushDlights( void )
if( R_CullSphere( l->origin, l->radius, 15 ))
continue;
R_MarkLights( l, 1<<i, cl.worldmodel->nodes );
R_MarkLights( l, 1<<i, RI.currentmodel->nodes );
}
}
@ -268,7 +271,7 @@ static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, const vec3
R_LightForPoint
=================
*/
void R_LightForPoint( const vec3_t point, color24 *ambientLight, qboolean invLight, float radius )
void R_LightForPoint( const vec3_t point, color24 *ambientLight, qboolean invLight, qboolean useAmbient, float radius )
{
dlight_t *dl;
pmtrace_t trace;
@ -368,6 +371,7 @@ void R_LightForPoint( const vec3_t point, color24 *ambientLight, qboolean invLig
// R_RecursiveLightPoint didn't hit anything, so use default value
ambient = bound( 0.1f, r_lighting_ambient->value, 1.0f );
if( !useAmbient ) ambient = 0.0f; // clear ambient
ambientLight->r = 255 * ambient;
ambientLight->g = 255 * ambient;
ambientLight->b = 255 * ambient;

View File

@ -63,6 +63,9 @@ static qboolean R_StaticEntity( cl_entity_t *ent )
if( ent->curstate.frame || ent->model->flags & MODEL_CONVEYOR )
return false;
if( ent->curstate.scale ) // waveheight specified
return false;
if( !VectorIsNull( ent->origin ) || !VectorIsNull( ent->angles ))
return false;
@ -375,7 +378,9 @@ R_Clear
*/
static void R_Clear( int bitMask )
{
int bits;
int bits;
pglClearColor( 0.5f, 0.5f, 0.5f, 1.0f );
bits = GL_DEPTH_BUFFER_BIT;
@ -570,8 +575,9 @@ static void R_SetupFrame( void )
R_AnimateLight();
R_RunViewmodelEvents();
tr.framecount++;
// g-cont. keep actual frame for all viewpasses
if( !RI.refdef.nextView ) tr.framecount++;
// sort translucents entities by rendermode and distance
qsort( tr.trans_entities, tr.num_trans_entities, sizeof( cl_entity_t* ), R_TransEntityCompare );
@ -828,7 +834,7 @@ void R_DrawEntitiesOnList( void )
pglDepthMask( GL_FALSE );
glState.drawTrans = true;
// then draw translicent entities
// then draw translucent entities
for( i = 0; i < tr.num_trans_entities; i++ )
{
if( RI.refdef.onlyClientDraw )

View File

@ -18,6 +18,249 @@ GNU General Public License for more details.
#include "gl_local.h"
#include "mod_local.h"
typedef struct
{
const char *texname;
const char *detail;
const char material;
int lMin;
int lMax;
} dmaterial_t;
// default rules for apply detail textures.
// move this to external script ?
static const dmaterial_t detail_table[] =
{
{ "crt", "dt_conc", 'C', 0, 0 }, // concrete
{ "rock", "dt_rock", 'C', 0, 0 },
{ "conc", "dt_conc", 'C', 0, 0 },
{ "wall", "dt_brick", 'C', 0, 0 },
{ "crete", "dt_conc", 'C', 0, 0 },
{ "generic", "dt_brick", 'C', 0, 0 },
{ "metal", "dt_metal%i", 'M', 1, 2 }, // metal
{ "mtl", "dt_metal%i", 'M', 1, 2 },
{ "pipe", "dt_metal%i", 'M', 1, 2 },
{ "elev", "dt_metal%i", 'M', 1, 2 },
{ "sign", "dt_metal%i", 'M', 1, 2 },
{ "barrel", "dt_metal%i", 'M', 1, 2 },
{ "bath", "dt_ssteel1", 'M', 1, 2 },
{ "refbridge", "dt_metal%i", 'M', 1, 2 },
{ "panel", "dt_ssteel1", 'M', 0, 0 },
{ "brass", "dt_ssteel1", 'M', 0, 0 },
{ "car", "dt_metal%i", 'M', 1, 2 },
{ "circuit", "dt_metal%i", 'M', 1, 2 },
{ "steel", "dt_ssteel1", 'M', 0, 0 },
{ "dirt", "dt_ground%i", 'D', 1, 5 }, // dirt
{ "drt", "dt_ground%i", 'D', 1, 5 },
{ "out", "dt_ground%i", 'D', 1, 5 },
{ "grass", "dt_grass1", 'D', 0, 0 },
{ "mud", "dt_carpet1", 'D', 0, 0 }, // FIXME
{ "vent", "dt_ssteel1", 'V', 1, 4 }, // vent
{ "duct", "dt_ssteel1", 'V', 1, 4 },
{ "tile", "dt_smooth%i", 'T', 1, 2 },
{ "labflr", "dt_smooth%i", 'T', 1, 2 },
{ "bath", "dt_smooth%i", 'T', 1, 2 },
{ "grate", "dt_stone%i", 'G', 1, 4 }, // vent
{ "stone", "dt_stone%i", 'G', 1, 4 },
{ "grt", "dt_stone%i", 'G', 1, 4 },
{ "wood", "dt_wood%i", 'W', 1, 3 },
{ "wd", "dt_wood%i", 'W', 1, 3 },
{ "table", "dt_wood%i", 'W', 1, 3 },
{ "board", "dt_wood%i", 'W', 1, 3 },
{ "chair", "dt_wood%i", 'W', 1, 3 },
{ "brd", "dt_wood%i", 'W', 1, 3 },
{ "carp", "dt_carpet1", 'W', 1, 3 },
{ "book", "dt_wood%i", 'W', 1, 3 },
{ "box", "dt_wood%i", 'W', 1, 3 },
{ "cab", "dt_wood%i", 'W', 1, 3 },
{ "couch", "dt_wood%i", 'W', 1, 3 },
{ "crate", "dt_wood%i", 'W', 1, 3 },
{ "poster", "dt_plaster%i", 'W', 1, 2 },
{ "sheet", "dt_plaster%i", 'W', 1, 2 },
{ "stucco", "dt_plaster%i", 'W', 1, 2 },
{ "comp", "dt_smooth1", 'P', 0, 0 },
{ "cmp", "dt_smooth1", 'P', 0, 0 },
{ "elec", "dt_smooth1", 'P', 0, 0 },
{ "vend", "dt_smooth1", 'P', 0, 0 },
{ "monitor", "dt_smooth1", 'P', 0, 0 },
{ "phone", "dt_smooth1", 'P', 0, 0 },
{ "glass", "dt_ssteel1", 'Y', 0, 0 },
{ "window", "dt_ssteel1", 'Y', 0, 0 },
{ "flesh", "dt_rough1", 'F', 0, 0 },
{ "meat", "dt_rough1", 'F', 0, 0 },
{ "fls", "dt_rough1", 'F', 0, 0 },
{ "ground", "dt_ground%i", 'D', 1, 5 },
{ "gnd", "dt_ground%i", 'D', 1, 5 },
{ "snow", "dt_snow%i", 'O', 1, 2 }, // snow
{ NULL, NULL, 0, 0, 0 }
};
static const char *R_DetailTextureForName( const char *name )
{
const dmaterial_t *table;
if( !name || !*name ) return NULL;
if( !Q_strnicmp( name, "sky", 3 ))
return NULL; // never details for sky
// never apply details for liquids
if( !Q_strnicmp( name + 1, "!lava", 5 ))
return NULL;
if( !Q_strnicmp( name + 1, "!slime", 6 ))
return NULL;
if( !Q_strnicmp( name, "!cur_90", 7 ))
return NULL;
if( !Q_strnicmp( name, "!cur_0", 6 ))
return NULL;
if( !Q_strnicmp( name, "!cur_270", 8 ))
return NULL;
if( !Q_strnicmp( name, "!cur_180", 8 ))
return NULL;
if( !Q_strnicmp( name, "!cur_up", 7 ))
return NULL;
if( !Q_strnicmp( name, "!cur_dwn", 8 ))
return NULL;
if( name[0] == '!' )
return NULL;
// never apply details to the special textures
if( !Q_strnicmp( name, "origin", 6 ))
return NULL;
if( !Q_strnicmp( name, "clip", 4 ))
return NULL;
if( !Q_strnicmp( name, "hint", 4 ))
return NULL;
if( !Q_strnicmp( name, "skip", 4 ))
return NULL;
if( !Q_strnicmp( name, "translucent", 11 ))
return NULL;
if( !Q_strnicmp( name, "3dsky", 5 ))
return NULL;
if( name[0] == '@' )
return NULL;
// last check ...
if( !Q_strnicmp( name, "null", 4 ))
return NULL;
for( table = detail_table; table && table->texname; table++ )
{
if( Q_stristr( name, table->texname ))
{
if(( table->lMin + table->lMax ) > 0 )
return va( table->detail, Com_RandomLong( table->lMin, table->lMax ));
return table->detail;
}
}
return "dt_smooth1"; // default
}
void R_CreateDetailTexturesList( const char *filename )
{
file_t *detail_txt = NULL;
const char *detail_name, *texname;
int i;
for( i = 0; i < cl.worldmodel->numtextures; i++ )
{
texname = cl.worldmodel->textures[i]->name;
detail_name = R_DetailTextureForName( texname );
if( !detail_name ) continue;
// detailtexture detected
if( detail_name )
{
if( !detail_txt ) detail_txt = FS_Open( filename, "wb", false );
if( !detail_txt )
{
MsgDev( D_ERROR, "Can't write %s\n", filename );
break;
}
// store detailtexture description
FS_Printf( detail_txt, "%s detail/%s 10.0 10.0\n", texname, detail_name );
}
}
if( detail_txt ) FS_Close( detail_txt );
}
void R_ParseDetailTextures( const char *filename )
{
char *afile, *pfile;
string token, texname, detail_texname;
float xScale, yScale;
texture_t *tex;
int i;
if( r_detailtextures->integer >= 2 && !FS_FileExists( filename, false ))
{
// use built-in generator for detail textures
R_CreateDetailTexturesList( filename );
}
afile = FS_LoadFile( filename, NULL, false );
if( !afile ) return;
pfile = afile;
// format: 'texturename' 'detailtexture' 'xScale' 'yScale'
while(( pfile = COM_ParseFile( pfile, token )) != NULL )
{
texname[0] = '\0';
// read texname
if( token[0] == '{' )
{
// NOTE: COM_ParseFile handled some symbols seperately
// this code will be fix it
pfile = COM_ParseFile( pfile, token );
Q_strncat( texname, "{", sizeof( texname ));
Q_strncat( texname, token, sizeof( texname ));
}
else Q_strncpy( texname, token, sizeof( texname ));
// read detailtexture name
pfile = COM_ParseFile( pfile, token );
Q_snprintf( detail_texname, sizeof( detail_texname ), "gfx/%s.tga", token );
// read scales
pfile = COM_ParseFile( pfile, token );
xScale = Q_atof( token );
pfile = COM_ParseFile( pfile, token );
yScale = Q_atof( token );
if( xScale <= 0.0f || yScale <= 0.0f )
continue;
// search for existing texture and uploading detail texture
for( i = 0; i < cl.worldmodel->numtextures; i++ )
{
tex = cl.worldmodel->textures[i];
if( Q_stricmp( tex->name, texname ))
continue;
tex->dt_texturenum = GL_LoadTexture( detail_texname, NULL, 0, TF_FORCE_COLOR );
// texture is loaded
if( tex->dt_texturenum )
{
gltexture_t *glt;
GL_SetTextureType( tex->dt_texturenum, TEX_DETAIL );
glt = R_GetTexture( tex->dt_texturenum );
glt->xscale = xScale;
glt->yscale = yScale;
}
break;
}
}
Mem_Free( afile );
}
void R_NewMap( void )
{
int i;
@ -45,4 +288,16 @@ void R_NewMap( void )
cl.worldmodel->textures[i]->texturechain = NULL;
}
// upload detailtextures
if( r_detailtextures->integer )
{
string mapname, filepath;
Q_strncpy( mapname, cl.worldmodel->name, sizeof( mapname ));
FS_StripExtension( mapname );
Q_sprintf( filepath, "%s_detail.txt", mapname );
R_ParseDetailTextures( filepath );
}
}

View File

@ -35,6 +35,8 @@ static byte visbytes[MAX_MAP_LEAFS/8];
static uint r_blocklights[BLOCK_WIDTH*BLOCK_HEIGHT*3];
static glpoly_t *fullbright_polys[MAX_TEXTURES];
static qboolean draw_fullbrights = false;
static glpoly_t *detail_polys[MAX_TEXTURES];
static qboolean draw_details = false;
static gllightmapstate_t gl_lms;
static msurface_t *skychain = NULL;
@ -163,7 +165,8 @@ static void SubdividePolygon_r( msurface_t *warpface, int numverts, float *verts
total_s += s;
total_t += t;
if(!( warpface->flags & SURF_DRAWTURB ))
// for speed reasons
if( !( warpface->flags & SURF_DRAWTURB ))
{
// lightmap texture coordinates
s = DotProduct( verts, warpface->texinfo->vecs[0] ) + warpface->texinfo->vecs[0][3];
@ -191,8 +194,12 @@ static void SubdividePolygon_r( msurface_t *warpface, int numverts, float *verts
VectorScale( total, vertsDiv, poly->verts[0] );
poly->verts[0][3] = total_s * vertsDiv;
poly->verts[0][4] = total_t * vertsDiv;
poly->verts[0][5] = total_ls * vertsDiv;
poly->verts[0][6] = total_lt * vertsDiv;
if( !( warpface->flags & SURF_DRAWTURB ))
{
poly->verts[0][5] = total_ls * vertsDiv;
poly->verts[0][6] = total_lt * vertsDiv;
}
// copy first vertex to last
Q_memcpy( poly->verts[i+1], poly->verts[1], sizeof( poly->verts[0] ));
@ -250,13 +257,6 @@ void GL_BuildPolygonFromSurface( msurface_t *fa )
if( !fa->texinfo || !fa->texinfo->texture )
return; // bad polygon ?
if( fa->texinfo->texture->anim_total < 0 )
{
// random tileing. subdivide the polygon
GL_SubdivideSurface( fa );
return;
}
// reconstruct the polygon
pedges = loadmodel->edges;
lnumverts = fa->numedges;
@ -324,17 +324,17 @@ texture_t *R_TextureAnimation( texture_t *base, int surfacenum )
int reletive;
int count, speed;
// random tileng textures
// random tiling textures
if( base->anim_total < 0 )
{
reletive = surfacenum % abs( base->anim_total );
reletive = abs( surfacenum ) % abs( base->anim_total );
count = 0;
while( base->anim_min > reletive || base->anim_max <= reletive )
{
base = base->anim_next;
if( !base ) Host_Error( "R_TextureAnimation: broken loop\n" );
if( ++count > 100 ) Host_Error( "R_TextureAnimation: infinite loop\n" );
if( !base ) Host_Error( "R_TextureRandomTiling: broken loop\n" );
if( ++count > 100 ) Host_Error( "R_TextureRandomTiling: infinite loop\n" );
}
return base;
}
@ -618,14 +618,13 @@ static void R_BuildLightMap( msurface_t *surf, byte *dest, int stride )
DrawGLPoly
================
*/
void DrawGLPoly( glpoly_t *p, texture_t *tex )
void DrawGLPoly( glpoly_t *p, float xScale, float yScale )
{
float *v;
float sOffset, sy;
float tOffset, cy;
cl_entity_t *e = RI.currententity;
qboolean random_tiles = false;
texture_t *t;
glpoly_t *base;
int i, hasScale = false;
if( p->flags & SURF_CONVEYOR )
{
@ -657,29 +656,21 @@ void DrawGLPoly( glpoly_t *p, texture_t *tex )
sOffset = tOffset = 0.0f;
}
if( p && p->next ) random_tiles = true;
if( xScale != 0.0f && yScale != 0.0f )
hasScale = true;
for( base = p; p != NULL; p = p->next )
pglBegin( GL_POLYGON );
for( i = 0, v = p->verts[0]; i < p->numverts; i++, v += VERTEXSIZE )
{
float *v;
int i;
if( hasScale )
pglTexCoord2f(( v[3] + sOffset ) * xScale, ( v[4] + tOffset ) * yScale );
else pglTexCoord2f( v[3] + sOffset, v[4] + tOffset );
if( random_tiles && tex )
{
t = R_TextureAnimation( tex, base - p );
GL_MBind( t->gl_texturenum );
}
pglBegin( GL_POLYGON );
for( i = 0, v = p->verts[0]; i < p->numverts; i++, v += VERTEXSIZE )
{
pglTexCoord2f( v[3] + sOffset, v[4] + tOffset );
pglVertex3fv( v );
}
pglEnd();
pglVertex3fv( v );
}
pglEnd();
}
/*
@ -698,23 +689,19 @@ void DrawGLPolyChain( glpoly_t *p, float soffset, float toffset )
for( ; p != NULL; p = p->chain )
{
glpoly_t *p2;
float *v;
int i;
for( p2 = p; p2 != NULL; p2 = p2->next )
{
pglBegin( GL_POLYGON );
pglBegin( GL_POLYGON );
v = p2->verts[0];
for( i = 0; i < p2->numverts; i++, v += VERTEXSIZE )
{
if( !dynamic ) pglTexCoord2f( v[5], v[6] );
else pglTexCoord2f( v[5] - soffset, v[6] - toffset );
pglVertex3fv( v );
}
pglEnd ();
v = p->verts[0];
for( i = 0; i < p->numverts; i++, v += VERTEXSIZE )
{
if( !dynamic ) pglTexCoord2f( v[5], v[6] );
else pglTexCoord2f( v[5] - soffset, v[6] - toffset );
pglVertex3fv( v );
}
pglEnd ();
}
}
@ -822,7 +809,7 @@ void R_BlendLightmaps( void )
info = SURF_INFO( surf, RI.currentmodel );
// try uploading the block now
if( !LM_AllocBlock( smax, tmax, &info->dlight_s, &info->dlight_t ) )
if( !LM_AllocBlock( smax, tmax, &info->dlight_s, &info->dlight_t ))
Host_Error( "AllocBlock: full\n" );
base = gl_lms.lightmap_buffer;
@ -887,7 +874,7 @@ void R_RenderFullbrights( void )
{
if( p->flags & SURF_DRAWTURB )
EmitWaterPolys( p, ( p->flags & SURF_NOCULL ));
else DrawGLPoly( p, NULL ); // disable random tiling (chain is already used)
else DrawGLPoly( p, 0.0f, 0.0f );
}
fullbright_polys[i] = NULL;
@ -901,6 +888,50 @@ void R_RenderFullbrights( void )
draw_fullbrights = false;
}
/*
================
R_RenderDetails
================
*/
void R_RenderDetails( void )
{
gltexture_t *glt;
glpoly_t *p;
int i;
if( !draw_details )
return;
pglEnable( GL_BLEND );
pglBlendFunc( GL_DST_COLOR, GL_SRC_COLOR );
pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL );
if( RI.currententity->curstate.rendermode == kRenderTransAlpha )
pglDepthFunc( GL_EQUAL );
for( i = 1; i < MAX_TEXTURES; i++ )
{
if( !detail_polys[i] )
continue;
GL_Bind( GL_TEXTURE0, i );
glt = R_GetTexture( i );
for( p = detail_polys[i]; p; p = p->next )
DrawGLPoly( p, glt->xscale, glt->yscale );
detail_polys[i] = NULL;
}
pglDisable( GL_BLEND );
pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
if( RI.currententity->curstate.rendermode == kRenderTransAlpha )
pglDepthFunc( GL_LEQUAL );
draw_details = false;
}
/*
================
R_RenderBrushPoly
@ -926,7 +957,7 @@ void R_RenderBrushPoly( msurface_t *fa )
return;
}
t = R_TextureAnimation( fa->texinfo->texture, 0 );
t = R_TextureAnimation( fa->texinfo->texture, fa - RI.currententity->model->surfaces );
GL_MBind( t->gl_texturenum );
if( fa->flags & SURF_DRAWTURB )
@ -943,8 +974,16 @@ void R_RenderBrushPoly( msurface_t *fa )
fullbright_polys[t->fb_texturenum] = fa->polys;
draw_fullbrights = true;
}
else if( r_detailtextures->integer && t->dt_texturenum )
{
// HACKHACK: store details in poly->next (only for non-water surfaces)
// can't rendering both luma and detail textures simultaneously
fa->polys->next = detail_polys[t->dt_texturenum];
detail_polys[t->dt_texturenum] = fa->polys;
draw_details = true;
}
DrawGLPoly( fa->polys, fa->texinfo->texture );
DrawGLPoly( fa->polys, 0.0f, 0.0f );
DrawSurfaceDecals( fa );
// check for lightmap modification
@ -1214,10 +1253,12 @@ void R_DrawBrushModel( cl_entity_t *e )
{
int i, k, num_sorted;
qboolean need_sort = false;
vec3_t origin_l, oldorigin;
vec3_t mins, maxs;
msurface_t *psurf;
model_t *clmodel;
qboolean rotated;
matrix4x4 imatrix;
dlight_t *l;
clmodel = e->model;
@ -1258,25 +1299,18 @@ void R_DrawBrushModel( cl_entity_t *e )
Matrix4x4_VectorITransform( RI.objectMatrix, temp, modelorg );
}
// calculate dynamic lighting for bmodel if it's not an
// instanced model
if( clmodel->firstmodelsurface != 0 )
// calculate dynamic lighting for bmodel
for( k = 0, l = cl_dlights; k < MAX_DLIGHTS; k++, l++ )
{
vec3_t origin_l, oldorigin;
matrix4x4 imatrix;
if( l->die < cl.time || !l->radius )
continue;
for( k = 0, l = cl_dlights; k < MAX_DLIGHTS; k++, l++ )
{
if( l->die < cl.time || !l->radius )
continue;
VectorCopy( l->origin, oldorigin ); // save oldorigin
Matrix4x4_Invert_Simple( imatrix, RI.objectMatrix );
Matrix4x4_VectorTransform( imatrix, l->origin, origin_l );
VectorCopy( origin_l, l->origin ); // move light in bmodel space
R_MarkLights( l, 1<<k, clmodel->nodes + clmodel->hulls[0].firstclipnode );
VectorCopy( oldorigin, l->origin ); // restore lightorigin
}
VectorCopy( l->origin, oldorigin ); // save oldorigin
Matrix4x4_Invert_Simple( imatrix, RI.objectMatrix );
Matrix4x4_VectorTransform( imatrix, l->origin, origin_l );
VectorCopy( origin_l, l->origin ); // move light in bmodel space
R_MarkLights( l, 1<<k, clmodel->nodes + clmodel->hulls[0].firstclipnode );
VectorCopy( oldorigin, l->origin ); // restore lightorigin
}
// setup the rendermode
@ -1349,6 +1383,8 @@ void R_DrawBrushModel( cl_entity_t *e )
R_BlendLightmaps();
R_RenderFullbrights();
R_RenderDetails();
R_LoadIdentity(); // restore worldmatrix
}
@ -1370,16 +1406,12 @@ void R_DrawStaticModel( cl_entity_t *e )
if( R_CullBox( clmodel->mins, clmodel->maxs, RI.clipFlags ))
return;
// calculate dynamic lighting for bmodel if it's not an
// instanced model
if( clmodel->firstmodelsurface != 0 )
// calculate dynamic lighting for bmodel
for( k = 0, l = cl_dlights; k < MAX_DLIGHTS; k++, l++ )
{
for( k = 0, l = cl_dlights; k < MAX_DLIGHTS; k++, l++ )
{
if( l->die < cl.time || !l->radius )
continue;
R_MarkLights( l, 1<<k, clmodel->nodes + clmodel->hulls[0].firstclipnode );
}
if( l->die < cl.time || !l->radius )
continue;
R_MarkLights( l, 1<<k, clmodel->nodes + clmodel->hulls[0].firstclipnode );
}
psurf = &clmodel->surfaces[clmodel->firstmodelsurface];
@ -1463,13 +1495,12 @@ void R_PlaneForMirror( msurface_t *surf, mplane_t *out )
R_RotateForEntity( ent );
else R_TranslateForEntity( ent );
// tranform mirror plane by entity matrix
// transform mirror plane by entity matrix
if( !tr.modelviewIdentity )
{
mplane_t tmp;
tmp = *out;
Matrix4x4_TransformPositivePlane( RI.objectMatrix, tmp.normal, tmp.dist, out->normal, &out->dist );
}
}
@ -1663,9 +1694,11 @@ R_DrawTriangleOutlines
*/
void R_DrawTriangleOutlines( void )
{
int i, j;
glpoly_t *p, *p2;
int i, j;
msurface_t *surf;
glpoly_t *p;
float *v;
if( !gl_wireframe->integer )
return;
@ -1674,30 +1707,38 @@ void R_DrawTriangleOutlines( void )
pglColor4f( 1.0f, 1.0f, 1.0f, 1.0f );
pglPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
// render static surfaces first
for( i = 0; i < MAX_LIGHTMAPS; i++ )
{
msurface_t *surf;
float *v;
for( surf = gl_lms.lightmap_surfaces[i]; surf != NULL; surf = surf->lightmapchain )
{
p = surf->polys;
// for( ; p != NULL; p = p->chain )
for( ; p != NULL; p = p->chain )
{
p2 = p;
for( p2 = p; p2; p2 = p2->next )
{
pglBegin( GL_POLYGON );
for( j = 0, v = p2->verts[0]; j < p2->numverts; j++, v += VERTEXSIZE )
pglVertex3fv( v );
pglEnd ();
}
pglBegin( GL_POLYGON );
v = p->verts[0];
for( j = 0; j < p->numverts; j++, v += VERTEXSIZE )
pglVertex3fv( v );
pglEnd ();
}
}
}
// render surfaces with dynamic lightmaps
for( surf = gl_lms.dynamic_surfaces; surf != NULL; surf = surf->lightmapchain )
{
p = surf->polys;
for( ; p != NULL; p = p->chain )
{
pglBegin( GL_POLYGON );
v = p->verts[0];
for( j = 0; j < p->numverts; j++, v += VERTEXSIZE )
pglVertex3fv( v );
pglEnd ();
}
}
pglPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
pglEnable( GL_DEPTH_TEST );
pglEnable( GL_TEXTURE_2D );
@ -1719,6 +1760,8 @@ void R_DrawWorld( void )
VectorCopy( RI.cullorigin, modelorg );
Q_memset( gl_lms.lightmap_surfaces, 0, sizeof( gl_lms.lightmap_surfaces ));
Q_memset( fullbright_polys, 0, sizeof( fullbright_polys ));
Q_memset( detail_polys, 0, sizeof( detail_polys ));
RI.currentWaveHeight = RI.waveHeight;
GL_SetRenderMode( kRenderNormal );
gl_lms.dynamic_surfaces = NULL;
@ -1735,6 +1778,7 @@ void R_DrawWorld( void )
R_BlendLightmaps();
R_RenderFullbrights();
R_RenderDetails();
if( skychain )
R_DrawSkyBox();

File diff suppressed because it is too large Load Diff

View File

@ -26,6 +26,7 @@ GNU General Public License for more details.
#define MAPSPRITE_SIZE 128
convar_t *r_sprite_lerping;
convar_t *r_sprite_lighting;
char group_suffix[8];
static vec3_t sprite_mins, sprite_maxs;
static float sprite_radius;
@ -39,6 +40,7 @@ R_SpriteInit
void R_SpriteInit( void )
{
r_sprite_lerping = Cvar_Get( "r_sprite_lerping", "1", CVAR_ARCHIVE, "enables sprite animation lerping" );
r_sprite_lighting = Cvar_Get( "r_sprite_lighting", "1", CVAR_ARCHIVE, "enables sprite lighting (blood etc)" );
}
/*
@ -459,7 +461,7 @@ mspriteframe_t *R_GetSpriteFrame( const model_t *pModel, int frame, float yaw )
}
else if( psprite->frames[frame].type == FRAME_ANGLED )
{
int angleframe = (int)(( RI.refdef.viewangles[1] - yaw ) / 360 * 8 + 0.5 - 4) & 7;
int angleframe = (int)(Q_rint(( RI.refdef.viewangles[1] - yaw + 45.0f ) / 360 * 8) - 4) & 7;
// e.g. doom-style sprite monsters
pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr;
@ -590,7 +592,7 @@ float R_GetSpriteFrameInterpolant( cl_entity_t *ent, mspriteframe_t **oldframe,
{
// e.g. doom-style sprite monsters
float yaw = ent->angles[YAW];
int angleframe = (int)(( RI.refdef.viewangles[1] - yaw ) / 360 * 8 + 0.5 - 4) & 7;
int angleframe = (int)(Q_rint(( RI.refdef.viewangles[1] - yaw + 45.0f ) / 360 * 8) - 4) & 7;
if( m_fDoInterp )
{
@ -732,6 +734,8 @@ static float R_SpriteGlowBlend( vec3_t origin, int rendermode, int renderfx, int
if( renderfx == kRenderFxNoDissipation )
return (float)alpha * (1.0f / 255.0f);
*pscale = 0.0f; // variable sized glow
// UNDONE: Tweak these magic numbers (19000 - falloff & 200 - sprite size)
brightness = 19000.0 / ( dist * dist );
brightness = bound( 0.01f, brightness, 1.0f );
@ -811,7 +815,7 @@ static void R_DrawSpriteQuad( mspriteframe_t *frame, vec3_t org, vec3_t v_right,
static _inline qboolean R_SpriteHasLightmap( cl_entity_t *e, int texFormat )
{
if( !r_lighting_extended->integer )
if( !r_sprite_lighting->integer )
return false;
if( texFormat != SPR_ALPHTEST )
@ -930,7 +934,7 @@ void R_DrawSpriteModel( cl_entity_t *e )
qboolean invLight;
invLight = (e->curstate.effects & EF_INVLIGHT) ? true : false;
R_LightForPoint( origin, &lightColor, invLight, sprite_radius );
R_LightForPoint( origin, &lightColor, invLight, true, sprite_radius );
color2[0] = (float)lightColor.r * ( 1.0f / 255.0f );
color2[1] = (float)lightColor.g * ( 1.0f / 255.0f );
color2[2] = (float)lightColor.b * ( 1.0f / 255.0f );
@ -1013,7 +1017,7 @@ void R_DrawSpriteModel( cl_entity_t *e )
if( ilerp != 0 )
{
pglColor4f( color[0], color[1], color[2], flAlpha * ilerp );
GL_Bind( GL_TEXTURE0, frame->gl_texturenum );
GL_Bind( GL_TEXTURE0, oldframe->gl_texturenum );
R_DrawSpriteQuad( oldframe, origin, v_right, v_up, scale );
}

View File

@ -57,10 +57,11 @@ typedef struct studiolight_s
convar_t *r_studio_lerping;
convar_t *r_studio_lambert;
convar_t *r_studio_lighting;
convar_t *r_drawviewmodel;
convar_t *r_customdraw_playermodel;
convar_t *cl_himodels;
cvar_t r_shadows = { "r_shadows", "0", 0, 0 };
cvar_t r_shadows = { "r_shadows", "0", 0, 0 }; // dead cvar. especially disabled
cvar_t r_shadowalpha = { "r_shadowalpha", "0.5", 0, 0 };
static r_studio_interface_t *pStudioDraw;
static float aliasXscale, aliasYscale; // software renderer scale
@ -110,6 +111,7 @@ void R_StudioInit( void )
r_studio_lerping = Cvar_Get( "r_studio_lerping", "1", CVAR_ARCHIVE, "enables studio animation lerping" );
r_drawviewmodel = Cvar_Get( "r_drawviewmodel", "1", 0, "draw firstperson weapon model" );
cl_himodels = Cvar_Get( "cl_himodels", "1", CVAR_ARCHIVE, "draw high-resolution player models in multiplayer" );
r_studio_lighting = Cvar_Get( "r_studio_lighting", "1", CVAR_ARCHIVE, "studio lighting models ( 0 - normal, 1 - extended, 2 - experimental )" );
// NOTE: some mods with custom studiomodel renderer may cause error when menu trying draw player model out of the loaded game
r_customdraw_playermodel = Cvar_Get( "r_customdraw_playermodel", "0", CVAR_ARCHIVE, "allow to drawing playermodel in menu with client renderer" );
@ -1230,7 +1232,7 @@ void R_StudioSetupChrome( float *pchrome, int bone, vec3_t normal )
vec3_t chromerightvec; // g_chrome s vector in world reference frame
vec3_t tmp; // vector pointing at bone in world reference frame
VectorScale( RI.currententity->origin, -1.0f, tmp );
VectorScale( cl.refdef.vieworg, -1.0f, tmp );
tmp[0] += g_bonestransform[bone][0][3];
tmp[1] += g_bonestransform[bone][1][3];
tmp[2] += g_bonestransform[bone][2][3];
@ -1363,7 +1365,7 @@ void R_StudioDynamicLight( cl_entity_t *ent, alight_t *lightinfo )
plight = &g_studiolight;
plight->numdlights = 0; // clear previous dlights
if( r_lighting_extended->integer == 2 )
if( r_studio_lighting->integer == 2 )
Matrix3x4_OriginFromMatrix( g_lighttransform[0], origin );
else Matrix3x4_OriginFromMatrix( g_rotationmatrix, origin );
@ -1373,7 +1375,7 @@ void R_StudioDynamicLight( cl_entity_t *ent, alight_t *lightinfo )
// setup ambient lighting
invLight = (ent->curstate.effects & EF_INVLIGHT) ? true : false;
R_LightForPoint( origin, &ambient, invLight, 0.0f ); // ignore dlights
R_LightForPoint( origin, &ambient, invLight, true, 0.0f ); // ignore dlights
plight->lightcolor[0] = ambient.r * (1.0f / 255.0f);
plight->lightcolor[1] = ambient.g * (1.0f / 255.0f);
@ -1452,7 +1454,7 @@ void R_StudioEntityLight( alight_t *lightinfo )
plight = &g_studiolight;
plight->numelights = 0; // clear previous elights
if( r_lighting_extended->integer == 2 )
if( r_studio_lighting->integer == 2 )
Matrix3x4_OriginFromMatrix( g_lighttransform[0], origin );
else Matrix3x4_OriginFromMatrix( g_rotationmatrix, origin );
@ -2257,12 +2259,12 @@ static int pfnIsHardware( void )
return true;
}
static void StudioDrawShadow( studiohdr_t *pstudiohdr, matrix3x4 transform[MAXSTUDIOBONES] )
static void StudioDrawShadow( void )
{
// in GoldSrc shadow call is dsiabled with 'return' at start of the function
// some mods used a hack with calling DrawShadow ahead of 'return'
// this code is for HL compatibility.
MsgDev( D_INFO, "GL_StudioDrawShadow()\n" ); // just a debug
// MsgDev( D_INFO, "GL_StudioDrawShadow()\n" ); // just a debug
}
/*
@ -2273,11 +2275,11 @@ GL_StudioDrawShadow
*/
void _cdecl GL_StudioDrawShadow( void )
{
int rendermode; // ecx@3
float shadow_alpha; // ST18_4@4
float shadow_alpha2; // ST14_4@4
GLenum depthmode; // [sp+14h] [bp-8h]@6
GLenum depthmode2; // [sp+14h] [bp-8h]@10
int rendermode;
float shadow_alpha;
float shadow_alpha2;
GLenum depthmode;
GLenum depthmode2;
pglDepthMask( GL_TRUE );
@ -2303,7 +2305,7 @@ void _cdecl GL_StudioDrawShadow( void )
depthmode = GL_GREATER;
pglDepthFunc( depthmode );
MsgDev( D_INFO, "GL_StudioDrawShadow()\n" ); // just a debug
StudioDrawShadow();
// if( flt_100DB994 == 0.0 || flt_107BA8A8 < 0.5 )
depthmode2 = GL_LEQUAL;

View File

@ -35,6 +35,7 @@ convar_t *gl_texturebits;
convar_t *gl_ignorehwgamma;
convar_t *gl_texture_anisotropy;
convar_t *gl_compress_textures;
convar_t *gl_luminance_textures;
convar_t *gl_texture_lodbias;
convar_t *gl_showtextures;
convar_t *gl_swapInterval;
@ -60,6 +61,7 @@ convar_t *r_norefresh;
convar_t *r_lighting_extended;
convar_t *r_lighting_modulate;
convar_t *r_lighting_ambient;
convar_t *r_detailtextures;
convar_t *r_faceplanecull;
convar_t *r_drawentities;
convar_t *r_adjust_fov;
@ -121,10 +123,11 @@ vidmode_t vidmode[] =
{ "Mode 13: 16x9", 1024, 600, true },
{ "Mode 14: 16x9", 1280, 720, true },
{ "Mode 15: 16x9", 1360, 768, true },
{ "Mode 16: 16x9", 1440, 900, true },
{ "Mode 17: 16x9", 1680, 1050, true },
{ "Mode 18: 16x9", 1920, 1200, true },
{ "Mode 19: 16x9", 2560, 1600, true },
{ "Mode 16: 16x9", 1366, 768, true },
{ "Mode 17: 16x9", 1440, 900, true },
{ "Mode 18: 16x9", 1680, 1050, true },
{ "Mode 19: 16x9", 1920, 1200, true },
{ "Mode 20: 16x9", 2560, 1600, true },
{ NULL, 0, 0, 0 },
};
@ -432,13 +435,18 @@ static dllfunc_t wgl_funcs[] =
{ "wglSwapBuffers" , (void **)&pwglSwapBuffers },
{ "wglCreateContext" , (void **)&pwglCreateContext },
{ "wglDeleteContext" , (void **)&pwglDeleteContext },
{ "wglGetProcAddress" , (void **)&pwglGetProcAddress },
{ "wglMakeCurrent" , (void **)&pwglMakeCurrent },
{ "wglGetCurrentContext" , (void **)&pwglGetCurrentContext },
{ "wglGetCurrentDC" , (void **)&pwglGetCurrentDC },
{ NULL, NULL }
};
static dllfunc_t wglproc_funcs[] =
{
{ "wglGetProcAddress" , (void **)&pwglGetProcAddress },
{ NULL, NULL }
};
static dllfunc_t wglswapintervalfuncs[] =
{
{ "wglSwapIntervalEXT" , (void **)&pwglSwapIntervalEXT },
@ -524,7 +532,7 @@ void GL_CheckExtension( const char *name, const dllfunc_t *funcs, const char *cv
if(( name[2] == '_' || name[3] == '_' ) && !Q_strstr( glConfig.extensions_string, name ))
{
GL_SetExtension( r_ext, false ); // update render info
MsgDev( D_NOTE, "- failed\n" );
MsgDev( D_NOTE, "- ^1failed\n" );
return;
}
@ -541,7 +549,7 @@ void GL_CheckExtension( const char *name, const dllfunc_t *funcs, const char *cv
}
if( GL_Support( r_ext ))
MsgDev( D_NOTE, "- enabled\n" );
MsgDev( D_NOTE, "- ^2enabled\n" );
}
/*
@ -1405,13 +1413,14 @@ void GL_InitCommands( void )
r_speeds = Cvar_Get( "r_speeds", "0", CVAR_ARCHIVE, "shows renderer speeds" );
r_fullbright = Cvar_Get( "r_fullbright", "0", CVAR_CHEAT, "disable lightmaps, get fullbright for entities" );
r_norefresh = Cvar_Get( "r_norefresh", "0", 0, "disable 3D rendering (use with caution)" );
r_lighting_extended = Cvar_Get( "r_lighting_extended", "1", CVAR_ARCHIVE, "allow to get lighting from bmodels" );
r_lighting_extended = Cvar_Get( "r_lighting_extended", "1", CVAR_ARCHIVE, "allow to get lighting from world and bmodels" );
r_lighting_modulate = Cvar_Get( "r_lighting_modulate", "0.6", CVAR_ARCHIVE, "lightstyles modulate scale" );
r_lighting_ambient = Cvar_Get( "r_lighting_ambient", "0.3", 0, "map ambient lighting scale" );
r_adjust_fov = Cvar_Get( "r_adjust_fov", "1", CVAR_ARCHIVE, "making FOV adjustment for wide-screens" );
r_novis = Cvar_Get( "r_novis", "0", 0, "ignore vis information (perfomance test)" );
r_nocull = Cvar_Get( "r_nocull", "0", 0, "ignore frustrum culling (perfomance test)" );
r_faceplanecull = Cvar_Get( "r_faceplanecull", "1", 0, "ignore face plane culling (perfomance test)" );
r_detailtextures = Cvar_Get( "r_detailtextures", "1", CVAR_ARCHIVE, "enable detail textures support, use \"2\" for auto-generate mapname_detail.txt" );
r_lockpvs = Cvar_Get( "r_lockpvs", "0", CVAR_CHEAT, "lockpvs area at current point (pvs test)" );
r_lockcull = Cvar_Get( "r_lockcull", "0", CVAR_CHEAT, "lock frustrum area at current point (cull test)" );
r_wateralpha = Cvar_Get( "r_wateralpha", "1", CVAR_ARCHIVE, "world water transparency factor" );
@ -1440,7 +1449,8 @@ void GL_InitCommands( void )
gl_extensions = Cvar_Get( "gl_extensions", "1", CVAR_GLCONFIG, "allow gl_extensions" );
gl_texture_anisotropy = Cvar_Get( "r_anisotropy", "2.0", CVAR_ARCHIVE, "textures anisotropic filter" );
gl_texture_lodbias = Cvar_Get( "gl_texture_lodbias", "0.0", CVAR_ARCHIVE, "LOD bias for mipmapped textures" );
gl_compress_textures = Cvar_Get( "gl_compress_textures", "0", CVAR_ARCHIVE|CVAR_LATCH_VIDEO, "compress textures to safe video memory" );
gl_compress_textures = Cvar_Get( "gl_compress_textures", "0", CVAR_GLCONFIG, "compress textures to safe video memory" );
gl_luminance_textures = Cvar_Get( "gl_luminance_textures", "0", CVAR_GLCONFIG, "force all textures to luminance" );
gl_allow_static = Cvar_Get( "gl_allow_static", "1", CVAR_ARCHIVE, "force to drawing non-moveable brushes as part of world (save FPS)" );
gl_showtextures = Cvar_Get( "r_showtextures", "0", CVAR_CHEAT, "show all uploaded textures (type values from 1 to 9)" );
gl_finish = Cvar_Get( "gl_finish", "0", CVAR_ARCHIVE, "use glFinish instead of glFlush" );
@ -1480,9 +1490,13 @@ void GL_InitExtensions( void )
glConfig.version_string = pglGetString( GL_VERSION );
glConfig.extensions_string = pglGetString( GL_EXTENSIONS );
MsgDev( D_INFO, "Video: %s\n", glConfig.renderer_string );
// initalize until base opengl functions loaded
GL_CheckExtension( "OpenGL Internal ProcAddress", wglproc_funcs, NULL, GL_WGL_PROCADDRESS );
GL_CheckExtension( "WGL_3DFX_gamma_control", wgl3DFXgammacontrolfuncs, NULL, GL_WGL_3DFX_GAMMA_CONTROL );
GL_CheckExtension( "WGL_EXT_swap_control", wglswapintervalfuncs, NULL, GL_WGL_SWAPCONTROL );
GL_CheckExtension( "glDrawRangeElements", drawrangeelementsfuncs, "gl_drawrangeelments", GL_DRAW_RANGEELEMENTS_EXT );
if( !GL_Support( GL_DRAW_RANGEELEMENTS_EXT ))

View File

@ -65,7 +65,7 @@ float r_turbsin[] =
static qboolean CheckSkybox( const char *name )
{
const char *skybox_ext[3] = { "tga", "bmp", "jpg" };
const char *skybox_ext[3] = { "jpg", "tga", "bmp" };
int i, j, num_checked_sides;
const char *sidename;
@ -448,6 +448,9 @@ void R_InitSky( mip_t *mt, texture_t *tx )
uint transpix;
int r, g, b;
int i, j, p;
char texname[32];
Q_snprintf( texname, sizeof( texname ), "%s%s.mip", ( mt->offsets[0] > 0 ) ? "#" : "", tx->name );
if( mt->offsets[0] > 0 )
{
@ -456,12 +459,12 @@ void R_InitSky( mip_t *mt, texture_t *tx )
int size = (int)sizeof( mip_t ) + ((mt->width * mt->height * 85)>>6);
if( world.version == HLBSP_VERSION ) size += sizeof( short ) + 768;
r_sky = FS_LoadImage( tx->name, (byte *)mt, size );
r_sky = FS_LoadImage( texname, (byte *)mt, size );
}
else
{
// okay, loading it from wad
r_sky = FS_LoadImage( tx->name, NULL, 0 );
r_sky = FS_LoadImage( texname, NULL, 0 );
}
// make sure what sky image is valid

View File

@ -601,6 +601,7 @@ void MIX_MixChannelsToPaintbuffer( int endtime, int rate, int outputRate )
{
case SOUND_11k:
case SOUND_22k:
case SOUND_32k:
case SOUND_44k:
if( rate != pSource->rate )
continue;
@ -1004,6 +1005,11 @@ void MIX_UpsampleAllPaintbuffers( int end, int count )
MIX_SetCurrentPaintbuffer( IROOMBUFFER );
S_MixUpsample( count / ( SOUND_DMA_SPEED / SOUND_22k ), FILTERTYPE_LINEAR );
#endif
#if (SOUND_DMA_SPEED >= SOUND_32k)
// mix 32khz sounds:
MIX_MixChannelsToPaintbuffer( end, SOUND_32k, SOUND_DMA_SPEED );
#endif
// mix all 44khz sounds to all active paintbuffers
MIX_MixChannelsToPaintbuffer( end, SOUND_44k, SOUND_DMA_SPEED );

View File

@ -28,6 +28,7 @@ extern byte *sndpool;
#define SOUND_DMA_SPEED 44100 // hardware playback rate
#define SOUND_11k 11025 // 11khz sample rate
#define SOUND_22k 22050 // 22khz sample rate
#define SOUND_32k 32000 // 32khz sample rate
#define SOUND_44k 44100 // 44khz sample rate
// fixed point stuff for real-time resampling

View File

@ -23,7 +23,7 @@ static char mond[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int Q_buildnum( void )
{
// do not touch this! Only author of Xash3D can increase buildnumbers!
#if 0
#if 1
int m = 0, d = 0, y = 0;
static int b = 0;

View File

@ -365,10 +365,6 @@ void Cmd_Alias_f( void )
=============================================================================
*/
#define CMD_EXTDLL BIT( 0 ) // added by game.dll
#define CMD_CLIENTDLL BIT( 1 ) // added by client.dll
typedef struct cmd_function_s
{
struct cmd_function_s *next;
@ -444,7 +440,7 @@ void Cmd_TokenizeString( char *text )
while( 1 )
{
// skip whitespace up to a /n
while( *text && *text <= ' ' && *text != '\n' )
while( *text && ((byte)*text) <= ' ' && *text != '\n' )
text++;
if( *text == '\n' )
@ -775,7 +771,7 @@ Cmd_Unlink
unlink all commands with flag CVAR_EXTDLL
============
*/
void Cmd_Unlink( void )
void Cmd_Unlink( int group )
{
cmd_function_t *cmd;
cmd_function_t **prev;
@ -793,7 +789,7 @@ void Cmd_Unlink( void )
cmd = *prev;
if( !cmd ) break;
if( !( cmd->flags & CMD_EXTDLL ))
if( group && !( cmd->flags & group ))
{
prev = &cmd->next;
continue;

View File

@ -64,7 +64,7 @@ char *COM_ParseFile( char *data, char *token )
// skip whitespace
skipwhite:
while(( c = *data ) <= ' ' )
while(( c = ((byte)*data)) <= ' ' )
{
if( c == 0 )
return NULL; // end of file;
@ -85,7 +85,7 @@ skipwhite:
data++;
while( 1 )
{
c = *data++;
c = (byte)*data++;
if( c == '\"' || !c )
{
token[len] = 0;
@ -111,7 +111,7 @@ skipwhite:
token[len] = c;
data++;
len++;
c = *data;
c = ((byte)*data);
if( c == '{' || c == '}' || c == ')' || c == '(' || c == '\'' || c == ',' )
break;

View File

@ -79,8 +79,8 @@ typedef enum
#define XASH_VERSION 0.85f // engine current version
// PERFORMANCE INFO
#define MIN_FPS 0.1 // host minimum fps value for maxfps.
#define MAX_FPS 1000.0 // upper limit for maxfps.
#define MIN_FPS 15.0 // host minimum fps value for maxfps.
#define MAX_FPS 500.0 // upper limit for maxfps.
#define MAX_FRAMETIME 0.1
#define MIN_FRAMETIME 0.001

View File

@ -91,6 +91,9 @@ typedef struct
// console input
field_t input;
// chatfiled
field_t chat;
// console history
field_t historyLines[CON_HISTORY];
int historyLine; // the line being displayed from history buffer will be <= nextHistoryLine
@ -107,6 +110,7 @@ typedef struct
} console_t;
static console_t con;
static qboolean chat_team; // say_team is active
void Field_CharEvent( field_t *edit, int ch );
@ -214,6 +218,52 @@ int Con_StringLength( const char *string )
return len;
}
/*
================
Con_MessageMode_f
================
*/
void Con_MessageMode_f( void )
{
chat_team = false;
Key_SetKeyDest( key_message );
}
/*
================
Con_MessageMode2_f
================
*/
void Con_MessageMode2_f( void )
{
chat_team = true;
Key_SetKeyDest( key_message );
}
/*
================
Con_ToggleChat_f
================
*/
void Con_ToggleChat_f( void )
{
Con_ClearTyping ();
if( cls.key_dest == key_console )
{
if( Cvar_VariableInteger( "sv_background" ))
UI_SetActiveMenu( true );
else UI_SetActiveMenu( false );
}
else
{
UI_SetActiveMenu( false );
Key_SetKeyDest( key_console );
}
Con_ClearNotify();
}
/*
================
Con_ToggleConsole_f
@ -395,12 +445,12 @@ static void Con_LoadConchars( void )
if( con_fontsize->integer > 2 ) Cvar_SetFloat( "con_fontsize", 2 );
// select properly fontsize
if( scr_width->integer < 640 ) Cvar_SetFloat( "con_fontsize", 0 );
if( scr_width->integer <= 640 ) Cvar_SetFloat( "con_fontsize", 0 );
else if( scr_width->integer >= 1280 ) Cvar_SetFloat( "con_fontsize", 2 );
else Cvar_SetFloat( "con_fontsize", 1 );
// loading conchars
con.chars.hFontTexture = GL_LoadTexture( va( "fonts/font%i", con_fontsize->integer ), NULL, 0, TF_FONT );
con.chars.hFontTexture = GL_LoadTexture( va( "fonts/font%i", con_fontsize->integer ), NULL, 0, TF_FONT|TF_NEAREST );
if( !con_fontsize->modified ) return; // font not changed
@ -592,6 +642,9 @@ void Con_Init( void )
Con_ClearField( &con.input );
con.input.widthInChars = con.linewidth;
Con_ClearField( &con.chat );
con.chat.widthInChars = con.linewidth;
for( i = 0; i < CON_HISTORY; i++ )
{
Con_ClearField( &con.historyLines[i] );
@ -601,6 +654,9 @@ void Con_Init( void )
Cmd_AddCommand( "toggleconsole", Con_ToggleConsole_f, "opens or closes the console" );
Cmd_AddCommand( "con_color", Con_SetColor_f, "set a custom console color" );
Cmd_AddCommand( "clear", Con_Clear_f, "clear console history" );
Cmd_AddCommand( "togglechat", Con_ToggleChat_f, "toggle console chat" );
Cmd_AddCommand( "messagemode", Con_MessageMode_f, "enable message mode \"say\"" );
Cmd_AddCommand( "messagemode2", Con_MessageMode2_f, "enable message mode \"say_team\"" );
MsgDev( D_NOTE, "Console initialized.\n" );
con.initialized = true;
@ -692,6 +748,13 @@ void Con_Print( const char *txt )
}
}
/*
================
Con_NPrint
Draw a single debug line with specified height
================
*/
void Con_NPrintf( int idx, char *fmt, ... )
{
va_list args;
@ -712,6 +775,13 @@ void Con_NPrintf( int idx, char *fmt, ... )
con.draw_notify = true;
}
/*
================
Con_NXPrint
Draw a single debug line with specified height, color and time to live
================
*/
void Con_NXPrintf( con_nprint_t *info, char *fmt, ... )
{
va_list args;
@ -734,6 +804,13 @@ void Con_NXPrintf( con_nprint_t *info, char *fmt, ... )
con.draw_notify = true;
}
/*
================
UI_NPrint
Draw a single debug line with specified height (menu version)
================
*/
void UI_NPrintf( int idx, char *fmt, ... )
{
va_list args;
@ -754,6 +831,13 @@ void UI_NPrintf( int idx, char *fmt, ... )
con.draw_notify = true;
}
/*
================
UI_NXPrint
Draw a single debug line with specified height, color and time to live (menu version)
================
*/
void UI_NXPrintf( con_nprint_t *info, char *fmt, ... )
{
va_list args;
@ -986,7 +1070,7 @@ Key events are used for non-printable characters, others are gotten from char ev
*/
void Field_KeyDownEvent( field_t *edit, int key )
{
int len;
int len;
// shift-insert is paste
if((( key == K_INS ) || ( key == K_KP_INS )) && Key_IsDown( K_SHIFT ))
@ -997,48 +1081,52 @@ void Field_KeyDownEvent( field_t *edit, int key )
len = Q_strlen( edit->buffer );
if ( key == K_DEL )
if( key == K_DEL )
{
if ( edit->cursor < len )
{
if( edit->cursor < len )
memmove( edit->buffer + edit->cursor, edit->buffer + edit->cursor + 1, len - edit->cursor );
}
return;
}
if ( key == K_BACKSPACE )
if( key == K_BACKSPACE )
{
if ( edit->cursor > 0 )
if( edit->cursor > 0 )
{
memmove( edit->buffer + edit->cursor - 1, edit->buffer + edit->cursor, len - edit->cursor + 1 );
edit->cursor--;
if ( edit->scroll ) edit->scroll--;
if( edit->scroll ) edit->scroll--;
}
return;
}
if ( key == K_RIGHTARROW )
if( key == K_RIGHTARROW )
{
if ( edit->cursor < len ) edit->cursor++;
if ( edit->cursor >= edit->scroll + edit->widthInChars && edit->cursor <= len )
if( edit->cursor < len ) edit->cursor++;
if( edit->cursor >= edit->scroll + edit->widthInChars && edit->cursor <= len )
edit->scroll++;
return;
}
if ( key == K_LEFTARROW )
if( key == K_LEFTARROW )
{
if ( edit->cursor > 0 ) edit->cursor--;
if ( edit->cursor < edit->scroll ) edit->scroll--;
if( edit->cursor > 0 ) edit->cursor--;
if( edit->cursor < edit->scroll ) edit->scroll--;
return;
}
if ( key == K_HOME || ( Q_tolower(key) == 'a' && Key_IsDown( K_CTRL )))
if( key == K_HOME || ( Q_tolower(key) == 'a' && Key_IsDown( K_CTRL )))
{
edit->cursor = 0;
return;
}
if ( key == K_END || ( Q_tolower(key) == 'e' && Key_IsDown( K_CTRL )))
if( key == K_END || ( Q_tolower(key) == 'e' && Key_IsDown( K_CTRL )))
{
edit->cursor = len;
return;
}
if ( key == K_INS )
if( key == K_INS )
{
host.key_overstrike = !host.key_overstrike;
return;
@ -1052,7 +1140,7 @@ Field_CharEvent
*/
void Field_CharEvent( field_t *edit, int ch )
{
int len;
int len;
if( ch == 'v' - 'a' + 1 )
{
@ -1060,6 +1148,7 @@ void Field_CharEvent( field_t *edit, int ch )
Field_Paste( edit );
return;
}
if( ch == 'c' - 'a' + 1 )
{
// ctrl-c clears the field
@ -1076,6 +1165,7 @@ void Field_CharEvent( field_t *edit, int ch )
edit->scroll = 0;
return;
}
if( ch == 'e' - 'a' + 1 )
{
// ctrl-e is end
@ -1085,9 +1175,9 @@ void Field_CharEvent( field_t *edit, int ch )
}
// ignore any other non printable chars
if ( ch < 32 ) return;
if( ch < 32 ) return;
if ( host.key_overstrike )
if( host.key_overstrike )
{
if ( edit->cursor == MAX_STRING - 1 ) return;
edit->buffer[edit->cursor] = ch;
@ -1106,6 +1196,76 @@ void Field_CharEvent( field_t *edit, int ch )
if( edit->cursor == len + 1) edit->buffer[edit->cursor] = 0;
}
/*
==================
Field_DrawInputLine
==================
*/
void Field_DrawInputLine( int x, int y, field_t *edit )
{
int len, cursorChar;
int drawLen, hideChar = -1;
int prestep, curPos;
char str[MAX_SYSPATH];
byte *colorDefault;
drawLen = edit->widthInChars;
len = Q_strlen( edit->buffer ) + 1;
colorDefault = g_color_table[ColorIndex( COLOR_DEFAULT )];
// guarantee that cursor will be visible
if( len <= drawLen )
{
prestep = 0;
}
else
{
if( edit->scroll + drawLen > len )
{
edit->scroll = len - drawLen;
if( edit->scroll < 0 ) edit->scroll = 0;
}
prestep = edit->scroll;
}
if( prestep + drawLen > len )
drawLen = len - prestep;
// extract <drawLen> characters from the field at <prestep>
ASSERT( drawLen < MAX_SYSPATH );
Q_memcpy( str, edit->buffer + prestep, drawLen );
str[drawLen] = 0;
// save char for overstrike
cursorChar = str[edit->cursor - prestep];
if( host.key_overstrike && cursorChar && !((int)( host.realtime * 4 ) & 1 ))
hideChar = edit->cursor - prestep; // skip this char
// draw it
Con_DrawGenericString( x, y, str, colorDefault, false, hideChar );
// draw the cursor
if((int)( host.realtime * 4 ) & 1 ) return; // off blink
// calc cursor position
str[edit->cursor - prestep] = 0;
Con_DrawStringLen( str, &curPos, NULL );
if( host.key_overstrike && cursorChar )
{
// overstrike cursor
pglEnable( GL_BLEND );
pglDisable( GL_ALPHA_TEST );
pglBlendFunc( GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA );
pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
Con_DrawGenericChar( x + curPos, y, cursorChar, colorDefault );
}
else Con_DrawCharacter( x + curPos, y, '_', colorDefault );
}
/*
=============================================================================
@ -1247,6 +1407,42 @@ void Key_Console( int key )
Field_KeyDownEvent( &con.input, key );
}
/*
================
Key_Message
In game talk message
================
*/
void Key_Message( int key )
{
char buffer[MAX_SYSPATH];
if( key == K_ESCAPE )
{
Key_SetKeyDest( key_game );
Con_ClearField( &con.chat );
return;
}
if( key == K_ENTER || key == K_KP_ENTER )
{
if( con.chat.buffer[0] && cls.state == ca_active )
{
if( chat_team ) Q_snprintf( buffer, sizeof( buffer ), "say_team \"%s\"\n", con.chat.buffer );
else Q_snprintf( buffer, sizeof( buffer ), "say \"%s\"\n", con.chat.buffer );
Cbuf_AddText( buffer );
}
Key_SetKeyDest( key_game );
Con_ClearField( &con.chat );
return;
}
Field_KeyDownEvent( &con.chat, key );
}
/*
==============================================================================
@ -1263,74 +1459,18 @@ The input line scrolls horizontally if typing goes beyond the right edge
*/
void Con_DrawInput( void )
{
int len;
int drawLen, hideChar = -1;
int prestep, curPos;
int x, y, cursorChar;
char str[MAX_SYSPATH];
byte *colorDefault;
int x, y;
// don't draw anything (always draw if not active)
if( cls.key_dest != key_console ) return;
x = QCHAR_WIDTH; // room for ']'
y = con.vislines - ( con.charHeight * 2 );
drawLen = con.input.widthInChars;
len = Q_strlen( con.input.buffer ) + 1;
colorDefault = g_color_table[ColorIndex( COLOR_DEFAULT )];
// guarantee that cursor will be visible
if( len <= drawLen )
{
prestep = 0;
}
else
{
if( con.input.scroll + drawLen > len )
{
con.input.scroll = len - drawLen;
if( con.input.scroll < 0 )
con.input.scroll = 0;
}
prestep = con.input.scroll;
}
if( prestep + drawLen > len )
drawLen = len - prestep;
// extract <drawLen> characters from the field at <prestep>
ASSERT( drawLen < MAX_SYSPATH );
Q_memcpy( str, con.input.buffer + prestep, drawLen );
str[drawLen] = 0;
// save char for overstrike
cursorChar = str[con.input.cursor - prestep];
if( host.key_overstrike && cursorChar && !((int)( host.realtime * 4 ) & 1 ))
hideChar = con.input.cursor - prestep; // skip this char
// draw it
Con_DrawGenericString( x, y, str, colorDefault, false, hideChar );
Con_DrawCharacter( QCHAR_WIDTH >> 1, y, ']', colorDefault );
// draw the cursor
if((int)( host.realtime * 4 ) & 1 ) return; // off blink
// calc cursor position
str[con.input.cursor - prestep] = 0;
Con_DrawStringLen( str, &curPos, NULL );
if( host.key_overstrike && cursorChar )
{
// overstrike cursor
pglEnable( GL_BLEND );
pglDisable( GL_ALPHA_TEST );
pglBlendFunc( GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA );
pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
Con_DrawGenericChar( x + curPos, y, cursorChar, colorDefault );
}
else Con_DrawCharacter( x + curPos, y, '_', colorDefault );
Field_DrawInputLine( x, y, &con.input );
}
/*
@ -1400,33 +1540,53 @@ void Con_DrawNotify( void )
short *text;
float time;
if( !host.developer || Cvar_VariableInteger( "sv_background" ))
return;
currentColor = 7;
pglColor4ubv( g_color_table[currentColor] );
for( i = con.current - CON_TIMES + 1; i <= con.current; i++ )
if( host.developer && !Cvar_VariableInteger( "sv_background" ))
{
if( i < 0 ) continue;
time = con.times[i % CON_TIMES];
if( time == 0 ) continue;
time = host.realtime - time;
currentColor = 7;
pglColor4ubv( g_color_table[currentColor] );
if( time > con_notifytime->value )
continue; // expired
for( i = con.current - CON_TIMES + 1; i <= con.current; i++ )
{
if( i < 0 ) continue;
time = con.times[i % CON_TIMES];
if( time == 0 ) continue;
time = host.realtime - time;
if( time > con_notifytime->value )
continue; // expired
text = con.text + (i % con.totallines) * con.linewidth;
start = con.charWidths[' ']; // offset one space at left screen side
for( x = 0; x < con.linewidth; x++ )
{
if((( text[x] >> 8 ) & 7 ) != currentColor )
currentColor = ( text[x] >> 8 ) & 7;
start += Con_DrawCharacter( start, v, text[x] & 0xFF, g_color_table[currentColor] );
}
v += con.charHeight;
}
}
if( cls.key_dest == key_message )
{
char buf[16];
int len;
currentColor = 7;
pglColor4ubv( g_color_table[currentColor] );
text = con.text + (i % con.totallines) * con.linewidth;
start = con.charWidths[' ']; // offset one space at left screen side
for( x = 0; x < con.linewidth; x++ )
{
if((( text[x] >> 8 ) & 7 ) != currentColor )
currentColor = ( text[x] >> 8 ) & 7;
start += Con_DrawCharacter( start, v, text[x] & 0xFF, g_color_table[currentColor] );
}
v += con.charHeight;
if( chat_team ) Q_strncpy( buf, "say_team: ", sizeof( buf ));
else Q_strncpy( buf, "say: ", sizeof( buf ));
Con_DrawStringLen( buf, &len, NULL );
Con_DrawString( start, v, buf, g_color_table[7] );
Field_DrawInputLine( start + len, v, &con.chat );
}
pglColor4ub( 255, 255, 255, 255 );
}
@ -1591,7 +1751,7 @@ void Con_DrawConsole( void )
{
if( con.displayFrac )
Con_DrawSolidConsole( con.displayFrac );
else if( cls.state == ca_active && cls.key_dest == key_game )
else if( cls.state == ca_active && ( cls.key_dest == key_game || cls.key_dest == key_message ))
Con_DrawNotify(); // draw notify lines
}
break;
@ -1613,9 +1773,11 @@ void Con_DrawVersion( void )
int start, height = scr_height->integer;
string curbuild;
if( cls.key_dest != key_menu ) return;
if( cls.key_dest != key_menu && cls.scrshot_action != scrshot_normal ) return;
Q_snprintf( curbuild, MAX_STRING, "v%i/%g (build %i)", PROTOCOL_VERSION, XASH_VERSION, Q_buildnum( ));
if( cls.scrshot_action == scrshot_normal )
Q_snprintf( curbuild, MAX_STRING, "Xash3D v%i/%g (build %i)", PROTOCOL_VERSION, XASH_VERSION, Q_buildnum( ));
else Q_snprintf( curbuild, MAX_STRING, "v%i/%g (build %i)", PROTOCOL_VERSION, XASH_VERSION, Q_buildnum( ));
Con_DrawStringLen( curbuild, &stringLen, &charH );
start = scr_width->integer - stringLen * 1.05f;
stringLen = Con_StringLength( curbuild );
@ -1668,9 +1830,24 @@ CONSOLE INTERFACE
==============================================================================
*/
/*
================
Con_CharEvent
Console input
================
*/
void Con_CharEvent( int key )
{
Field_CharEvent( &con.input, key );
// distribute the key down event to the apropriate handler
if( cls.key_dest == key_console )
{
Field_CharEvent( &con.input, key );
}
else if( cls.key_dest == key_message )
{
Field_CharEvent( &con.chat, key );
}
}
void Con_VidInit( void )

View File

@ -27,6 +27,9 @@ enum
TIME_FILENAME,
};
#define CMD_EXTDLL BIT( 0 ) // added by game.dll
#define CMD_CLIENTDLL BIT( 1 ) // added by client.dll
typedef void (*setpair_t)( const char *key, const char *value, void *buffer, void *numpairs );
typedef void (*xcommand_t)( void );
@ -118,7 +121,7 @@ uint Cmd_Argc( void );
char *Cmd_Args( void );
char *Cmd_Argv( int arg );
void Cmd_Init( void );
void Cmd_Unlink( void );
void Cmd_Unlink( int group );
void Cmd_AddCommand( const char *cmd_name, xcommand_t function, const char *cmd_desc );
void Cmd_AddGameCommand( const char *cmd_name, xcommand_t function );
void Cmd_AddClientCommand( const char *cmd_name, xcommand_t function );

View File

@ -450,7 +450,7 @@ convar_t *Cvar_Set2( const char *var_name, const char *value, qboolean force )
// step through the string, only copying back in characters that are printable
while( *pS )
{
if( *pS < 32 || *pS > 255 )
if( ((byte)*pS) < 32 || ((byte)*pS) > 255 )
{
pS++;
continue;

View File

@ -26,6 +26,7 @@ qboolean Image_LoadBMP( const char *name, const byte *buffer, size_t filesize )
byte palette[256][4];
int i, columns, column, rows, row, bpp = 1;
int cbPalBytes = 0, padSize = 0, bps = 0;
qboolean load_qfont = false;
bmp_t bhdr;
if( filesize < sizeof( bhdr )) return false;
@ -81,9 +82,21 @@ qboolean Image_LoadBMP( const char *name, const byte *buffer, size_t filesize )
image.width = columns = bhdr.width;
image.height = rows = abs( bhdr.height );
if(!Image_ValidSize( name ))
if( !Image_ValidSize( name ))
return false;
// special hack for loading qfont
if( !Q_strcmp( "#XASH_SYSTEMFONT_001", name ))
{
// NOTE: same as system font we can use 4-bit bmps only
// step1: move main layer into alpha-channel (give grayscale from RED channel)
// step2: fill main layer with 255 255 255 color (white)
// step3: ????
// step4: PROFIT!!! (economy up to 150 kb for menu.dll final size)
image.flags |= IMAGE_HAS_ALPHA;
load_qfont = true;
}
if( bhdr.bitsPerPixel <= 8 )
{
// figure out how many entires are actually in the table
@ -170,16 +183,36 @@ qboolean Image_LoadBMP( const char *name, const byte *buffer, size_t filesize )
case 4:
alpha = *buf_p++;
palIndex = alpha >> 4;
*pixbuf++ = palette[palIndex][2];
*pixbuf++ = palette[palIndex][1];
*pixbuf++ = palette[palIndex][0];
*pixbuf++ = palette[palIndex][3];
if( load_qfont )
{
*pixbuf++ = red = 255;
*pixbuf++ = green = 255;
*pixbuf++ = blue = 255;
*pixbuf++ = palette[palIndex][2];
}
else
{
*pixbuf++ = red = palette[palIndex][2];
*pixbuf++ = green = palette[palIndex][1];
*pixbuf++ = blue = palette[palIndex][0];
*pixbuf++ = palette[palIndex][3];
}
if( ++column == columns ) break;
palIndex = alpha & 0x0F;
*pixbuf++ = palette[palIndex][2];
*pixbuf++ = palette[palIndex][1];
*pixbuf++ = palette[palIndex][0];
*pixbuf++ = palette[palIndex][3];
if( load_qfont )
{
*pixbuf++ = red = 255;
*pixbuf++ = green = 255;
*pixbuf++ = blue = 255;
*pixbuf++ = palette[palIndex][2];
}
else
{
*pixbuf++ = red = palette[palIndex][2];
*pixbuf++ = green = palette[palIndex][1];
*pixbuf++ = blue = palette[palIndex][0];
*pixbuf++ = palette[palIndex][3];
}
break;
case 8:
palIndex = *buf_p++;

View File

@ -49,7 +49,7 @@ qboolean Image_LoadTGA( const char *name, const byte *buffer, size_t filesize )
if( targa_header.id_length != 0 ) buf_p += targa_header.id_length; // skip TARGA image comment
// check for tga file
if(!Image_ValidSize( name )) return false;
if( !Image_ValidSize( name )) return false;
image.type = PF_RGBA_32; // always exctracted to 32-bit buffer

View File

@ -95,8 +95,8 @@ static const loadpixformat_t load_null[] =
static const loadpixformat_t load_game[] =
{
{ "%s%s.%s", "bmp", Image_LoadBMP, IL_HINT_NO }, // WON menu images
{ "%s%s.%s", "tga", Image_LoadTGA, IL_HINT_NO }, // hl vgui menus
{ "%s%s.%s", "bmp", Image_LoadBMP, IL_HINT_NO }, // hl skyboxes
{ "%s%s.%s", "jpg", Image_LoadJPG, IL_HINT_NO }, // hl skyboxes
{ "%s%s.%s", "mip", Image_LoadMIP, IL_HINT_NO }, // hl textures from wad or buffer
{ "%s%s.%s", "mdl", Image_LoadMDL, IL_HINT_HL }, // hl studio model skins

View File

@ -78,10 +78,11 @@ qboolean Image_LoadFNT( const char *name, const byte *buffer, size_t filesize )
if( filesize < sizeof( font ))
return false;
Q_memcpy( &font, buffer, sizeof( font ));
// last sixty four bytes - what the hell ????
size = sizeof( qfont_t ) - 4 + ( 128 * font.width * QCHAR_WIDTH ) + sizeof( short ) + 768 + 64;
size = sizeof( qfont_t ) - 4 + ( font.height * font.width * QCHAR_WIDTH ) + sizeof( short ) + 768 + 64;
if( size != filesize )
{
@ -96,21 +97,22 @@ qboolean Image_LoadFNT( const char *name, const byte *buffer, size_t filesize )
image.height = font.height;
}
if(!Image_LumpValidSize( name )) return false;
fin = buffer + sizeof( font ) - 4;
if( !Image_LumpValidSize( name ))
return false;
fin = buffer + sizeof( font ) - 4;
pal = fin + (image.width * image.height);
numcolors = *(short *)pal, pal += sizeof( short );
image.flags |= IMAGE_HAS_ALPHA; // fonts always have transparency
if( numcolors == 768 )
{
// newstyle font
Image_GetPaletteLMP( pal, LUMP_QFONT );
image.flags |= IMAGE_HAS_ALPHA; // fonts always have transparency
}
else if( numcolors == 256 )
{
// oldstyle font
// oldstyle font (no transparency)
Image_GetPaletteLMP( pal, LUMP_TRANSPARENT );
}
else

View File

@ -548,6 +548,9 @@ void Key_Event( int key, qboolean down )
if( host.mouse_visible && cls.state != ca_cinematic )
return; // handled in client.dll
break;
case key_message:
Key_Message( key );
return;
case key_console:
if( cls.state == ca_active && !cl.background )
Key_SetKeyDest( key_game );
@ -636,6 +639,10 @@ void Key_Event( int key, qboolean down )
{
Key_Console( key );
}
else if( cls.key_dest == key_message )
{
Key_Message( key );
}
}
/*
@ -658,6 +665,9 @@ void Key_SetKeyDest( int key_dest )
case key_console:
cls.key_dest = key_console;
break;
case key_message:
cls.key_dest = key_message;
break;
default:
Host_Error( "Key_SetKeyDest: wrong destination (%i)\n", key_dest );
break;
@ -701,7 +711,7 @@ void CL_CharEvent( int key )
if( key == '`' || key == '~' ) return;
// distribute the key down event to the apropriate handler
if( cls.key_dest == key_console )
if( cls.key_dest == key_console || cls.key_dest == key_message )
{
Con_CharEvent( key );
}

View File

@ -164,7 +164,8 @@ qboolean LibraryLoadSymbols( dll_user_t *hInst )
goto table_error;
}
if( !Q_strcmp( section_header.Name, ".rdata" ))
if((( optional_header.DataDirectory[0].VirtualAddress >= section_header.VirtualAddress ) &&
(optional_header.DataDirectory[0].VirtualAddress < (section_header.VirtualAddress + section_header.Misc.VirtualSize))))
{
rdata_found = true;
break;

View File

@ -29,6 +29,7 @@ static model_t *com_models[MAX_MODELS]; // shared replacement modeltable
static model_t cm_models[MAX_MODELS];
static int cm_nummodels = 0;
static byte visdata[MAX_MAP_LEAFS/8]; // intermediate buffer
int bmodel_version; // global stuff to detect bsp version
model_t *loadmodel;
model_t *worldmodel;
@ -484,10 +485,13 @@ static void Mod_LoadTextures( const dlump_t *l )
mip_t *mt;
int i, j;
// release old sky layers first
GL_FreeTexture( tr.solidskyTexture );
GL_FreeTexture( tr.alphaskyTexture );
tr.solidskyTexture = tr.alphaskyTexture = 0;
if( world.loading )
{
// release old sky layers first
GL_FreeTexture( tr.solidskyTexture );
GL_FreeTexture( tr.alphaskyTexture );
tr.solidskyTexture = tr.alphaskyTexture = 0;
}
if( !l->filelen )
{
@ -535,7 +539,7 @@ static void Mod_LoadTextures( const dlump_t *l )
tx->height = mt->height;
// check for sky texture (quake1 only!)
if( world.version == Q1BSP_VERSION && !Q_strncmp( mt->name, "sky", 3 ))
if( world.loading && world.version == Q1BSP_VERSION && !Q_strncmp( mt->name, "sky", 3 ))
{
R_InitSky( mt, tx );
}
@ -544,7 +548,7 @@ static void Mod_LoadTextures( const dlump_t *l )
// NOTE: imagelib detect miptex version by size
// 770 additional bytes is indicated custom palette
int size = (int)sizeof( mip_t ) + ((mt->width * mt->height * 85)>>6);
if( world.version == HLBSP_VERSION ) size += sizeof( short ) + 768;
if( bmodel_version == HLBSP_VERSION ) size += sizeof( short ) + 768;
tx->gl_texturenum = GL_LoadTexture( texname, (byte *)mt, size, 0 );
}
@ -566,7 +570,7 @@ static void Mod_LoadTextures( const dlump_t *l )
// NOTE: imagelib detect miptex version by size
// 770 additional bytes is indicated custom palette
int size = (int)sizeof( mip_t ) + ((mt->width * mt->height * 85)>>6);
if( world.version == HLBSP_VERSION ) size += sizeof( short ) + 768;
if( bmodel_version == HLBSP_VERSION ) size += sizeof( short ) + 768;
tx->fb_texturenum = GL_LoadTexture( texname, (byte *)mt, size, TF_MAKELUMA|TF_NOMIPMAP );
}
@ -789,7 +793,7 @@ static void Mod_LoadLighting( const dlump_t *l )
if( !l->filelen ) return;
in = (void *)(mod_base + l->fileofs);
switch( world.version )
switch( bmodel_version )
{
case Q1BSP_VERSION:
// expand the white lighting data
@ -953,7 +957,7 @@ static void Mod_LoadSurfaces( const dlump_t *l )
if( loadmodel->lightdata && in->lightofs != -1 )
{
if( world.version == HLBSP_VERSION )
if( bmodel_version == HLBSP_VERSION )
out->samples = loadmodel->lightdata + (in->lightofs / 3);
else out->samples = loadmodel->lightdata + in->lightofs;
}
@ -961,7 +965,7 @@ static void Mod_LoadSurfaces( const dlump_t *l )
for( j = 0; j < MAXLIGHTMAPS; j++ )
out->styles[j] = in->styles[j];
if( out->flags & SURF_DRAWSKY && world.version == Q1BSP_VERSION )
if( world.loading && out->flags & SURF_DRAWSKY && world.version == Q1BSP_VERSION )
GL_SubdivideSurface( out ); // cut up polygon for warps
if( out->flags & SURF_DRAWTURB )
@ -1222,6 +1226,9 @@ Mod_LoadVisibility
*/
static void Mod_LoadVisibility( const dlump_t *l )
{
// bmodels has no visibility
if( !world.loading ) return;
if( !l->filelen )
{
MsgDev( D_WARN, "map ^2%s^7 has no visibility\n", loadmodel->name );
@ -1367,7 +1374,7 @@ void Mod_CalcPHS( void )
size_t phsdatasize;
// no worldmodel or no visdata
if( !worldmodel || !worldmodel->visdata )
if( !world.loading || !worldmodel || !worldmodel->visdata )
return;
MsgDev( D_NOTE, "Building PAS...\n" );
@ -1539,6 +1546,7 @@ static void Mod_LoadBrushModel( model_t *mod, const void *buffer )
// will be merged later
loadmodel->type = mod_brush;
if( world.loading ) world.version = i;
bmodel_version = i; // share it
// swap all the lumps
mod_base = (byte *)header;
@ -1705,6 +1713,7 @@ Loads a model into the cache
model_t *Mod_LoadModel( model_t *mod, qboolean crash )
{
byte *buf;
char tempname[64];
if( !mod )
{
@ -1717,12 +1726,17 @@ model_t *Mod_LoadModel( model_t *mod, qboolean crash )
if( mod->mempool || mod->name[0] == '*' )
return mod;
// store modelname to show error
Q_strncpy( tempname, mod->name, sizeof( tempname ));
buf = COM_LoadFile( mod->name, 0, NULL );
if( !buf )
{
if( crash ) Host_Error( "Mod_ForName: %s couldn't load\n", mod->name );
else MsgDev( D_ERROR, "Mod_ForName: %s couldn't load\n", mod->name );
Q_memset( mod, 0, sizeof( model_t ));
if( crash ) Host_Error( "Mod_ForName: %s couldn't load\n", tempname );
else MsgDev( D_ERROR, "Mod_ForName: %s couldn't load\n", tempname );
return NULL;
}
@ -1753,8 +1767,8 @@ model_t *Mod_LoadModel( model_t *mod, qboolean crash )
Mod_FreeModel( mod );
// check for loading problems
if( crash ) Host_Error( "Mod_ForName: %s unknown format\n", mod->name );
else MsgDev( D_ERROR, "Mod_ForName: %s unknown format\n", mod->name );
if( crash ) Host_Error( "Mod_ForName: %s unknown format\n", tempname );
else MsgDev( D_ERROR, "Mod_ForName: %s unknown format\n", tempname );
return NULL;
}
return mod;
@ -1800,11 +1814,13 @@ void Mod_LoadWorld( const char *name, uint *checksum, qboolean force )
}
// clear all studio submodels on restart
for( i = 0; i < cm_nummodels; i++ )
// HACKHACK: throw all external BSP-models to refresh their lightmaps properly
for( i = 1; i < cm_nummodels; i++ )
{
if( cm_models[i].type != mod_studio )
continue;
cm_models[i].submodels = NULL;
if( cm_models[i].type == mod_studio )
cm_models[i].submodels = NULL;
else if( cm_models[i].type == mod_brush )
Mod_FreeModel( cm_models + i );
}
// purge all submodels

View File

@ -59,6 +59,7 @@ GNU General Public License for more details.
// bytes will be stripped by the networking channel layer
#define NET_MAX_MESSAGE PAD_NUMBER(( NET_MAX_PAYLOAD + HEADER_BYTES ), 16 )
#define MASTERSERVER_ADR "hl1master.steampowered.com:27010"
#define PORT_MASTER 27010
#define PORT_CLIENT 27005
#define PORT_SERVER 27015
@ -201,6 +202,7 @@ void Netchan_Setup( netsrc_t sock, netchan_t *chan, netadr_t adr, int qport );
qboolean Netchan_CopyNormalFragments( netchan_t *chan, sizebuf_t *msg );
qboolean Netchan_CopyFileFragments( netchan_t *chan, sizebuf_t *msg );
void Netchan_CreateFragments( qboolean server, netchan_t *chan, sizebuf_t *msg );
int Netchan_CreateFileFragments( qboolean server, netchan_t *chan, const char *filename );
void Netchan_Transmit( netchan_t *chan, int lengthInBytes, byte *data );
void Netchan_TransmitBits( netchan_t *chan, int lengthInBits, byte *data );
void Netchan_OutOfBand( int net_socket, netadr_t adr, int length, byte *data );
@ -209,6 +211,7 @@ qboolean Netchan_Process( netchan_t *chan, sizebuf_t *msg );
void Netchan_UpdateProgress( netchan_t *chan );
qboolean Netchan_IncomingReady( netchan_t *chan );
qboolean Netchan_CanPacket( netchan_t *chan );
void Netchan_FragSend( netchan_t *chan );
void Netchan_Clear( netchan_t *chan );
// huffman compression

View File

@ -553,7 +553,7 @@ static void NET_OpenIP( void )
if( !ip_sockets[NS_SERVER] )
{
port = Cvar_Get("ip_hostport", "0", CVAR_INIT, "network server port" )->integer;
port = Cvar_Get( "ip_hostport", "0", CVAR_INIT, "network server port" )->integer;
if( !port ) port = Cvar_Get( "port", va( "%i", PORT_SERVER ), CVAR_INIT, "network default port" )->integer;
ip_sockets[NS_SERVER] = NET_IPSocket( net_ip->string, port );

View File

@ -16,7 +16,7 @@ GNU General Public License for more details.
#ifndef PROTOCOL_H
#define PROTOCOL_H
#define PROTOCOL_VERSION 41
#define PROTOCOL_VERSION 42
// server to client
#define svc_bad 0 // immediately crash client when received
@ -29,7 +29,7 @@ GNU General Public License for more details.
#define svc_time 7 // [float] server time
#define svc_print 8 // [byte] id [string] null terminated string
#define svc_stufftext 9 // [string] stuffed into client's console buffer
#define svc_setangle 10 // [angle angle] set the view angle to this absolute value
#define svc_setangle 10 // [angle angle angle] set the view angle to this absolute value
#define svc_serverdata 11 // [long] protocol ...
#define svc_lightstyle 12 // [index][pattern]
#define svc_updateuserinfo 13 // [byte] playernum, [string] userinfo
@ -62,9 +62,9 @@ GNU General Public License for more details.
#define svc_packetentities 40 // [short][...]
#define svc_deltapacketentities 41 // [short][byte][...]
#define svc_chokecount 42 // [byte]
#define svc_resourcelist 43 // [short][...]
#define svc_deltamovevars 44 // [movevars_t]
#define svc_customization 45
#define svc_crosshairangle 47 // [byte][byte]
#define svc_soundfade 48 // [float*4] sound fade parms
@ -154,4 +154,13 @@ GNU General Public License for more details.
#error MAX_COORD_INTEGER does not match COORD_INTEGER_BITS
#endif
#define MAX_RESOURCES (MAX_MODELS+MAX_SOUNDS+MAX_CUSTOM+MAX_EVENTS)
typedef struct
{
int rescount;
int restype[MAX_RESOURCES];
char resnames[MAX_RESOURCES][CS_SIZE];
} resourcelist_t;
#endif//PROTOCOL_H

Binary file not shown.

Binary file not shown.

View File

@ -17,246 +17,33 @@ GNU General Public License for more details.
/*
=======================================================================
LIBMAD DEFINITION
MPG123 DEFINITION
=======================================================================
*/
#define BUFFER_GUARD 8
#define BUFFER_MDLEN (511 + 2048 + BUFFER_GUARD)
#define BUFFER_SIZE 4096 // must be large than BUFFER_MDLEN
#define MP3_ERR -1
#define MP3_OK 0
#define MP3_NEED_MORE 1
#define MPEG_F_FRACBITS 28
#define MPEG_F( x ) ((int)( x##L ))
#define MPEG_F_ONE MPEG_F( 0x10000000 )
#define FRAME_SIZE 16384 // must match with mp3 frame size
enum
typedef struct mpeg_s
{
MP3_ERROR_NONE = 0x0000, // no error
MP3_ERROR_BUFLEN = 0x0001, // input buffer too small (or EOF)
MP3_ERROR_BUFPTR = 0x0002, // invalid (null) buffer pointer
MP3_ERROR_NOMEM = 0x0031, // not enough memory
MP3_ERROR_LOSTSYNC = 0x0101, // lost synchronization
MP3_ERROR_BADLAYER = 0x0102, // reserved header layer value
MP3_ERROR_BADBITRATE = 0x0103, // forbidden bitrate value
MP3_ERROR_BADSAMPLERATE = 0x0104, // reserved sample frequency value
MP3_ERROR_BADEMPHASIS = 0x0105, // reserved emphasis value
MP3_ERROR_BADCRC = 0x0201, // CRC check failed
MP3_ERROR_BADBITALLOC = 0x0211, // forbidden bit allocation value
MP3_ERROR_BADSCALEFACTOR = 0x0221, // bad scalefactor index
MP3_ERROR_BADMODE = 0x0222, // bad bitrate/mode combination
MP3_ERROR_BADFRAMELEN = 0x0231, // bad frame length
MP3_ERROR_BADBIGVALUES = 0x0232, // bad big_values count
MP3_ERROR_BADBLOCKTYPE = 0x0233, // reserved block_type
MP3_ERROR_BADSCFSI = 0x0234, // bad scalefactor selection info
MP3_ERROR_BADDATAPTR = 0x0235, // bad main_data_begin pointer
MP3_ERROR_BADPART3LEN = 0x0236, // bad audio data length
MP3_ERROR_BADHUFFTABLE = 0x0237, // bad Huffman table select
MP3_ERROR_BADHUFFDATA = 0x0238, // Huffman data overrun
MP3_ERROR_BADSTEREO = 0x0239 // incompatible block_type for JS
};
void *state; // hidden decoder state
void *vbrtag; // valid for VBR-encoded mpegs
enum
{
MODE_SINGLE_CHANNEL = 0, // single channel
MODE_DUAL_CHANNEL, // dual channel
MODE_JOINT_STEREO, // joint (MS/intensity) stereo
MODE_STEREO // normal LR stereo
};
typedef struct
{
uint samplerate; // sampling frequency (Hz)
word channels; // number of channels
word length; // number of samples per channel
int samples[2][1152]; // PCM output samples [ch][sample]
} pcm_t;
typedef struct
{
int filter[2][2][2][16][8]; // polyphase filterbank outputs
uint phase; // current processing phase
pcm_t pcm; // PCM output
} synth_t;
typedef struct
{
const byte *byte;
word cache;
word left;
} bitptr_t;
typedef struct
{
long seconds; // whole seconds
dword fraction; // 1 / TIMER_RESOLUTION seconds
} mp3_timer_t;
typedef struct
{
int layer; // audio layer (1, 2, or 3)
int mode; // channel mode (see above)
int mode_extension; // additional mode info
int emphasis; // de-emphasis to use (see above)
dword bitrate; // stream bitrate (bps)
uint samplerate; // sampling frequency (Hz)
word crc_check; // frame CRC accumulator
word crc_target; // final target CRC checksum
int flags; // flags (see below)
int private_bits; // private bits (see below)
mp3_timer_t duration; // audio playing time of frame
} mp3_header_t;
typedef struct
{
mp3_header_t header; // MPEG audio header
int options; // decoding options (from stream)
int sbsample[2][36][32]; // synthesis subband filter samples
int (*overlap)[2][32][18]; // Layer III block overlap data
} mp3_frame_t;
typedef struct
{
const byte *buffer; // input bitstream buffer
const byte *bufend; // end of buffer
dword skiplen; // bytes to skip before next frame
int sync; // stream sync found
dword freerate; // free bitrate (fixed)
const byte *this_frame; // start of current frame
const byte *next_frame; // start of next frame
bitptr_t ptr; // current processing bit pointer
bitptr_t anc_ptr; // ancillary bits pointer
uint anc_bitlen; // number of ancillary bits
byte (*data)[BUFFER_MDLEN];
// Layer III data()
uint md_len; // bytes in data
int options; // decoding options
int error; // error code
} mp3_stream_t;
typedef struct
{
synth_t synth;
mp3_stream_t stream;
mp3_frame_t frame;
int buffer_length; // for reading
byte buffer[BUFFER_SIZE];// frame buffer
} mpegfile_t;
// libmad exports
extern void mad_synth_init( synth_t* );
extern void mad_synth_frame( synth_t*, const mp3_frame_t* );
extern void mad_stream_init( mp3_stream_t* );
extern void mad_stream_buffer( mp3_stream_t*, const byte*, dword );
extern void mad_stream_finish( mp3_stream_t* );
extern void mad_frame_init( mp3_frame_t* );
extern int mad_frame_decode( mp3_frame_t*, mp3_stream_t* );
extern void mad_frame_finish( mp3_frame_t* );
/*
=================================================================
MPEG decompression
=================================================================
*/
static int mpeg_read( file_t *file, mpegfile_t *mpeg )
{
int ret;
while( 1 )
{
ret = FS_Read( file, &mpeg->buffer[mpeg->buffer_length], BUFFER_SIZE - mpeg->buffer_length );
// no more bytes are left
if( ret <= 0 ) break;
mpeg->buffer_length += ret;
while( 1 )
{
mad_stream_buffer( &mpeg->stream, mpeg->buffer, mpeg->buffer_length );
ret = mad_frame_decode( &mpeg->frame, &mpeg->stream );
if( mpeg->stream.next_frame )
{
int length;
length = mpeg->buffer + mpeg->buffer_length - mpeg->stream.next_frame;
memmove( mpeg->buffer, mpeg->stream.next_frame, length );
mpeg->buffer_length = length;
}
if( !ret ) return 1;
if( mpeg->stream.error == MP3_ERROR_BUFLEN )
break;
}
}
return 0;
}
static int mpeg_read_mem( const byte *buffer, int *pos, size_t filesize, mpegfile_t *mpeg )
{
int ret, readSize;
while( 1 )
{
readSize = ( BUFFER_SIZE - mpeg->buffer_length );
if(( *pos + readSize ) > filesize )
readSize = ( filesize - *pos );
Q_memcpy( &mpeg->buffer[mpeg->buffer_length], buffer + *pos, readSize );
// no more bytes are left
if( readSize <= 0 ) break;
*pos += readSize;
mpeg->buffer_length += readSize;
while( 1 )
{
mad_stream_buffer( &mpeg->stream, mpeg->buffer, mpeg->buffer_length );
ret = mad_frame_decode( &mpeg->frame, &mpeg->stream );
if( mpeg->stream.next_frame )
{
int length;
length = mpeg->buffer + mpeg->buffer_length - mpeg->stream.next_frame;
memmove( mpeg->buffer, mpeg->stream.next_frame, length );
mpeg->buffer_length = length;
}
if( !ret ) return 1;
if( mpeg->stream.error == MP3_ERROR_BUFLEN )
break;
}
}
return 0;
}
static int mpeg_scale( int sample )
{
sample += (1 << ( MPEG_F_FRACBITS - 16 ));
if( sample >= MPEG_F_ONE ) sample = MPEG_F_ONE - 1;
else if( sample < -MPEG_F_ONE ) sample = -MPEG_F_ONE;
return sample >> ( MPEG_F_FRACBITS + 1 - 16 );
}
static int mpeg_size( mp3_frame_t *frame, long bytes )
{
return bytes * 8 / frame->header.bitrate * sound.channels * sound.rate * sound.width;
}
int channels; // num channels
int samples; // per one second
int play_time;// stream size in milliseconds
int rate; // frequency
int outsize; // current data size
char out[8192];// temporary buffer
} mpeg_t;
// mpg123 exports
int create_decoder( mpeg_t *mpg );
int read_mpeg_header( mpeg_t *mpg, const char *data, long bufsize, long streamsize );
int read_mpeg_stream( mpeg_t *mpg, const char *data, long bufsize );
void close_decoder( mpeg_t *mpg );
/*
=================================================================
@ -267,39 +54,38 @@ static int mpeg_size( mp3_frame_t *frame, long bytes )
*/
qboolean Sound_LoadMPG( const char *name, const byte *buffer, size_t filesize )
{
mpegfile_t mpeg;
size_t pos = 0;
size_t bytesWrite = 0;
mpeg_t mpeg;
size_t pos = 0;
size_t bytesWrite = 0;
// load the file
if( !buffer || filesize <= 0 )
if( !buffer || filesize < FRAME_SIZE )
return false;
Q_memset( &mpeg, 0, sizeof( mpeg ));
mad_synth_init( &mpeg.synth );
mad_stream_init( &mpeg.stream );
mad_frame_init( &mpeg.frame );
// couldn't create decoder
if( !create_decoder( &mpeg ))
return false;
if( mpeg_read_mem( buffer, &pos, filesize, &mpeg ) == 0 )
// trying to read header
if( !read_mpeg_header( &mpeg, buffer, FRAME_SIZE, filesize ))
{
MsgDev( D_ERROR, "Sound_LoadMPG: (%s) is probably corrupted\n", name );
mad_stream_finish( &mpeg.stream );
mad_frame_finish( &mpeg.frame );
close_decoder( &mpeg );
return false;
}
sound.channels = ( mpeg.frame.header.mode == MODE_SINGLE_CHANNEL ) ? 1 : 2;
sound.rate = mpeg.frame.header.samplerate;
sound.channels = mpeg.channels;
sound.rate = mpeg.rate;
sound.width = 2; // always 16-bit PCM
sound.loopstart = -1;
sound.size = mpeg_size( &mpeg.frame, filesize );
sound.size = ( sound.channels * sound.rate * sound.width ) * ( mpeg.play_time / 1000 ); // in bytes
pos += FRAME_SIZE; // evaluate pos
if( !sound.size )
{
// bad ogg file
// bad mpeg file ?
MsgDev( D_ERROR, "Sound_LoadMPG: (%s) is probably corrupted\n", name );
mad_stream_finish( &mpeg.stream );
mad_frame_finish( &mpeg.frame );
close_decoder( &mpeg );
return false;
}
@ -309,35 +95,36 @@ qboolean Sound_LoadMPG( const char *name, const byte *buffer, size_t filesize )
// decompress mpg into pcm wav format
while( bytesWrite < sound.size )
{
word *data;
int i;
int outsize;
mad_synth_frame( &mpeg.synth, &mpeg.frame );
data = (short *)(sound.wav + bytesWrite);
for( i = 0; i < mpeg.synth.pcm.length; i++ )
if( read_mpeg_stream( &mpeg, NULL, 0 ) != MP3_OK )
{
if( sound.channels == 2 )
{
*data++ = mpeg_scale( mpeg.synth.pcm.samples[0][i] );
*data++ = mpeg_scale( mpeg.synth.pcm.samples[1][i] );
}
else
{
*data++ = mpeg_scale( mpeg.synth.pcm.samples[0][i] );
}
char *data = (char *)buffer + pos;
int bufsize;
bytesWrite += ( sound.width * sound.channels );
if( bytesWrite >= sound.size ) break;
// if there are no bytes remainig so we can decompress the new frame
if( pos + FRAME_SIZE > filesize )
bufsize = ( filesize - pos );
else bufsize = FRAME_SIZE;
pos += bufsize;
if( read_mpeg_stream( &mpeg, data, bufsize ) != MP3_OK )
break; // there was end of the stream
}
if( !mpeg_read_mem( buffer, &pos, filesize, &mpeg ))
break;
if( bytesWrite + mpeg.outsize > sound.size )
{
outsize = ( sound.size - bytesWrite );
Msg( "merge size from %i, to %i\n", mpeg.outsize, outsize );
}
else outsize = mpeg.outsize;
Q_memcpy( &sound.wav[bytesWrite], mpeg.out, outsize );
bytesWrite += outsize;
}
sound.samples = bytesWrite / ( sound.width * sound.channels );
mad_stream_finish( &mpeg.stream );
mad_frame_finish( &mpeg.frame );
close_decoder( &mpeg );
return true;
}
@ -349,28 +136,55 @@ Stream_OpenMPG
*/
stream_t *Stream_OpenMPG( const char *filename )
{
mpegfile_t *mpegFile;
stream_t *stream;
file_t *file;
mpeg_t *mpegFile;
stream_t *stream;
file_t *file;
long filesize, read_len;
char tempbuff[FRAME_SIZE];
file = FS_Open( filename, "rb", false );
if( !file ) return NULL;
filesize = FS_FileLength( file );
if( filesize < FRAME_SIZE )
{
MsgDev( D_ERROR, "Stream_OpenMPG: %s is probably corrupted\n", filename );
FS_Close( file );
return NULL;
}
// at this point we have valid stream
stream = Mem_Alloc( host.soundpool, sizeof( stream_t ));
stream->file = file;
mpegFile = Mem_Alloc( host.soundpool, sizeof( mpegfile_t ));
mpegFile = Mem_Alloc( host.soundpool, sizeof( mpeg_t ));
mad_synth_init( &mpegFile->synth );
mad_stream_init( &mpegFile->stream );
mad_frame_init( &mpegFile->frame );
if( mpeg_read( file, mpegFile ) == 0 )
// couldn't create decoder
if( !create_decoder( mpegFile ))
{
MsgDev( D_ERROR, "Stream_OpenMPG: couldn't open %s\n", filename );
mad_stream_finish( &mpegFile->stream );
mad_frame_finish( &mpegFile->frame );
MsgDev( D_ERROR, "Stream_OpenMPG: couldn't create decoder\n" );
Mem_Free( mpegFile );
Mem_Free( stream );
FS_Close( file );
return NULL;
}
read_len = FS_Read( file, tempbuff, sizeof( tempbuff ));
if( read_len < sizeof( tempbuff ))
{
MsgDev( D_ERROR, "Stream_OpenMPG: %s is probably corrupted\n", filename );
close_decoder( mpegFile );
Mem_Free( mpegFile );
Mem_Free( stream );
FS_Close( file );
return NULL;
}
// trying to read header
if( !read_mpeg_header( mpegFile, tempbuff, sizeof( tempbuff ), filesize ))
{
MsgDev( D_ERROR, "Sound_LoadMPG: (%s) is probably corrupted\n", filename );
close_decoder( mpegFile );
Mem_Free( mpegFile );
Mem_Free( stream );
FS_Close( file );
@ -378,19 +192,12 @@ stream_t *Stream_OpenMPG( const char *filename )
}
stream->pos = 0; // how many samples left from previous frame
stream->channels = ( mpegFile->frame.header.mode == MODE_SINGLE_CHANNEL ) ? 1 : 2;
stream->rate = mpegFile->frame.header.samplerate;
stream->channels = mpegFile->channels;
stream->rate = mpegFile->rate;
stream->width = 2; // always 16 bit
stream->ptr = mpegFile;
stream->type = WF_MPGDATA;
// g-cont: there is a stupid way...
if( stream->rate > 44100 )
{
mpegFile->stream.options = 0x0002;
stream->rate /= 2;
}
return stream;
}
@ -401,52 +208,49 @@ Stream_ReadMPG
assume stream is valid
=================
*/
long Stream_ReadMPG( stream_t *stream, long bytes, void *buffer )
long Stream_ReadMPG( stream_t *stream, long needBytes, void *buffer )
{
// buffer handling
int bytesRead = 0;
mpegfile_t *mpg;
int bytesWritten = 0;
mpeg_t *mpg;
mpg = (mpegfile_t *)stream->ptr;
mpg = (mpeg_t *)stream->ptr;
ASSERT( mpg != NULL );
while( 1 )
{
pcm_t *wav;
word *data;
int i;
char tempbuff[FRAME_SIZE];
long read_len, outsize;
byte *data;
if( !stream->pos )
{
// if there are no bytes remainig so we can synth new frame
mad_synth_frame( &mpg->synth, &mpg->frame );
}
wav = &mpg->synth.pcm;
data = (word *)((byte *)buffer + bytesRead);
for( i = stream->pos; i < wav->length; i++ )
{
if( stream->channels == 2 )
if( read_mpeg_stream( mpg, NULL, 0 ) != MP3_OK )
{
*data++ = mpeg_scale( wav->samples[0][i] );
*data++ = mpeg_scale( wav->samples[1][i] );
}
else
{
*data++ = mpeg_scale( wav->samples[0][i] );
}
bytesRead += ( stream->width * stream->channels );
if( bytesRead >= bytes )
{
// continue from this sample on a next call
stream->pos = i;
return bytesRead;
// if there are no bytes remainig so we can decompress the new frame
read_len = FS_Read( stream->file, tempbuff, sizeof( tempbuff ));
if( read_mpeg_stream( mpg, tempbuff, read_len ) != MP3_OK )
break; // there was end of the stream
}
}
stream->pos = 0; // no bytes remainig
if( !mpeg_read( stream->file, mpg )) break;
// check remaining size
if( bytesWritten + mpg->outsize > needBytes )
outsize = ( needBytes - bytesWritten );
else outsize = mpg->outsize;
// copy raw sample to output buffer
data = (byte *)buffer + bytesWritten;
Q_memcpy( data, &mpg->out[stream->pos], outsize );
bytesWritten += outsize;
mpg->outsize -= outsize;
stream->pos += outsize;
// continue from this sample on a next call
if( bytesWritten >= needBytes )
return bytesWritten;
stream->pos = 0; // no bytes remaining
}
return 0;
}
@ -462,12 +266,10 @@ void Stream_FreeMPG( stream_t *stream )
{
if( stream->ptr )
{
mpegfile_t *mpg;
mpeg_t *mpg;
mpg = (mpegfile_t *)stream->ptr;
mad_stream_finish( &mpg->stream );
mad_frame_finish( &mpg->frame );
mpg = (mpeg_t *)stream->ptr;
close_decoder( mpg );
Mem_Free( stream->ptr );
}

View File

@ -0,0 +1,480 @@
/*
snd_mp3.c - mp3 format loading and streaming
Copyright (C) 2010 Uncle Mike
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include "soundlib.h"
/*
=======================================================================
LIBMAD DEFINITION
=======================================================================
*/
#define BUFFER_GUARD 8
#define BUFFER_MDLEN (511 + 2048 + BUFFER_GUARD)
#define BUFFER_SIZE 4096 // must be large than BUFFER_MDLEN
#define MPEG_F_FRACBITS 28
#define MPEG_F( x ) ((int)( x##L ))
#define MPEG_F_ONE MPEG_F( 0x10000000 )
enum
{
MP3_ERROR_NONE = 0x0000, // no error
MP3_ERROR_BUFLEN = 0x0001, // input buffer too small (or EOF)
MP3_ERROR_BUFPTR = 0x0002, // invalid (null) buffer pointer
MP3_ERROR_NOMEM = 0x0031, // not enough memory
MP3_ERROR_LOSTSYNC = 0x0101, // lost synchronization
MP3_ERROR_BADLAYER = 0x0102, // reserved header layer value
MP3_ERROR_BADBITRATE = 0x0103, // forbidden bitrate value
MP3_ERROR_BADSAMPLERATE = 0x0104, // reserved sample frequency value
MP3_ERROR_BADEMPHASIS = 0x0105, // reserved emphasis value
MP3_ERROR_BADCRC = 0x0201, // CRC check failed
MP3_ERROR_BADBITALLOC = 0x0211, // forbidden bit allocation value
MP3_ERROR_BADSCALEFACTOR = 0x0221, // bad scalefactor index
MP3_ERROR_BADMODE = 0x0222, // bad bitrate/mode combination
MP3_ERROR_BADFRAMELEN = 0x0231, // bad frame length
MP3_ERROR_BADBIGVALUES = 0x0232, // bad big_values count
MP3_ERROR_BADBLOCKTYPE = 0x0233, // reserved block_type
MP3_ERROR_BADSCFSI = 0x0234, // bad scalefactor selection info
MP3_ERROR_BADDATAPTR = 0x0235, // bad main_data_begin pointer
MP3_ERROR_BADPART3LEN = 0x0236, // bad audio data length
MP3_ERROR_BADHUFFTABLE = 0x0237, // bad Huffman table select
MP3_ERROR_BADHUFFDATA = 0x0238, // Huffman data overrun
MP3_ERROR_BADSTEREO = 0x0239 // incompatible block_type for JS
};
enum
{
MODE_SINGLE_CHANNEL = 0, // single channel
MODE_DUAL_CHANNEL, // dual channel
MODE_JOINT_STEREO, // joint (MS/intensity) stereo
MODE_STEREO // normal LR stereo
};
typedef struct
{
uint samplerate; // sampling frequency (Hz)
word channels; // number of channels
word length; // number of samples per channel
int samples[2][1152]; // PCM output samples [ch][sample]
} pcm_t;
typedef struct
{
int filter[2][2][2][16][8]; // polyphase filterbank outputs
uint phase; // current processing phase
pcm_t pcm; // PCM output
} synth_t;
typedef struct
{
const byte *byte;
word cache;
word left;
} bitptr_t;
typedef struct
{
long seconds; // whole seconds
dword fraction; // 1 / TIMER_RESOLUTION seconds
} mp3_timer_t;
typedef struct
{
int layer; // audio layer (1, 2, or 3)
int mode; // channel mode (see above)
int mode_extension; // additional mode info
int emphasis; // de-emphasis to use (see above)
dword bitrate; // stream bitrate (bps)
uint samplerate; // sampling frequency (Hz)
word crc_check; // frame CRC accumulator
word crc_target; // final target CRC checksum
int flags; // flags (see below)
int private_bits; // private bits (see below)
mp3_timer_t duration; // audio playing time of frame
} mp3_header_t;
typedef struct
{
mp3_header_t header; // MPEG audio header
int options; // decoding options (from stream)
int sbsample[2][36][32]; // synthesis subband filter samples
int (*overlap)[2][32][18]; // Layer III block overlap data
} mp3_frame_t;
typedef struct
{
const byte *buffer; // input bitstream buffer
const byte *bufend; // end of buffer
dword skiplen; // bytes to skip before next frame
int sync; // stream sync found
dword freerate; // free bitrate (fixed)
const byte *this_frame; // start of current frame
const byte *next_frame; // start of next frame
bitptr_t ptr; // current processing bit pointer
bitptr_t anc_ptr; // ancillary bits pointer
uint anc_bitlen; // number of ancillary bits
byte (*data)[BUFFER_MDLEN];
// Layer III data()
uint md_len; // bytes in data
int options; // decoding options
int error; // error code
} mp3_stream_t;
typedef struct
{
synth_t synth;
mp3_stream_t stream;
mp3_frame_t frame;
int buffer_length; // for reading
byte buffer[BUFFER_SIZE];// frame buffer
} mpegfile_t;
// libmad exports
extern void mad_synth_init( synth_t* );
extern void mad_synth_frame( synth_t*, const mp3_frame_t* );
extern void mad_stream_init( mp3_stream_t* );
extern void mad_stream_buffer( mp3_stream_t*, const byte*, dword );
extern void mad_stream_finish( mp3_stream_t* );
extern void mad_frame_init( mp3_frame_t* );
extern int mad_frame_decode( mp3_frame_t*, mp3_stream_t* );
extern void mad_frame_finish( mp3_frame_t* );
/*
=================================================================
MPEG decompression
=================================================================
*/
static int mpeg_read( file_t *file, mpegfile_t *mpeg )
{
int ret;
while( 1 )
{
ret = FS_Read( file, &mpeg->buffer[mpeg->buffer_length], BUFFER_SIZE - mpeg->buffer_length );
// no more bytes are left
if( ret <= 0 ) break;
mpeg->buffer_length += ret;
while( 1 )
{
mad_stream_buffer( &mpeg->stream, mpeg->buffer, mpeg->buffer_length );
ret = mad_frame_decode( &mpeg->frame, &mpeg->stream );
if( mpeg->stream.next_frame )
{
int length;
length = mpeg->buffer + mpeg->buffer_length - mpeg->stream.next_frame;
memmove( mpeg->buffer, mpeg->stream.next_frame, length );
mpeg->buffer_length = length;
}
if( !ret ) return 1;
if( mpeg->stream.error == MP3_ERROR_BUFLEN )
break;
}
}
return 0;
}
static int mpeg_read_mem( const byte *buffer, int *pos, size_t filesize, mpegfile_t *mpeg )
{
int ret, readSize;
while( 1 )
{
readSize = ( BUFFER_SIZE - mpeg->buffer_length );
if(( *pos + readSize ) > filesize )
readSize = ( filesize - *pos );
Q_memcpy( &mpeg->buffer[mpeg->buffer_length], buffer + *pos, readSize );
// no more bytes are left
if( readSize <= 0 ) break;
*pos += readSize;
mpeg->buffer_length += readSize;
while( 1 )
{
mad_stream_buffer( &mpeg->stream, mpeg->buffer, mpeg->buffer_length );
ret = mad_frame_decode( &mpeg->frame, &mpeg->stream );
if( mpeg->stream.next_frame )
{
int length;
length = mpeg->buffer + mpeg->buffer_length - mpeg->stream.next_frame;
memmove( mpeg->buffer, mpeg->stream.next_frame, length );
mpeg->buffer_length = length;
}
if( !ret ) return 1;
if( mpeg->stream.error == MP3_ERROR_BUFLEN )
break;
}
}
return 0;
}
static int mpeg_scale( int sample )
{
sample += (1 << ( MPEG_F_FRACBITS - 16 ));
if( sample >= MPEG_F_ONE ) sample = MPEG_F_ONE - 1;
else if( sample < -MPEG_F_ONE ) sample = -MPEG_F_ONE;
return sample >> ( MPEG_F_FRACBITS + 1 - 16 );
}
static int mpeg_size( mp3_frame_t *frame, long bytes )
{
return bytes * 8 / frame->header.bitrate * sound.channels * sound.rate * sound.width;
}
/*
=================================================================
MPEG decompression
=================================================================
*/
qboolean Sound_LoadMPG( const char *name, const byte *buffer, size_t filesize )
{
mpegfile_t mpeg;
size_t pos = 0;
size_t bytesWrite = 0;
// load the file
if( !buffer || filesize <= 0 )
return false;
Q_memset( &mpeg, 0, sizeof( mpeg ));
mad_synth_init( &mpeg.synth );
mad_stream_init( &mpeg.stream );
mad_frame_init( &mpeg.frame );
if( mpeg_read_mem( buffer, &pos, filesize, &mpeg ) == 0 )
{
MsgDev( D_ERROR, "Sound_LoadMPG: (%s) is probably corrupted\n", name );
mad_stream_finish( &mpeg.stream );
mad_frame_finish( &mpeg.frame );
return false;
}
sound.channels = ( mpeg.frame.header.mode == MODE_SINGLE_CHANNEL ) ? 1 : 2;
sound.rate = mpeg.frame.header.samplerate;
sound.width = 2; // always 16-bit PCM
sound.loopstart = -1;
sound.size = mpeg_size( &mpeg.frame, filesize );
if( !sound.size )
{
// bad ogg file
MsgDev( D_ERROR, "Sound_LoadMPG: (%s) is probably corrupted\n", name );
mad_stream_finish( &mpeg.stream );
mad_frame_finish( &mpeg.frame );
return false;
}
sound.type = WF_PCMDATA;
sound.wav = (byte *)Mem_Alloc( host.soundpool, sound.size );
// decompress mpg into pcm wav format
while( bytesWrite < sound.size )
{
word *data;
int i;
mad_synth_frame( &mpeg.synth, &mpeg.frame );
data = (short *)(sound.wav + bytesWrite);
for( i = 0; i < mpeg.synth.pcm.length; i++ )
{
if( sound.channels == 2 )
{
*data++ = mpeg_scale( mpeg.synth.pcm.samples[0][i] );
*data++ = mpeg_scale( mpeg.synth.pcm.samples[1][i] );
}
else
{
*data++ = mpeg_scale( mpeg.synth.pcm.samples[0][i] );
}
bytesWrite += ( sound.width * sound.channels );
if( bytesWrite >= sound.size ) break;
}
if( !mpeg_read_mem( buffer, &pos, filesize, &mpeg ))
break;
}
sound.samples = bytesWrite / ( sound.width * sound.channels );
mad_stream_finish( &mpeg.stream );
mad_frame_finish( &mpeg.frame );
return true;
}
/*
=================
Stream_OpenMPG
=================
*/
stream_t *Stream_OpenMPG( const char *filename )
{
mpegfile_t *mpegFile;
stream_t *stream;
file_t *file;
file = FS_Open( filename, "rb", false );
if( !file ) return NULL;
// at this point we have valid stream
stream = Mem_Alloc( host.soundpool, sizeof( stream_t ));
stream->file = file;
mpegFile = Mem_Alloc( host.soundpool, sizeof( mpegfile_t ));
mad_synth_init( &mpegFile->synth );
mad_stream_init( &mpegFile->stream );
mad_frame_init( &mpegFile->frame );
if( mpeg_read( file, mpegFile ) == 0 )
{
MsgDev( D_ERROR, "Stream_OpenMPG: couldn't open %s\n", filename );
mad_stream_finish( &mpegFile->stream );
mad_frame_finish( &mpegFile->frame );
Mem_Free( mpegFile );
Mem_Free( stream );
FS_Close( file );
return NULL;
}
stream->pos = 0; // how many samples left from previous frame
stream->channels = ( mpegFile->frame.header.mode == MODE_SINGLE_CHANNEL ) ? 1 : 2;
stream->rate = mpegFile->frame.header.samplerate;
stream->width = 2; // always 16 bit
stream->ptr = mpegFile;
stream->type = WF_MPGDATA;
// g-cont: there is a stupid way...
if( stream->rate > 44100 )
{
mpegFile->stream.options = 0x0002;
stream->rate /= 2;
}
return stream;
}
/*
=================
Stream_ReadMPG
assume stream is valid
=================
*/
long Stream_ReadMPG( stream_t *stream, long bytes, void *buffer )
{
// buffer handling
int bytesRead = 0;
mpegfile_t *mpg;
mpg = (mpegfile_t *)stream->ptr;
ASSERT( mpg != NULL );
while( 1 )
{
pcm_t *wav;
word *data;
int i;
if( !stream->pos )
{
// if there are no bytes remainig so we can synth new frame
mad_synth_frame( &mpg->synth, &mpg->frame );
}
wav = &mpg->synth.pcm;
data = (word *)((byte *)buffer + bytesRead);
for( i = stream->pos; i < wav->length; i++ )
{
if( stream->channels == 2 )
{
*data++ = mpeg_scale( wav->samples[0][i] );
*data++ = mpeg_scale( wav->samples[1][i] );
}
else
{
*data++ = mpeg_scale( wav->samples[0][i] );
}
bytesRead += ( stream->width * stream->channels );
if( bytesRead >= bytes )
{
// continue from this sample on a next call
stream->pos = i;
return bytesRead;
}
}
stream->pos = 0; // no bytes remainig
if( !mpeg_read( stream->file, mpg )) break;
}
return 0;
}
/*
=================
Stream_FreeMPG
assume stream is valid
=================
*/
void Stream_FreeMPG( stream_t *stream )
{
if( stream->ptr )
{
mpegfile_t *mpg;
mpg = (mpegfile_t *)stream->ptr;
mad_stream_finish( &mpg->stream );
mad_frame_finish( &mpg->frame );
Mem_Free( stream->ptr );
}
if( stream->file )
{
FS_Close( stream->file );
}
Mem_Free( stream );
}

View File

@ -146,7 +146,7 @@ qboolean Sound_LoadWAV( const char *name, const byte *buffer, size_t filesize )
iff_data = buffer;
iff_end = buffer + filesize;
// dind "RIFF" chunk
// find "RIFF" chunk
FindChunk( "RIFF" );
if( !( iff_dataPtr && !Q_strncmp( iff_dataPtr + 8, "WAVE", 4 )))
{

View File

@ -259,6 +259,20 @@ typedef struct enginefuncs_s
qboolean (*pfnVoice_SetClientListening)(int iReceiver, int iSender, qboolean bListen);
const char *(*pfnGetPlayerAuthId) ( edict_t *e );
void *(*pfnSequenceGet)( const char *fileName, const char *entryName );
void *(*pfnSequencePickSentence)( const char *groupName, int pickMethod, int *picked );
int (*pfnGetFileSize)( char *filename );
unsigned int (*pfnGetApproxWavePlayLen)( const char *filepath );
int (*pfnIsCareerMatch)( void );
int (*pfnGetLocalizedStringLength)( const char *label );
void (*pfnRegisterTutorMessageShown)( int mid );
int (*pfnGetTimesTutorMessageShown)( int mid );
void (*pfnProcessTutorMessageDecayBuffer)( int *buffer, int bufferLength );
void (*pfnConstructTutorMessageDecayBuffer)( int *buffer, int bufferLength );
void (*pfnResetTutorMessageDecayData)( void );
void (*pfnQueryClientCvarValue)( const edict_t *player, const char *cvarName );
void (*pfnQueryClientCvarValue2)( const edict_t *player, const char *cvarName, int requestID );
} enginefuncs_t;
// ONLY ADD NEW FUNCTIONS TO THE END OF THIS STRUCT. INTERFACE VERSION IS FROZEN AT 138
@ -476,8 +490,8 @@ typedef struct
void (*pfnOnFreeEntPrivateData)( edict_t *pEnt );
void (*pfnGameShutdown)(void);
int (*pfnShouldCollide)( edict_t *pentTouched, edict_t *pentOther );
int (*pfnCreate)( edict_t *pent, const char *szName ); // passed through pfnCreate (0 is attempt to create, -1 is reject)
int (*pfnPhysicsEntity)( edict_t *pEntity ); // run custom physics for each entity (return 0 to use engine physic)
void (*pfnCvarValue)( const edict_t *pEnt, const char *value );
void (*pfnCvarValue2)( const edict_t *pEnt, int requestID, const char *cvarName, const char *value );
} NEW_DLL_FUNCTIONS;
typedef int (*NEW_DLL_FUNCTIONS_FN)( NEW_DLL_FUNCTIONS *pFunctionTable, int *interfaceVersion );

38
engine/physint.h Normal file
View File

@ -0,0 +1,38 @@
/*
physint.h - Server Physics Interface
Copyright (C) 2011 Uncle Mike
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#ifndef PHYSINT_H
#define PHYSINT_H
#define SV_PHYSICS_INTERFACE_VERSION 1
typedef struct server_physics_api_s
{
// unlink edict from old position and link onto new
void ( *pfnLinkEdict) ( edict_t *ent, qboolean touch_triggers );
double ( *pfnGetServerTime )( void ); // unclamped
} server_physics_api_t;
// physic callbacks
typedef struct physics_interface_s
{
int version;
// passed through pfnCreate (0 is attempt to create, -1 is reject)
int ( *SV_CreateEntity )( edict_t *pent, const char *szName );
// run custom physics for each entity (return 0 to use built-in engine physic)
int ( *SV_PhysicsEntity )( edict_t *pEntity );
} physics_interface_t;
#endif//PHYSINT_H

View File

@ -19,6 +19,7 @@ GNU General Public License for more details.
#include "mathlib.h"
#include "edict.h"
#include "eiface.h"
#include "physint.h" // physics interface
#include "mod_local.h"
#include "pm_defs.h"
#include "pm_movevars.h"
@ -29,7 +30,6 @@ GNU General Public License for more details.
#include "world.h"
//=============================================================================
#define MAX_MASTERS 8 // max recipients for heartbeat packets
#define SV_UPDATE_MASK (SV_UPDATE_BACKUP - 1)
extern int SV_UPDATE_BACKUP;
@ -105,6 +105,9 @@ typedef struct server_s
string name; // map name
string startspot; // player_start name on nextmap
double lastchecktime;
int lastcheck; // number of last checked client
char model_precache[MAX_MODELS][CS_SIZE];
char sound_precache[MAX_SOUNDS][CS_SIZE];
char files_precache[MAX_CUSTOM][CS_SIZE];
@ -322,6 +325,7 @@ typedef struct
globalvars_t *globals; // server globals
DLL_FUNCTIONS dllFuncs; // dll exported funcs
NEW_DLL_FUNCTIONS dllFuncs2; // new dll exported funcs (can be NULL)
physics_interface_t physFuncs; // physics interface functions (Xash3D extension)
byte *mempool; // server premamnent pool: edicts etc
byte *stringspool; // for shared strings
@ -353,7 +357,6 @@ typedef struct
//=============================================================================
extern netadr_t master_adr[MAX_MASTERS]; // address of the master server
extern server_static_t svs; // persistant server info
extern server_t sv; // local server
extern svgame_static_t svgame; // persistant game info
@ -396,6 +399,7 @@ extern convar_t *sv_send_resources;
extern convar_t *sv_send_logos;
extern convar_t *sv_sendvelocity;
extern convar_t *mp_consistency;
extern convar_t *public_server;
extern convar_t *physinfo;
//===========================================================
@ -415,6 +419,9 @@ void SV_InitOperatorCommands( void );
void SV_KillOperatorCommands( void );
void SV_UserinfoChanged( sv_client_t *cl, const char *userinfo );
void SV_PrepWorldFrame( void );
void SV_ProcessFile( sv_client_t *cl, char *filename );
void SV_SendResourceList( sv_client_t *cl );
void Master_Add( void );
void Master_Heartbeat( void );
void Master_Packet( void );
@ -431,6 +438,7 @@ qboolean SV_SpawnServer( const char *server, const char *startspot );
// sv_phys.c
//
void SV_Physics( void );
qboolean SV_InitPhysicsAPI( void );
void SV_CheckVelocity( edict_t *ent );
qboolean SV_CheckWater( edict_t *ent );
qboolean SV_RunThink( edict_t *ent );

View File

@ -933,7 +933,7 @@ void SV_PutClientInServer( edict_t *ent )
return;
}
// enable dev-mode to prevent crash cheat-protecting from invasion
// enable dev-mode to prevent crash cheat-protecting from Invasion mod
if( ent->v.flags & (FL_GODMODE|FL_NOTARGET) && !Q_stricmp( GI->gamefolder, "invasion" ))
SV_ExecuteClientCommand( client, "test\n" );
@ -943,6 +943,7 @@ void SV_PutClientInServer( edict_t *ent )
BF_WriteByte( &client->netchan.message, svc_setangle );
BF_WriteBitAngle( &client->netchan.message, ent->v.angles[0], 16 );
BF_WriteBitAngle( &client->netchan.message, ent->v.angles[1], 16 );
BF_WriteBitAngle( &client->netchan.message, ent->v.angles[2], 16 );
ent->v.fixangle = 0;
}
ent->v.effects |= EF_NOINTERP;
@ -1054,14 +1055,111 @@ void SV_New_f( sv_client_t *cl )
// set up the entity for the client
ent = EDICT_NUM( playernum + 1 );
cl->edict = ent;
Q_memset( &cl->lastcmd, 0, sizeof( cl->lastcmd ));
// begin fetching modellist
BF_WriteByte( &cl->netchan.message, svc_stufftext );
BF_WriteString( &cl->netchan.message, va( "cmd modellist %i %i\n", svs.spawncount, 0 ));
if( sv_maxclients->integer == 1 )
{
Q_memset( &cl->lastcmd, 0, sizeof( cl->lastcmd ));
// begin fetching modellist
BF_WriteByte( &cl->netchan.message, svc_stufftext );
BF_WriteString( &cl->netchan.message, va( "cmd modellist %i %i\n", svs.spawncount, 0 ));
}
else
{
// request resource list
BF_WriteByte( &cl->netchan.message, svc_stufftext );
BF_WriteString( &cl->netchan.message, va( "cmd getresourelist\n" ));
}
}
}
/*
==================
SV_ContinueLoading_f
==================
*/
void SV_ContinueLoading_f( sv_client_t *cl )
{
if( cl->state != cs_connected )
{
MsgDev( D_INFO, "continueloading is not valid from the console\n" );
return;
}
Q_memset( &cl->lastcmd, 0, sizeof( cl->lastcmd ));
// begin fetching modellist
BF_WriteByte( &cl->netchan.message, svc_stufftext );
BF_WriteString( &cl->netchan.message, va( "cmd modellist %i %i\n", svs.spawncount, 0 ));
}
/*
=======================
SV_SendResourceList
NOTE: Sending the list of cached resources.
g-cont. this is fucking big message!!! i've rewriting this code
=======================
*/
void SV_SendResourceList_f( sv_client_t *cl )
{
int index = 0;
int rescount = 0;
resourcelist_t reslist;
size_t msg_size;
Q_memset( &reslist, 0, sizeof( resourcelist_t ));
reslist.restype[rescount] = t_world; // terminator
Q_strcpy( reslist.resnames[rescount], "NULL" );
rescount++;
for( index = 1; index < MAX_MODELS && sv.model_precache[index][0]; index++ )
{
if( sv.model_precache[index][0] == '*' ) // internal bmodel
continue;
reslist.restype[rescount] = t_model;
Q_strcpy( reslist.resnames[rescount], sv.model_precache[index] );
rescount++;
}
for( index = 1; index < MAX_SOUNDS && sv.sound_precache[index][0]; index++ )
{
reslist.restype[rescount] = t_sound;
Q_strcpy( reslist.resnames[rescount], sv.sound_precache[index] );
rescount++;
}
for( index = 1; index < MAX_EVENTS && sv.event_precache[index][0]; index++ )
{
reslist.restype[rescount] = t_eventscript;
Q_strcpy( reslist.resnames[rescount], sv.event_precache[index] );
rescount++;
}
for( index = 1; index < MAX_CUSTOM && sv.files_precache[index][0]; index++ )
{
reslist.restype[rescount] = t_generic;
Q_strcpy( reslist.resnames[rescount], sv.files_precache[index] );
rescount++;
}
msg_size = BF_GetRealBytesWritten( &cl->netchan.message ); // start
BF_WriteByte( &cl->netchan.message, svc_resourcelist );
BF_WriteWord( &cl->netchan.message, rescount );
for( index = 1; index < rescount; index++ )
{
BF_WriteWord( &cl->netchan.message, reslist.restype[index] );
BF_WriteString( &cl->netchan.message, reslist.resnames[index] );
}
Msg( "Count res: %d\n", rescount );
Msg( "ResList size: %s\n", Q_memprint( BF_GetRealBytesWritten( &cl->netchan.message ) - msg_size ));
}
/*
==================
SV_WriteModels_f
@ -1692,6 +1790,8 @@ ucmd_t ucmds[] =
{ "usermsgs", SV_UserMessages_f },
{ "userinfo", SV_UpdateUserinfo_f },
{ "lightstyles", SV_WriteLightstyles_f },
{ "getresourelist", SV_SendResourceList_f },
{ "continueloading", SV_ContinueLoading_f },
{ NULL, NULL }
};
@ -1741,6 +1841,10 @@ void SV_ConnectionlessPacket( netadr_t from, sizebuf_t *msg )
char *args;
char *c, buf[MAX_SYSPATH];
int len = sizeof( buf );
dword challenge;
int index, count = 0;
char query[512];
word port;
BF_Clear( msg );
BF_ReadLong( msg );// skip the -1 marker
@ -1758,6 +1862,25 @@ void SV_ConnectionlessPacket( netadr_t from, sizebuf_t *msg )
else if( !Q_strcmp( c, "getchallenge" )) SV_GetChallenge( from );
else if( !Q_strcmp( c, "connect" )) SV_DirectConnect( from );
else if( !Q_strcmp( c, "rcon" )) SV_RemoteCommand( from, msg );
else if( msg->pData[0] == 0xFF && msg->pData[1] == 0xFF && msg->pData[2] == 0xFF && msg->pData[3] == 0xFF && msg->pData[4] == 0x4E && msg->pData[5] == 0x0A )
{
challenge = *(dword *)&msg->pData[6];
port = Cvar_Get( "ip_hostport", "0", CVAR_INIT, "network server port" )->integer;
if( !port ) port = Cvar_Get( "port", va( "%i", PORT_SERVER ), CVAR_INIT, "network default port" )->integer;
for( index = 0; index < sv_maxclients->integer; index++ )
{
if( svs.clients[index].state >= cs_connected )
count++;
}
Q_snprintf( query, sizeof( query ),
"0\n\\protocol\\7\\challenge\\%ld\\players\\%d\\max\\%d\\bots\\0\\gamedir\\%s_xash\\map\\%s\\password\\0\\os\\w\\lan\\0\\region\\255\\gameport\\%d\\specport\\27015\\dedicated\\1\\appid\\70\\type\\d\\secure\\0\\version\\1.1.2.1\\product\\valve\n",
challenge, count, sv_maxclients->integer, GI->gamefolder, sv.name, port );
NET_SendPacket( NS_SERVER, Q_strlen( query ), query, from );
}
else if( svgame.dllFuncs.pfnConnectionlessPacket( &from, args, buf, &len ))
{
// user out of band message (must be handled in CL_ConnectionlessPacket)
@ -1903,6 +2026,19 @@ static void SV_ParseClientMove( sv_client_t *cl, sizebuf_t *msg )
player->v.animtime = sv.time + host.frametime;
}
/*
===================
SV_ParseResourceList
Parse resource list
===================
*/
void SV_ParseResourceList( sv_client_t *cl, sizebuf_t *msg )
{
Netchan_CreateFileFragments( true, &cl->netchan, BF_ReadString( msg ));
Netchan_FragSend( &cl->netchan );
}
/*
===================
SV_ExecuteClientMessage
@ -1976,6 +2112,9 @@ void SV_ExecuteClientMessage( sv_client_t *cl, sizebuf_t *msg )
if( ++stringCmdCount < 8 ) SV_ExecuteClientCommand( cl, s );
if( cl->state == cs_zombie ) return; // disconnect command
break;
case clc_resourcelist:
SV_ParseResourceList( cl, msg );
break;
default:
MsgDev( D_ERROR, "SV_ReadClientMessage: clc_bad\n" );
SV_DropClient( cl );

View File

@ -96,51 +96,6 @@ void SV_BroadcastCommand( char *fmt, ... )
BF_WriteString( &sv.reliable_datagram, string );
}
/*
====================
SV_SetMaster_f
Specify a list of master servers
====================
*/
void SV_SetMaster_f( void )
{
int i, slot;
// only dedicated servers send heartbeats
if( host.type != HOST_DEDICATED )
{
Msg( "Only dedicated servers use masters.\n" );
return;
}
// make sure the server is listed public
Cvar_Set( "public", "1" );
for( i = 1; i < MAX_MASTERS; i++ )
{
Q_memset( &master_adr[i], 0, sizeof( master_adr[i] ));
}
// slot 0 will always contain the id master
for( i = 1, slot = 1; i < Cmd_Argc(); i++ )
{
if( slot == MAX_MASTERS ) break;
if( !NET_StringToAdr( Cmd_Argv( i ), &master_adr[i] ))
{
Msg( "Bad address: %s\n", Cmd_Argv( i ));
continue;
}
if( !master_adr[slot].port ) master_adr[slot].port = BF_BigShort( PORT_MASTER );
Msg( "Master server at %s\n", NET_AdrToString( master_adr[slot] ));
Msg( "Sending a ping.\n" );
Netchan_OutOfBandPrint( NS_SERVER, master_adr[slot], "ping" );
slot++;
}
svs.last_heartbeat = MAX_HEARTBEAT;
}
/*
==================
SV_SetPlayer
@ -832,7 +787,6 @@ void SV_InitOperatorCommands( void )
if( host.type == HOST_DEDICATED )
{
Cmd_AddCommand( "say", SV_ConSay_f, "send a chat message to everyone on the server" );
Cmd_AddCommand( "setmaster", SV_SetMaster_f, "set ip address for dedicated server" );
Cmd_AddCommand( "killserver", SV_KillServer_f, "shutdown current server" );
}

View File

@ -381,6 +381,7 @@ void SV_WriteClientdataToMessage( sv_client_t *cl, sizebuf_t *msg )
BF_WriteByte( msg, svc_setangle );
BF_WriteBitAngle( msg, clent->v.angles[0], 16 );
BF_WriteBitAngle( msg, clent->v.angles[1], 16 );
BF_WriteBitAngle( msg, clent->v.angles[2], 16 );
clent->v.effects |= EF_NOINTERP;
break;
case 2:

View File

@ -21,11 +21,10 @@ GNU General Public License for more details.
#include "pm_defs.h"
#include "const.h"
#define DEBUG_NEW_CLIENTPVS_CHECK
// fatpvs stuff
static byte fatpvs[MAX_MAP_LEAFS/8];
static byte fatphs[MAX_MAP_LEAFS/8];
static byte clientpvs[MAX_MAP_LEAFS/8]; // for find client in PVS
static vec3_t viewPoint[MAX_CLIENTS];
static byte *bitvector;
static int fatbytes;
@ -314,6 +313,10 @@ qboolean SV_Send( int dest, const vec3_t origin, const edict_t *ent )
clientnum = cl - svs.clients;
viewOrg = viewPoint[clientnum];
// Invasion issues: wrong camera position received in ENGINE_SET_PVS
if( cl->pViewEntity && !VectorCompare( viewOrg, cl->pViewEntity->v.origin ))
viewOrg = cl->pViewEntity->v.origin;
// -1 is because pvs rows are 1 based, not 0 based like leafs
leafnum = Mod_PointLeafnum( viewOrg ) - 1;
if( mask && (!(mask[leafnum>>3] & (1<<( leafnum & 7 )))))
@ -766,7 +769,7 @@ edict_t* SV_AllocPrivateData( edict_t *ent, string_t className )
if( !SpawnEdict )
{
// attempt to create custom entity (Xash3D extension)
if( svgame.dllFuncs2.pfnCreate && svgame.dllFuncs2.pfnCreate( ent, pszClassName ) != -1 )
if( svgame.physFuncs.SV_CreateEntity && svgame.physFuncs.SV_CreateEntity( ent, pszClassName ) != -1 )
return ent;
ent->v.flags |= FL_KILLME;
@ -1249,73 +1252,98 @@ edict_t *pfnFindEntityInSphere( edict_t *pStartEdict, const float *org, float fl
return NULL;
}
/*
=================
SV_CheckClientPVS
build the new client PVS
=================
*/
int SV_CheckClientPVS( int check )
{
byte *pvs;
edict_t *ent;
mleaf_t *leaf;
vec3_t view;
int i;
// cycle to the next one
check = bound( 1, check, svgame.globals->maxClients );
if( check == svgame.globals->maxClients )
i = 1;
else i = check + 1;
for( ;; i++ )
{
if( i == svgame.globals->maxClients + 1 )
i = 1;
ent = EDICT_NUM( i );
if( i == check ) break; // didn't find anything else
if( ent->free ) continue;
if( !ent->pvPrivateData ) continue;
if( ent->v.flags & FL_NOTARGET ) continue;
// anything that is a client, or has a client as an enemy
break;
}
// get the PVS for the entity
VectorAdd( ent->v.origin, ent->v.view_ofs, view );
leaf = Mod_PointInLeaf( view, sv.worldmodel->nodes );
pvs = Mod_LeafPVS( leaf, sv.worldmodel );
memcpy( clientpvs, pvs, (sv.worldmodel->numleafs + 7) >> 3 );
return i;
}
/*
=================
pfnFindClientInPVS
FIXME: this code is totally wrong. Get PF_checkclient from QW.
=================
*/
edict_t* pfnFindClientInPVS( edict_t *pEdict )
{
edict_t *pClient;
sv_client_t *cl;
vec3_t view1, view2;
int i;
edict_t *pClient;
mleaf_t *leaf;
vec3_t view;
int i;
if( !SV_IsValidEdict( pEdict ))
return svgame.edicts;
VectorAdd( pEdict->v.origin, pEdict->v.view_ofs, view1 );
for( i = 0; i < svgame.globals->maxClients; i++ )
// find a new check if on a new frame
if(( sv.time - sv.lastchecktime ) >= 0.1 )
{
pClient = EDICT_NUM( i + 1 );
if(( cl = SV_ClientFromEdict( pClient, true )) == NULL )
continue;
// check for SET_VIEW
if( SV_IsValidEdict( cl->pViewEntity ))
VectorAdd( cl->pViewEntity->v.origin, cl->pViewEntity->v.view_ofs, view2 );
else VectorAdd( pClient->v.origin, pClient->v.view_ofs, view2 );
if( pEdict->v.modelindex )
{
// can use entity leafs or headnode for fast testing
// FIXME: this is need to be detail tested!
mleaf_t *leaf = Mod_PointInLeaf( view2, sv.worldmodel->nodes );
byte *mask = Mod_LeafPVS( leaf, sv.worldmodel );
if( pfnCheckVisibility( pEdict, mask ))
{
return pClient;
}
#ifdef DEBUG_NEW_CLIENTPVS_CHECK
else if( sv_check_errors->integer )
{
trace_t tr;
tr = SV_Move( view1, vec3_origin, vec3_origin, view2, MOVE_WORLDONLY, NULL );
if( tr.fraction == 1.0f && !tr.allsolid )
{
MsgDev( D_ERROR, "CHECK_CLIENT_PVS: fail to see %s, probably client is underwater\n", SV_ClassName( pEdict ));
}
}
#endif
}
else
{
if( SV_OriginIn( DVIS_PVS, view1, view2 ))
return pClient;
}
sv.lastcheck = SV_CheckClientPVS( sv.lastcheck );
sv.lastchecktime = sv.time;
}
return svgame.edicts;
// return check if it might be visible
pClient = EDICT_NUM( sv.lastcheck );
if( !SV_ClientFromEdict( pClient, true ))
return svgame.edicts;
VectorAdd( pEdict->v.origin, pEdict->v.view_ofs, view );
leaf = Mod_PointInLeaf( view, sv.worldmodel->nodes );
i = (leaf - sv.worldmodel->leafs) - 1;
if( i < 0 || !((clientpvs[i>>3]) & (1 << (i & 7))))
return svgame.edicts;
// client which currently in PVS
return pClient;
}
/*
=================
pfnEntitiesInPVS
FIXME: rewrite this code. get rid of hack
=================
*/
edict_t *pfnEntitiesInPVS( edict_t *pplayer )
@ -1673,6 +1701,8 @@ void SV_StartSound( edict_t *ent, int chan, const char *sample, float vol, float
int msg_dest;
vec3_t origin;
if( !sample ) return;
if( attn < ATTN_NONE || attn > ATTN_IDLE )
{
MsgDev( D_ERROR, "SV_StartSound: attenuation %g must be in range 0-2\n", attn );
@ -1781,6 +1811,8 @@ void pfnEmitAmbientSound( edict_t *ent, float *pos, const char *sample, float vo
int msg_dest = MSG_PAS_R;
vec3_t origin;
if( !sample ) return;
if( attn < ATTN_NONE || attn > ATTN_IDLE )
{
MsgDev( D_ERROR, "SV_AmbientSound: attenuation must be in range 0-2\n" );
@ -3282,6 +3314,9 @@ char *pfnGetInfoKeyBuffer( edict_t *e )
{
sv_client_t *cl;
if( !SV_IsValidEdict( e ))
return Cvar_Serverinfo(); // otherwise return ServerInfo
cl = SV_ClientFromEdict( e, false ); // pfnUserInfoChanged passed
if( cl == NULL )
{
@ -3947,6 +3982,150 @@ const char *pfnGetPlayerAuthId( edict_t *e )
return result;
}
/*
=============
pfnSequenceGet
used by CS:CZ
=============
*/
void *pfnSequenceGet( const char *fileName, const char *entryName )
{
return NULL;
}
/*
=============
pfnSequencePickSentence
used by CS:CZ
=============
*/
void *pfnSequencePickSentence( const char *groupName, int pickMethod, int *picked )
{
return NULL;
}
/*
=============
pfnGetFileSize
returns the filesize in bytes
=============
*/
int pfnGetFileSize( char *filename )
{
return FS_FileSize( filename, false );
}
/*
=============
pfnGetApproxWavePlayLen
returns the wave length in samples
=============
*/
uint pfnGetApproxWavePlayLen( const char *filepath )
{
return 0;
}
/*
=============
pfnIsCareerMatch
used by CS:CZ
=============
*/
int pfnIsCareerMatch( void )
{
return 0;
}
/*
=============
pfnGetLocalizedStringLength
=============
*/
int pfnGetLocalizedStringLength( const char *label )
{
return 0;
}
/*
=============
pfnRegisterTutorMessageShown
=============
*/
void pfnRegisterTutorMessageShown( int mid )
{
}
/*
=============
pfnGetTimesTutorMessageShown
=============
*/
int pfnGetTimesTutorMessageShown( int mid )
{
return 0;
}
/*
=============
pfnProcessTutorMessageDecayBuffer
=============
*/
void pfnProcessTutorMessageDecayBuffer( int *buffer, int bufferLength )
{
}
/*
=============
pfnConstructTutorMessageDecayBuffer
=============
*/
void pfnConstructTutorMessageDecayBuffer( int *buffer, int bufferLength )
{
}
/*
=============
pfnSequenceGet
=============
*/
void pfnResetTutorMessageDecayData( void )
{
}
/*
=============
pfnQueryClientCvarValue
request client cvar value
=============
*/
void pfnQueryClientCvarValue( const edict_t *player, const char *cvarName )
{
}
/*
=============
pfnQueryClientCvarValue2
request client cvar value (bugfixed)
=============
*/
void pfnQueryClientCvarValue2( const edict_t *player, const char *cvarName, int requestID )
{
}
// engine callbacks
static enginefuncs_t gEngfuncs =
@ -4095,6 +4274,19 @@ static enginefuncs_t gEngfuncs =
pfnVoice_GetClientListening,
pfnVoice_SetClientListening,
pfnGetPlayerAuthId,
pfnSequenceGet,
pfnSequencePickSentence,
pfnGetFileSize,
pfnGetApproxWavePlayLen,
pfnIsCareerMatch,
pfnGetLocalizedStringLength,
pfnRegisterTutorMessageShown,
pfnGetTimesTutorMessageShown,
pfnProcessTutorMessageDecayBuffer,
pfnConstructTutorMessageDecayBuffer,
pfnResetTutorMessageDecayData,
pfnQueryClientCvarValue,
pfnQueryClientCvarValue2,
};
/*
@ -4388,6 +4580,9 @@ qboolean SV_LoadProgs( const char *name )
// make sure what new dll functions is cleared
Q_memset( &svgame.dllFuncs2, 0, sizeof( svgame.dllFuncs2 ));
// make sure what physic functions is cleared
Q_memset( &svgame.physFuncs, 0, sizeof( svgame.physFuncs ));
// make local copy of engfuncs to prevent overwrite it with bots.dll
Q_memcpy( &gpEngfuncs, &gEngfuncs, sizeof( gpEngfuncs ));
@ -4395,7 +4590,7 @@ qboolean SV_LoadProgs( const char *name )
GetEntityAPI2 = (APIFUNCTION2)Com_GetProcAddress( svgame.hInstance, "GetEntityAPI2" );
GiveNewDllFuncs = (NEW_DLL_FUNCTIONS_FN)Com_GetProcAddress( svgame.hInstance, "GetNewDLLFunctions" );
if( !GetEntityAPI )
if( !GetEntityAPI && !GetEntityAPI2 )
{
Com_FreeLibrary( svgame.hInstance );
MsgDev( D_NOTE, "SV_LoadProgs: failed to get address of GetEntityAPI proc\n" );
@ -4430,22 +4625,29 @@ qboolean SV_LoadProgs( const char *name )
version = INTERFACE_VERSION;
if( !GetEntityAPI( &svgame.dllFuncs, version ))
if( GetEntityAPI2 )
{
if( !GetEntityAPI2 )
if( !GetEntityAPI2( &svgame.dllFuncs, &version ))
{
Com_FreeLibrary( svgame.hInstance );
MsgDev( D_ERROR, "SV_LoadProgs: couldn't get entity API\n" );
svgame.hInstance = NULL;
return false;
}
else if( !GetEntityAPI2( &svgame.dllFuncs, &version ))
{
Com_FreeLibrary( svgame.hInstance );
MsgDev( D_ERROR, "SV_LoadProgs: interface version %i should be %i\n", INTERFACE_VERSION, version );
svgame.hInstance = NULL;
return false;
MsgDev( D_WARN, "SV_LoadProgs: interface version %i should be %i\n", INTERFACE_VERSION, version );
// fallback to old API
if( !GetEntityAPI( &svgame.dllFuncs, version ))
{
Com_FreeLibrary( svgame.hInstance );
MsgDev( D_ERROR, "SV_LoadProgs: couldn't get entity API\n" );
svgame.hInstance = NULL;
return false;
}
}
else MsgDev( D_AICONSOLE, "SV_LoadProgs: ^2initailized extended EntityAPI ^7ver. %i\n", version );
}
else if( !GetEntityAPI( &svgame.dllFuncs, version ))
{
Com_FreeLibrary( svgame.hInstance );
MsgDev( D_ERROR, "SV_LoadProgs: couldn't get entity API\n" );
svgame.hInstance = NULL;
return false;
}
if( !SV_InitStudioAPI( ))
@ -4456,6 +4658,11 @@ qboolean SV_LoadProgs( const char *name )
return false;
}
if( !SV_InitPhysicsAPI( ))
{
MsgDev( D_WARN, "SV_LoadProgs: couldn't get physics API\n" );
}
// grab function SV_SaveGameComment
SV_InitSaveRestore ();

View File

@ -337,6 +337,12 @@ void SV_ActivateServer( void )
sv.paused = false;
Host_SetServerState( sv.state );
if( sv_maxclients->integer > 1 && public_server->integer )
{
MsgDev( D_INFO, "Add your server, to master server list\n" );
Master_Add( );
}
}
/*
@ -540,7 +546,6 @@ A brand new game has been started
*/
void SV_InitGame( void )
{
string idmaster;
edict_t *ent;
int i;
@ -621,8 +626,6 @@ void SV_InitGame( void )
// heartbeats will always be sent to the id master
svs.last_heartbeat = MAX_HEARTBEAT; // send immediately
Q_sprintf( idmaster, "192.246.40.37:%i", PORT_MASTER ); // TODO: parse woncomm.lst
NET_StringToAdr( idmaster, &master_adr[0] );
// set client fields on player ents
for( i = 0; i < svgame.globals->maxClients; i++ )

View File

@ -19,8 +19,6 @@ GNU General Public License for more details.
#define HEARTBEAT_SECONDS 300.0f // 300 seconds
netadr_t master_adr[MAX_MASTERS]; // address of group servers
convar_t *sv_zmax;
convar_t *sv_novis; // disable server culling entities by vis
convar_t *sv_unlag;
@ -364,7 +362,7 @@ void SV_ReadPackets( void )
if( Netchan_CopyFileFragments( &cl->netchan, &net_message ))
{
// SV_ProcessFile( cl, cl->netchan.incomingfilename );
SV_ProcessFile( cl, cl->netchan.incomingfilename );
}
}
@ -460,6 +458,17 @@ void SV_PrepWorldFrame( void )
}
}
/*
=================
SV_ProcessFile
=================
*/
void SV_ProcessFile( sv_client_t *cl, char *filename )
{
// some other file...
MsgDev( D_INFO, "Received file %s from %s\n", filename, cl->name );
}
/*
=================
SV_IsSimulating
@ -543,6 +552,23 @@ void Host_ServerFrame( void )
//============================================================================
/*
=================
Master_Add
=================
*/
void Master_Add( void )
{
netadr_t adr;
NET_Config( true ); // allow remote
if( !NET_StringToAdr( MASTERSERVER_ADR, &adr ))
MsgDev( D_INFO, "Can't resolve adr: %s\n", MASTERSERVER_ADR );
NET_SendPacket( NS_SERVER, 2, "\x4D\xFF", adr );
}
/*
================
Master_Heartbeat
@ -553,11 +579,8 @@ let it know we are alive, and log information
*/
void Master_Heartbeat( void )
{
char *string;
int i;
if( host.type != HOST_DEDICATED || !public_server->integer )
return; // only dedicated servers send heartbeats
if( !public_server->integer || sv_maxclients->integer == 1 )
return; // only public servers send heartbeats
// check for time wraparound
if( svs.last_heartbeat > host.realtime )
@ -568,18 +591,7 @@ void Master_Heartbeat( void )
svs.last_heartbeat = host.realtime;
// send the same string that we would give for a status OOB command
string = SV_StatusString( );
// send to group master
for( i = 0; i < MAX_MASTERS; i++ )
{
if( master_adr[i].port )
{
MsgDev( D_INFO, "Sending heartbeat to %s\n", NET_AdrToString( master_adr[i] ));
Netchan_OutOfBandPrint( NS_SERVER, master_adr[i], "heartbeat\n%s", string );
}
}
Master_Add();
}
/*
@ -591,20 +603,6 @@ Informs all masters that this server is going down
*/
void Master_Shutdown( void )
{
int i;
if( host.type != HOST_DEDICATED || !public_server->integer )
return; // only dedicated servers send heartbeats
// send to group master
for( i = 0; i < MAX_MASTERS; i++ )
{
if( master_adr[i].port )
{
if( i ) MsgDev( D_INFO, "Sending heartbeat to %s\n", NET_AdrToString( master_adr[i] ));
Netchan_OutOfBandPrint( NS_SERVER, master_adr[i], "shutdown" );
}
}
}
//============================================================================

View File

@ -16,6 +16,9 @@ GNU General Public License for more details.
#include "common.h"
#include "server.h"
#include "const.h"
#include "library.h"
typedef int (*PHYSICAPI)( int, server_physics_api_t*, physics_interface_t* );
/*
@ -311,7 +314,7 @@ qboolean SV_CheckWater( edict_t *ent )
ent->v.watertype = cont;
ent->v.waterlevel = 1;
point[2] = ent->v.origin[2] + (ent->v.mins[2] + ent->v.maxs[2]) * 0.5f;
point[2] = ent->v.origin[2] + (ent->v.mins[2] + ent->v.maxs[2]) * 0.5f;
svs.groupmask = ent->v.groupinfo;
cont = SV_PointContents( point );
@ -593,7 +596,7 @@ PUSHMOVE
============
SV_AllowPushRotate
Allows to chnage entity yaw?
Allows to change entity yaw?
============
*/
qboolean SV_AllowPushRotate( edict_t *ent )
@ -1060,7 +1063,7 @@ void SV_Physics_Pusher( edict_t *ent )
{
if( VectorLength2( ent->v.velocity ) > STOP_EPSILON )
{
pBlocker = SV_PushRotate( ent, movetime );
pBlocker = SV_PushRotate( ent, movetime );
if( !pBlocker )
{
@ -1529,7 +1532,7 @@ void SV_Physics_Step( edict_t *ent )
point[0] = x ? maxs[0] : mins[0];
point[1] = y ? maxs[1] : mins[1];
trace = SV_Move( point, vec3_origin, vec3_origin, point, MOVE_NORMAL, ent );
trace = SV_Move( point, vec3_origin, vec3_origin, point, MOVE_NORMAL, ent );
if( trace.startsolid )
{
@ -1595,9 +1598,9 @@ static void SV_Physics_Entity( edict_t *ent )
}
// user dll can override movement type (Xash3D extension)
if( svgame.dllFuncs2.pfnPhysicsEntity )
if( svgame.physFuncs.SV_PhysicsEntity )
{
if( svgame.dllFuncs2.pfnPhysicsEntity( ent ))
if( svgame.physFuncs.SV_PhysicsEntity( ent ))
{
if( ent->v.flags & FL_KILLME )
SV_FreeEdict( ent );
@ -1680,4 +1683,52 @@ void SV_Physics( void )
// decrement svgame.numEntities if the highest number entities died
for( ; EDICT_NUM( svgame.numEntities - 1 )->free; svgame.numEntities-- );
}
/*
================
SV_GetServerTime
Inplementation for new physics interface
================
*/
double SV_GetServerTime( void )
{
return sv.time;
}
static server_physics_api_t gPhysicsAPI =
{
SV_LinkEdict,
SV_GetServerTime,
};
/*
===============
SV_InitPhysicsAPI
Initialize server external physics
===============
*/
qboolean SV_InitPhysicsAPI( void )
{
static PHYSICAPI pPhysIface;
pPhysIface = (PHYSICAPI)Com_GetProcAddress( svgame.hInstance, "Server_GetPhysicsInterface" );
if( pPhysIface )
{
if( pPhysIface( SV_PHYSICS_INTERFACE_VERSION, &gPhysicsAPI, &svgame.physFuncs ))
{
MsgDev( D_AICONSOLE, "SV_LoadProgs: ^2initailized extended PhysicAPI ^7ver. %i\n", SV_PHYSICS_INTERFACE_VERSION );
return true;
}
// make sure what physic functions is cleared
Q_memset( &svgame.physFuncs, 0, sizeof( svgame.physFuncs ));
return false; // just tell user about problems
}
// physic interface is missed
return true;
}

View File

@ -831,9 +831,9 @@ SV_RunCmd
*/
void SV_RunCmd( sv_client_t *cl, usercmd_t *ucmd, int random_seed )
{
usercmd_t cmd;
edict_t *clent;
vec3_t oldvel;
usercmd_t cmd;
int oldmsec;
clent = cl->edict;

View File

@ -143,8 +143,11 @@ hull_t *SV_HullForEntity( edict_t *ent, int hullNumber, vec3_t mins, vec3_t maxs
float lastdiff = 999;
int i;
#ifdef RANDOM_HULL_NULLIZATION
hullNumber = Com_RandomLong( 0, 0 );
#else
hullNumber = 0; // assume we fail
#endif
// select the hull automatically
for( i = 0; i < 4; i++ )
{

View File

@ -25,7 +25,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "extdll.h"
#include "basemenu.h"
#include "keydefs.h"
#include "images.h" // built-in resources
#include "menufont.h" // built-in menu font
#include "utils.h"
#include "menu_btnsbmp_table.h"
//CR
@ -56,6 +56,7 @@ int uiInputFgColor = 0xFF555555; // 85, 85, 85, 255 // field, scrollist, che
int uiColorWhite = 0xFFFFFFFF; // 255, 255, 255, 255 // useful for bitmaps
int uiColorDkGrey = 0xFF404040; // 64, 64, 64, 255 // shadow and grayed items
int uiColorBlack = 0xFF000000; // 0, 0, 0, 255 // some controls background
int uiColorConsole = 0xFFF0B418; // just for reference
// color presets (this is nasty hack to allow color presets to part of text)
const int g_iColorTable[8] =
@ -204,9 +205,12 @@ void UI_DrawString( int x, int y, int w, int h, const char *string, const int co
if( !string || !string[0] )
return;
#if 0 // g-cont. disabled 29/06/2011
// this code do a bad things with prompt dialogues
// vertically centered
if( !strchr( string, '\n' ))
y = y + (( h - charH ) / 2 );
#endif
if( shadow )
{
@ -727,6 +731,20 @@ void UI_RefreshServerList( void )
CLIENT_COMMAND( FALSE, "localservers\n" );
}
/*
=================
UI_RefreshInternetServerList
=================
*/
void UI_RefreshInternetServerList( void )
{
uiStatic.numServers = 0;
memset( uiStatic.serverAddresses, 0, sizeof( uiStatic.serverAddresses ));
memset( uiStatic.serverNames, 0, sizeof( uiStatic.serverNames ));
CLIENT_COMMAND( FALSE, "internetservers\n" );
}
/*
=================
UI_StartBackGroundMap
@ -1201,6 +1219,7 @@ void UI_Precache( void )
UI_SaveLoad_Precache();
UI_MultiPlayer_Precache();
UI_Options_Precache();
UI_InternetGames_Precache();
UI_LanGame_Precache();
UI_PlayerSetup_Precache();
UI_Controls_Precache();
@ -1275,11 +1294,15 @@ void UI_ApplyCustomColors( void )
{
UI_ParseColor( pfile, &uiInputFgColor );
}
else if( !stricmp( token, "CON_TEXT_COLOR" ))
{
UI_ParseColor( pfile, &uiColorConsole );
}
}
int r, g, b;
UnpackRGB( r, g, b, uiPromptTextColor );
UnpackRGB( r, g, b, uiColorConsole );
ConsoleSetColor( r, g, b );
FREE_FILE( afile );
@ -1323,9 +1346,6 @@ UI_VidInit
int UI_VidInit( void )
{
UI_Precache ();
// setup game info
GetGameInfo( &gMenu.m_gameinfo );
uiStatic.scaleX = ScreenWidth / 1024.0f;
uiStatic.scaleY = ScreenHeight / 768.0f;
@ -1350,8 +1370,17 @@ int UI_VidInit( void )
// trying to load chapterbackgrounds.txt
UI_LoadBackgroundMapList ();
// register ui font
uiStatic.hFont = PIC_Load( "menufont", font_tga, sizeof( font_tga ));
// register menu font
uiStatic.hFont = PIC_Load( "#XASH_SYSTEMFONT_001", menufont_bmp, sizeof( menufont_bmp ));
#if 0
FILE *f;
// dump menufont onto disk
f = fopen( "menufont.bmp", "wb" );
fwrite( menufont_bmp, sizeof( menufont_bmp ), 1, f );
fclose( f );
#endif
// reload all menu buttons
UI_LoadBmpButtons ();
@ -1387,6 +1416,7 @@ void UI_Init( void )
Cmd_AddCommand( "menu_multiplayer", UI_MultiPlayer_Menu );
Cmd_AddCommand( "menu_options", UI_Options_Menu );
Cmd_AddCommand( "menu_langame", UI_LanGame_Menu );
Cmd_AddCommand( "menu_intenetgames", UI_InternetGames_Menu );
Cmd_AddCommand( "menu_playersetup", UI_PlayerSetup_Menu );
Cmd_AddCommand( "menu_controls", UI_Controls_Menu );
Cmd_AddCommand( "menu_advcontrols", UI_AdvControls_Menu );
@ -1403,6 +1433,12 @@ void UI_Init( void )
memset( uiEmptyString, ' ', sizeof( uiEmptyString )); // HACKHACK
uiStatic.initialized = true;
// setup game info
GetGameInfo( &gMenu.m_gameinfo );
// load custom strings
UI_LoadCustomStrings();
//CR
UI_InitTitleAnim();
}
@ -1424,6 +1460,7 @@ void UI_Shutdown( void )
Cmd_RemoveCommand( "menu_saveload" );
Cmd_RemoveCommand( "menu_multiplayer" );
Cmd_RemoveCommand( "menu_options" );
Cmd_RemoveCommand( "menu_intenetgames" );
Cmd_RemoveCommand( "menu_langame" );
Cmd_RemoveCommand( "menu_playersetup" );
Cmd_RemoveCommand( "menu_controls" );

View File

@ -404,6 +404,7 @@ void UI_AdjustCursor( menuFramework_s *menu, int dir );
void UI_DrawMenu( menuFramework_s *menu );
const char *UI_DefaultKey( menuFramework_s *menu, int key, int down );
const char *UI_ActivateItem( menuFramework_s *menu, menuCommon_s *item );
void UI_RefreshInternetServerList( void );
void UI_RefreshServerList( void );
int UI_CreditsActive( void );
void UI_DrawFinalCredits( void );
@ -420,6 +421,7 @@ void UI_SaveGame_Precache( void );
void UI_SaveLoad_Precache( void );
void UI_MultiPlayer_Precache( void );
void UI_Options_Precache( void );
void UI_InternetGames_Precache( void );
void UI_LanGame_Precache( void );
void UI_PlayerSetup_Precache( void );
void UI_Controls_Precache( void );
@ -442,6 +444,7 @@ void UI_SaveGame_Menu( void );
void UI_SaveLoad_Menu( void );
void UI_MultiPlayer_Menu( void );
void UI_Options_Menu( void );
void UI_InternetGames_Menu( void );
void UI_LanGame_Menu( void );
void UI_PlayerSetup_Menu( void );
void UI_Controls_Menu( void );

View File

@ -154,6 +154,10 @@ SOURCE=.\menu_gameoptions.cpp
# End Source File
# Begin Source File
SOURCE=.\menu_internetgames.cpp
# End Source File
# Begin Source File
SOURCE=.\menu_langame.cpp
# End Source File
# Begin Source File
@ -186,6 +190,10 @@ SOURCE=.\menu_saveload.cpp
# End Source File
# Begin Source File
SOURCE=.\menu_strings.cpp
# End Source File
# Begin Source File
SOURCE=.\menu_video.cpp
# End Source File
# Begin Source File
@ -226,11 +234,15 @@ SOURCE=.\extdll.h
# End Source File
# Begin Source File
SOURCE=.\images.h
SOURCE=.\menu_btnsbmp_table.h
# End Source File
# Begin Source File
SOURCE=.\menu_btnsbmp_table.h
SOURCE=.\menu_strings.h
# End Source File
# Begin Source File
SOURCE=.\menufont.H
# End Source File
# Begin Source File

View File

@ -23,6 +23,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "utils.h"
#include "../cl_dll/kbutton.h"
#include "menu_btnsbmp_table.h"
#include "menu_strings.h"
#define ART_BANNER "gfx/shell/head_advanced"
@ -233,7 +234,7 @@ static void UI_AdvControls_Init( void )
uiAdvControls.invertMouse.generic.flags = QMF_HIGHLIGHTIFFOCUS|QMF_NOTIFY|QMF_ACT_ONRELEASE|QMF_MOUSEONLY|QMF_DROPSHADOW;
uiAdvControls.invertMouse.generic.x = 72;
uiAdvControls.invertMouse.generic.y = 280;
uiAdvControls.invertMouse.generic.name = "Reverse mouse";
uiAdvControls.invertMouse.generic.name = MenuStrings[HINT_REVERSE_MOUSE];
uiAdvControls.invertMouse.generic.callback = UI_AdvControls_Callback;
uiAdvControls.invertMouse.generic.statusText = "Reverse mouse up/down axis";
@ -285,7 +286,7 @@ static void UI_AdvControls_Init( void )
uiAdvControls.sensitivity.generic.id = ID_SENSITIVITY;
uiAdvControls.sensitivity.generic.type = QMTYPE_SLIDER;
uiAdvControls.sensitivity.generic.flags = QMF_PULSEIFFOCUS|QMF_DROPSHADOW;
uiAdvControls.sensitivity.generic.name = "Mouse sensitivity";
uiAdvControls.sensitivity.generic.name = MenuStrings[HINT_MOUSE_SENSE];
uiAdvControls.sensitivity.generic.x = 72;
uiAdvControls.sensitivity.generic.y = 625;
uiAdvControls.sensitivity.generic.callback = UI_AdvControls_Callback;

View File

@ -25,7 +25,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define ART_BUTTONS_MAIN "gfx/shell/btns_main.bmp" // we support bmp only
const char *MenuStrings[PC_BUTTONCOUNT] =
const char *MenuButtons[PC_BUTTONCOUNT] =
{
"New game",
"Resume Game",
@ -143,21 +143,21 @@ void UI_LoadBmpButtons( void )
int pallete_sz = pHdr->bmp_offset - sizeof( bmphdr_t ) - pInfoHdr->biSize;
uiStatic.buttons_height = 78;
uiStatic.buttons_height = ( pInfoHdr->biBitCount == 4 ) ? 80 : 78; // bugstompers issues
uiStatic.buttons_width = pInfoHdr->biWidth - 3; // make some offset
int cutted_img_sz = pInfoHdr->biWidth * uiStatic.buttons_height * pInfoHdr->biBitCount / 8;
int CuttedBmpSize = sizeof( bmphdr_t ) + pInfoHdr->biSize + pallete_sz + cutted_img_sz;
byte *img_data = &bmp_buffer[bmp_len_holder-cutted_img_sz];
if ( pInfoHdr->biBitCount == 8 )
if ( pInfoHdr->biBitCount <= 8 )
{
byte*pallete=&bmp_buffer[sizeof( bmphdr_t ) + pInfoHdr->biSize];
byte*firstpixel_col=&pallete[img_data[0]*4];
byte* pallete=&bmp_buffer[sizeof( bmphdr_t ) + pInfoHdr->biSize];
byte* firstpixel_col=&pallete[img_data[0]*4];
firstpixel_col[0]=firstpixel_col[1]=firstpixel_col[2]=0;
}
CuttedDibHdr.biHeight = uiStatic.buttons_height;
CuttedDibHdr.biHeight = 78; //uiStatic.buttons_height;
CuttedHdr.filesz = CuttedBmpSize;
CuttedDibHdr.biSizeImage = CuttedBmpSize - CuttedHdr.bmp_offset;
@ -178,7 +178,7 @@ void UI_LoadBmpButtons( void )
memcpy( &raw_img_buff[offset], &CuttedDibHdr, CuttedDibHdr.biSize );
offset += CuttedDibHdr.biSize;
if( CuttedDibHdr.biBitCount == 8 )
if( CuttedDibHdr.biBitCount <= 8 )
{
memcpy( &raw_img_buff[offset], &bmp_buffer[offset], pallete_sz );
offset += pallete_sz;

View File

@ -96,14 +96,14 @@ enum
#define BUTTON_FOCUS 1
#define BUTTON_PRESSED 2
extern const char *MenuStrings[PC_BUTTONCOUNT];
extern const char *MenuButtons[PC_BUTTONCOUNT];
inline int PicButtonWidth( int pic_id )
{
if( pic_id < 0 || pic_id > PC_BUTTONCOUNT )
return 0;
return strlen( MenuStrings[pic_id] );
return strlen( MenuButtons[pic_id] );
}
#endif//MENU_BTNSBMP_TABLE_H

View File

@ -0,0 +1,467 @@
/*
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.
*/
#include "extdll.h"
#include "basemenu.h"
#include "utils.h"
#include "keydefs.h"
#include "menu_btnsbmp_table.h"
#define ART_BANNER "gfx/shell/head_inetgames"
#define ID_BACKGROUND 0
#define ID_BANNER 1
#define ID_JOINGAME 2
#define ID_CREATEGAME 3
#define ID_GAMEINFO 4
#define ID_REFRESH 5
#define ID_DONE 6
#define ID_SERVERSLIST 7
#define ID_TABLEHINT 8
#define ID_MSGBOX 9
#define ID_MSGTEXT 10
#define ID_YES 130
#define ID_NO 131
#define GAME_LENGTH 18
#define MAPNAME_LENGTH 20+GAME_LENGTH
#define TYPE_LENGTH 16+MAPNAME_LENGTH
#define MAXCL_LENGTH 15+TYPE_LENGTH
typedef struct
{
char gameDescription[UI_MAX_SERVERS][256];
char *gameDescriptionPtr[UI_MAX_SERVERS];
menuFramework_s menu;
menuBitmap_s background;
menuBitmap_s banner;
menuPicButton_s joinGame;
menuPicButton_s createGame;
menuPicButton_s gameInfo;
menuPicButton_s refresh;
menuPicButton_s done;
// joingame prompt dialog
menuAction_s msgBox;
menuAction_s dlgMessage1;
menuAction_s dlgMessage2;
menuPicButton_s yes;
menuPicButton_s no;
menuScrollList_s gameList;
menuAction_s hintMessage;
char hintText[MAX_HINT_TEXT];
int refreshTime;
} uiInternetGames_t;
static uiInternetGames_t uiInternetGames;
static void UI_PromptDialog( void )
{
// toggle main menu between active\inactive
// show\hide quit dialog
uiInternetGames.joinGame.generic.flags ^= QMF_INACTIVE;
uiInternetGames.createGame.generic.flags ^= QMF_INACTIVE;
uiInternetGames.gameInfo.generic.flags ^= QMF_INACTIVE;
uiInternetGames.refresh.generic.flags ^= QMF_INACTIVE;
uiInternetGames.done.generic.flags ^= QMF_INACTIVE;
uiInternetGames.gameList.generic.flags ^= QMF_INACTIVE;
uiInternetGames.msgBox.generic.flags ^= QMF_HIDDEN;
uiInternetGames.dlgMessage1.generic.flags ^= QMF_HIDDEN;
uiInternetGames.dlgMessage2.generic.flags ^= QMF_HIDDEN;
uiInternetGames.no.generic.flags ^= QMF_HIDDEN;
uiInternetGames.yes.generic.flags ^= QMF_HIDDEN;
}
/*
=================
UI_InternetGames_KeyFunc
=================
*/
static const char *UI_InternetGames_KeyFunc( int key, int down )
{
if( down && key == K_ESCAPE && !( uiInternetGames.dlgMessage1.generic.flags & QMF_HIDDEN ))
{
UI_PromptDialog();
return uiSoundNull;
}
return UI_DefaultKey( &uiInternetGames.menu, key, down );
}
/*
=================
UI_InternetGames_GetGamesList
=================
*/
static void UI_InternetGames_GetGamesList( void )
{
int i;
const char *info;
for( i = 0; i < uiStatic.numServers; i++ )
{
if( i >= UI_MAX_SERVERS ) break;
info = uiStatic.serverNames[i];
#if 1
// NOTE: Xash3D is support hot switching between games in multiplayer
// but this feature not detail tested and may be bugly
if( stricmp( gMenu.m_gameinfo.gamefolder, Info_ValueForKey( info, "gamedir" )))
continue; // filter by game
#endif
StringConcat( uiInternetGames.gameDescription[i], Info_ValueForKey( info, "host" ), GAME_LENGTH );
StringConcat( uiInternetGames.gameDescription[i], uiEmptyString, GAME_LENGTH );
StringConcat( uiInternetGames.gameDescription[i], Info_ValueForKey( info, "map" ), MAPNAME_LENGTH );
StringConcat( uiInternetGames.gameDescription[i], uiEmptyString, MAPNAME_LENGTH );
if( !strcmp( Info_ValueForKey( info, "dm" ), "1" ))
StringConcat( uiInternetGames.gameDescription[i], "deathmatch", TYPE_LENGTH );
else if( !strcmp( Info_ValueForKey( info, "coop" ), "1" ))
StringConcat( uiInternetGames.gameDescription[i], "coop", TYPE_LENGTH );
else if( !strcmp( Info_ValueForKey( info, "team" ), "1" ))
StringConcat( uiInternetGames.gameDescription[i], "teamplay", TYPE_LENGTH );
StringConcat( uiInternetGames.gameDescription[i], uiEmptyString, TYPE_LENGTH );
StringConcat( uiInternetGames.gameDescription[i], Info_ValueForKey( info, "numcl" ), MAXCL_LENGTH );
StringConcat( uiInternetGames.gameDescription[i], "\\", MAXCL_LENGTH );
StringConcat( uiInternetGames.gameDescription[i], Info_ValueForKey( info, "maxcl" ), MAXCL_LENGTH );
StringConcat( uiInternetGames.gameDescription[i], uiEmptyString, MAXCL_LENGTH );
uiInternetGames.gameDescriptionPtr[i] = uiInternetGames.gameDescription[i];
}
for( ; i < UI_MAX_SERVERS; i++ )
uiInternetGames.gameDescriptionPtr[i] = NULL;
uiInternetGames.gameList.itemNames = (const char **)uiInternetGames.gameDescriptionPtr;
uiInternetGames.gameList.numItems = 0; // reset it
if( !uiInternetGames.gameList.generic.charHeight )
return; // to avoid divide integer by zero
// count number of items
while( uiInternetGames.gameList.itemNames[uiInternetGames.gameList.numItems] )
uiInternetGames.gameList.numItems++;
// calculate number of visible rows
uiInternetGames.gameList.numRows = (uiInternetGames.gameList.generic.height2 / uiInternetGames.gameList.generic.charHeight) - 2;
if( uiInternetGames.gameList.numRows > uiInternetGames.gameList.numItems ) uiInternetGames.gameList.numRows = uiInternetGames.gameList.numItems;
if( uiStatic.numServers )
uiInternetGames.joinGame.generic.flags &= ~QMF_GRAYED;
}
/*
=================
UI_InternetGames_JoinGame
=================
*/
static void UI_InternetGames_JoinGame( void )
{
if( !strlen( uiInternetGames.gameDescription[uiInternetGames.gameList.curItem] ))
return;
CLIENT_JOIN( uiStatic.serverAddresses[uiInternetGames.gameList.curItem] );
}
/*
=================
UI_Background_Ownerdraw
=================
*/
static void UI_Background_Ownerdraw( void *self )
{
menuCommon_s *item = (menuCommon_s *)self;
if( !CVAR_GET_FLOAT( "sv_background" ))
UI_DrawPic(item->x, item->y, item->width, item->height, uiColorWhite, ((menuBitmap_s *)self)->pic);
if( uiStatic.realTime > uiInternetGames.refreshTime )
{
uiInternetGames.refreshTime = uiStatic.realTime + 10000; // refresh every 10 secs
UI_RefreshInternetServerList();
}
// serverinfo has been changed update display
if( uiStatic.updateServers )
{
UI_InternetGames_GetGamesList ();
uiStatic.updateServers = false;
}
}
/*
=================
UI_MsgBox_Ownerdraw
=================
*/
static void UI_MsgBox_Ownerdraw( void *self )
{
menuCommon_s *item = (menuCommon_s *)self;
UI_FillRect( item->x, item->y, item->width, item->height, uiPromptBgColor );
}
/*
=================
UI_InternetGames_Callback
=================
*/
static void UI_InternetGames_Callback( void *self, int event )
{
menuCommon_s *item = (menuCommon_s *)self;
if( event != QM_ACTIVATED )
return;
switch( item->id )
{
case ID_JOINGAME:
if( CL_IsActive( ))
UI_PromptDialog();
else UI_InternetGames_JoinGame();
break;
case ID_CREATEGAME:
CVAR_SET_FLOAT( "public", 1.0f );
UI_CreateGame_Menu();
break;
case ID_GAMEINFO:
// UNDONE: not implemented
break;
case ID_REFRESH:
UI_RefreshInternetServerList();
break;
case ID_DONE:
UI_PopMenu();
break;
case ID_YES:
UI_InternetGames_JoinGame();
break;
case ID_NO:
UI_PromptDialog();
break;
}
}
/*
=================
UI_InternetGames_Init
=================
*/
static void UI_InternetGames_Init( void )
{
memset( &uiInternetGames, 0, sizeof( uiInternetGames_t ));
uiInternetGames.menu.vidInitFunc = UI_InternetGames_Init;
uiInternetGames.menu.keyFunc = UI_InternetGames_KeyFunc;
StringConcat( uiInternetGames.hintText, "Game", GAME_LENGTH );
StringConcat( uiInternetGames.hintText, uiEmptyString, GAME_LENGTH );
StringConcat( uiInternetGames.hintText, "Map", MAPNAME_LENGTH );
StringConcat( uiInternetGames.hintText, uiEmptyString, MAPNAME_LENGTH );
StringConcat( uiInternetGames.hintText, "Type", TYPE_LENGTH );
StringConcat( uiInternetGames.hintText, uiEmptyString, TYPE_LENGTH );
StringConcat( uiInternetGames.hintText, "Num/Max Clients", MAXCL_LENGTH );
StringConcat( uiInternetGames.hintText, uiEmptyString, MAXCL_LENGTH );
uiInternetGames.background.generic.id = ID_BACKGROUND;
uiInternetGames.background.generic.type = QMTYPE_BITMAP;
uiInternetGames.background.generic.flags = QMF_INACTIVE;
uiInternetGames.background.generic.x = 0;
uiInternetGames.background.generic.y = 0;
uiInternetGames.background.generic.width = 1024;
uiInternetGames.background.generic.height = 768;
uiInternetGames.background.pic = ART_BACKGROUND;
uiInternetGames.background.generic.ownerdraw = UI_Background_Ownerdraw;
uiInternetGames.banner.generic.id = ID_BANNER;
uiInternetGames.banner.generic.type = QMTYPE_BITMAP;
uiInternetGames.banner.generic.flags = QMF_INACTIVE|QMF_DRAW_ADDITIVE;
uiInternetGames.banner.generic.x = UI_BANNER_POSX;
uiInternetGames.banner.generic.y = UI_BANNER_POSY;
uiInternetGames.banner.generic.width = UI_BANNER_WIDTH;
uiInternetGames.banner.generic.height = UI_BANNER_HEIGHT;
uiInternetGames.banner.pic = ART_BANNER;
uiInternetGames.joinGame.generic.id = ID_JOINGAME;
uiInternetGames.joinGame.generic.type = QMTYPE_BM_BUTTON;
uiInternetGames.joinGame.generic.flags = QMF_HIGHLIGHTIFFOCUS|QMF_DROPSHADOW|QMF_GRAYED;
uiInternetGames.joinGame.generic.x = 72;
uiInternetGames.joinGame.generic.y = 230;
uiInternetGames.joinGame.generic.name = "Join game";
uiInternetGames.joinGame.generic.statusText = "Join to selected game";
uiInternetGames.joinGame.generic.callback = UI_InternetGames_Callback;
UI_UtilSetupPicButton( &uiInternetGames.joinGame, PC_JOIN_GAME );
uiInternetGames.createGame.generic.id = ID_CREATEGAME;
uiInternetGames.createGame.generic.type = QMTYPE_BM_BUTTON;
uiInternetGames.createGame.generic.flags = QMF_HIGHLIGHTIFFOCUS|QMF_DROPSHADOW;
uiInternetGames.createGame.generic.x = 72;
uiInternetGames.createGame.generic.y = 280;
uiInternetGames.createGame.generic.name = "Create game";
uiInternetGames.createGame.generic.statusText = "Create new Internet game";
uiInternetGames.createGame.generic.callback = UI_InternetGames_Callback;
UI_UtilSetupPicButton( &uiInternetGames.createGame, PC_CREATE_GAME );
uiInternetGames.gameInfo.generic.id = ID_GAMEINFO;
uiInternetGames.gameInfo.generic.type = QMTYPE_BM_BUTTON;
uiInternetGames.gameInfo.generic.flags = QMF_HIGHLIGHTIFFOCUS|QMF_DROPSHADOW|QMF_GRAYED;
uiInternetGames.gameInfo.generic.x = 72;
uiInternetGames.gameInfo.generic.y = 330;
uiInternetGames.gameInfo.generic.name = "View game info";
uiInternetGames.gameInfo.generic.statusText = "Get detail game info";
uiInternetGames.gameInfo.generic.callback = UI_InternetGames_Callback;
UI_UtilSetupPicButton( &uiInternetGames.gameInfo, PC_VIEW_GAME_INFO );
uiInternetGames.refresh.generic.id = ID_REFRESH;
uiInternetGames.refresh.generic.type = QMTYPE_BM_BUTTON;
uiInternetGames.refresh.generic.flags = QMF_HIGHLIGHTIFFOCUS|QMF_DROPSHADOW;
uiInternetGames.refresh.generic.x = 72;
uiInternetGames.refresh.generic.y = 380;
uiInternetGames.refresh.generic.name = "Refresh";
uiInternetGames.refresh.generic.statusText = "Refresh servers list";
uiInternetGames.refresh.generic.callback = UI_InternetGames_Callback;
UI_UtilSetupPicButton( &uiInternetGames.refresh, PC_REFRESH );
uiInternetGames.done.generic.id = ID_DONE;
uiInternetGames.done.generic.type = QMTYPE_BM_BUTTON;
uiInternetGames.done.generic.flags = QMF_HIGHLIGHTIFFOCUS|QMF_DROPSHADOW;
uiInternetGames.done.generic.x = 72;
uiInternetGames.done.generic.y = 430;
uiInternetGames.done.generic.name = "Done";
uiInternetGames.done.generic.statusText = "Return to main menu";
uiInternetGames.done.generic.callback = UI_InternetGames_Callback;
UI_UtilSetupPicButton( &uiInternetGames.done, PC_DONE );
uiInternetGames.msgBox.generic.id = ID_MSGBOX;
uiInternetGames.msgBox.generic.type = QMTYPE_ACTION;
uiInternetGames.msgBox.generic.flags = QMF_INACTIVE|QMF_HIDDEN;
uiInternetGames.msgBox.generic.ownerdraw = UI_MsgBox_Ownerdraw; // just a fill rectangle
uiInternetGames.msgBox.generic.x = 192;
uiInternetGames.msgBox.generic.y = 256;
uiInternetGames.msgBox.generic.width = 640;
uiInternetGames.msgBox.generic.height = 256;
uiInternetGames.dlgMessage1.generic.id = ID_MSGTEXT;
uiInternetGames.dlgMessage1.generic.type = QMTYPE_ACTION;
uiInternetGames.dlgMessage1.generic.flags = QMF_INACTIVE|QMF_HIDDEN|QMF_DROPSHADOW;
uiInternetGames.dlgMessage1.generic.name = "Join a network game will exit";
uiInternetGames.dlgMessage1.generic.x = 248;
uiInternetGames.dlgMessage1.generic.y = 280;
uiInternetGames.dlgMessage2.generic.id = ID_MSGTEXT;
uiInternetGames.dlgMessage2.generic.type = QMTYPE_ACTION;
uiInternetGames.dlgMessage2.generic.flags = QMF_INACTIVE|QMF_HIDDEN|QMF_DROPSHADOW;
uiInternetGames.dlgMessage2.generic.name = "any current game, OK to exit?";
uiInternetGames.dlgMessage2.generic.x = 248;
uiInternetGames.dlgMessage2.generic.y = 310;
uiInternetGames.yes.generic.id = ID_YES;
uiInternetGames.yes.generic.type = QMTYPE_BM_BUTTON;
uiInternetGames.yes.generic.flags = QMF_HIGHLIGHTIFFOCUS|QMF_HIDDEN|QMF_DROPSHADOW;
uiInternetGames.yes.generic.name = "Ok";
uiInternetGames.yes.generic.x = 380;
uiInternetGames.yes.generic.y = 460;
uiInternetGames.yes.generic.callback = UI_InternetGames_Callback;
UI_UtilSetupPicButton( &uiInternetGames.yes, PC_OK );
uiInternetGames.no.generic.id = ID_NO;
uiInternetGames.no.generic.type = QMTYPE_BM_BUTTON;
uiInternetGames.no.generic.flags = QMF_HIGHLIGHTIFFOCUS|QMF_HIDDEN|QMF_DROPSHADOW;
uiInternetGames.no.generic.name = "Cancel";
uiInternetGames.no.generic.x = 530;
uiInternetGames.no.generic.y = 460;
uiInternetGames.no.generic.callback = UI_InternetGames_Callback;
UI_UtilSetupPicButton( &uiInternetGames.no, PC_CANCEL );
uiInternetGames.hintMessage.generic.id = ID_TABLEHINT;
uiInternetGames.hintMessage.generic.type = QMTYPE_ACTION;
uiInternetGames.hintMessage.generic.flags = QMF_INACTIVE|QMF_SMALLFONT;
uiInternetGames.hintMessage.generic.color = uiColorHelp;
uiInternetGames.hintMessage.generic.name = uiInternetGames.hintText;
uiInternetGames.hintMessage.generic.x = 360;
uiInternetGames.hintMessage.generic.y = 225;
uiInternetGames.gameList.generic.id = ID_SERVERSLIST;
uiInternetGames.gameList.generic.type = QMTYPE_SCROLLLIST;
uiInternetGames.gameList.generic.flags = QMF_HIGHLIGHTIFFOCUS|QMF_SMALLFONT;
uiInternetGames.gameList.generic.x = 360;
uiInternetGames.gameList.generic.y = 255;
uiInternetGames.gameList.generic.width = 640;
uiInternetGames.gameList.generic.height = 440;
uiInternetGames.gameList.generic.callback = UI_InternetGames_Callback;
uiInternetGames.gameList.itemNames = (const char **)uiInternetGames.gameDescriptionPtr;
// server.dll needs for reading savefiles or startup newgame
if( !CheckGameDll( ))
uiInternetGames.createGame.generic.flags |= QMF_GRAYED; // server.dll is missed - remote servers only
UI_AddItem( &uiInternetGames.menu, (void *)&uiInternetGames.background );
UI_AddItem( &uiInternetGames.menu, (void *)&uiInternetGames.banner );
UI_AddItem( &uiInternetGames.menu, (void *)&uiInternetGames.joinGame );
UI_AddItem( &uiInternetGames.menu, (void *)&uiInternetGames.createGame );
UI_AddItem( &uiInternetGames.menu, (void *)&uiInternetGames.gameInfo );
UI_AddItem( &uiInternetGames.menu, (void *)&uiInternetGames.refresh );
UI_AddItem( &uiInternetGames.menu, (void *)&uiInternetGames.done );
UI_AddItem( &uiInternetGames.menu, (void *)&uiInternetGames.hintMessage );
UI_AddItem( &uiInternetGames.menu, (void *)&uiInternetGames.gameList );
UI_AddItem( &uiInternetGames.menu, (void *)&uiInternetGames.msgBox );
UI_AddItem( &uiInternetGames.menu, (void *)&uiInternetGames.dlgMessage1 );
UI_AddItem( &uiInternetGames.menu, (void *)&uiInternetGames.dlgMessage2 );
UI_AddItem( &uiInternetGames.menu, (void *)&uiInternetGames.no );
UI_AddItem( &uiInternetGames.menu, (void *)&uiInternetGames.yes );
uiInternetGames.refreshTime = uiStatic.realTime + 500; // delay before update 0.5 sec
}
/*
=================
UI_InternetGames_Precache
=================
*/
void UI_InternetGames_Precache( void )
{
PIC_Load( ART_BACKGROUND );
PIC_Load( ART_BANNER );
}
/*
=================
UI_InternetGames_Menu
=================
*/
void UI_InternetGames_Menu( void )
{
if ( gMenu.m_gameinfo.gamemode == GAME_SINGLEPLAYER_ONLY )
return;
UI_InternetGames_Precache();
UI_InternetGames_Init();
UI_PushMenu( &uiInternetGames.menu );
}

View File

@ -240,6 +240,7 @@ static void UI_LanGame_Callback( void *self, int event )
else UI_LanGame_JoinGame();
break;
case ID_CREATEGAME:
CVAR_SET_FLOAT( "public", 0.0f );
UI_CreateGame_Menu();
break;
case ID_GAMEINFO:

View File

@ -23,6 +23,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "utils.h"
#include "keydefs.h"
#include "menu_btnsbmp_table.h"
#include "menu_strings.h"
#define ART_MINIMIZE_N "gfx/shell/min_n"
#define ART_MINIMIZE_F "gfx/shell/min_f"
@ -40,7 +41,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define ID_SAVERESTORE 6
#define ID_MULTIPLAYER 7
#define ID_CUSTOMGAME 8
#define ID_CREDITS 9
#define ID_PREVIEWS 9
#define ID_QUIT 10
#define ID_QUIT_BUTTON 11
#define ID_MINIMIZE 12
@ -62,7 +63,7 @@ typedef struct
menuPicButton_s saveRestore;
menuPicButton_s multiPlayer;
menuPicButton_s customGame;
menuPicButton_s credits;
menuPicButton_s previews;
menuPicButton_s quit;
menuBitmap_s minimizeBtn;
@ -72,7 +73,6 @@ typedef struct
menuAction_s msgBox;
menuAction_s quitMessage;
menuAction_s dlgMessage1;
menuAction_s dlgMessage2;
menuPicButton_s yes;
menuPicButton_s no;
} uiMain_t;
@ -133,7 +133,7 @@ static void UI_QuitDialog( void )
uiMain.configuration.generic.flags ^= QMF_INACTIVE;
uiMain.multiPlayer.generic.flags ^= QMF_INACTIVE;
uiMain.customGame.generic.flags ^= QMF_INACTIVE;
uiMain.credits.generic.flags ^= QMF_INACTIVE;
uiMain.previews.generic.flags ^= QMF_INACTIVE;
uiMain.quit.generic.flags ^= QMF_INACTIVE;
uiMain.minimizeBtn.generic.flags ^= QMF_INACTIVE;
uiMain.quitButton.generic.flags ^= QMF_INACTIVE;
@ -157,14 +157,13 @@ static void UI_PromptDialog( void )
uiMain.configuration.generic.flags ^= QMF_INACTIVE;
uiMain.multiPlayer.generic.flags ^= QMF_INACTIVE;
uiMain.customGame.generic.flags ^= QMF_INACTIVE;
uiMain.credits.generic.flags ^= QMF_INACTIVE;
uiMain.previews.generic.flags ^= QMF_INACTIVE;
uiMain.quit.generic.flags ^= QMF_INACTIVE;
uiMain.minimizeBtn.generic.flags ^= QMF_INACTIVE;
uiMain.quitButton.generic.flags ^= QMF_INACTIVE;
uiMain.msgBox.generic.flags ^= QMF_HIDDEN;
uiMain.dlgMessage1.generic.flags ^= QMF_HIDDEN;
uiMain.dlgMessage2.generic.flags ^= QMF_HIDDEN;
uiMain.no.generic.flags ^= QMF_HIDDEN;
uiMain.yes.generic.flags ^= QMF_HIDDEN;
@ -220,7 +219,7 @@ static void UI_Main_HazardCourse( void )
if( CVAR_GET_FLOAT( "host_serverstate" ) && CVAR_GET_FLOAT( "maxplayers" ) > 1 )
HOST_ENDGAME( "end of the game" );
CVAR_SET_FLOAT( "skill", 0.0f );
CVAR_SET_FLOAT( "skill", 1.0f );
CVAR_SET_FLOAT( "deathmatch", 0.0f );
CVAR_SET_FLOAT( "teamplay", 0.0f );
CVAR_SET_FLOAT( "pausable", 1.0f ); // singleplayer is always allowing pause
@ -286,8 +285,8 @@ static void UI_Main_Callback( void *self, int event )
case ID_CUSTOMGAME:
UI_CustomGame_Menu();
break;
case ID_CREDITS:
UI_Credits_Menu();
case ID_PREVIEWS:
SHELL_EXECUTE( MenuStrings[HINT_PREVIEWS_CMD], NULL, false );
break;
case ID_QUIT:
case ID_QUIT_BUTTON:
@ -361,7 +360,7 @@ static void UI_Main_Init( void )
uiMain.resumeGame.generic.type = QMTYPE_BM_BUTTON;
uiMain.resumeGame.generic.name = "Resume game";
uiMain.resumeGame.generic.flags = QMF_HIGHLIGHTIFFOCUS|QMF_DROPSHADOW|QMF_NOTIFY;
uiMain.resumeGame.generic.statusText = "Return to game.";
uiMain.resumeGame.generic.statusText = MenuStrings[HINT_RESUME_GAME];
uiMain.resumeGame.generic.x = 72;
uiMain.resumeGame.generic.y = 230;
uiMain.resumeGame.generic.callback = UI_Main_Callback;
@ -372,7 +371,7 @@ static void UI_Main_Init( void )
uiMain.newGame.generic.type = QMTYPE_BM_BUTTON;
uiMain.newGame.generic.name = "New game";
uiMain.newGame.generic.flags = QMF_HIGHLIGHTIFFOCUS|QMF_DROPSHADOW|QMF_NOTIFY;
uiMain.newGame.generic.statusText = "Start a new game.";
uiMain.newGame.generic.statusText = MenuStrings[HINT_NEWGAME];
uiMain.newGame.generic.x = 72;
uiMain.newGame.generic.y = 280;
uiMain.newGame.generic.callback = UI_Main_Callback;
@ -386,7 +385,7 @@ static void UI_Main_Init( void )
uiMain.hazardCourse.generic.type = QMTYPE_BM_BUTTON;
uiMain.hazardCourse.generic.name = "Hazard course";
uiMain.hazardCourse.generic.flags = QMF_HIGHLIGHTIFFOCUS|QMF_DROPSHADOW|QMF_NOTIFY;
uiMain.hazardCourse.generic.statusText = "Learn how to play the game";
uiMain.hazardCourse.generic.statusText = MenuStrings[HINT_HAZARD_COURSE];
uiMain.hazardCourse.generic.x = 72;
uiMain.hazardCourse.generic.y = 330;
uiMain.hazardCourse.generic.callback = UI_Main_Callback;
@ -408,13 +407,13 @@ static void UI_Main_Init( void )
if( CL_IsActive( ))
{
uiMain.saveRestore.generic.name = "Save\\Load Game";
uiMain.saveRestore.generic.statusText = "Load a saved game, save the current game.";
uiMain.saveRestore.generic.statusText = MenuStrings[HINT_SAVELOADGAME];
UI_UtilSetupPicButton(&uiMain.saveRestore,PC_SAVE_LOAD_GAME);
}
else
{
uiMain.saveRestore.generic.name = "Load Game";
uiMain.saveRestore.generic.statusText = "Load a previously saved game.";
uiMain.saveRestore.generic.statusText = MenuStrings[HINT_LOADGAME];
uiMain.resumeGame.generic.flags |= QMF_HIDDEN;
UI_UtilSetupPicButton( &uiMain.saveRestore, PC_LOAD_GAME );
}
@ -427,7 +426,7 @@ static void UI_Main_Init( void )
uiMain.configuration.generic.type = QMTYPE_BM_BUTTON;
uiMain.configuration.generic.flags = QMF_HIGHLIGHTIFFOCUS|QMF_DROPSHADOW|QMF_NOTIFY;
uiMain.configuration.generic.name = "Configuration";
uiMain.configuration.generic.statusText = "Change game settings, configure controls";
uiMain.configuration.generic.statusText = MenuStrings[HINT_CONFIGURATION];
uiMain.configuration.generic.x = 72;
uiMain.configuration.generic.y = bTrainMap ? 430 : 380;
uiMain.configuration.generic.callback = UI_Main_Callback;
@ -438,7 +437,7 @@ static void UI_Main_Init( void )
uiMain.multiPlayer.generic.type = QMTYPE_BM_BUTTON;
uiMain.multiPlayer.generic.flags = QMF_HIGHLIGHTIFFOCUS|QMF_DROPSHADOW|QMF_NOTIFY;
uiMain.multiPlayer.generic.name = "Multiplayer";
uiMain.multiPlayer.generic.statusText = "Search for internet servers, configure character";
uiMain.multiPlayer.generic.statusText = MenuStrings[HINT_MULTIPLAYER];
uiMain.multiPlayer.generic.x = 72;
uiMain.multiPlayer.generic.y = bTrainMap ? 480 : 430;
uiMain.multiPlayer.generic.callback = UI_Main_Callback;
@ -458,29 +457,33 @@ static void UI_Main_Init( void )
uiMain.customGame.generic.type = QMTYPE_BM_BUTTON;
uiMain.customGame.generic.flags = QMF_HIGHLIGHTIFFOCUS|QMF_DROPSHADOW|QMF_NOTIFY;
uiMain.customGame.generic.name = "Custom Game";
uiMain.customGame.generic.statusText = "Select a custom game";
uiMain.customGame.generic.statusText = MenuStrings[HINT_CUSTOM_GAME];
uiMain.customGame.generic.x = 72;
uiMain.customGame.generic.y = bTrainMap ? 530 : 480;
uiMain.customGame.generic.callback = UI_Main_Callback;
UI_UtilSetupPicButton( &uiMain.customGame, PC_CUSTOM_GAME );
uiMain.credits.generic.id = ID_CREDITS;
uiMain.credits.generic.type = QMTYPE_BM_BUTTON;
uiMain.credits.generic.flags = QMF_HIGHLIGHTIFFOCUS|QMF_DROPSHADOW|QMF_NOTIFY;
uiMain.credits.generic.name = "About";
uiMain.credits.generic.statusText = "Game credits";
uiMain.credits.generic.x = 72;
uiMain.credits.generic.y = (bCustomGame) ? (bTrainMap ? 580 : 530) : (bTrainMap ? 530 : 480);
uiMain.credits.generic.callback = UI_Main_Callback;
uiMain.previews.generic.id = ID_PREVIEWS;
uiMain.previews.generic.type = QMTYPE_BM_BUTTON;
uiMain.previews.generic.flags = QMF_HIGHLIGHTIFFOCUS|QMF_DROPSHADOW|QMF_NOTIFY;
uiMain.previews.generic.name = "Previews";
uiMain.previews.generic.statusText = MenuStrings[HINT_PREVIEWS_TEXT];
uiMain.previews.generic.x = 72;
uiMain.previews.generic.y = (bCustomGame) ? (bTrainMap ? 580 : 530) : (bTrainMap ? 530 : 480);
uiMain.previews.generic.callback = UI_Main_Callback;
UI_UtilSetupPicButton( &uiMain.credits, PC_VIEW_README );
// too short execute string - not a real command
if( strlen( MenuStrings[HINT_PREVIEWS_CMD] ) <= 3 )
uiMain.previews.generic.flags |= QMF_GRAYED;
UI_UtilSetupPicButton( &uiMain.previews, PC_PREVIEWS );
uiMain.quit.generic.id = ID_QUIT;
uiMain.quit.generic.type = QMTYPE_BM_BUTTON;
uiMain.quit.generic.flags = QMF_HIGHLIGHTIFFOCUS|QMF_DROPSHADOW|QMF_NOTIFY;
uiMain.quit.generic.name = "Quit";
uiMain.quit.generic.statusText = "Quit from game";
uiMain.quit.generic.statusText = MenuStrings[HINT_QUIT_BUTTON];
uiMain.quit.generic.x = 72;
uiMain.quit.generic.y = (bCustomGame) ? (bTrainMap ? 630 : 580) : (bTrainMap ? 580 : 530);
uiMain.quit.generic.callback = UI_Main_Callback;
@ -520,24 +523,21 @@ static void UI_Main_Init( void )
uiMain.quitMessage.generic.id = ID_MSGBOX;
uiMain.quitMessage.generic.type = QMTYPE_ACTION;
uiMain.quitMessage.generic.flags = QMF_INACTIVE|QMF_DROPSHADOW|QMF_HIDDEN;
uiMain.quitMessage.generic.name = "Are you sure you want to quit?";
uiMain.quitMessage.generic.x = 248;
uiMain.quitMessage.generic.flags = QMF_INACTIVE|QMF_DROPSHADOW|QMF_HIDDEN|QMF_CENTER_JUSTIFY;
uiMain.quitMessage.generic.name = (CL_IsActive( )) ? MenuStrings[HINT_QUIT_ACTIVE] : MenuStrings[HINT_QUIT];
uiMain.quitMessage.generic.x = 192;
uiMain.quitMessage.generic.y = 280;
uiMain.quitMessage.generic.width = 640;
uiMain.quitMessage.generic.height = 256;
uiMain.dlgMessage1.generic.id = ID_MSGTEXT;
uiMain.dlgMessage1.generic.type = QMTYPE_ACTION;
uiMain.dlgMessage1.generic.flags = QMF_INACTIVE|QMF_DROPSHADOW|QMF_HIDDEN;
uiMain.dlgMessage1.generic.name = "Starting a Hazard Course will exit";
uiMain.dlgMessage1.generic.x = 212;
uiMain.dlgMessage1.generic.flags = QMF_INACTIVE|QMF_DROPSHADOW|QMF_HIDDEN|QMF_CENTER_JUSTIFY;
uiMain.dlgMessage1.generic.name = MenuStrings[HINT_RESTART_HZ];
uiMain.dlgMessage1.generic.x = 192;
uiMain.dlgMessage1.generic.y = 280;
uiMain.dlgMessage2.generic.id = ID_MSGTEXT;
uiMain.dlgMessage2.generic.type = QMTYPE_ACTION;
uiMain.dlgMessage2.generic.flags = QMF_INACTIVE|QMF_DROPSHADOW|QMF_HIDDEN;
uiMain.dlgMessage2.generic.name = "any current game, OK to exit?";
uiMain.dlgMessage2.generic.x = 256;
uiMain.dlgMessage2.generic.y = 310;
uiMain.dlgMessage1.generic.width = 640;
uiMain.dlgMessage1.generic.height = 256;
uiMain.yes.generic.id = ID_YES;
uiMain.yes.generic.type = QMTYPE_BM_BUTTON;
@ -577,14 +577,13 @@ static void UI_Main_Init( void )
if ( bCustomGame )
UI_AddItem( &uiMain.menu, (void *)&uiMain.customGame );
UI_AddItem( &uiMain.menu, (void *)&uiMain.credits );
UI_AddItem( &uiMain.menu, (void *)&uiMain.previews );
UI_AddItem( &uiMain.menu, (void *)&uiMain.quit );
UI_AddItem( &uiMain.menu, (void *)&uiMain.minimizeBtn );
UI_AddItem( &uiMain.menu, (void *)&uiMain.quitButton );
UI_AddItem( &uiMain.menu, (void *)&uiMain.msgBox );
UI_AddItem( &uiMain.menu, (void *)&uiMain.quitMessage );
UI_AddItem( &uiMain.menu, (void *)&uiMain.dlgMessage1 );
UI_AddItem( &uiMain.menu, (void *)&uiMain.dlgMessage2 );
UI_AddItem( &uiMain.menu, (void *)&uiMain.no );
UI_AddItem( &uiMain.menu, (void *)&uiMain.yes );
}

View File

@ -67,6 +67,8 @@ static void UI_MultiPlayer_Callback( void *self, int event )
switch( item->id )
{
case ID_INTERNETGAMES:
UI_InternetGames_Menu();
break;
case ID_SPECTATEGAMES:
// UNDONE: write menus
break;
@ -116,7 +118,7 @@ static void UI_MultiPlayer_Init( void )
uiMultiPlayer.internetGames.generic.id = ID_INTERNETGAMES;
uiMultiPlayer.internetGames.generic.type = QMTYPE_BM_BUTTON;
uiMultiPlayer.internetGames.generic.flags = QMF_HIGHLIGHTIFFOCUS|QMF_DROPSHADOW|QMF_NOTIFY|QMF_GRAYED;
uiMultiPlayer.internetGames.generic.flags = QMF_HIGHLIGHTIFFOCUS|QMF_DROPSHADOW|QMF_NOTIFY;
uiMultiPlayer.internetGames.generic.x = 72;
uiMultiPlayer.internetGames.generic.y = 230;
uiMultiPlayer.internetGames.generic.name = "Internet games";

View File

@ -23,6 +23,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "utils.h"
#include "keydefs.h"
#include "menu_btnsbmp_table.h"
#include "menu_strings.h"
#define ART_BANNER "gfx/shell/head_newgame"
@ -54,7 +55,6 @@ typedef struct
// newgame prompt dialog
menuAction_s msgBox;
menuAction_s dlgMessage1;
menuAction_s dlgMessage2;
menuPicButton_s yes;
menuPicButton_s no;
@ -103,7 +103,6 @@ static void UI_PromptDialog( float skill )
uiNewGame.msgBox.generic.flags ^= QMF_HIDDEN;
uiNewGame.dlgMessage1.generic.flags ^= QMF_HIDDEN;
uiNewGame.dlgMessage2.generic.flags ^= QMF_HIDDEN;
uiNewGame.no.generic.flags ^= QMF_HIDDEN;
uiNewGame.yes.generic.flags ^= QMF_HIDDEN;
@ -139,14 +138,14 @@ static void UI_NewGame_Callback( void *self, int event )
switch( item->id )
{
case ID_EASY:
UI_PromptDialog( 0.0f );
break;
case ID_MEDIUM:
UI_PromptDialog( 1.0f );
break;
case ID_DIFFICULT:
case ID_MEDIUM:
UI_PromptDialog( 2.0f );
break;
case ID_DIFFICULT:
UI_PromptDialog( 3.0f );
break;
case ID_CANCEL:
UI_PopMenu();
break;
@ -154,7 +153,7 @@ static void UI_NewGame_Callback( void *self, int event )
UI_NewGame_StartGame( uiNewGame.skill );
break;
case ID_NO:
UI_PromptDialog( 0.0f ); // clear skill
UI_PromptDialog( 1.0f ); // clear skill
break;
}
}
@ -205,7 +204,7 @@ static void UI_NewGame_Init( void )
uiNewGame.easy.generic.type = QMTYPE_BM_BUTTON;
uiNewGame.easy.generic.flags = QMF_HIGHLIGHTIFFOCUS|QMF_DROPSHADOW|QMF_NOTIFY;
uiNewGame.easy.generic.name = "Easy";
uiNewGame.easy.generic.statusText = "Play the game on the 'easy' skill setting";
uiNewGame.easy.generic.statusText = MenuStrings[HINT_SKILL_EASY];
uiNewGame.easy.generic.x = 72;
uiNewGame.easy.generic.y = 230;
uiNewGame.easy.generic.callback = UI_NewGame_Callback;
@ -216,7 +215,7 @@ static void UI_NewGame_Init( void )
uiNewGame.medium.generic.type = QMTYPE_BM_BUTTON;
uiNewGame.medium.generic.flags = QMF_HIGHLIGHTIFFOCUS|QMF_DROPSHADOW|QMF_NOTIFY;
uiNewGame.medium.generic.name = "Medium";
uiNewGame.medium.generic.statusText = "Play the game on the 'medium' skill setting";
uiNewGame.medium.generic.statusText = MenuStrings[HINT_SKILL_NORMAL];
uiNewGame.medium.generic.x = 72;
uiNewGame.medium.generic.y = 280;
uiNewGame.medium.generic.callback = UI_NewGame_Callback;
@ -227,7 +226,7 @@ static void UI_NewGame_Init( void )
uiNewGame.hard.generic.type = QMTYPE_BM_BUTTON;
uiNewGame.hard.generic.flags = QMF_HIGHLIGHTIFFOCUS|QMF_DROPSHADOW|QMF_NOTIFY;
uiNewGame.hard.generic.name = "Difficult";
uiNewGame.hard.generic.statusText = "Play the game on the 'difficult' skill setting";
uiNewGame.hard.generic.statusText = MenuStrings[HINT_SKILL_HARD];
uiNewGame.hard.generic.x = 72;
uiNewGame.hard.generic.y = 330;
uiNewGame.hard.generic.callback = UI_NewGame_Callback;
@ -256,17 +255,12 @@ static void UI_NewGame_Init( void )
uiNewGame.dlgMessage1.generic.id = ID_MSGTEXT;
uiNewGame.dlgMessage1.generic.type = QMTYPE_ACTION;
uiNewGame.dlgMessage1.generic.flags = QMF_INACTIVE|QMF_HIDDEN|QMF_DROPSHADOW;
uiNewGame.dlgMessage1.generic.name = "Starting a new game will exit";
uiNewGame.dlgMessage1.generic.x = 248;
uiNewGame.dlgMessage1.generic.flags = QMF_INACTIVE|QMF_HIDDEN|QMF_DROPSHADOW|QMF_CENTER_JUSTIFY;
uiNewGame.dlgMessage1.generic.name = MenuStrings[HINT_RESTART_GAME];
uiNewGame.dlgMessage1.generic.x = 192;
uiNewGame.dlgMessage1.generic.y = 280;
uiNewGame.dlgMessage2.generic.id = ID_MSGTEXT;
uiNewGame.dlgMessage2.generic.type = QMTYPE_ACTION;
uiNewGame.dlgMessage2.generic.flags = QMF_INACTIVE|QMF_HIDDEN|QMF_DROPSHADOW;
uiNewGame.dlgMessage2.generic.name = "any current game, OK to exit?";
uiNewGame.dlgMessage2.generic.x = 248;
uiNewGame.dlgMessage2.generic.y = 310;
uiNewGame.dlgMessage1.generic.width = 640;
uiNewGame.dlgMessage1.generic.height = 256;
uiNewGame.yes.generic.id = ID_YES;
uiNewGame.yes.generic.type = QMTYPE_BM_BUTTON;
@ -296,7 +290,6 @@ static void UI_NewGame_Init( void )
UI_AddItem( &uiNewGame.menu, (void *)&uiNewGame.cancel );
UI_AddItem( &uiNewGame.menu, (void *)&uiNewGame.msgBox );
UI_AddItem( &uiNewGame.menu, (void *)&uiNewGame.dlgMessage1 );
UI_AddItem( &uiNewGame.menu, (void *)&uiNewGame.dlgMessage2 );
UI_AddItem( &uiNewGame.menu, (void *)&uiNewGame.no );
UI_AddItem( &uiNewGame.menu, (void *)&uiNewGame.yes );
}

634
mainui/menu_strings.cpp Normal file
View File

@ -0,0 +1,634 @@
/*
menu_strings.cpp - custom menu strings
Copyright (C) 2011 Uncle Mike
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include "extdll.h"
#include "basemenu.h"
#include "utils.h"
#include "menu_strings.h"
char *MenuStrings[HINT_MAXSTRINGS] =
{
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 10
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 20
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 30
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 40
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 50
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 60
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 70
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 80
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 90
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 100
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 110
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 120
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 130
"",
"Display mode",
"",
"",
"",
"",
"",
"",
"",
"", // 140
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 150
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 160
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 170
"Reverse mouse",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 180
"",
"",
"",
"Mouse sensitivity",
"",
"",
"",
"Return to game.",
"Start a new game.",
"", // 190
"Load a previously saved game.",
"Load a saved game, save the current game.",
"Change game settings, configure controls",
"",
"",
"",
"",
"",
"",
"", // 200
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 210
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 220
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 230
"",
"",
"",
"Starting a Hazard Course will exit\nany current game, OK to exit?",
"", // filled in UI_LoadCustomStrings
"Are you sure you want to quit?",
"",
"",
"",
"Starting a new game will exit\nany current game, OK to exit?", // 240
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 250
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 260
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 270
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 280
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 290
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 300
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 310
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 320
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 330
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 340
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 350
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 360
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 370
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 380
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 390
"",
"",
"",
"",
"",
"",
"",
"",
"",
"Find more about Valve's product lineup", // 400
"",
"http://www.valvesoftware.com/projects.htm",
"",
"",
"",
"",
"",
"",
"",
"", // 410
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 420
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 430
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 440
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 450
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 460
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 470
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 480
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 490
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 500
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 510
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 520
"",
"",
"",
"",
"",
"",
"",
"",
"",
"Select a custom game", // 530
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 540
"",
"",
"",
"",
"",
"",
"",
"",
"",
"", // 550
};
void UI_InitAliasStrings( void )
{
char token[1024];
// some strings needs to be initialized here
sprintf( token, "Quit %s without\nsaving current game?", gMenu.m_gameinfo.title );
MenuStrings[HINT_QUIT_ACTIVE] = StringCopy( token );
sprintf( token, "Learn how to play %s", gMenu.m_gameinfo.title );
MenuStrings[HINT_HAZARD_COURSE] = StringCopy( token );
sprintf( token, "Play %s on the 'easy' skill setting", gMenu.m_gameinfo.title );
MenuStrings[HINT_SKILL_EASY] = StringCopy( token );
sprintf( token, "Play %s on the 'medium' skill setting", gMenu.m_gameinfo.title );
MenuStrings[HINT_SKILL_NORMAL] = StringCopy( token );
sprintf( token, "Play %s on the 'difficult' skill setting", gMenu.m_gameinfo.title );
MenuStrings[HINT_SKILL_HARD] = StringCopy( token );
sprintf( token, "Quit playing %s", gMenu.m_gameinfo.title );
MenuStrings[HINT_QUIT_BUTTON] = StringCopy( token );
sprintf( token, "Search for %s servers, configure character", gMenu.m_gameinfo.title );
MenuStrings[HINT_MULTIPLAYER] = StringCopy( token );
}
void UI_LoadCustomStrings( void )
{
char *afile = (char *)LOAD_FILE( "gfx/shell/strings.lst", NULL );
char *pfile = afile;
char token[1024];
int string_num;
UI_InitAliasStrings ();
if( !afile )
return;
while(( pfile = COM_ParseFile( pfile, token )) != NULL )
{
if( isdigit( token[0] ))
{
string_num = atoi( token );
// check for bad stiringnum
if( string_num < 0 ) continue;
if( string_num > ( HINT_MAXSTRINGS - 1 ))
continue;
}
else continue; // invalid declaration ?
// parse new string
pfile = COM_ParseFile( pfile, token );
MenuStrings[string_num] = StringCopy( token ); // replace default string with custom
}
FREE_FILE( afile );
}

39
mainui/menu_strings.h Normal file
View File

@ -0,0 +1,39 @@
/*
menu_strings.h - custom menu strings
Copyright (C) 2011 Uncle Mike
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#define HINT_DISPLAYMODE 132
#define HINT_REVERSE_MOUSE 171
#define HINT_MOUSE_SENSE 184
#define HINT_RESUME_GAME 188
#define HINT_NEWGAME 189
#define HINT_HAZARD_COURSE 190
#define HINT_LOADGAME 191
#define HINT_SAVELOADGAME 192
#define HINT_CONFIGURATION 193
#define HINT_QUIT_BUTTON 196
#define HINT_MULTIPLAYER 198
#define HINT_SKILL_EASY 200
#define HINT_SKILL_NORMAL 201
#define HINT_SKILL_HARD 202
#define HINT_RESTART_HZ 234
#define HINT_QUIT_ACTIVE 235
#define HINT_QUIT 236
#define HINT_RESTART_GAME 240
#define HINT_PREVIEWS_TEXT 400
#define HINT_PREVIEWS_CMD 402 // this buton will execute program or open HTML-window
#define HINT_CUSTOM_GAME 530
#define HINT_MAXSTRINGS 551 // 550 strings allowed
extern char *MenuStrings[HINT_MAXSTRINGS];

View File

@ -22,6 +22,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "basemenu.h"
#include "utils.h"
#include "menu_btnsbmp_table.h"
#include "menu_strings.h"
#define ART_BANNER "gfx/shell/head_vidmodes"
@ -54,6 +55,7 @@ static const char *uiVideoModes[] =
"1024 x 600 (wide)",
"1280 x 720 (wide)",
"1360 x 768 (wide)",
"1366 x 768 (wide)",
"1440 x 900 (wide)",
"1680 x 1050 (wide)",
"1920 x 1200 (wide)",
@ -216,7 +218,7 @@ static void UI_VidModes_Init( void )
uiVidModes.listCaption.generic.type = QMTYPE_BM_BUTTON;
uiVidModes.listCaption.generic.flags = QMF_INACTIVE|QMF_SMALLFONT;
uiVidModes.listCaption.generic.color = uiColorHelp;
uiVidModes.listCaption.generic.name = "Display mode";
uiVidModes.listCaption.generic.name = MenuStrings[HINT_DISPLAYMODE];
uiVidModes.listCaption.generic.x = 400;
uiVidModes.listCaption.generic.y = 270;

2553
mainui/menufont.H Normal file

File diff suppressed because it is too large Load Diff

View File

@ -97,6 +97,16 @@ void StringConcat( char *dst, const char *src, size_t size )
return;
}
char *StringCopy( const char *input )
{
if( !input ) return NULL;
char *out = (char *)MALLOC( strlen( input ) + 1 );
strcpy( out, input );
return out;
}
/*
============
COM_FileBase

View File

@ -117,5 +117,8 @@ extern int UI_FadeAlpha( int starttime, int endtime );
extern void StringConcat( char *dst, const char *src, size_t size ); // strncat safe prototype
extern char *Info_ValueForKey( const char *s, const char *key );
extern int KEY_GetKey( const char *binding ); // ripped out from engine
extern char *StringCopy( const char *input ); // copy string into new memory
extern void UI_LoadCustomStrings( void );
#endif//UTILS_H