Source SDK

Source SDK

Not enough ratings
Añadir mirillas "Ironsights" - Motor Source 2013
By Oitnemood
Este es un tutorial de cómo añadir ironsighting procedural y es una alternativa a una guía de valve obsoleta, porque ya no tiene parte del código.

Este enfoque está en red y permite controlar el cambio en el lado del servidor, lo que hace posible afectar a la lógica del juego (por ejemplo, añadir una dispersión de balas diferente) fácilmente. También tiene ángulos de trabajo y cambio de fov.
   
Award
Favorite
Favorited
Unfavorite
Weaponscript/Script del arma
Para empezar, añada variables de script para poder ajustar el desplazamiento de la mira a través del script del arma.

weapon_parse.h

Añade esto a los miembros-variables de FileWeaponInfo_t:

Vector vecIronsightPosOffset; QAngle angIronsightAngOffset; float flIronsightFOVOffset;

weapon_parse.cpp

Esto va a FileWeaponInfo_t::Parse:

KeyValues *pSights = pKeyValuesData->FindKey( "IronSight" ); if (pSights) { vecIronsightPosOffset.x = pSights->GetFloat( "forward", 0.0f ); vecIronsightPosOffset.y = pSights->GetFloat( "right", 0.0f ); vecIronsightPosOffset.z = pSights->GetFloat( "up", 0.0f ); angIronsightAngOffset[PITCH] = pSights->GetFloat( "pitch", 0.0f ); angIronsightAngOffset[YAW] = pSights->GetFloat( "yaw", 0.0f ); angIronsightAngOffset[ROLL] = pSights->GetFloat( "roll", 0.0f ); flIronsightFOVOffset = pSights->GetFloat( "fov", 0.0f ); } else { //note: you can set a bool here if you'd like to disable ironsights for weapons with no IronSight-key vecIronsightPosOffset = vec3_origin; angIronsightAngOffset.Init(); flIronsightFOVOffset = 0.0f; }

weapon_smg1.txt

Este es un ejemplo de cómo añadir los ajustes de la vista de "ironsight" a su script:
IronSight { "forward" "-10" "right" "-6.91" "up" "0.185" "roll" "-20" "fov" "-20" }
Obtención de las compensaciones
Ahora agrega funciones simples para obtener la información que parseamos de los scripts de las armas. Adicionalmente agrega ConVars para sobreescribir la información parseada para que podamos fácilmente hacer y ajustar nuevos ajustes de la mira a través de la consola.

Antes de eso, sin embargo, necesitamos incluir "c_baseplayer.h" ya que mucho de este código hace referencia a este archivo para el procesamiento del lado del cliente.

basecombatweapon_shared.cpp


Básicamente, debes incluir "c_baseplayer.h" dentro de las etiquetas #ifdef CLIENT_DLL, de lo contrario arrojará errores al intentar compilar.

#ifdef CLIENT_DLL #include "c_baseplayer.h" #endif

Deberías añadir eso justo debajo del:

#if defined ( TF_DLL ) | defined ( TF_CLIENT_DLL ) #include "tf_shareddefs.h" #endif

basecombatweapon_shared.h

Esto va a las funciones públicas:

Vector GetIronsightPositionOffset( void ) const; QAngle GetIronsightAngleOffset( void ) const; float GetIronsightFOVOffset( void ) const;

basecombatweapon_shared.cpp

Y luego añades las definiciones de las funciones: (donde va esto es causa de errores)

Vector CBaseCombatWeapon::GetIronsightPositionOffset( void ) const { if( viewmodel_adjust_enabled.GetBool() ) return Vector( viewmodel_adjust_forward.GetFloat(), viewmodel_adjust_right.GetFloat(), viewmodel_adjust_up.GetFloat() ); return GetWpnData().vecIronsightPosOffset; } QAngle CBaseCombatWeapon::GetIronsightAngleOffset( void ) const { if( viewmodel_adjust_enabled.GetBool() ) return QAngle( viewmodel_adjust_pitch.GetFloat(), viewmodel_adjust_yaw.GetFloat(), viewmodel_adjust_roll.GetFloat() ); return GetWpnData().angIronsightAngOffset; } float CBaseCombatWeapon::GetIronsightFOVOffset( void ) const { if( viewmodel_adjust_enabled.GetBool() ) return viewmodel_adjust_fov.GetFloat(); return GetWpnData().flIronsightFOVOffset; }

basecombatweapon_shared.cpp

Estos ConVar suelen ir detrás de los includes:

//declaraciones de callbacks utilizadas por viewmodel_adjust_enable y viewmodel_adjust_fov void vm_adjust_enable_callback( IConVar *pConVar, char const *pOldString, float flOldValue ); void vm_adjust_fov_callback( IConVar *pConVar, const char *pOldString, float flOldValue ); ConVar viewmodel_adjust_forward( "viewmodel_adjust_forward", "0", FCVAR_REPLICATED ); ConVar viewmodel_adjust_right( "viewmodel_adjust_right", "0", FCVAR_REPLICATED ); ConVar viewmodel_adjust_up( "viewmodel_adjust_up", "0", FCVAR_REPLICATED ); ConVar viewmodel_adjust_pitch( "viewmodel_adjust_pitch", "0", FCVAR_REPLICATED ); ConVar viewmodel_adjust_yaw( "viewmodel_adjust_yaw", "0", FCVAR_REPLICATED ); ConVar viewmodel_adjust_roll( "viewmodel_adjust_roll", "0", FCVAR_REPLICATED ); ConVar viewmodel_adjust_fov( "viewmodel_adjust_fov", "0", FCVAR_REPLICATED, "Note: this feature is not available during any kind of zoom", vm_adjust_fov_callback ); ConVar viewmodel_adjust_enabled( "viewmodel_adjust_enabled", "0", FCVAR_REPLICATED|FCVAR_CHEAT, "enabled viewmodel adjusting", vm_adjust_enable_callback );

E implementar las devoluciones de llamada.

void vm_adjust_enable_callback( IConVar *pConVar, char const *pOldString, float flOldValue ) { ConVarRef sv_cheats( "sv_cheats" ); if( !sv_cheats.IsValid() || sv_cheats.GetBool() ) return; ConVarRef var( pConVar ); if( var.GetBool() ) var.SetValue( "0" ); } void vm_adjust_fov_callback( IConVar *pConVar, char const *pOldString, float flOldValue ) { if( !viewmodel_adjust_enabled.GetBool() ) return; ConVarRef var( pConVar ); CBasePlayer *pPlayer = #ifdef GAME_DLL UTIL_GetCommandClient(); #else C_BasePlayer::GetLocalPlayer(); #endif if( !pPlayer ) return; if( !pPlayer->SetFOV( pPlayer, pPlayer->GetDefaultFOV() + var.GetFloat(), 0.1f ) ) { Warning( "Could not set FOV\n" ); var.SetValue( "0" ); } }
Añadir funciones de conmutación "toggle"
Por supuesto que también quieres poder usar las miras.

basecombatweapon_shared.h
Añade estas dos variables de red:
CNetworkVar( bool, m_bIsIronsighted ); CNetworkVar( float, m_flIronsightedTime );
basecombatweapon_shared.cpp
Haz una red, deja que se predigan y dales valores por defecto.

Constructor:
m_bIsIronsighted = false; m_flIronsightedTime = 0.0f;

Tabla de red (DT_BaseCombatWeapon):

SendPropBool( SENDINFO( m_bIsIronsighted ), SendPropFloat( SENDINFO( m_flIronsightedTime ),

RecvPropInt( RECVINFO( m_bIsIronsighted ), 0, RecvProxy_ToggleSights ), //nota: RecvPropBool es en realidad RecvPropInt (ver su implementación), pero necesitamos un proxy RecvPropFloat( RECVINFO( m_flIronsightedTime ) ),
Como hemos dicho, necesitamos un RecvProxy sobre esa variable. No sólo queremos que el booleano se actualice, también queremos que la ironsights cambie si se cambió sólo en el servidor.

Así que, aquí está el código para el proxy:
#ifdef CLIENT_DLL void RecvProxy_ToggleSights( const CRecvProxyData* pData, void* pStruct, void* pOut ) { CBaseCombatWeapon *pWeapon = (CBaseCombatWeapon*)pStruct; if( pData->m_Value.m_Int ) pWeapon->EnableIronsights(); else pWeapon->DisableIronsights(); } #endif
Tabla de predicción (CBaseCombatWeapon):
DEFINE_PRED_FIELD( m_bIsIronsighted, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ), DEFINE_PRED_FIELD( m_flIronsightedTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),

Si quieres que las variables se guarden en una partida guardada, añádelas también en la descripción de datos:
DEFINE_FIELD( m_bIsIronsighted, FIELD_BOOLEAN ), DEFINE_FIELD( m_flIronsightedTime, FIELD_FLOAT ),

basecombatweapon_shared.h

Ahora agrega los accesorios para las variables de la vista:

virtual bool HasIronsights( void ) { return true; } //default yes; override and return false for weapons with no ironsights (like weapon_crowbar) bool IsIronsighted( void ); void ToggleIronsights( void ); void EnableIronsights( void ); void DisableIronsights( void ); void SetIronsightTime( void );

basecombatweapon_shared.cpp

Y, por supuesto, definirlos:

bool CBaseCombatWeapon::IsIronsighted( void ) { return ( m_bIsIronsighted || viewmodel_adjust_enabled.GetBool() ); } void CBaseCombatWeapon::ToggleIronsights( void ) { if( m_bIsIronsighted ) DisableIronsights(); else EnableIronsights(); } void CBaseCombatWeapon::EnableIronsights( void ) { #ifdef CLIENT_DLL if( !prediction->IsFirstTimePredicted() ) return; #endif if( !HasIronsights() || m_bIsIronsighted ) return; CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); if( !pOwner ) return; if( pOwner->SetFOV( this, pOwner->GetDefaultFOV() + GetIronsightFOVOffset(), 1.0f ) ) //modify the last value to adjust how fast the fov is applied { m_bIsIronsighted = true; SetIronsightTime(); } } void CBaseCombatWeapon::DisableIronsights( void ) { #ifdef CLIENT_DLL if( !prediction->IsFirstTimePredicted() ) return; #endif if( !HasIronsights() || !m_bIsIronsighted ) return; CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); if( !pOwner ) return; if( pOwner->SetFOV( this, 0, 0.4f ) ) //modify the last value to adjust how fast the fov is applied { m_bIsIronsighted = false; SetIronsightTime(); } } void CBaseCombatWeapon::SetIronsightTime( void ) { m_flIronsightedTime = gpGlobals->curtime; }

El uso de la predicción requiere la inclusión de la cabecera prediction.h en el cliente.
Toggle-command
ConCommand

Probablemente quieras un comando para cambiar entre el modo normal y el modo de vista. Realmente no importa donde lo pongas, siempre y cuando incluyas las cabeceras de CBasePlayer y CBaseCombatWeapon y sea del lado del cliente.

#ifdef CLIENT_DLL void CC_ToggleIronSights( void ) { CBasePlayer* pPlayer = C_BasePlayer::GetLocalPlayer(); if( pPlayer == NULL ) return; CBaseCombatWeapon *pWeapon = pPlayer->GetActiveWeapon(); if( pWeapon == NULL ) return; pWeapon->ToggleIronsights(); engine->ServerCmd( "toggle_ironsight" ); //forward to server } static ConCommand toggle_ironsight("toggle_ironsight", CC_ToggleIronSights); #endif

player.cpp

Entonces en CBasePlayer::ClientCommand, añade esto antes de devolver false:

else if( stricmp( cmd, "toggle_ironsight" ) == 0 ) { CBaseCombatWeapon *pWeapon = GetActiveWeapon(); if( pWeapon != NULL ) pWeapon->ToggleIronsights(); return true; }
boton automático "ironsight-toggle"
basecombatweapon_shared.cpp

Añadir

DisableIronsights();

a los siguientes lugares:
  • bool CBaseCombatWeapon::Holster( CBaseCombatWeapon *pSwitchingTo ) for switching/holstering weapons
  • bool CBaseCombatWeapon::DefaultReload( int iClipSize1, int iClipSize2, int iActivity ) for reloading weapons
  • void CBaseCombatWeapon::Drop( const Vector &vecVelocity ) for weapon dropping

Nota: bool CBaseCombatWeapon::DefaultDeploy

Mucho mejor también escribir la función de Desactivar Vista "ironsight" de Despliegue. Esto ayudará con el cambio automático cuando toda la munición se ha utilizado.

basecombatweapon_shared.cpp

buscar bool CWeaponShotgun::StartReload( void )
y algunos estaban dentro de Add

DisableIronsights();

Ajustar el modelo de vista
Ok, ahora para el último paso queremos mover el viewmodel de acuerdo a los offsets.

Sin embargo, todavía hay algunos pequeños problemas que arreglar. Viewmodel-bob sigue activo mientras se mira con la plancha y el viewmodel se retrasa demasiado cuando se sale de la plancha y se rota la vista.

baseviewmodel_shared.cpp

void CBaseViewModel::CalcIronsights( Vector &pos, QAngle &ang ) { CBaseCombatWeapon *pWeapon = GetOwningWeapon(); if ( !pWeapon ) return; //get delta time for interpolation float delta = ( gpGlobals->curtime - pWeapon->m_flIronsightedTime ) * 2.5f; //modify this value to adjust how fast the interpolation is float exp = ( pWeapon->IsIronsighted() ) ? ( delta > 1.0f ) ? 1.0f : delta : //normal blending ( delta > 1.0f ) ? 0.0f : 1.0f - delta; //reverse interpolation if( exp <= 0.001f ) //no es totalmente irresponsable; ahorra rendimiento return; Vector newPos = pos; QAngle newAng = ang; Vector vForward, vRight, vUp, vOffset; AngleVectors( newAng, &vForward, &vRight, &vUp ); vOffset = pWeapon->GetIronsightPositionOffset(); newPos += vForward * vOffset.x; newPos += vRight * vOffset.y; newPos += vUp * vOffset.z; newAng += pWeapon->GetIronsightAngleOffset(); //fov is handled by CBaseCombatWeapon pos += ( newPos - pos ) * exp; ang += ( newAng - ang ) * exp; }

Utilice este código en CBaseViewModel::CalcViewModelView:

void CBaseViewModel::CalcViewModelView( CBasePlayer *owner, const Vector& eyePosition, const QAngle& eyeAngles ) { // NO HAY NADA QUE HACER: ¿Calcar esto en el servidor? Desactivado por ahora ya que parece innecesario tener esta información en el servidor #if defined( CLIENT_DLL ) QAngle vmangoriginal = eyeAngles; QAngle vmangles = eyeAngles; Vector vmorigin = eyePosition; CBaseCombatWeapon *pWeapon = m_hWeapon.Get(); /Permitir el retardo del arma //sólo si no está en modo de visión artificial if( pWeapon == NULL || !pWeapon->IsIronsighted() ) { if ( pWeapon != NULL ) { #if defined( CLIENT_DLL ) if ( !prediction->InPrediction() ) #endif { // añadir un bob específico para cada arma pWeapon->AddViewmodelBob( this, vmorigin, vmangles ); } } // Añadir un movimiento específico del modelo aunque no tenga un arma asociada (para el movimiento de la cabeza de los modelos sin mano) AddViewModelBob( owner, vmorigin, vmangles ); // Add lag CalcViewModelLag( vmorigin, vmangles, vmangoriginal ); #if defined( CLIENT_DLL ) if ( !prediction->InPrediction() ) { // Deja que el viewmodel se agite a un 10% de la amplitud de la vista del jugador vieweffects->ApplyShake( vmorigin, vmangles, 0.1 ); } #endif } CalcIronsights( vmorigin, vmangles ); SetLocalOrigin( vmorigin ); SetLocalAngles( vmangles ); #endif }

baseviewmodel_shared.h

No olvides declarar la nueva función:

void CalcIronsights( Vector &pos, QAngle &ang );

Arreglo para un jugador (el modelo de arma no se queda en la mira)
En la mayoría de los casos el modelo del arma (modelo de la vista) no se mantiene en la posición definida cuando se activa la mira.

La solución es simple pero requiere algunos arreglos (mezcla del arma a la vista y mezcla hacia afuera).

Ir a basecombatweapon_shared.cpp

Cuando agregó dos funciones EnableIronsight y DisableIronsight. Cámbialas por:

void CBaseCombatWeapon::EnableIronsights( void ) { /* #ifdef CLIENT_DLL if( !prediction->IsFirstTimePredicted() ) return; #endif*/ if( !HasIronsights() || m_bIsIronsighted ) return; CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); if( !pOwner ) return; if( pOwner->SetFOV( this, pOwner->GetDefaultFOV() + GetIronsightFOVOffset(), 0.4f ) ) //modificar el último valor para ajustar la rapidez con la que se aplica la fov { m_bIsIronsighted = true; SetIronsightTime(); } } void CBaseCombatWeapon::DisableIronsights( void ) { /* #ifdef CLIENT_DLL if( !prediction->IsFirstTimePredicted() ) return; #endif*/ // No utilizamos la predicción en un solo jugador if( !HasIronsights() || !m_bIsIronsighted ) return; CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); if( !pOwner ) return; if( pOwner->SetFOV( this, 0, 0.2f ) ) //modify the last value to adjust how fast the fov is applied { m_bIsIronsighted = false; SetIronsightTime(); } }
Episodio 1 - Arreglo del motor
En el motor del Episodio 1, SetFOV es sólo del lado del servidor. Una solución sencilla sería establecer el FOV sólo en el servidor, pero aquí hay una implementación del lado del cliente, parcialmente derivada del código del OB.

m_hZoomOwner

Configurar m_hZoomOwner para la red en player.cpp/.h utilizando un CNetworkHandle y añadiéndolo a la tabla de envío.

En c_baseplayer.cpp/.h, añade el m_hZoomOwner a la declaración de clase, para que pueda ser recibido. Establécelo a NULL en el constructor y añádelo a la tabla de predicción y recv.

También añade una función SetFOV a C_BasePlayer y defínela así:

bool C_BasePlayer::SetFOV( C_BaseEntity *pRequester, int FOV, float zoomRate ) { //NOTENOTE: You MUST specify who is requesting the zoom change assert( pRequester != NULL ); if ( pRequester == NULL ) return false; if( ( m_hZoomOwner.Get() != NULL ) && ( m_hZoomOwner.Get() != pRequester ) ) return false; else { //FIXME: Maybe do this is as an accessor instead if ( FOV == 0 ) { m_hZoomOwner = NULL; } else { m_hZoomOwner = pRequester; } } m_iFOV = FOV; m_Local.m_flFOVRate = zoomRate; return true; }

ConVarRef

ConVarRef no existe en el motor del Episodio 1. Puedes utilizar la biblioteca OB tier1 (nota: no se ha probado; puede que no funcione) o, en lugar de ConVarRef("sv_cheats" ), poner extern ConVar sv_cheats; en la parte superior de cbasecombatweapon_shared.cpp y utilizar el operador de miembro por puntero (->) en lugar del operador de miembro (.).

Para los ConVar en las devoluciones de llamada, simplemente usa los ConVar directamente o convierte el IConVar en un ConVar.
Ajuste de la dispersión de la bala
Abre el CPP del arma que quieres que se vea afectada por las miras. Para ello he utilizado la SMG.

weapon_smg1.cpp

Busca la función GetBulletSpread. Se verá algo así:

virtual const Vector& GetBulletSpread( void ) { static const Vector cone = VECTOR_CONE_5DEGREES; return cone; }

Puede, por ejemplo, sustituirlo por lo siguiente:

virtual const Vector& GetBulletSpread( void ) { if ( m_bIsIronsighted ) { static const Vector cone = VECTOR_CONE_1DEGREES; return cone; } else { static const Vector cone = VECTOR_CONE_5DEGREES; return cone; } }

Aquí hay una imagen de ejemplo con un cono de 5 grados a la izquierda, y un cono de 1 grado a la derecha.
Imagen - click para verla[i132.photobucket.com]
Añadir enlace de teclas (keybind)
Por último, pero no por ello menos importante, añade la tecla al menú Opciones/Teclado:

kb_act.lst

Probablemente en algún lugar de "#Valve_Combat_Title":

"toggle_ironsight" "#MOD_Toggle_Ironsight"

Y no olvides añadir este #MOD_Toggle_Ironsight a tu resource/MOD_english.txt (y a los otros idiomas).
Sonidos Ironsight
basecombatweapon_shared.cpp
Añadir
pPlayer->EmitSound( "WWIPlayer.IronSightIn" );

a EnableIronsights y
pPlayer->EmitSound( "WWIPlayer.IronSightOut" );

a DisableIronsights.
Cambia las cadenas WWIPlayer.IronSightin y WWIPlayer.IronSightOut por tus propios sonidos y pre-cálcalos.
Evitar apuntar durante la recarga
Mientras se recarga, se puede seguir utilizando la mira telescópica, incluso si se escribe DisableIronsights (); en bool CBaseCombatWeapon::
DefaultReload
Por supuesto, se apagará, pero si vuelves a pulsar el botón de apuntar, Ironsight empezará a funcionar y la animación de recarga se reproducirá desde la vista de "apuntar". No es muy bonito.

Para evitarlo, en:
basecombatweapon_shared.cpp

Encontrar la función:CBaseCombatWeapon::ToggleIronsights(void)

Y sustituirlo completamente por:
void CBaseCombatWeapon::ToggleIronsights(void) //No es posible utilizar la mira mientras se recarga { if (m_bInReload == true) { DisableIronsights(); } else { if (m_bIsIronsighted) DisableIronsights(); else EnableIronsights(); } }