Learning BASIC next school year

bscly.

[code]//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include “cbase.h”
#include “gamemovement.h”
#include “in_buttons.h”
#include <stdarg.h>
#include “movevars_shared.h”
#include “engine/IEngineTrace.h”
#include “SoundEmitterSystem/isoundemittersystembase.h”
#include “decals.h”
#include “coordsize.h”
#include “rumble_shared.h”

#if defined(HL2_DLL) || defined(HL2_CLIENT_DLL)
#include “hl_movedata.h”
#endif

// memdbgon must be the last include file in a .cpp file!!!
#include “tier0/memdbgon.h”

#define STOP_EPSILON 0.1
#define MAX_CLIP_PLANES 5

#include “filesystem.h”
#include <stdarg.h>

extern IFileSystem *filesystem;

#ifndef CLIENT_DLL
#include “env_player_surface_trigger.h”
static ConVar dispcoll_drawplane( “dispcoll_drawplane”, “0” );
#endif

// tickcount currently isn’t set during prediction, although gpGlobals->curtime and
// gpGlobals->frametime are. We should probably set tickcount (to player->m_nTickBase),
// but we’re REALLY close to shipping, so we can change that later and people can use
// player->CurrentCommandNumber() in the meantime.
#define tickcount USE_PLAYER_CURRENT_COMMAND_NUMBER__INSTEAD_OF_TICKCOUNT

#if defined( HL2_DLL )
ConVar xc_uncrouch_on_jump( “xc_uncrouch_on_jump”, “1”, FCVAR_ARCHIVE, “Uncrouch when jump occurs” );
#endif

#if defined( HL2_DLL ) || defined( HL2_CLIENT_DLL )
ConVar player_limit_jump_speed( “player_limit_jump_speed”, “1”, FCVAR_REPLICATED );
#endif

ConVar cl_enable_bunnyhop( “cl_enable_bunnyhop”, “0”, FCVAR_ARCHIVE );

// option_duck_method is a carrier convar. Its sole purpose is to serve an easy-to-flip
// convar which is ONLY set by the X360 controller menu to tell us which way to bind the
// duck controls. Its value is meaningless anytime we don’t have the options window open.
ConVar option_duck_method(“option_duck_method”, “1”, FCVAR_REPLICATED|FCVAR_ARCHIVE );// 0 = HOLD to duck, 1 = Duck is a toggle

// [MD] I’ll remove this eventually. For now, I want the ability to A/B the optimizations.
bool g_bMovementOptimizations = true;

// Roughly how often we want to update the info about the ground surface we’re on.
// We don’t need to do this very often.
#define CATEGORIZE_GROUND_SURFACE_INTERVAL 0.3f
#define CATEGORIZE_GROUND_SURFACE_TICK_INTERVAL ( (int)( CATEGORIZE_GROUND_SURFACE_INTERVAL / TICK_INTERVAL ) )

#define CHECK_STUCK_INTERVAL 1.0f
#define CHECK_STUCK_TICK_INTERVAL ( (int)( CHECK_STUCK_INTERVAL / TICK_INTERVAL ) )

#define CHECK_STUCK_INTERVAL_SP 0.2f
#define CHECK_STUCK_TICK_INTERVAL_SP ( (int)( CHECK_STUCK_INTERVAL_SP / TICK_INTERVAL ) )

#define CHECK_LADDER_INTERVAL 0.2f
#define CHECK_LADDER_TICK_INTERVAL ( (int)( CHECK_LADDER_INTERVAL / TICK_INTERVAL ) )

#define NUM_CROUCH_HINTS 3

extern IGameMovement *g_pGameMovement;

#if defined( PLAYER_GETTING_STUCK_TESTING )

// If you ever get stuck walking around, then you can run this code to find the code which would leave the player in a bad spot
void CMoveData::SetAbsOrigin( const Vector &vec )
{
CGameMovement *gm = dynamic_cast< CGameMovement * >( g_pGameMovement );
if ( gm && gm->GetMoveData() &&
gm->player &&
gm->player->entindex() == 1 &&
gm->player->GetMoveType() == MOVETYPE_WALK )
{
trace_t pm;
gm->TracePlayerBBox( vec, vec, gm->PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, pm );
if ( pm.startsolid || pm.allsolid || pm.fraction != 1.0f )
{
Msg( “Player will become stuck at %f %f %f\n”, VectorExpand( vec ) );
}
}

m_vecAbsOrigin = vec;

}

#endif

// See shareddefs.h
#if PREDICTION_ERROR_CHECK_LEVEL > 0

static ConVar diffcheck( “diffcheck”, “0”, FCVAR_REPLICATED );

class IDiffMgr
{
public:
virtual void StartCommand( bool bServer, int nCommandNumber ) = 0;
virtual void AddToDiff( bool bServer, int nCommandNumber, char const *string ) = 0;
virtual void Validate( bool bServer, int nCommandNumber ) = 0;
};

static IDiffMgr *g_pDiffMgr = NULL;

class CDiffStr
{
public:
CDiffStr()
{
m_str[ 0 ] = 0;
}

CDiffStr( char const *str )
{
    Q_strncpy( m_str, str, sizeof( m_str ) );
}

CDiffStr( const CDiffStr &src )
{
    Q_strncpy( m_str, src.m_str, sizeof( m_str ) );
}

char const *String()
{
    return m_str;
}

private:

char m_str[ 128 ];

};

// Per tick data
class CDiffInfo
{
public:
CDiffInfo() : m_nCommandNumber( 0 ) {}
CDiffInfo( const CDiffInfo& src )
{
m_nCommandNumber = src.m_nCommandNumber;
for ( int i = 0; i < src.m_Lines.Count(); ++i )
{
m_Lines.AddToTail( src.m_Lines[ i ] );
}
}

static bool Less( const CDiffInfo& lhs, const CDiffInfo& rhs )
{
    return lhs.m_nCommandNumber < rhs.m_nCommandNumber;
}
int                            m_nCommandNumber;
CUtlVector< CDiffStr >    m_Lines;
bool                        m_bChecked;

};

class CDiffManager : public IDiffMgr
{
public:
CDiffManager() :
m_Client( 0, 0, CDiffInfo::Less ),
m_Server( 0, 0, CDiffInfo::Less ),
m_flLastSpew( -1.0f )
{
g_pDiffMgr = this;
}

virtual void StartCommand( bool bServer, int nCommandNumber )
{

#if defined( CLIENT_DLL )

    if ( !diffcheck.GetInt() )
        return;

    g_pDiffMgr = reinterpret_cast< IDiffMgr * >( diffcheck.GetInt() );
    g_pDiffMgr->StartCommand( bServer, nCommandNumber );
    return;

#endif

    // Msg( "%s Startcommand %d\n", bServer ? "sv" : "cl", nCommandNumber );

    diffcheck.SetValue( reinterpret_cast< int >( this ) );

    Assert( CBaseEntity::IsServer() );

    CUtlRBTree< CDiffInfo, int >& rb = bServer ? m_Server : m_Client;

    CDiffInfo search;
    search.m_nCommandNumber = nCommandNumber;
    int idx = rb.Find( search );
    if ( idx == rb.InvalidIndex() )
    {
        idx = rb.Insert( search );
    }

    CDiffInfo *slot = &rb[ idx ];
    slot->m_Lines.RemoveAll();
}

virtual void AddToDiff( bool bServer, int nCommandNumber, char const *string )
{

#if defined( CLIENT_DLL )

    if ( !diffcheck.GetInt() )
        return;

    g_pDiffMgr = reinterpret_cast< IDiffMgr * >( diffcheck.GetInt() );
    g_pDiffMgr->AddToDiff( bServer, nCommandNumber, string );
    return;

#endif
Assert( CBaseEntity::IsServer() );

    // Msg( "%s Add %d %s\n", bServer ? "sv" : "cl", nCommandNumber, string );

    CUtlRBTree< CDiffInfo, int >& rb = bServer ? m_Server : m_Client;

    CDiffInfo search;
    search.m_nCommandNumber = nCommandNumber;
    int idx = rb.Find( search );
    if ( idx == rb.InvalidIndex() )
    {
        Assert( 0 );
        idx = rb.Insert( search );
    }

    CDiffInfo *slot = &rb[ idx ];
    CDiffStr line( string );
    slot->m_Lines.AddToTail( line );
}

enum EMismatched
{
    DIFFCHECK_NOTREADY = 0,
    DIFFCHECK_MATCHED,
    DIFFCHECK_DIFFERS
};

bool ClientRecordExists( int cmd )
{
    CDiffInfo clsearch;
    clsearch.m_nCommandNumber = cmd;
    int clidx = m_Client.Find( clsearch );
    return m_Client.IsValidIndex( clidx );
}

EMismatched IsMismatched( int svidx )
{
    CDiffInfo *serverslot = &m_Server[ svidx ];

    // Now find the client version of this one
    CDiffInfo clsearch;
    clsearch.m_nCommandNumber = serverslot->m_nCommandNumber;
    int clidx = m_Client.Find( clsearch );
    if ( clidx == m_Client.InvalidIndex() )
        return DIFFCHECK_NOTREADY;

    // Now compare them
    CDiffInfo *clientslot = &m_Client[ clidx ];

    bool bSpew = false;
    if ( serverslot->m_Lines.Count() !=
        clientslot->m_Lines.Count() )
    {
        return DIFFCHECK_DIFFERS;
    }

    int maxSlot = max( serverslot->m_Lines.Count(), clientslot->m_Lines.Count() );
    if ( !bSpew )
    {
        for ( int i = 0; i < maxSlot; ++i )
        {
            CDiffStr *sv = NULL;
            CDiffStr *cl = NULL;
            if ( i < serverslot->m_Lines.Count() )
            {
                sv = &serverslot->m_Lines[ i ];
            }
            if ( i < clientslot->m_Lines.Count() )
            {
                cl = &clientslot->m_Lines[ i ];
            }

            if ( Q_stricmp( sv ? sv->String() : "(missing)", cl ? cl->String() : "(missing)" ) )
            {
                return DIFFCHECK_DIFFERS;
            }
        }
    }

    return DIFFCHECK_MATCHED;
}

virtual void Validate( bool bServer, int nCommandNumber )
{

#if defined( CLIENT_DLL )

    if ( !diffcheck.GetInt() )
        return;

    g_pDiffMgr = reinterpret_cast< IDiffMgr * >( diffcheck.GetInt() );
    g_pDiffMgr->Validate( bServer, nCommandNumber );
    return;

#endif
Assert( CBaseEntity::IsServer() );

    // Only do this on the client
    if ( !bServer )
        return;

    // Find the last server command number
    if ( m_Server.Count() <= 0 )
        return;

    int svidx = m_Server.LastInorder();
    EMismatched eMisMatched = IsMismatched( svidx );
    if ( eMisMatched == DIFFCHECK_NOTREADY )
    {
        return;
    }

    if ( eMisMatched == DIFFCHECK_DIFFERS )
    {
        CUtlVector< int > vecPrev;

        int nCur = svidx;
        do
        {
            int prev = m_Server.PrevInorder( nCur );
            if ( m_Server.IsValidIndex( prev ) &&
                ClientRecordExists( m_Server[ prev ].m_nCommandNumber ) )
            {
                //SpewRecords( "prev", prev );
                vecPrev.AddToHead( prev );
            }
            else
            {
                break;
            }

            nCur = prev;
        } while ( vecPrev.Count() < 10 );

        Msg( "-----\n" );

        for ( int p = 0; p < vecPrev.Count(); ++p )
        {
            SpewRecords( "prev", vecPrev[ p ] );
        }

        SpewRecords( "bad ", svidx );
    }
}

void SpewRecords( char const *prefix, int svidx )
{
    CDiffInfo *serverslot = &m_Server[ svidx ];

    // Now find the client version of this one
    CDiffInfo clsearch;
    clsearch.m_nCommandNumber = serverslot->m_nCommandNumber;
    int clidx = m_Client.Find( clsearch );
    if ( clidx == m_Client.InvalidIndex() )
        return;

    // Now compare them
    CDiffInfo *clientslot = &m_Client[ clidx ];

    int maxSlot = max( serverslot->m_Lines.Count(), clientslot->m_Lines.Count() );

    for ( int i = 0; i < maxSlot; ++i )
    {
        char const *sv = "(missing)";
        char const *cl = "(missing)";

        if ( i < serverslot->m_Lines.Count() )
        {
            sv = serverslot->m_Lines[ i ].String();
        }
        if ( i < clientslot->m_Lines.Count() )
        {
            cl = clientslot->m_Lines[ i ].String();
        }

        bool bDiffers = Q_stricmp( sv, cl ) ? true : false;

        Msg( "%s%s%d:  sv[%50.50s] cl[%50.50s]\n",
            prefix,
            bDiffers ? "+++" : "   ",
            serverslot->m_nCommandNumber,
            sv,
            cl );
    }
}

private:

CUtlRBTree< CDiffInfo, int >    m_Server;
CUtlRBTree< CDiffInfo, int >    m_Client;
float                            m_flLastSpew;

};

static CDiffManager g_DiffMgr;

void DiffPrint( bool bServer, int nCommandNumber, char const *fmt, … )
{
// Only track stuff for local player
CBasePlayer *pPlayer = CBaseEntity::GetPredictionPlayer();
if ( pPlayer && pPlayer->entindex() != 1 )
{
return;
}

va_list        argptr;
char        string[1024];
va_start (argptr,fmt);
int len = Q_vsnprintf(string, sizeof( string ), fmt,argptr);
va_end (argptr);

if ( g_pDiffMgr )
{
    // Strip any \n at the end that the user accidently put int
    if ( len > 0 && string[ len -1 ] == '\n' )
    {
        string[ len - 1 ] = 0;
    }
    
    g_pDiffMgr->AddToDiff( bServer, nCommandNumber, string );
}

}

void _CheckV( int tick, char const *ctx, const Vector &vel )
{
DiffPrint( CBaseEntity::IsServer(), tick, “%20.20s %f %f %f”, ctx, vel.x, vel.y, vel.z );
}

#define CheckV( tick, ctx, vel ) _CheckV( tick, ctx, vel );

static void StartCommand( bool bServer, int nCommandNumber )
{
// Only track stuff for local player
CBasePlayer *pPlayer = CBaseEntity::GetPredictionPlayer();
if ( pPlayer && pPlayer->entindex() != 1 )
{
return;
}

if ( g_pDiffMgr )
{
    g_pDiffMgr->StartCommand( bServer, nCommandNumber );
}

}

static void Validate( bool bServer, int nCommandNumber )
{
// Only track stuff for local player
CBasePlayer *pPlayer = CBaseEntity::GetPredictionPlayer();
if ( pPlayer && pPlayer->entindex() != 1 )
{
return;
}

if ( g_pDiffMgr )
{
    g_pDiffMgr->Validate( bServer, nCommandNumber );
}

}

void CGameMovement: :smiley: iffPrint( char const *fmt, … )
{
if ( !player )
return;

va_list        argptr;
char        string[1024];
va_start (argptr,fmt);
Q_vsnprintf(string, sizeof( string ), fmt,argptr);
va_end (argptr);

: :D iffPrint( CBaseEntity::IsServer(), player->CurrentCommandNumber(), "%s", string );

}

#else
static void DiffPrint( bool bServer, int nCommandNumber, char const *fmt, … )
{
// Nothing
}
static void StartCommand( bool bServer, int nCommandNumber )
{
}

static void Validate( bool bServer, int nCommandNumber )
{
}

#define CheckV( tick, ctx, vel )

void CGameMovement: :smiley: iffPrint( char const *fmt, … )
{
}

#endif // !PREDICTION_ERROR_CHECK_LEVEL

#ifndef _XBOX
void COM_Log( char *pszFile, char *fmt, …)
{
va_list argptr;
char string[1024];
FileHandle_t fp;
char *pfilename;

if ( !pszFile )
{
    pfilename = "hllog.txt";
}
else
{
    pfilename = pszFile;
}
va_start (argptr,fmt);
Q_vsnprintf(string, sizeof( string ), fmt,argptr);
va_end (argptr);

fp = filesystem->Open( pfilename, "a+t");
if (fp)
{
    filesystem->FPrintf(fp, "%s", string);
    filesystem->Close(fp);
}

}
#endif

#ifndef CLIENT_DLL
//-----------------------------------------------------------------------------
// Purpose: Debug - draw the displacement collision plane.
//-----------------------------------------------------------------------------
void DrawDispCollPlane( CBaseTrace *pTrace )
{
float flLength = 30.0f;

// Create a basis, based on the impact normal.
int nMajorAxis = 0;
Vector vecBasisU, vecBasisV, vecNormal;
vecNormal = pTrace->plane.normal;
float flAxisValue = vecNormal[0];
if ( fabs( vecNormal[1] ) > fabs( flAxisValue ) ) { nMajorAxis = 1; flAxisValue = vecNormal[1]; }
if ( fabs( vecNormal[2] ) > fabs( flAxisValue ) ) { nMajorAxis = 2; }
if ( ( nMajorAxis == 1 ) || ( nMajorAxis == 2 ) )
{
    vecBasisU.Init( 1.0f, 0.0f, 0.0f );
}
else
{
    vecBasisU.Init( 0.0f, 1.0f, 0.0f );
}

vecBasisV = vecNormal.Cross( vecBasisU );
VectorNormalize( vecBasisV );

vecBasisU = vecBasisV.Cross( vecNormal );
VectorNormalize( vecBasisU );

// Create the impact point.  Push off the surface a bit.
Vector vecImpactPoint = pTrace->startpos + pTrace->fraction * ( pTrace->endpos - pTrace->startpos );
vecImpactPoint += vecNormal;

// Generate a quad to represent the plane.
Vector vecPlanePoints[4];
vecPlanePoints[0] = vecImpactPoint + ( vecBasisU * -flLength ) + ( vecBasisV * -flLength );
vecPlanePoints[1] = vecImpactPoint + ( vecBasisU * -flLength ) + ( vecBasisV * flLength );
vecPlanePoints[2] = vecImpactPoint + ( vecBasisU * flLength ) + ( vecBasisV * flLength );
vecPlanePoints[3] = vecImpactPoint + ( vecBasisU * flLength ) + ( vecBasisV * -flLength );

#if 0
// Test facing.
Vector vecEdges[2];
vecEdges[0] = vecPlanePoints[1] - vecPlanePoints[0];
vecEdges[1] = vecPlanePoints[2] - vecPlanePoints[0];
Vector vecCross = vecEdges[0].Cross( vecEdges[1] );
if ( vecCross.Dot( vecNormal ) < 0.0f )
{
// Reverse winding.
}
#endif

// Draw the plane.
NDebugOverlay::Triangle( vecPlanePoints[0], vecPlanePoints[1], vecPlanePoints[2], 125, 125, 125, 125, false, 5.0f );
NDebugOverlay::Triangle( vecPlanePoints[0], vecPlanePoints[2], vecPlanePoints[3], 125, 125, 125, 125, false, 5.0f );

NDebugOverlay::Line( vecPlanePoints[0], vecPlanePoints[1], 255, 255, 255, false, 5.0f );
NDebugOverlay::Line( vecPlanePoints[1], vecPlanePoints[2], 255, 255, 255, false, 5.0f );
NDebugOverlay::Line( vecPlanePoints[2], vecPlanePoints[3], 255, 255, 255, false, 5.0f );
NDebugOverlay::Line( vecPlanePoints[3], vecPlanePoints[0], 255, 255, 255, false, 5.0f );

// Draw the normal.
NDebugOverlay::Line( vecImpactPoint, vecImpactPoint + ( vecNormal * flLength ), 255, 0, 0, false, 5.0f );

}
#endif

//-----------------------------------------------------------------------------
// Purpose: Constructs GameMovement interface
//-----------------------------------------------------------------------------
CGameMovement::CGameMovement( void )
{
m_nOldWaterLevel = WL_NotInWater;
m_flWaterEntryTime = 0;
m_nOnLadder = 0;

mv                    = NULL;

memset( m_flStuckCheckTime, 0, sizeof(m_flStuckCheckTime) );

}

WHY DID YOU READ ALL THIS!?!?!?!?!?!
Anyhow, its part of the HL2 movement engine.[/code]

at least Basic is much easier than C++ which is pain in the ass :stuck_out_tongue:

How is C++ a pain in the ass? I disagree whole-heartedly, my friend.

I dunno, right now I have a program that is segfaulting for no apparent reason. It’s making me want to hit ducklings with a meat mallet.

Founded in 2004, Leakfree.org became one of the first online communities dedicated to Valve’s Source engine development. It is more famously known for the formation of Black Mesa: Source under the 'Leakfree Modification Team' handle in September 2004.